Compare commits
9 Commits
kurt-speed
...
parser-in-
Author | SHA1 | Date | |
---|---|---|---|
ae068b7ed6 | |||
5bdc899831 | |||
e0e6acf231 | |||
668bc863df | |||
59ce602d6e | |||
8ce819b28f | |||
ed5e276377 | |||
8549bd9486 | |||
56f9078812 |
@ -1,6 +1,5 @@
|
|||||||
VITE_KC_API_WS_MODELING_URL=wss://api.dev.zoo.dev/ws/modeling/commands
|
VITE_KC_API_WS_MODELING_URL=wss://api.dev.zoo.dev/ws/modeling/commands
|
||||||
VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
|
VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
|
||||||
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
|
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
|
||||||
VITE_KC_WASM_OVERRIDE_URL=""
|
|
||||||
VITE_KC_SKIP_AUTH=false
|
VITE_KC_SKIP_AUTH=false
|
||||||
VITE_KC_CONNECTION_TIMEOUT_MS=5000
|
VITE_KC_CONNECTION_TIMEOUT_MS=5000
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
VITE_KC_API_WS_MODELING_URL=wss://api.zoo.dev/ws/modeling/commands
|
VITE_KC_API_WS_MODELING_URL=wss://api.zoo.dev/ws/modeling/commands
|
||||||
VITE_KC_API_BASE_URL=https://api.zoo.dev
|
VITE_KC_API_BASE_URL=https://api.zoo.dev
|
||||||
VITE_KC_SITE_BASE_URL=https://zoo.dev
|
VITE_KC_SITE_BASE_URL=https://zoo.dev
|
||||||
VITE_KC_WASM_OVERRIDE_URL=""
|
|
||||||
VITE_KC_SKIP_AUTH=false
|
VITE_KC_SKIP_AUTH=false
|
||||||
VITE_KC_CONNECTION_TIMEOUT_MS=15000
|
VITE_KC_CONNECTION_TIMEOUT_MS=15000
|
||||||
|
35
.github/workflows/playwright.yml
vendored
@ -14,31 +14,9 @@ permissions:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-wasm-lib-changes:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
url: ${{ steps.set-output.outputs.url }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
fetch-depth: 0 # Fetches all history for all branches and tags
|
|
||||||
|
|
||||||
- name: Check for changes in src/wasm-lib
|
|
||||||
id: set-output
|
|
||||||
run: |
|
|
||||||
if git diff --quiet origin/main...HEAD -- src/wasm-lib; then
|
|
||||||
echo "url=https://app.zoo.dev" >> $GITHUB_OUTPUT
|
|
||||||
echo "No changes detected in src/wasm-lib"
|
|
||||||
else
|
|
||||||
echo "Changes detected in src/wasm-lib"
|
|
||||||
echo "url=" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
playwright-ubuntu:
|
playwright-ubuntu:
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
runs-on: ubuntu-latest-8-cores
|
runs-on: ubuntu-latest-8-cores
|
||||||
needs: check-wasm-lib-changes
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
@ -50,19 +28,13 @@ jobs:
|
|||||||
run: yarn
|
run: yarn
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
run: yarn playwright install --with-deps
|
run: yarn playwright install --with-deps
|
||||||
- name: Print WASM Lib Changes URL
|
|
||||||
run: |
|
|
||||||
echo "WASM Lib Changes URL: ${{ needs.check-wasm-lib-changes.outputs.url }}"
|
|
||||||
- name: Setup Rust
|
- name: Setup Rust
|
||||||
if: ${{ needs.check-wasm-lib-changes.outputs.url }} == ''
|
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
- name: Cache wasm
|
- name: Cache wasm
|
||||||
if: ${{ needs.check-wasm-lib-changes.outputs.url }} == ''
|
|
||||||
uses: Swatinem/rust-cache@v2
|
uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
workspaces: './src/wasm-lib'
|
workspaces: './src/wasm-lib'
|
||||||
- name: build wasm
|
- name: build wasm
|
||||||
if: ${{ needs.check-wasm-lib-changes.outputs.url }} == ''
|
|
||||||
run: yarn build:wasm
|
run: yarn build:wasm
|
||||||
- name: build web
|
- name: build web
|
||||||
run: yarn build:local
|
run: yarn build:local
|
||||||
@ -72,7 +44,6 @@ jobs:
|
|||||||
CI: true
|
CI: true
|
||||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
|
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
|
||||||
WASM_OVERRIDE: ${{ steps.check-wasm-lib-changes.outputs.url }}
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
@ -108,7 +79,6 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
WASM_OVERRIDE: ${{ steps.check-wasm-lib-changes.outputs.url }}
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
@ -119,7 +89,6 @@ jobs:
|
|||||||
playwright-macos:
|
playwright-macos:
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
runs-on: macos-14
|
runs-on: macos-14
|
||||||
needs: check-wasm-lib-changes
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
@ -131,15 +100,12 @@ jobs:
|
|||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
run: yarn playwright install --with-deps
|
run: yarn playwright install --with-deps
|
||||||
- name: Setup Rust
|
- name: Setup Rust
|
||||||
if: needs.check-wasm-lib-changes.outputs.url == ''
|
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
- name: Cache wasm
|
- name: Cache wasm
|
||||||
if: needs.check-wasm-lib-changes.outputs.url == ''
|
|
||||||
uses: Swatinem/rust-cache@v2
|
uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
workspaces: './src/wasm-lib'
|
workspaces: './src/wasm-lib'
|
||||||
- name: build wasm
|
- name: build wasm
|
||||||
if: needs.check-wasm-lib-changes.outputs.url == ''
|
|
||||||
run: yarn build:wasm
|
run: yarn build:wasm
|
||||||
- name: build web
|
- name: build web
|
||||||
run: yarn build:local
|
run: yarn build:local
|
||||||
@ -150,7 +116,6 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
WASM_OVERRIDE: ${{ steps.check-wasm-lib-changes.outputs.url }}
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: ['@babel/preset-env'],
|
presets: ["@babel/preset-env"],
|
||||||
}
|
}
|
||||||
|
7550
docs/kcl/std.json
@ -104,7 +104,6 @@ test('Basic sketch', async ({ page }) => {
|
|||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
|> line([${commonPoints.num1}, 0], %)
|
|> line([${commonPoints.num1}, 0], %)
|
||||||
|> line([0, ${commonPoints.num1}], %)`)
|
|> line([0, ${commonPoints.num1}], %)`)
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
@ -329,7 +328,9 @@ test('if you write invalid kcl you get inlined errors', async ({ page }) => {
|
|||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('if your kcl gets an error from the engine it is inlined', async ({
|
/* Ignore this test for now since its causing engine to crash
|
||||||
|
*
|
||||||
|
* test('if your kcl gets an error from the engine it is inlined', async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
const u = getUtils(page)
|
const u = getUtils(page)
|
||||||
@ -348,7 +349,7 @@ const sketch001 = startSketchOn(box, "revolveAxis")
|
|||||||
|> startProfileAt([5, 10], %)
|
|> startProfileAt([5, 10], %)
|
||||||
|> line([0, -10], %)
|
|> line([0, -10], %)
|
||||||
|> line([2, 0], %)
|
|> line([2, 0], %)
|
||||||
|> line([0, -10], %)
|
|> line([0, 10], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> revolve({
|
|> revolve({
|
||||||
axis: getEdge('revolveAxis', box),
|
axis: getEdge('revolveAxis', box),
|
||||||
@ -363,7 +364,7 @@ angle: 90
|
|||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
await u.openDebugPanel()
|
u.openDebugPanel()
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
@ -377,7 +378,7 @@ angle: 90
|
|||||||
'sketch profile must lie entirely on one side of the revolution axis'
|
'sketch profile must lie entirely on one side of the revolution axis'
|
||||||
)
|
)
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
})
|
})*/
|
||||||
|
|
||||||
test('executes on load', async ({ page }) => {
|
test('executes on load', async ({ page }) => {
|
||||||
const u = getUtils(page)
|
const u = getUtils(page)
|
||||||
@ -565,9 +566,7 @@ test('Auto complete works', async ({ page }) => {
|
|||||||
|
|
||||||
await page.keyboard.press('Tab')
|
await page.keyboard.press('Tab')
|
||||||
await page.keyboard.type('12')
|
await page.keyboard.type('12')
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.keyboard.press('Tab')
|
await page.keyboard.press('Tab')
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.keyboard.press('Tab')
|
await page.keyboard.press('Tab')
|
||||||
await page.keyboard.press('Tab')
|
await page.keyboard.press('Tab')
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
@ -737,7 +736,7 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
|||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
|
||||||
const xAxisClick = () =>
|
const xAxisClick = () =>
|
||||||
page.mouse.click(700, 253).then(() => page.waitForTimeout(100))
|
page.mouse.click(700, 250).then(() => page.waitForTimeout(100))
|
||||||
const emptySpaceClick = () =>
|
const emptySpaceClick = () =>
|
||||||
page.mouse.click(728, 343).then(() => page.waitForTimeout(100))
|
page.mouse.click(728, 343).then(() => page.waitForTimeout(100))
|
||||||
const topHorzSegmentClick = () =>
|
const topHorzSegmentClick = () =>
|
||||||
@ -762,7 +761,6 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
|||||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)`)
|
|> startProfileAt(${commonPoints.startAt}, %)`)
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
@ -770,14 +768,12 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
|||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
|> line([${commonPoints.num1}, 0], %)`)
|
|> line([${commonPoints.num1}, 0], %)`)
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
|> line([${commonPoints.num1}, 0], %)
|
|> line([${commonPoints.num1}, 0], %)
|
||||||
|> line([0, ${commonPoints.num1}], %)`)
|
|> line([0, ${commonPoints.num1}], %)`)
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
@ -790,14 +786,10 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
|||||||
await page.getByRole('button', { name: 'Line' }).click()
|
await page.getByRole('button', { name: 'Line' }).click()
|
||||||
|
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
const selectionSequence = async (isSecondTime = false) => {
|
const selectionSequence = async () => {
|
||||||
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
|
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
await page.mouse.move(startXPx + PUR * 15, 500 - PUR * 10)
|
||||||
await page.mouse.move(
|
|
||||||
startXPx + PUR * 15,
|
|
||||||
isSecondTime ? 430 : 500 - PUR * 10
|
|
||||||
)
|
|
||||||
|
|
||||||
await expect(page.getByTestId('hover-highlight')).toBeVisible()
|
await expect(page.getByTestId('hover-highlight')).toBeVisible()
|
||||||
// bg-yellow-200 is more brittle than hover-highlight, but is closer to the user experience
|
// bg-yellow-200 is more brittle than hover-highlight, but is closer to the user experience
|
||||||
@ -807,10 +799,7 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
|||||||
// check mousing off, than mousing onto another line
|
// check mousing off, than mousing onto another line
|
||||||
await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 15) // mouse off
|
await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 15) // mouse off
|
||||||
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
|
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
|
||||||
await page.mouse.move(
|
await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 20) // mouse onto another line
|
||||||
startXPx + PUR * 10,
|
|
||||||
isSecondTime ? 295 : 500 - PUR * 20
|
|
||||||
) // mouse onto another line
|
|
||||||
await expect(page.getByTestId('hover-highlight')).toBeVisible()
|
await expect(page.getByTestId('hover-highlight')).toBeVisible()
|
||||||
|
|
||||||
// now check clicking works including axis
|
// now check clicking works including axis
|
||||||
@ -820,7 +809,6 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
|||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
const absYButton = page.getByRole('button', { name: 'ABS Y' })
|
const absYButton = page.getByRole('button', { name: 'ABS Y' })
|
||||||
await expect(absYButton).toBeDisabled()
|
await expect(absYButton).toBeDisabled()
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await xAxisClick()
|
await xAxisClick()
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
await absYButton.and(page.locator(':not([disabled])')).waitFor()
|
await absYButton.and(page.locator(':not([disabled])')).waitFor()
|
||||||
@ -829,12 +817,10 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
|||||||
// clear selection by clicking on nothing
|
// clear selection by clicking on nothing
|
||||||
await emptySpaceClick()
|
await emptySpaceClick()
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
// same selection but click the axis first
|
// same selection but click the axis first
|
||||||
await xAxisClick()
|
await xAxisClick()
|
||||||
await expect(absYButton).toBeDisabled()
|
await expect(absYButton).toBeDisabled()
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await topHorzSegmentClick()
|
await topHorzSegmentClick()
|
||||||
|
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
@ -847,7 +833,6 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
|||||||
await page.getByText(` |> line([-${commonPoints.num2}, 0], %)`).click()
|
await page.getByText(` |> line([-${commonPoints.num2}, 0], %)`).click()
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await expect(absYButton).toBeDisabled()
|
await expect(absYButton).toBeDisabled()
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await xAxisClick()
|
await xAxisClick()
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
await expect(absYButton).not.toBeDisabled()
|
await expect(absYButton).not.toBeDisabled()
|
||||||
@ -890,16 +875,11 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
|||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
// enter sketch again
|
// enter sketch again
|
||||||
await u.doAndWaitForCmd(
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
() => page.getByRole('button', { name: 'Edit Sketch' }).click(),
|
|
||||||
'default_camera_get_settings'
|
|
||||||
)
|
|
||||||
await page.waitForTimeout(150)
|
|
||||||
|
|
||||||
await page.waitForTimeout(300) // wait for animation
|
await page.waitForTimeout(300) // wait for animation
|
||||||
|
|
||||||
// hover again and check it works
|
// hover again and check it works
|
||||||
await selectionSequence(true)
|
await selectionSequence()
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Command bar tests', () => {
|
test.describe('Command bar tests', () => {
|
||||||
@ -1035,7 +1015,6 @@ const part001 = startSketchOn('-XZ')
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('Can add multiple sketches', async ({ page }) => {
|
test('Can add multiple sketches', async ({ page }) => {
|
||||||
test.skip(process.platform === 'darwin', 'Can add multiple sketches')
|
|
||||||
const u = getUtils(page)
|
const u = getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
@ -1086,7 +1065,6 @@ test('Can add multiple sketches', async ({ page }) => {
|
|||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
|> line([${commonPoints.num1}, 0], %)
|
|> line([${commonPoints.num1}, 0], %)
|
||||||
|> line([0, ${commonPoints.num1}], %)`)
|
|> line([0, ${commonPoints.num1}], %)`)
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||||
const finalCodeFirstSketch = `const part001 = startSketchOn('-XZ')
|
const finalCodeFirstSketch = `const part001 = startSketchOn('-XZ')
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
@ -1102,33 +1080,24 @@ test('Can add multiple sketches', async ({ page }) => {
|
|||||||
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
|
||||||
await u.updateCamPosition([100, 100, 100])
|
await u.updateCamPosition([0, 100, 100])
|
||||||
await page.waitForTimeout(250)
|
|
||||||
|
|
||||||
// start a new sketch
|
// start a new sketch
|
||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
await page.waitForTimeout(400)
|
await page.waitForTimeout(100)
|
||||||
await page.mouse.click(650, 450)
|
await page.mouse.click(673, 384)
|
||||||
|
|
||||||
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
||||||
await u.clearAndCloseDebugPanel()
|
await u.clearAndCloseDebugPanel()
|
||||||
|
|
||||||
// on mock os there are issues with getting the camera to update
|
|
||||||
// it should not be selecting the 'XZ' plane here if the camera updated
|
|
||||||
// properly, but if we just role with it we can still verify everything
|
|
||||||
// in the rest of the test
|
|
||||||
const plane = process.platform === 'darwin' ? 'XZ' : 'XY'
|
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
const startAt2 =
|
const startAt2 = '[0.93,-1.25]'
|
||||||
process.platform === 'darwin' ? '[9.75, -13.16]' : '[0.93, -1.25]'
|
|
||||||
await expect(
|
await expect(
|
||||||
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||||
).toBe(
|
).toBe(
|
||||||
`${finalCodeFirstSketch}
|
`${finalCodeFirstSketch}
|
||||||
const part002 = startSketchOn('${plane}')
|
const part002 = startSketchOn('XY')
|
||||||
|> startProfileAt(${startAt2}, %)`.replace(/\s/g, '')
|
|> startProfileAt(${startAt2}, %)`.replace(/\s/g, '')
|
||||||
)
|
)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
@ -1137,12 +1106,12 @@ const part002 = startSketchOn('${plane}')
|
|||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
const num2 = process.platform === 'darwin' ? 9.84 : 0.94
|
const num2 = 0.94
|
||||||
await expect(
|
await expect(
|
||||||
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||||
).toBe(
|
).toBe(
|
||||||
`${finalCodeFirstSketch}
|
`${finalCodeFirstSketch}
|
||||||
const part002 = startSketchOn('${plane}')
|
const part002 = startSketchOn('XY')
|
||||||
|> startProfileAt(${startAt2}, %)
|
|> startProfileAt(${startAt2}, %)
|
||||||
|> line([${num2}, 0], %)`.replace(/\s/g, '')
|
|> line([${num2}, 0], %)`.replace(/\s/g, '')
|
||||||
)
|
)
|
||||||
@ -1152,29 +1121,21 @@ const part002 = startSketchOn('${plane}')
|
|||||||
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||||
).toBe(
|
).toBe(
|
||||||
`${finalCodeFirstSketch}
|
`${finalCodeFirstSketch}
|
||||||
const part002 = startSketchOn('${plane}')
|
const part002 = startSketchOn('XY')
|
||||||
|> startProfileAt(${startAt2}, %)
|
|> startProfileAt(${startAt2}, %)
|
||||||
|> line([${num2}, 0], %)
|
|> line([${num2}, 0], %)
|
||||||
|> line([0, ${roundOff(
|
|> line([0, ${roundOff(num2 - 0.01)}], %)`.replace(/\s/g, '')
|
||||||
num2 + (process.platform === 'darwin' ? 0.01 : -0.01)
|
|
||||||
)}], %)`.replace(/\s/g, '')
|
|
||||||
)
|
)
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||||
await expect(
|
await expect(
|
||||||
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||||
).toBe(
|
).toBe(
|
||||||
`${finalCodeFirstSketch}
|
`${finalCodeFirstSketch}
|
||||||
const part002 = startSketchOn('${plane}')
|
const part002 = startSketchOn('XY')
|
||||||
|> startProfileAt(${startAt2}, %)
|
|> startProfileAt(${startAt2}, %)
|
||||||
|> line([${num2}, 0], %)
|
|> line([${num2}, 0], %)
|
||||||
|> line([0, ${roundOff(
|
|> line([0, ${roundOff(num2 - 0.01)}], %)
|
||||||
num2 + (process.platform === 'darwin' ? 0.01 : -0.01)
|
|> line([-1.87, 0], %)`.replace(/\s/g, '')
|
||||||
)}], %)
|
|
||||||
|> line([-${process.platform === 'darwin' ? 19.59 : 1.87}, 0], %)`.replace(
|
|
||||||
/\s/g,
|
|
||||||
''
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1378,12 +1339,10 @@ test('Deselecting line tool should mean nothing happens on click', async ({
|
|||||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.mouse.click(700, 300)
|
await page.mouse.click(700, 300)
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.mouse.click(750, 300)
|
await page.mouse.click(750, 300)
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
@ -1408,16 +1367,16 @@ test('Can edit segments by dragging their handles', async ({ page }) => {
|
|||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
|
|
||||||
const startPX = [665, 458]
|
const startPX = [652, 418]
|
||||||
const lineEndPX = [842, 458]
|
const lineEndPX = [794, 416]
|
||||||
const arcEndPX = [971, 342]
|
const arcEndPX = [893, 318]
|
||||||
|
|
||||||
const dragPX = 30
|
const dragPX = 30
|
||||||
|
|
||||||
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
|
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
|
||||||
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
|
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
await page.waitForTimeout(400)
|
await page.waitForTimeout(100)
|
||||||
let prevContent = await page.locator('.cm-content').innerText()
|
let prevContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
const step5 = { steps: 5 }
|
const step5 = { steps: 5 }
|
||||||
@ -1427,7 +1386,7 @@ test('Can edit segments by dragging their handles', async ({ page }) => {
|
|||||||
await page.mouse.down()
|
await page.mouse.down()
|
||||||
await page.mouse.move(startPX[0] + dragPX, startPX[1] - dragPX, step5)
|
await page.mouse.move(startPX[0] + dragPX, startPX[1] - dragPX, step5)
|
||||||
await page.mouse.up()
|
await page.mouse.up()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
prevContent = await page.locator('.cm-content').innerText()
|
prevContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
@ -1455,9 +1414,9 @@ test('Can edit segments by dragging their handles', async ({ page }) => {
|
|||||||
// 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 part001 = startSketchOn('-XZ')
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|> startProfileAt([6.44, -12.07], %)
|
|> startProfileAt([7.01, -11.79], %)
|
||||||
|> line([14.04, 2.03], %)
|
|> line([14.69, 2.73], %)
|
||||||
|> tangentialArcTo([27.19, -4.2], %)`)
|
|> tangentialArcTo([27.6, -3.25], %)`)
|
||||||
})
|
})
|
||||||
|
|
||||||
const doSnapAtDifferentScales = async (
|
const doSnapAtDifferentScales = async (
|
||||||
@ -1576,46 +1535,38 @@ test('Sketch on face', async ({ page }) => {
|
|||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
await page.waitForTimeout(300)
|
|
||||||
|
|
||||||
let previousCodeContent = await page.locator('.cm-content').innerText()
|
let previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
await u.openAndClearDebugPanel()
|
await page.mouse.click(793, 133)
|
||||||
await u.doAndWaitForCmd(
|
|
||||||
() => page.mouse.click(793, 133),
|
|
||||||
'default_camera_get_settings',
|
|
||||||
true
|
|
||||||
)
|
|
||||||
await page.waitForTimeout(150)
|
|
||||||
|
|
||||||
const firstClickPosition = [612, 238]
|
const firstClickPosition = [612, 238]
|
||||||
const secondClickPosition = [661, 242]
|
const secondClickPosition = [661, 242]
|
||||||
const thirdClickPosition = [609, 267]
|
const thirdClickPosition = [609, 267]
|
||||||
|
|
||||||
|
await page.waitForTimeout(300)
|
||||||
|
|
||||||
await page.mouse.click(firstClickPosition[0], firstClickPosition[1])
|
await page.mouse.click(firstClickPosition[0], firstClickPosition[1])
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.mouse.click(secondClickPosition[0], secondClickPosition[1])
|
await page.mouse.click(secondClickPosition[0], secondClickPosition[1])
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.mouse.click(thirdClickPosition[0], thirdClickPosition[1])
|
await page.mouse.click(thirdClickPosition[0], thirdClickPosition[1])
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.mouse.click(firstClickPosition[0], firstClickPosition[1])
|
await page.mouse.click(firstClickPosition[0], firstClickPosition[1])
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|
||||||
|> startProfileAt([-12.83, 6.7], %)
|
|> startProfileAt([1.03, 1.03], %)
|
||||||
|> line([2.87, -0.23], %)
|
|> line([4.18, -0.35], %)
|
||||||
|> line([-3.05, -1.47], %)
|
|> line([-4.44, -2.13], %)
|
||||||
|> close(%)`)
|
|> close(%)`)
|
||||||
|
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
@ -1625,14 +1576,9 @@ test('Sketch on face', async ({ page }) => {
|
|||||||
await u.updateCamPosition([1049, 239, 686])
|
await u.updateCamPosition([1049, 239, 686])
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await page.getByText('startProfileAt([-12.83, 6.7], %)').click()
|
await page.getByText('startProfileAt([1.03, 1.03], %)').click()
|
||||||
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
|
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
|
||||||
await u.doAndWaitForCmd(
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
() => page.getByRole('button', { name: 'Edit Sketch' }).click(),
|
|
||||||
'default_camera_get_settings',
|
|
||||||
true
|
|
||||||
)
|
|
||||||
await page.waitForTimeout(150)
|
|
||||||
await page.setViewportSize({ width: 1200, height: 1200 })
|
await page.setViewportSize({ width: 1200, height: 1200 })
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await u.updateCamPosition([452, -152, 1166])
|
await u.updateCamPosition([452, -152, 1166])
|
||||||
@ -1652,11 +1598,11 @@ test('Sketch on face', async ({ page }) => {
|
|||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|
||||||
|> startProfileAt([-12.83, 6.7], %)
|
|> startProfileAt([1.03, 1.03], %)
|
||||||
|> line([${process?.env?.CI ? 2.28 : 2.28}, -${
|
|> line([${process?.env?.CI ? 2.74 : 2.93}, -${
|
||||||
process?.env?.CI ? 0.07 : 0.07
|
process?.env?.CI ? 0.24 : 0.2
|
||||||
}], %)
|
}], %)
|
||||||
|> line([-3.05, -1.47], %)
|
|> line([-4.44, -2.13], %)
|
||||||
|> close(%)`)
|
|> close(%)`)
|
||||||
|
|
||||||
// exit sketch
|
// exit sketch
|
||||||
@ -1664,7 +1610,7 @@ test('Sketch on face', async ({ page }) => {
|
|||||||
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"]')
|
||||||
|
|
||||||
await page.getByText('startProfileAt([-12.83, 6.7], %)').click()
|
await page.getByText('startProfileAt([1.03, 1.03], %)').click()
|
||||||
|
|
||||||
await expect(page.getByRole('button', { name: 'Extrude' })).not.toBeDisabled()
|
await expect(page.getByRole('button', { name: 'Extrude' })).not.toBeDisabled()
|
||||||
await page.getByRole('button', { name: 'Extrude' }).click()
|
await page.getByRole('button', { name: 'Extrude' }).click()
|
||||||
@ -1678,11 +1624,11 @@ test('Sketch on face', async ({ page }) => {
|
|||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|
||||||
|> startProfileAt([-12.83, 6.7], %)
|
|> startProfileAt([1.03, 1.03], %)
|
||||||
|> line([${process?.env?.CI ? 2.28 : 2.28}, -${
|
|> line([${process?.env?.CI ? 2.74 : 2.93}, -${
|
||||||
process?.env?.CI ? 0.07 : 0.07
|
process?.env?.CI ? 0.24 : 0.2
|
||||||
}], %)
|
}], %)
|
||||||
|> line([-3.05, -1.47], %)
|
|> line([-4.44, -2.13], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5 + 7, %)`)
|
|> extrude(5 + 7, %)`)
|
||||||
})
|
})
|
||||||
@ -1715,11 +1661,11 @@ test('Can code mod a line length', async ({ page }) => {
|
|||||||
|
|
||||||
// enter sketch again
|
// enter sketch again
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
await page.waitForTimeout(350) // wait for animation
|
await page.waitForTimeout(300) // wait for animation
|
||||||
|
|
||||||
const startXPx = 500
|
const startXPx = 500
|
||||||
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
|
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
|
||||||
await page.mouse.click(615, 102)
|
await page.mouse.click(615, 133)
|
||||||
await page.getByRole('button', { name: 'length', exact: true }).click()
|
await page.getByRole('button', { name: 'length', exact: true }).click()
|
||||||
await page.getByText('Add constraining value').click()
|
await page.getByText('Add constraining value').click()
|
||||||
|
|
||||||
@ -1727,42 +1673,3 @@ test('Can code mod a line length', async ({ page }) => {
|
|||||||
`const length001 = 20const part001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> line([0, 20], %) |> xLine(-length001, %)`
|
`const length001 = 20const part001 = startSketchOn('XY') |> startProfileAt([-10, -10], %) |> line([20, 0], %) |> line([0, 20], %) |> xLine(-length001, %)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Extrude from command bar selects extrude line after', async ({
|
|
||||||
page,
|
|
||||||
}) => {
|
|
||||||
await page.addInitScript(async () => {
|
|
||||||
localStorage.setItem(
|
|
||||||
'persistCode',
|
|
||||||
`const part001 = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-10, -10], %)
|
|
||||||
|> line([20, 0], %)
|
|
||||||
|> line([0, 20], %)
|
|
||||||
|> xLine(-20, %)
|
|
||||||
|> close(%)
|
|
||||||
`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const u = getUtils(page)
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
|
||||||
await page.goto('/')
|
|
||||||
await u.waitForAuthSkipAppStart()
|
|
||||||
await u.openDebugPanel()
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
|
|
||||||
// Click the line of code for xLine.
|
|
||||||
await page.getByText(`close(%)`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Extrude' }).click()
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await expect(page.locator('.cm-activeLine')).toHaveText(
|
|
||||||
` |> extrude(5 + 7, %)`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 45 KiB |
@ -7,7 +7,6 @@ export const TEST_SETTINGS = {
|
|||||||
theme: Themes.Dark,
|
theme: Themes.Dark,
|
||||||
onboardingStatus: 'dismissed',
|
onboardingStatus: 'dismissed',
|
||||||
projectDirectory: '',
|
projectDirectory: '',
|
||||||
enableSSAO: false,
|
|
||||||
},
|
},
|
||||||
modeling: {
|
modeling: {
|
||||||
defaultUnit: 'in',
|
defaultUnit: 'in',
|
||||||
|
@ -57,7 +57,7 @@ echo "New version number without 'v': $new_version_number"
|
|||||||
git checkout -b "cut-release-$new_version"
|
git checkout -b "cut-release-$new_version"
|
||||||
|
|
||||||
echo "$(jq --arg v "$new_version_number" '.version=$v' package.json --indent 2)" > package.json
|
echo "$(jq --arg v "$new_version_number" '.version=$v' package.json --indent 2)" > package.json
|
||||||
echo "$(jq --arg v "$new_version_number" '.version=$v' src-tauri/tauri.conf.json --indent 2)" > src-tauri/tauri.conf.json
|
echo "$(jq --arg v "$new_version_number" '.package.version=$v' src-tauri/tauri.conf.json --indent 2)" > src-tauri/tauri.conf.json
|
||||||
|
|
||||||
git add package.json src-tauri/tauri.conf.json
|
git add package.json src-tauri/tauri.conf.json
|
||||||
git commit -m "Cut release $new_version"
|
git commit -m "Cut release $new_version"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "untitled-app",
|
"name": "untitled-app",
|
||||||
"version": "0.18.1",
|
"version": "0.17.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.16.0",
|
"@codemirror/autocomplete": "^6.16.0",
|
||||||
@ -8,7 +8,7 @@
|
|||||||
"@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",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
"@headlessui/react": "^1.7.19",
|
"@headlessui/react": "^1.7.18",
|
||||||
"@headlessui/tailwindcss": "^0.2.0",
|
"@headlessui/tailwindcss": "^0.2.0",
|
||||||
"@kittycad/lib": "^0.0.58",
|
"@kittycad/lib": "^0.0.58",
|
||||||
"@lezer/javascript": "^1.4.9",
|
"@lezer/javascript": "^1.4.9",
|
||||||
@ -84,8 +84,8 @@
|
|||||||
"test:e2e:tauri": "E2E_TAURI_ENABLED=true TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' wdio run wdio.conf.ts",
|
"test:e2e:tauri": "E2E_TAURI_ENABLED=true TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' wdio run wdio.conf.ts",
|
||||||
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||||
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
||||||
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e",
|
"fmt": "prettier --write ./src && prettier --write ./e2e",
|
||||||
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e",
|
"fmt-check": "prettier --check ./src && prettier --check ./e2e",
|
||||||
"build:wasm-dev": "(cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
|
"build:wasm-dev": "(cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
|
||||||
"build:wasm": "(cd src/wasm-lib && wasm-pack build --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
|
"build:wasm": "(cd src/wasm-lib && wasm-pack build --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
|
||||||
"build:wasm-clean": "yarn wasm-prep && yarn build:wasm",
|
"build:wasm-clean": "yarn wasm-prep && yarn build:wasm",
|
||||||
@ -132,7 +132,6 @@
|
|||||||
"@types/wicg-file-system-access": "^2023.10.5",
|
"@types/wicg-file-system-access": "^2023.10.5",
|
||||||
"@types/ws": "^8.5.10",
|
"@types/ws": "^8.5.10",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
"@vitest/web-worker": "^1.5.0",
|
|
||||||
"@wdio/cli": "^8.24.3",
|
"@wdio/cli": "^8.24.3",
|
||||||
"@wdio/globals": "^8.36.0",
|
"@wdio/globals": "^8.36.0",
|
||||||
"@wdio/local-runner": "^8.36.0",
|
"@wdio/local-runner": "^8.36.0",
|
||||||
|
@ -18,7 +18,7 @@ export default defineConfig({
|
|||||||
/* Retry on CI only */
|
/* Retry on CI only */
|
||||||
retries: process.env.CI ? 3 : 0,
|
retries: process.env.CI ? 3 : 0,
|
||||||
/* Opt out of parallel tests on CI. */
|
/* Opt out of parallel tests on CI. */
|
||||||
workers: process.env.CI ? 2 : 1,
|
workers: process.env.CI ? 1 : 1,
|
||||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
reporter: 'html',
|
reporter: 'html',
|
||||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||||
@ -49,6 +49,8 @@ export default defineConfig({
|
|||||||
// use: { ...devices['Desktop Chrome'] },
|
// use: { ...devices['Desktop Chrome'] },
|
||||||
// },
|
// },
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Test against mobile viewports. */
|
/* Test against mobile viewports. */
|
||||||
// {
|
// {
|
||||||
// name: 'Mobile Chrome',
|
// name: 'Mobile Chrome',
|
||||||
@ -72,7 +74,7 @@ export default defineConfig({
|
|||||||
|
|
||||||
/* Run your local dev server before starting the tests */
|
/* Run your local dev server before starting the tests */
|
||||||
webServer: {
|
webServer: {
|
||||||
command: 'VITE_KC_WASM_OVERRIDE_URL=$WASM_OVERRIDE yarn serve',
|
command: 'yarn serve',
|
||||||
// url: 'http://127.0.0.1:3000',
|
// url: 'http://127.0.0.1:3000',
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: !process.env.CI,
|
||||||
},
|
},
|
||||||
|
44
src-tauri/Cargo.lock
generated
@ -1592,7 +1592,7 @@ version = "0.18.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc"
|
checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.4.1",
|
"heck",
|
||||||
"proc-macro-crate 2.0.2",
|
"proc-macro-crate 2.0.2",
|
||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -1735,12 +1735,6 @@ version = "0.4.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "heck"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
@ -2205,9 +2199,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad"
|
name = "kittycad"
|
||||||
version = "0.3.0"
|
version = "0.2.67"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ddc922f0da3abc22661bf49423c9bfcc02ce6ae92dae007ede6990874789545b"
|
checksum = "fc460442c165c8e707b1154551cefd08938d10bb80c78940e10cd9869487c325"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -4328,7 +4322,7 @@ version = "0.24.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
|
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.4.1",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
@ -4417,7 +4411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e5fa6fb9ee296c0dc2df41a656ca7948546d061958115ddb0bcaae43ad0d17d2"
|
checksum = "e5fa6fb9ee296c0dc2df41a656ca7948546d061958115ddb0bcaae43ad0d17d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-expr",
|
"cfg-expr",
|
||||||
"heck 0.4.1",
|
"heck",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"toml 0.7.8",
|
"toml 0.7.8",
|
||||||
"version-compare",
|
"version-compare",
|
||||||
@ -4514,7 +4508,7 @@ dependencies = [
|
|||||||
"getrandom 0.2.14",
|
"getrandom 0.2.14",
|
||||||
"glob",
|
"glob",
|
||||||
"gtk",
|
"gtk",
|
||||||
"heck 0.4.1",
|
"heck",
|
||||||
"http 1.1.0",
|
"http 1.1.0",
|
||||||
"jni",
|
"jni",
|
||||||
"libc",
|
"libc",
|
||||||
@ -4549,15 +4543,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-build"
|
name = "tauri-build"
|
||||||
version = "2.0.0-beta.13"
|
version = "2.0.0-beta.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "abcf98a9b4527567c3e5ca9723431d121e001c2145651b3fa044d22b5e025a7e"
|
checksum = "33de24aabe2b9c340d67005800cb6dd40aac5283126a42896fc8eec0b87cbe45"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cargo_toml",
|
"cargo_toml",
|
||||||
"dirs-next",
|
"dirs-next",
|
||||||
"glob",
|
"glob",
|
||||||
"heck 0.5.0",
|
"heck",
|
||||||
"json-patch",
|
"json-patch",
|
||||||
"schemars",
|
"schemars",
|
||||||
"semver",
|
"semver",
|
||||||
@ -4602,7 +4596,7 @@ version = "2.0.0-beta.12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b096f63f2724a1280ae0f5a34d0731de18ca18305e2ef6e5e9a39bb2710e8a85"
|
checksum = "b096f63f2724a1280ae0f5a34d0731de18ca18305e2ef6e5e9a39bb2710e8a85"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.4.1",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.48",
|
"syn 2.0.48",
|
||||||
@ -4629,9 +4623,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-dialog"
|
name = "tauri-plugin-dialog"
|
||||||
version = "2.0.0-beta.6"
|
version = "2.0.0-beta.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "87caf6f2b704b0d27b4c64ef1fdd1f6ef97e2f5293216e230ad1efe61de54131"
|
checksum = "db4476c824a1488a52f4672d2b419a71fbf3dc97249013ef3c2c08fae2a23b71"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dunce",
|
"dunce",
|
||||||
"log",
|
"log",
|
||||||
@ -4647,9 +4641,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-fs"
|
name = "tauri-plugin-fs"
|
||||||
version = "2.0.0-beta.6"
|
version = "2.0.0-beta.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "609f53d90f08808679ecdd81455d9a4d0053291b92780695569f7400fdba27d5"
|
checksum = "c138126392c350aa68554e3461529b02680062c9146ab7b41d3ef97a2deaf93b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"glob",
|
"glob",
|
||||||
@ -4666,9 +4660,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-http"
|
name = "tauri-plugin-http"
|
||||||
version = "2.0.0-beta.6"
|
version = "2.0.0-beta.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7c32962a2e2141b3bc034e5c04f363635965e59435794b6bdcf97a027f0925a"
|
checksum = "f27b2c90ed5473e1c068f523927fa0024212bc3a3f3a47c2a9c0b10b4b2e3ee4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"data-url",
|
"data-url",
|
||||||
"http 1.1.0",
|
"http 1.1.0",
|
||||||
@ -4806,16 +4800,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-utils"
|
name = "tauri-utils"
|
||||||
version = "2.0.0-beta.13"
|
version = "2.0.0-beta.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4709765385f035338ecc330f3fba753b8ee283c659c235da9768949cdb25469"
|
checksum = "760ac613d7f0de95067bcbcbcea175fe1df88fc4ab59c7f0b2cc2d01dc16a199"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"brotli",
|
"brotli",
|
||||||
"cargo_metadata",
|
"cargo_metadata",
|
||||||
"ctor",
|
"ctor",
|
||||||
"dunce",
|
"dunce",
|
||||||
"glob",
|
"glob",
|
||||||
"heck 0.5.0",
|
"heck",
|
||||||
"html5ever",
|
"html5ever",
|
||||||
"infer",
|
"infer",
|
||||||
"json-patch",
|
"json-patch",
|
||||||
|
@ -12,18 +12,18 @@ rust-version = "1.70"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "2.0.0-beta.13", features = [] }
|
tauri-build = { version = "2.0.0-beta.12", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
kittycad = "0.3.0"
|
kittycad = "0.2.67"
|
||||||
oauth2 = "4.4.2"
|
oauth2 = "4.4.2"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
tauri = { version = "2.0.0-beta.15", features = [ "devtools", "unstable"] }
|
tauri = { version = "2.0.0-beta.15", features = [ "devtools", "unstable"] }
|
||||||
tauri-plugin-dialog = { version = "2.0.0-beta.6" }
|
tauri-plugin-dialog = { version = "2.0.0-beta.5" }
|
||||||
tauri-plugin-fs = { version = "2.0.0-beta.6" }
|
tauri-plugin-fs = { version = "2.0.0-beta.5" }
|
||||||
tauri-plugin-http = { version = "2.0.0-beta.6" }
|
tauri-plugin-http = { version = "2.0.0-beta.5" }
|
||||||
tauri-plugin-os = { version = "2.0.0-beta.2" }
|
tauri-plugin-os = { version = "2.0.0-beta.2" }
|
||||||
tauri-plugin-process = { version = "2.0.0-beta.2" }
|
tauri-plugin-process = { version = "2.0.0-beta.2" }
|
||||||
tauri-plugin-shell = { version = "2.0.0-beta.2" }
|
tauri-plugin-shell = { version = "2.0.0-beta.2" }
|
||||||
|
@ -55,5 +55,5 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"productName": "Zoo Modeling App",
|
"productName": "Zoo Modeling App",
|
||||||
"version": "0.18.1"
|
"version": "0.17.3"
|
||||||
}
|
}
|
||||||
|
@ -246,31 +246,13 @@ export class CameraControls {
|
|||||||
camSettings.center.y,
|
camSettings.center.y,
|
||||||
camSettings.center.z
|
camSettings.center.z
|
||||||
)
|
)
|
||||||
this.camera.up.set(camSettings.up.x, camSettings.up.y, camSettings.up.z)
|
|
||||||
if (this.camera instanceof PerspectiveCamera && camSettings.ortho) {
|
|
||||||
this.useOrthographicCamera()
|
|
||||||
}
|
|
||||||
if (this.camera instanceof OrthographicCamera && !camSettings.ortho) {
|
|
||||||
this.usePerspectiveCamera()
|
|
||||||
}
|
|
||||||
if (this.camera instanceof PerspectiveCamera && camSettings.fov_y) {
|
if (this.camera instanceof PerspectiveCamera && camSettings.fov_y) {
|
||||||
this.camera.fov = camSettings.fov_y
|
this.camera.fov = camSettings.fov_y
|
||||||
} else if (
|
} else if (
|
||||||
this.camera instanceof OrthographicCamera &&
|
this.camera instanceof OrthographicCamera &&
|
||||||
camSettings.ortho_scale
|
camSettings.ortho_scale
|
||||||
) {
|
) {
|
||||||
const distanceToTarget = new Vector3(
|
this.camera.zoom = camSettings.ortho_scale
|
||||||
camSettings.pos.x,
|
|
||||||
camSettings.pos.y,
|
|
||||||
camSettings.pos.z
|
|
||||||
).distanceTo(
|
|
||||||
new Vector3(
|
|
||||||
camSettings.center.x,
|
|
||||||
camSettings.center.y,
|
|
||||||
camSettings.center.z
|
|
||||||
)
|
|
||||||
)
|
|
||||||
this.camera.zoom = (camSettings.ortho_scale * 40) / distanceToTarget
|
|
||||||
}
|
}
|
||||||
this.onCameraChange()
|
this.onCameraChange()
|
||||||
}
|
}
|
||||||
@ -983,10 +965,10 @@ export class CameraControls {
|
|||||||
// Pure function helpers
|
// Pure function helpers
|
||||||
|
|
||||||
function calculateNearFarFromFOV(fov: number) {
|
function calculateNearFarFromFOV(fov: number) {
|
||||||
// const nearFarRatio = (fov - 3) / (45 - 3)
|
const nearFarRatio = (fov - 3) / (45 - 3)
|
||||||
// const z_near = 0.1 + nearFarRatio * (5 - 0.1)
|
// const z_near = 0.1 + nearFarRatio * (5 - 0.1)
|
||||||
// const z_far = 1000 + nearFarRatio * (100000 - 1000)
|
const z_far = 1000 + nearFarRatio * (100000 - 1000)
|
||||||
return { z_near: 0.1, z_far: 1000 }
|
return { z_near: 0.1, z_far }
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertThreeCamValuesToEngineCam({
|
function convertThreeCamValuesToEngineCam({
|
||||||
@ -1061,62 +1043,3 @@ function _getInteractionType(
|
|||||||
if (enableZoom && interactionGuards.zoom.dragCallback(event)) return 'zoom'
|
if (enableZoom && interactionGuards.zoom.dragCallback(event)) return 'zoom'
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells the engine to fire it's animation waits for it to finish and then requests camera settings
|
|
||||||
* to ensure the client-side camera is synchronized with the engine's camera state.
|
|
||||||
*
|
|
||||||
* @param engineCommandManager Our websocket singleton
|
|
||||||
* @param entityId - The ID of face or sketchPlane.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export async function letEngineAnimateAndSyncCamAfter(
|
|
||||||
engineCommandManager: EngineCommandManager,
|
|
||||||
entityId: string
|
|
||||||
) {
|
|
||||||
await engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'enable_sketch_mode',
|
|
||||||
adjust_camera: true,
|
|
||||||
animated: !isReducedMotion(),
|
|
||||||
ortho: false,
|
|
||||||
entity_id: entityId,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
// wait 600ms (animation takes 500, + 100 for safety)
|
|
||||||
await new Promise((resolve) =>
|
|
||||||
setTimeout(resolve, isReducedMotion() ? 100 : 600)
|
|
||||||
)
|
|
||||||
await engineCommandManager.sendSceneCommand({
|
|
||||||
// CameraControls subscribes to default_camera_get_settings response events
|
|
||||||
// firing this at connection ensure the camera's are synced initially
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_get_settings',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
await engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'enable_sketch_mode',
|
|
||||||
adjust_camera: true,
|
|
||||||
animated: false,
|
|
||||||
ortho: true,
|
|
||||||
entity_id: entityId,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 50))
|
|
||||||
await engineCommandManager.sendSceneCommand({
|
|
||||||
// CameraControls subscribes to default_camera_get_settings response events
|
|
||||||
// firing this at connection ensure the camera's are synced initially
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_get_settings',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -3,6 +3,7 @@ import { useModelingContext } from 'hooks/useModelingContext'
|
|||||||
|
|
||||||
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
|
import { useStore } from 'useStore'
|
||||||
import { ARROWHEAD, DEBUG_SHOW_BOTH_SCENES } from './sceneInfra'
|
import { ARROWHEAD, DEBUG_SHOW_BOTH_SCENES } from './sceneInfra'
|
||||||
import { ReactCameraProperties } from './CameraControls'
|
import { ReactCameraProperties } from './CameraControls'
|
||||||
import { throttle } from 'lib/utils'
|
import { throttle } from 'lib/utils'
|
||||||
@ -46,6 +47,10 @@ export const ClientSideScene = ({
|
|||||||
const canvasRef = useRef<HTMLDivElement>(null)
|
const canvasRef = useRef<HTMLDivElement>(null)
|
||||||
const { state, send, context } = useModelingContext()
|
const { state, send, context } = useModelingContext()
|
||||||
const { hideClient, hideServer } = useShouldHideScene()
|
const { hideClient, hideServer } = useShouldHideScene()
|
||||||
|
const { setHighlightRange } = useStore((s) => ({
|
||||||
|
setHighlightRange: s.setHighlightRange,
|
||||||
|
highlightRange: s.highlightRange,
|
||||||
|
}))
|
||||||
|
|
||||||
// Listen for changes to the camera controls setting
|
// Listen for changes to the camera controls setting
|
||||||
// and update the client-side scene's controls accordingly.
|
// and update the client-side scene's controls accordingly.
|
||||||
@ -64,6 +69,7 @@ export const ClientSideScene = ({
|
|||||||
const canvas = canvasRef.current
|
const canvas = canvasRef.current
|
||||||
canvas.appendChild(sceneInfra.renderer.domElement)
|
canvas.appendChild(sceneInfra.renderer.domElement)
|
||||||
sceneInfra.animate()
|
sceneInfra.animate()
|
||||||
|
sceneInfra.setHighlightCallback(setHighlightRange)
|
||||||
canvas.addEventListener('mousemove', sceneInfra.onMouseMove, false)
|
canvas.addEventListener('mousemove', sceneInfra.onMouseMove, false)
|
||||||
canvas.addEventListener('mousedown', sceneInfra.onMouseDown, false)
|
canvas.addEventListener('mousedown', sceneInfra.onMouseDown, false)
|
||||||
canvas.addEventListener('mouseup', sceneInfra.onMouseUp, false)
|
canvas.addEventListener('mouseup', sceneInfra.onMouseUp, false)
|
||||||
|
@ -57,7 +57,6 @@ import {
|
|||||||
kclManager,
|
kclManager,
|
||||||
sceneInfra,
|
sceneInfra,
|
||||||
codeManager,
|
codeManager,
|
||||||
editorManager,
|
|
||||||
} from 'lib/singletons'
|
} from 'lib/singletons'
|
||||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { executeAst, useStore } from 'useStore'
|
import { executeAst, useStore } from 'useStore'
|
||||||
@ -215,9 +214,8 @@ export class SceneEntities {
|
|||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
const baseXColor = 0x000055
|
const baseXColor = 0x000055
|
||||||
const baseYColor = 0x550000
|
const baseYColor = 0x550000
|
||||||
const axisPixelWidth = 1.6
|
const xAxisGeometry = new BoxGeometry(100000, 0.3, 0.01)
|
||||||
const xAxisGeometry = new BoxGeometry(100000, axisPixelWidth, 0.01)
|
const yAxisGeometry = new BoxGeometry(0.3, 100000, 0.01)
|
||||||
const yAxisGeometry = new BoxGeometry(axisPixelWidth, 100000, 0.01)
|
|
||||||
const xAxisMaterial = new MeshBasicMaterial({
|
const xAxisMaterial = new MeshBasicMaterial({
|
||||||
color: baseXColor,
|
color: baseXColor,
|
||||||
depthTest: false,
|
depthTest: false,
|
||||||
@ -1325,31 +1323,30 @@ export class SceneEntities {
|
|||||||
selected.material.color = defaultPlaneColor(type)
|
selected.material.color = defaultPlaneColor(type)
|
||||||
},
|
},
|
||||||
onClick: async (args) => {
|
onClick: async (args) => {
|
||||||
const checkExtrudeFaceClick = async (): Promise<
|
const checkExtrudeFaceClick = async (): Promise<boolean> => {
|
||||||
['face' | 'plane' | 'other', string]
|
|
||||||
> => {
|
|
||||||
const { streamDimensions } = useStore.getState()
|
const { streamDimensions } = useStore.getState()
|
||||||
const { entity_id } = await sendSelectEventToEngine(
|
const { entity_id } = await sendSelectEventToEngine(
|
||||||
args?.mouseEvent,
|
args?.mouseEvent,
|
||||||
document.getElementById('video-stream') as HTMLVideoElement,
|
document.getElementById('video-stream') as HTMLVideoElement,
|
||||||
streamDimensions
|
streamDimensions
|
||||||
)
|
)
|
||||||
if (!entity_id) return ['other', '']
|
if (!entity_id) return false
|
||||||
if (
|
|
||||||
engineCommandManager.defaultPlanes?.xy === entity_id ||
|
|
||||||
engineCommandManager.defaultPlanes?.xz === entity_id ||
|
|
||||||
engineCommandManager.defaultPlanes?.yz === entity_id
|
|
||||||
) {
|
|
||||||
return ['plane', entity_id]
|
|
||||||
}
|
|
||||||
const artifact = this.engineCommandManager.artifactMap[entity_id]
|
const artifact = this.engineCommandManager.artifactMap[entity_id]
|
||||||
if (artifact?.commandType !== 'solid3d_get_extrusion_face_info')
|
if (artifact?.commandType !== 'solid3d_get_extrusion_face_info')
|
||||||
return ['other', entity_id]
|
return false
|
||||||
|
const faceInfo: Models['FaceIsPlanar_type'] = (
|
||||||
const faceInfo = await getFaceDetails(entity_id)
|
await this.engineCommandManager.sendSceneCommand({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'face_is_planar',
|
||||||
|
object_id: entity_id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)?.data?.data
|
||||||
if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis)
|
if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis)
|
||||||
return ['other', entity_id]
|
return false
|
||||||
const { z_axis, y_axis, origin } = faceInfo
|
const { z_axis, origin, y_axis } = faceInfo
|
||||||
const pathToNode = getNodePathFromSourceRange(
|
const pathToNode = getNodePathFromSourceRange(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
artifact.range
|
artifact.range
|
||||||
@ -1369,15 +1366,12 @@ export class SceneEntities {
|
|||||||
artifact?.additionalData?.type === 'cap'
|
artifact?.additionalData?.type === 'cap'
|
||||||
? artifact.additionalData.info
|
? artifact.additionalData.info
|
||||||
: 'none',
|
: 'none',
|
||||||
faceId: entity_id,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return ['face', entity_id]
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const faceResult = await checkExtrudeFaceClick()
|
if (await checkExtrudeFaceClick()) return
|
||||||
console.log('faceResult', faceResult)
|
|
||||||
if (faceResult[0] === 'face') return
|
|
||||||
|
|
||||||
if (!args || !args.intersects?.[0]) return
|
if (!args || !args.intersects?.[0]) return
|
||||||
if (args.mouseEvent.which !== 1) return
|
if (args.mouseEvent.which !== 1) return
|
||||||
@ -1403,7 +1397,6 @@ export class SceneEntities {
|
|||||||
plane: planeString,
|
plane: planeString,
|
||||||
zAxis,
|
zAxis,
|
||||||
yAxis,
|
yAxis,
|
||||||
planeId: faceResult[1],
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -1430,7 +1423,7 @@ export class SceneEntities {
|
|||||||
parent.userData.pathToNode,
|
parent.userData.pathToNode,
|
||||||
'CallExpression'
|
'CallExpression'
|
||||||
).node
|
).node
|
||||||
editorManager.setHighlightRange([node.start, node.end])
|
sceneInfra.highlightCallback([node.start, node.end])
|
||||||
const yellow = 0xffff00
|
const yellow = 0xffff00
|
||||||
colorSegment(selected, yellow)
|
colorSegment(selected, yellow)
|
||||||
const extraSegmentGroup = parent.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
const extraSegmentGroup = parent.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
||||||
@ -1466,10 +1459,10 @@ export class SceneEntities {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
editorManager.setHighlightRange([0, 0])
|
sceneInfra.highlightCallback([0, 0])
|
||||||
},
|
},
|
||||||
onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => {
|
onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => {
|
||||||
editorManager.setHighlightRange([0, 0])
|
sceneInfra.highlightCallback([0, 0])
|
||||||
const parent = getParentGroup(selected, [
|
const parent = getParentGroup(selected, [
|
||||||
STRAIGHT_SEGMENT,
|
STRAIGHT_SEGMENT,
|
||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
TANGENTIAL_ARC_TO_SEGMENT,
|
||||||
@ -1687,7 +1680,7 @@ export async function getSketchOrientationDetails(
|
|||||||
sketchPathToNode: PathToNode
|
sketchPathToNode: PathToNode
|
||||||
): Promise<{
|
): Promise<{
|
||||||
quat: Quaternion
|
quat: Quaternion
|
||||||
sketchDetails: SketchDetails & { faceId?: string }
|
sketchDetails: SketchDetails
|
||||||
}> {
|
}> {
|
||||||
const sketchGroup = sketchGroupFromPathToNode({
|
const sketchGroup = sketchGroupFromPathToNode({
|
||||||
pathToNode: sketchPathToNode,
|
pathToNode: sketchPathToNode,
|
||||||
@ -1703,13 +1696,20 @@ export async function getSketchOrientationDetails(
|
|||||||
zAxis: [zAxis.x, zAxis.y, zAxis.z],
|
zAxis: [zAxis.x, zAxis.y, zAxis.z],
|
||||||
yAxis: [sketchGroup.yAxis.x, sketchGroup.yAxis.y, sketchGroup.yAxis.z],
|
yAxis: [sketchGroup.yAxis.x, sketchGroup.yAxis.y, sketchGroup.yAxis.z],
|
||||||
origin: [0, 0, 0],
|
origin: [0, 0, 0],
|
||||||
faceId: sketchGroup.on.id,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sketchGroup.on.type === 'face') {
|
if (sketchGroup.on.type === 'face') {
|
||||||
const faceInfo = await getFaceDetails(sketchGroup.on.faceId)
|
const faceInfo: Models['FaceIsPlanar_type'] = (
|
||||||
|
await engineCommandManager.sendSceneCommand({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'face_is_planar',
|
||||||
|
object_id: sketchGroup.on.faceId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)?.data?.data
|
||||||
if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis)
|
if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis)
|
||||||
throw new Error('faceInfo')
|
throw new Error('faceInfo')
|
||||||
const { z_axis, y_axis, origin } = faceInfo
|
const { z_axis, y_axis, origin } = faceInfo
|
||||||
@ -1724,7 +1724,6 @@ export async function getSketchOrientationDetails(
|
|||||||
zAxis: [z_axis.x, z_axis.y, z_axis.z],
|
zAxis: [z_axis.x, z_axis.y, z_axis.z],
|
||||||
yAxis: [y_axis.x, y_axis.y, y_axis.z],
|
yAxis: [y_axis.x, y_axis.y, y_axis.z],
|
||||||
origin: [origin.x, origin.y, origin.z],
|
origin: [origin.x, origin.y, origin.z],
|
||||||
faceId: sketchGroup.on.faceId,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1733,46 +1732,6 @@ export async function getSketchOrientationDetails(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves orientation details for a given entity representing a face (brep face or default plane).
|
|
||||||
* This function asynchronously fetches and returns the origin, x-axis, y-axis, and z-axis details
|
|
||||||
* for a specified entity ID. It is primarily used to obtain the orientation of a face in the scene,
|
|
||||||
* which is essential for calculating the correct positioning and alignment of the client side sketch.
|
|
||||||
*
|
|
||||||
* @param entityId - The ID of the entity for which orientation details are being fetched.
|
|
||||||
* @returns A promise that resolves with the orientation details of the face.
|
|
||||||
*/
|
|
||||||
async function getFaceDetails(
|
|
||||||
entityId: string
|
|
||||||
): Promise<Models['FaceIsPlanar_type']> {
|
|
||||||
// TODO mode engine connection to allow batching returns and batch the following
|
|
||||||
await engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'enable_sketch_mode',
|
|
||||||
adjust_camera: false,
|
|
||||||
animated: false,
|
|
||||||
ortho: false,
|
|
||||||
entity_id: entityId,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
// TODO change typing to get_sketch_mode_plane once lib is updated
|
|
||||||
const faceInfo: Models['FaceIsPlanar_type'] = (
|
|
||||||
await engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: { type: 'get_sketch_mode_plane' },
|
|
||||||
})
|
|
||||||
)?.data?.data
|
|
||||||
await engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: { type: 'sketch_mode_disable' },
|
|
||||||
})
|
|
||||||
return faceInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getQuaternionFromZAxis(zAxis: Vector3): Quaternion {
|
export function getQuaternionFromZAxis(zAxis: Vector3): Quaternion {
|
||||||
const dummyCam = new PerspectiveCamera()
|
const dummyCam = new PerspectiveCamera()
|
||||||
dummyCam.up.set(0, 0, 1)
|
dummyCam.up.set(0, 0, 1)
|
||||||
|
@ -24,6 +24,7 @@ import {
|
|||||||
import { compareVec2Epsilon2 } from 'lang/std/sketch'
|
import { compareVec2Epsilon2 } from 'lang/std/sketch'
|
||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import * as TWEEN from '@tweenjs/tween.js'
|
import * as TWEEN from '@tweenjs/tween.js'
|
||||||
|
import { SourceRange } from 'lang/wasm'
|
||||||
import { Axis } from 'lib/selections'
|
import { Axis } from 'lib/selections'
|
||||||
import { type BaseUnit } from 'lib/settings/settingsTypes'
|
import { type BaseUnit } from 'lib/settings/settingsTypes'
|
||||||
import { CameraControls } from './CameraControls'
|
import { CameraControls } from './CameraControls'
|
||||||
@ -148,6 +149,10 @@ export class SceneInfra {
|
|||||||
onMouseLeave: () => {},
|
onMouseLeave: () => {},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
highlightCallback: (a: SourceRange) => void = () => {}
|
||||||
|
setHighlightCallback(cb: (a: SourceRange) => void) {
|
||||||
|
this.highlightCallback = cb
|
||||||
|
}
|
||||||
|
|
||||||
modelingSend: SendType = (() => {}) as any
|
modelingSend: SendType = (() => {}) as any
|
||||||
setSend(send: SendType) {
|
setSend(send: SendType) {
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import { editorManager, kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
|
import { useStore } from 'useStore'
|
||||||
|
|
||||||
export function AstExplorer() {
|
export function AstExplorer() {
|
||||||
|
const setHighlightRange = useStore((s) => s.setHighlightRange)
|
||||||
const { context } = useModelingContext()
|
const { context } = useModelingContext()
|
||||||
const pathToNode = getNodePathFromSourceRange(
|
const pathToNode = getNodePathFromSourceRange(
|
||||||
// TODO maybe need to have callback to make sure it stays in sync
|
// TODO maybe need to have callback to make sure it stays in sync
|
||||||
@ -40,7 +42,7 @@ export function AstExplorer() {
|
|||||||
<div
|
<div
|
||||||
className="h-full relative"
|
className="h-full relative"
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
editorManager.setHighlightRange([0, 0])
|
setHighlightRange([0, 0])
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<pre className="text-xs">
|
<pre className="text-xs">
|
||||||
@ -86,6 +88,7 @@ function DisplayObj({
|
|||||||
filterKeys: string[]
|
filterKeys: string[]
|
||||||
node: any
|
node: any
|
||||||
}) {
|
}) {
|
||||||
|
const setHighlightRange = useStore((s) => s.setHighlightRange)
|
||||||
const { send } = useModelingContext()
|
const { send } = useModelingContext()
|
||||||
const ref = useRef<HTMLPreElement>(null)
|
const ref = useRef<HTMLPreElement>(null)
|
||||||
const [hasCursor, setHasCursor] = useState(false)
|
const [hasCursor, setHasCursor] = useState(false)
|
||||||
@ -109,12 +112,12 @@ function DisplayObj({
|
|||||||
hasCursor ? 'bg-violet-100/80 dark:bg-violet-100/25' : ''
|
hasCursor ? 'bg-violet-100/80 dark:bg-violet-100/25' : ''
|
||||||
}`}
|
}`}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => {
|
||||||
editorManager.setHighlightRange([obj?.start || 0, obj.end])
|
setHighlightRange([obj?.start || 0, obj.end])
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
}}
|
}}
|
||||||
onMouseMove={(e) => {
|
onMouseMove={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
editorManager.setHighlightRange([obj?.start || 0, obj.end])
|
setHighlightRange([obj?.start || 0, obj.end])
|
||||||
}}
|
}}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
send({
|
send({
|
||||||
|
@ -137,33 +137,34 @@ export function useCalc({
|
|||||||
setAvailableVarInfo(varInfo)
|
setAvailableVarInfo(varInfo)
|
||||||
}, [kclManager.ast, kclManager.programMemory, selectionRange])
|
}, [kclManager.ast, kclManager.programMemory, selectionRange])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(async () => {
|
||||||
try {
|
try {
|
||||||
const code = `const __result__ = ${value}`
|
const code = `const __result__ = ${value}`
|
||||||
const ast = parse(code)
|
parse(code).then((ast) => {
|
||||||
const _programMem: any = { root: {}, return: null }
|
const _programMem: any = { root: {}, return: null }
|
||||||
availableVarInfo.variables.forEach(({ key, value }) => {
|
availableVarInfo.variables.forEach(({ key, value }) => {
|
||||||
_programMem.root[key] = { type: 'userVal', value, __meta: [] }
|
_programMem.root[key] = { type: 'userVal', value, __meta: [] }
|
||||||
})
|
})
|
||||||
executeAst({
|
executeAst({
|
||||||
ast,
|
ast,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
useFakeExecutor: true,
|
useFakeExecutor: true,
|
||||||
programMemoryOverride: JSON.parse(
|
programMemoryOverride: JSON.parse(
|
||||||
JSON.stringify(kclManager.programMemory)
|
JSON.stringify(kclManager.programMemory)
|
||||||
),
|
),
|
||||||
}).then(({ programMemory }) => {
|
}).then(({ programMemory }) => {
|
||||||
const resultDeclaration = ast.body.find(
|
const resultDeclaration = ast.body.find(
|
||||||
(a) =>
|
(a) =>
|
||||||
a.type === 'VariableDeclaration' &&
|
a.type === 'VariableDeclaration' &&
|
||||||
a.declarations?.[0]?.id?.name === '__result__'
|
a.declarations?.[0]?.id?.name === '__result__'
|
||||||
)
|
)
|
||||||
const init =
|
const init =
|
||||||
resultDeclaration?.type === 'VariableDeclaration' &&
|
resultDeclaration?.type === 'VariableDeclaration' &&
|
||||||
resultDeclaration?.declarations?.[0]?.init
|
resultDeclaration?.declarations?.[0]?.init
|
||||||
const result = programMemory?.root?.__result__?.value
|
const result = programMemory?.root?.__result__?.value
|
||||||
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
||||||
init && setValueNode(init)
|
init && setValueNode(init)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setCalcResult('NAN')
|
setCalcResult('NAN')
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { useMachine } from '@xstate/react'
|
import { useMachine } from '@xstate/react'
|
||||||
import { editorManager } from 'lib/singletons'
|
|
||||||
import { commandBarMachine } from 'machines/commandBarMachine'
|
import { commandBarMachine } from 'machines/commandBarMachine'
|
||||||
import { createContext, useEffect } from 'react'
|
import { createContext } from 'react'
|
||||||
import { EventFrom, StateFrom } from 'xstate'
|
import { EventFrom, StateFrom } from 'xstate'
|
||||||
|
|
||||||
type CommandsContextType = {
|
type CommandsContextType = {
|
||||||
@ -31,10 +30,6 @@ export const CommandBarProvider = ({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
editorManager.setCommandBarSend(commandBarSend)
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CommandsContext.Provider
|
<CommandsContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
@ -3,12 +3,13 @@ import { useCommandsContext } from 'hooks/useCommandsContext'
|
|||||||
import { useKclContext } from 'lang/KclProvider'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
import { CommandArgument } from 'lib/commandTypes'
|
import { CommandArgument } from 'lib/commandTypes'
|
||||||
import {
|
import {
|
||||||
|
ResolvedSelectionType,
|
||||||
canSubmitSelectionArg,
|
canSubmitSelectionArg,
|
||||||
getSelectionType,
|
getSelectionType,
|
||||||
getSelectionTypeDisplayText,
|
getSelectionTypeDisplayText,
|
||||||
} from 'lib/selections'
|
} from 'lib/selections'
|
||||||
import { modelingMachine } from 'machines/modelingMachine'
|
import { modelingMachine } from 'machines/modelingMachine'
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
import { StateFrom } from 'xstate'
|
import { StateFrom } from 'xstate'
|
||||||
|
|
||||||
@ -29,13 +30,13 @@ function CommandBarSelectionInput({
|
|||||||
const { commandBarState, commandBarSend } = useCommandsContext()
|
const { commandBarState, commandBarSend } = useCommandsContext()
|
||||||
const [hasSubmitted, setHasSubmitted] = useState(false)
|
const [hasSubmitted, setHasSubmitted] = useState(false)
|
||||||
const selection = useSelector(arg.machineActor, selectionSelector)
|
const selection = useSelector(arg.machineActor, selectionSelector)
|
||||||
const initSelectionsByType = useCallback(() => {
|
const [selectionsByType, setSelectionsByType] = useState<
|
||||||
const selectionRangeEnd = selection.codeBasedSelections[0]?.range[1]
|
'none' | ResolvedSelectionType[]
|
||||||
return !selectionRangeEnd || selectionRangeEnd === code.length
|
>(
|
||||||
|
selection.codeBasedSelections[0]?.range[1] === code.length
|
||||||
? 'none'
|
? 'none'
|
||||||
: getSelectionType(selection)
|
: getSelectionType(selection)
|
||||||
}, [selection, code])
|
)
|
||||||
const selectionsByType = initSelectionsByType()
|
|
||||||
const [canSubmitSelection, setCanSubmitSelection] = useState<boolean>(
|
const [canSubmitSelection, setCanSubmitSelection] = useState<boolean>(
|
||||||
canSubmitSelectionArg(selectionsByType, arg)
|
canSubmitSelectionArg(selectionsByType, arg)
|
||||||
)
|
)
|
||||||
@ -50,14 +51,17 @@ function CommandBarSelectionInput({
|
|||||||
inputRef.current?.focus()
|
inputRef.current?.focus()
|
||||||
}, [selection, inputRef])
|
}, [selection, inputRef])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectionsByType(
|
||||||
|
selection.codeBasedSelections[0]?.range[1] === code.length
|
||||||
|
? 'none'
|
||||||
|
: getSelectionType(selection)
|
||||||
|
)
|
||||||
|
}, [selection])
|
||||||
|
|
||||||
// Fast-forward through this arg if it's marked as skippable
|
// Fast-forward through this arg if it's marked as skippable
|
||||||
// and we have a valid selection already
|
// and we have a valid selection already
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('selection input effect', {
|
|
||||||
selectionsByType,
|
|
||||||
canSubmitSelection,
|
|
||||||
arg,
|
|
||||||
})
|
|
||||||
setCanSubmitSelection(canSubmitSelectionArg(selectionsByType, arg))
|
setCanSubmitSelection(canSubmitSelectionArg(selectionsByType, arg))
|
||||||
const argValue = commandBarState.context.argumentsToSubmit[arg.name]
|
const argValue = commandBarState.context.argumentsToSubmit[arg.name]
|
||||||
if (canSubmitSelection && arg.skip && argValue === undefined) {
|
if (canSubmitSelection && arg.skip && argValue === undefined) {
|
||||||
|
@ -17,7 +17,6 @@ import {
|
|||||||
sceneInfra,
|
sceneInfra,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
codeManager,
|
codeManager,
|
||||||
editorManager,
|
|
||||||
} from 'lib/singletons'
|
} from 'lib/singletons'
|
||||||
import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance'
|
import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance'
|
||||||
import {
|
import {
|
||||||
@ -54,9 +53,10 @@ import { exportFromEngine } from 'lib/exportFromEngine'
|
|||||||
import { Models } from '@kittycad/lib/dist/types/src'
|
import { Models } from '@kittycad/lib/dist/types/src'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { EditorSelection } from '@uiw/react-codemirror'
|
import { EditorSelection } from '@uiw/react-codemirror'
|
||||||
|
import { Vector3 } from 'three'
|
||||||
|
import { quaternionFromUpNForward } from 'clientSideScene/helpers'
|
||||||
import { CoreDumpManager } from 'lib/coredump'
|
import { CoreDumpManager } from 'lib/coredump'
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls'
|
|
||||||
|
|
||||||
type MachineContext<T extends AnyStateMachine> = {
|
type MachineContext<T extends AnyStateMachine> = {
|
||||||
state: StateFrom<T>
|
state: StateFrom<T>
|
||||||
@ -77,7 +77,7 @@ export const ModelingMachineProvider = ({
|
|||||||
auth,
|
auth,
|
||||||
settings: {
|
settings: {
|
||||||
context: {
|
context: {
|
||||||
app: { theme, enableSSAO },
|
app: { theme },
|
||||||
modeling: { defaultUnit, highlightEdges },
|
modeling: { defaultUnit, highlightEdges },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -87,7 +87,6 @@ export const ModelingMachineProvider = ({
|
|||||||
useSetupEngineManager(streamRef, token, {
|
useSetupEngineManager(streamRef, token, {
|
||||||
theme: theme.current,
|
theme: theme.current,
|
||||||
highlightEdges: highlightEdges.current,
|
highlightEdges: highlightEdges.current,
|
||||||
enableSSAO: enableSSAO.current,
|
|
||||||
})
|
})
|
||||||
const { htmlRef } = useStore((s) => ({
|
const { htmlRef } = useStore((s) => ({
|
||||||
htmlRef: s.htmlRef,
|
htmlRef: s.htmlRef,
|
||||||
@ -99,6 +98,17 @@ export const ModelingMachineProvider = ({
|
|||||||
)
|
)
|
||||||
useHotkeys('meta + shift + .', () => coreDump(coreDumpManager, true))
|
useHotkeys('meta + shift + .', () => coreDump(coreDumpManager, true))
|
||||||
|
|
||||||
|
const {
|
||||||
|
isShiftDown,
|
||||||
|
editorView,
|
||||||
|
setLastCodeMirrorSelectionUpdatedFromScene,
|
||||||
|
} = useStore((s) => ({
|
||||||
|
isShiftDown: s.isShiftDown,
|
||||||
|
editorView: s.editorView,
|
||||||
|
setLastCodeMirrorSelectionUpdatedFromScene:
|
||||||
|
s.setLastCodeMirrorSelectionUpdatedFromScene,
|
||||||
|
}))
|
||||||
|
|
||||||
// Settings machine setup
|
// Settings machine setup
|
||||||
// const retrievedSettings = useRef(
|
// const retrievedSettings = useRef(
|
||||||
// localStorage?.getItem(MODELING_PERSIST_KEY) || '{}'
|
// localStorage?.getItem(MODELING_PERSIST_KEY) || '{}'
|
||||||
@ -125,33 +135,29 @@ export const ModelingMachineProvider = ({
|
|||||||
'Set selection': assign(({ selectionRanges }, event) => {
|
'Set selection': assign(({ selectionRanges }, event) => {
|
||||||
if (event.type !== 'Set selection') return {} // this was needed for ts after adding 'Set selection' action to on done modal events
|
if (event.type !== 'Set selection') return {} // this was needed for ts after adding 'Set selection' action to on done modal events
|
||||||
const setSelections = event.data
|
const setSelections = event.data
|
||||||
if (!editorManager.editorView) return {}
|
if (!editorView) return {}
|
||||||
const dispatchSelection = (selection?: EditorSelection) => {
|
const dispatchSelection = (selection?: EditorSelection) => {
|
||||||
if (!selection) return // TODO less of hack for the below please
|
if (!selection) return // TODO less of hack for the below please
|
||||||
editorManager.lastSelectionEvent = Date.now()
|
setLastCodeMirrorSelectionUpdatedFromScene(Date.now())
|
||||||
setTimeout(() => {
|
setTimeout(() => editorView.dispatch({ selection }))
|
||||||
if (editorManager.editorView) {
|
|
||||||
editorManager.editorView.dispatch({ selection })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
let selections: Selections = {
|
let selections: Selections = {
|
||||||
codeBasedSelections: [],
|
codeBasedSelections: [],
|
||||||
otherSelections: [],
|
otherSelections: [],
|
||||||
}
|
}
|
||||||
if (setSelections.selectionType === 'singleCodeCursor') {
|
if (setSelections.selectionType === 'singleCodeCursor') {
|
||||||
if (!setSelections.selection && editorManager.isShiftDown) {
|
if (!setSelections.selection && isShiftDown) {
|
||||||
} else if (!setSelections.selection && !editorManager.isShiftDown) {
|
} else if (!setSelections.selection && !isShiftDown) {
|
||||||
selections = {
|
selections = {
|
||||||
codeBasedSelections: [],
|
codeBasedSelections: [],
|
||||||
otherSelections: [],
|
otherSelections: [],
|
||||||
}
|
}
|
||||||
} else if (setSelections.selection && !editorManager.isShiftDown) {
|
} else if (setSelections.selection && !isShiftDown) {
|
||||||
selections = {
|
selections = {
|
||||||
codeBasedSelections: [setSelections.selection],
|
codeBasedSelections: [setSelections.selection],
|
||||||
otherSelections: [],
|
otherSelections: [],
|
||||||
}
|
}
|
||||||
} else if (setSelections.selection && editorManager.isShiftDown) {
|
} else if (setSelections.selection && isShiftDown) {
|
||||||
selections = {
|
selections = {
|
||||||
codeBasedSelections: [
|
codeBasedSelections: [
|
||||||
...selectionRanges.codeBasedSelections,
|
...selectionRanges.codeBasedSelections,
|
||||||
@ -174,7 +180,6 @@ export const ModelingMachineProvider = ({
|
|||||||
engineCommandManager.sendSceneCommand(event)
|
engineCommandManager.sendSceneCommand(event)
|
||||||
)
|
)
|
||||||
updateSceneObjectColors()
|
updateSceneObjectColors()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
selectionRanges: selections,
|
selectionRanges: selections,
|
||||||
}
|
}
|
||||||
@ -187,7 +192,7 @@ export const ModelingMachineProvider = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (setSelections.selectionType === 'otherSelection') {
|
if (setSelections.selectionType === 'otherSelection') {
|
||||||
if (editorManager.isShiftDown) {
|
if (isShiftDown) {
|
||||||
selections = {
|
selections = {
|
||||||
codeBasedSelections: selectionRanges.codeBasedSelections,
|
codeBasedSelections: selectionRanges.codeBasedSelections,
|
||||||
otherSelections: [setSelections.selection],
|
otherSelections: [setSelections.selection],
|
||||||
@ -268,12 +273,10 @@ export const ModelingMachineProvider = ({
|
|||||||
'has valid extrude selection': ({ selectionRanges }) => {
|
'has valid extrude selection': ({ selectionRanges }) => {
|
||||||
// A user can begin extruding if they either have 1+ faces selected or nothing selected
|
// A user can begin extruding if they either have 1+ faces selected or nothing selected
|
||||||
// TODO: I believe this guard only allows for extruding a single face at a time
|
// TODO: I believe this guard only allows for extruding a single face at a time
|
||||||
|
if (selectionRanges.codeBasedSelections.length < 1) return false
|
||||||
const isPipe = isSketchPipe(selectionRanges)
|
const isPipe = isSketchPipe(selectionRanges)
|
||||||
|
|
||||||
if (
|
if (isSelectionLastLine(selectionRanges, codeManager.code))
|
||||||
selectionRanges.codeBasedSelections.length === 0 ||
|
|
||||||
isSelectionLastLine(selectionRanges, codeManager.code)
|
|
||||||
)
|
|
||||||
return true
|
return true
|
||||||
if (!isPipe) return false
|
if (!isPipe) return false
|
||||||
|
|
||||||
@ -321,9 +324,16 @@ export const ModelingMachineProvider = ({
|
|||||||
)
|
)
|
||||||
await kclManager.executeAstMock(modifiedAst)
|
await kclManager.executeAstMock(modifiedAst)
|
||||||
|
|
||||||
await letEngineAnimateAndSyncCamAfter(
|
const forward = new Vector3(...data.zAxis)
|
||||||
engineCommandManager,
|
const up = new Vector3(...data.yAxis)
|
||||||
data.faceId
|
|
||||||
|
let target = new Vector3(...data.position).multiplyScalar(
|
||||||
|
sceneInfra._baseUnitMultiplier
|
||||||
|
)
|
||||||
|
const quaternion = quaternionFromUpNForward(up, forward)
|
||||||
|
await sceneInfra.camControls.tweenCameraToQuaternion(
|
||||||
|
quaternion,
|
||||||
|
target
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -338,7 +348,6 @@ export const ModelingMachineProvider = ({
|
|||||||
data.plane
|
data.plane
|
||||||
)
|
)
|
||||||
await kclManager.updateAst(modifiedAst, false)
|
await kclManager.updateAst(modifiedAst, false)
|
||||||
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
|
||||||
const quat = await getSketchQuaternion(pathToNode, data.zAxis)
|
const quat = await getSketchQuaternion(pathToNode, data.zAxis)
|
||||||
await sceneInfra.camControls.tweenCameraToQuaternion(quat)
|
await sceneInfra.camControls.tweenCameraToQuaternion(quat)
|
||||||
return {
|
return {
|
||||||
@ -355,9 +364,9 @@ export const ModelingMachineProvider = ({
|
|||||||
sourceRange
|
sourceRange
|
||||||
)
|
)
|
||||||
const info = await getSketchOrientationDetails(sketchPathToNode || [])
|
const info = await getSketchOrientationDetails(sketchPathToNode || [])
|
||||||
await letEngineAnimateAndSyncCamAfter(
|
await sceneInfra.camControls.tweenCameraToQuaternion(
|
||||||
engineCommandManager,
|
info.quat,
|
||||||
info?.sketchDetails?.faceId || ''
|
new Vector3(...info.sketchDetails.origin)
|
||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
sketchPathToNode: sketchPathToNode || [],
|
sketchPathToNode: sketchPathToNode || [],
|
||||||
@ -507,19 +516,6 @@ export const ModelingMachineProvider = ({
|
|||||||
})
|
})
|
||||||
}, [modelingSend])
|
}, [modelingSend])
|
||||||
|
|
||||||
// Give the state back to the editorManager.
|
|
||||||
useEffect(() => {
|
|
||||||
editorManager.modelingSend = modelingSend
|
|
||||||
}, [modelingSend])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
editorManager.modelingEvent = modelingState.event
|
|
||||||
}, [modelingState.event])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
editorManager.selectionRanges = modelingState.context.selectionRanges
|
|
||||||
}, [modelingState.context.selectionRanges])
|
|
||||||
|
|
||||||
useStateMachineCommands({
|
useStateMachineCommands({
|
||||||
machineId: 'modeling',
|
machineId: 'modeling',
|
||||||
state: modelingState,
|
state: modelingState,
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
|
import { undo, redo } from '@codemirror/commands'
|
||||||
import ReactCodeMirror from '@uiw/react-codemirror'
|
import ReactCodeMirror from '@uiw/react-codemirror'
|
||||||
import { TEST } from 'env'
|
import { TEST } from 'env'
|
||||||
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
|
import { useConvertToVariable } from 'hooks/useToolbarGuards'
|
||||||
import { Themes, getSystemTheme } from 'lib/theme'
|
import { Themes, getSystemTheme } from 'lib/theme'
|
||||||
import { useEffect, useMemo } from 'react'
|
import { useEffect, useMemo, useRef } from 'react'
|
||||||
|
import { useStore } from 'useStore'
|
||||||
|
import { processCodeMirrorRanges } from 'lib/selections'
|
||||||
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search'
|
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search'
|
||||||
import { lineHighlightField } from 'editor/highlightextension'
|
import { lineHighlightField } from 'editor/highlightextension'
|
||||||
import { roundOff } from 'lib/utils'
|
import { roundOff } from 'lib/utils'
|
||||||
@ -16,6 +21,7 @@ import {
|
|||||||
EditorView,
|
EditorView,
|
||||||
dropCursor,
|
dropCursor,
|
||||||
drawSelection,
|
drawSelection,
|
||||||
|
ViewUpdate,
|
||||||
} from '@codemirror/view'
|
} from '@codemirror/view'
|
||||||
import {
|
import {
|
||||||
indentWithTab,
|
indentWithTab,
|
||||||
@ -23,7 +29,7 @@ import {
|
|||||||
historyKeymap,
|
historyKeymap,
|
||||||
history,
|
history,
|
||||||
} from '@codemirror/commands'
|
} from '@codemirror/commands'
|
||||||
import { lintGutter, lintKeymap } from '@codemirror/lint'
|
import { lintGutter, lintKeymap, linter } from '@codemirror/lint'
|
||||||
import {
|
import {
|
||||||
foldGutter,
|
foldGutter,
|
||||||
foldKeymap,
|
foldKeymap,
|
||||||
@ -33,20 +39,25 @@ import {
|
|||||||
syntaxHighlighting,
|
syntaxHighlighting,
|
||||||
defaultHighlightStyle,
|
defaultHighlightStyle,
|
||||||
} from '@codemirror/language'
|
} from '@codemirror/language'
|
||||||
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import interact from '@replit/codemirror-interact'
|
import interact from '@replit/codemirror-interact'
|
||||||
import { kclManager, editorManager, codeManager } from 'lib/singletons'
|
import { engineCommandManager, sceneInfra, kclManager } from 'lib/singletons'
|
||||||
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
|
import { ModelingMachineEvent } from 'machines/modelingMachine'
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
import { isTauri } from 'lib/isTauri'
|
import { isTauri } from 'lib/isTauri'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { paths } from 'lib/paths'
|
import { paths } from 'lib/paths'
|
||||||
import makeUrlPathRelative from 'lib/makeUrlPathRelative'
|
import makeUrlPathRelative from 'lib/makeUrlPathRelative'
|
||||||
import { useLspContext } from 'components/LspProvider'
|
import { useLspContext } from 'components/LspProvider'
|
||||||
import { Prec, EditorState, Extension } from '@codemirror/state'
|
import { Prec, EditorState, Extension, SelectionRange } from '@codemirror/state'
|
||||||
import {
|
import {
|
||||||
closeBrackets,
|
closeBrackets,
|
||||||
closeBracketsKeymap,
|
closeBracketsKeymap,
|
||||||
completionKeymap,
|
completionKeymap,
|
||||||
|
hasNextSnippetField,
|
||||||
} from '@codemirror/autocomplete'
|
} from '@codemirror/autocomplete'
|
||||||
|
import { kclErrorsToDiagnostics } from 'lang/errors'
|
||||||
|
|
||||||
export const editorShortcutMeta = {
|
export const editorShortcutMeta = {
|
||||||
formatCode: {
|
formatCode: {
|
||||||
@ -66,6 +77,13 @@ export const KclEditorPane = () => {
|
|||||||
context.app.theme.current === Themes.System
|
context.app.theme.current === Themes.System
|
||||||
? getSystemTheme()
|
? getSystemTheme()
|
||||||
: context.app.theme.current
|
: context.app.theme.current
|
||||||
|
const { editorView, setEditorView, isShiftDown } = useStore((s) => ({
|
||||||
|
editorView: s.editorView,
|
||||||
|
setEditorView: s.setEditorView,
|
||||||
|
isShiftDown: s.isShiftDown,
|
||||||
|
}))
|
||||||
|
const { editorCode, errors } = useKclContext()
|
||||||
|
const lastEvent = useRef({ event: '', time: Date.now() })
|
||||||
const { copilotLSP, kclLSP } = useLspContext()
|
const { copilotLSP, kclLSP } = useLspContext()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
@ -78,15 +96,90 @@ export const KclEditorPane = () => {
|
|||||||
|
|
||||||
useHotkeys('mod+z', (e) => {
|
useHotkeys('mod+z', (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
editorManager.undo()
|
if (editorView) {
|
||||||
|
undo(editorView)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
useHotkeys('mod+shift+z', (e) => {
|
useHotkeys('mod+shift+z', (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
editorManager.redo()
|
if (editorView) {
|
||||||
|
redo(editorView)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const textWrapping = context.textEditor.textWrapping
|
const {
|
||||||
const cursorBlinking = context.textEditor.blinkingCursor
|
context: { selectionRanges },
|
||||||
|
send,
|
||||||
|
state,
|
||||||
|
} = useModelingContext()
|
||||||
|
|
||||||
|
const { settings } = useSettingsAuthContext()
|
||||||
|
const textWrapping = settings.context.textEditor.textWrapping
|
||||||
|
const cursorBlinking = settings.context.textEditor.blinkingCursor
|
||||||
|
const { commandBarSend } = useCommandsContext()
|
||||||
|
const { enable: convertEnabled, handleClick: convertCallback } =
|
||||||
|
useConvertToVariable()
|
||||||
|
|
||||||
|
const lastSelection = useRef('')
|
||||||
|
const onUpdate = (viewUpdate: ViewUpdate) => {
|
||||||
|
// If we are just fucking around in a snippet, return early and don't
|
||||||
|
// trigger stuff below that might cause the component to re-render.
|
||||||
|
// Otherwise we will not be able to tab thru the snippet portions.
|
||||||
|
// We explicitly dont check HasPrevSnippetField because we always add
|
||||||
|
// a ${} to the end of the function so that's fine.
|
||||||
|
if (hasNextSnippetField(viewUpdate.view.state)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!editorView) {
|
||||||
|
setEditorView(viewUpdate.view)
|
||||||
|
}
|
||||||
|
const selString = stringifyRanges(
|
||||||
|
viewUpdate?.state?.selection?.ranges || []
|
||||||
|
)
|
||||||
|
if (selString === lastSelection.current) {
|
||||||
|
// onUpdate is noisy and is fired a lot by extensions
|
||||||
|
// since we're only interested in selections changes we can ignore most of these.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lastSelection.current = selString
|
||||||
|
|
||||||
|
if (
|
||||||
|
// TODO find a less lazy way of getting the last
|
||||||
|
Date.now() - useStore.getState().lastCodeMirrorSelectionUpdatedFromScene <
|
||||||
|
150
|
||||||
|
)
|
||||||
|
return // update triggered by scene selection
|
||||||
|
if (sceneInfra.selected) return // mid drag
|
||||||
|
const ignoreEvents: ModelingMachineEvent['type'][] = [
|
||||||
|
'Equip Line tool',
|
||||||
|
'Equip tangential arc to',
|
||||||
|
]
|
||||||
|
if (ignoreEvents.includes(state.event.type)) return
|
||||||
|
const eventInfo = processCodeMirrorRanges({
|
||||||
|
codeMirrorRanges: viewUpdate.state.selection.ranges,
|
||||||
|
selectionRanges,
|
||||||
|
isShiftDown,
|
||||||
|
})
|
||||||
|
if (!eventInfo) return
|
||||||
|
const deterministicEventInfo = {
|
||||||
|
...eventInfo,
|
||||||
|
engineEvents: eventInfo.engineEvents.map((e) => ({
|
||||||
|
...e,
|
||||||
|
cmd_id: 'static',
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
const stringEvent = JSON.stringify(deterministicEventInfo)
|
||||||
|
if (
|
||||||
|
stringEvent === lastEvent.current.event &&
|
||||||
|
Date.now() - lastEvent.current.time < 500
|
||||||
|
)
|
||||||
|
return // don't repeat events
|
||||||
|
lastEvent.current = { event: stringEvent, time: Date.now() }
|
||||||
|
send(eventInfo.modelingEvent)
|
||||||
|
eventInfo.engineEvents.forEach((event) =>
|
||||||
|
engineCommandManager.sendSceneCommand(event)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const editorExtensions = useMemo(() => {
|
const editorExtensions = useMemo(() => {
|
||||||
const extensions = [
|
const extensions = [
|
||||||
@ -109,7 +202,7 @@ export const KclEditorPane = () => {
|
|||||||
{
|
{
|
||||||
key: 'Meta-k',
|
key: 'Meta-k',
|
||||||
run: () => {
|
run: () => {
|
||||||
editorManager.commandBarSend({ type: 'Open' })
|
commandBarSend({ type: 'Open' })
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -123,7 +216,11 @@ export const KclEditorPane = () => {
|
|||||||
{
|
{
|
||||||
key: editorShortcutMeta.convertToVariable.codeMirror,
|
key: editorShortcutMeta.convertToVariable.codeMirror,
|
||||||
run: () => {
|
run: () => {
|
||||||
return editorManager.convertToVariable()
|
if (convertEnabled) {
|
||||||
|
convertCallback()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
@ -136,6 +233,9 @@ export const KclEditorPane = () => {
|
|||||||
if (!TEST) {
|
if (!TEST) {
|
||||||
extensions.push(
|
extensions.push(
|
||||||
lintGutter(),
|
lintGutter(),
|
||||||
|
linter((_view: EditorView) => {
|
||||||
|
return kclErrorsToDiagnostics(errors)
|
||||||
|
}),
|
||||||
lineNumbers(),
|
lineNumbers(),
|
||||||
highlightActiveLineGutter(),
|
highlightActiveLineGutter(),
|
||||||
highlightSpecialChars(),
|
highlightSpecialChars(),
|
||||||
@ -188,7 +288,13 @@ export const KclEditorPane = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return extensions
|
return extensions
|
||||||
}, [kclLSP, copilotLSP, textWrapping.current, cursorBlinking.current])
|
}, [
|
||||||
|
kclLSP,
|
||||||
|
copilotLSP,
|
||||||
|
textWrapping.current,
|
||||||
|
cursorBlinking.current,
|
||||||
|
convertCallback,
|
||||||
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -196,15 +302,18 @@ export const KclEditorPane = () => {
|
|||||||
className={'absolute inset-0 ' + (cursorBlinking.current ? 'blink' : '')}
|
className={'absolute inset-0 ' + (cursorBlinking.current ? 'blink' : '')}
|
||||||
>
|
>
|
||||||
<ReactCodeMirror
|
<ReactCodeMirror
|
||||||
value={codeManager.code}
|
value={editorCode}
|
||||||
extensions={editorExtensions}
|
extensions={editorExtensions}
|
||||||
|
onUpdate={onUpdate}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
onCreateEditor={(_editorView) =>
|
onCreateEditor={(_editorView) => setEditorView(_editorView)}
|
||||||
editorManager.setEditorView(_editorView)
|
|
||||||
}
|
|
||||||
indentWithTab={false}
|
indentWithTab={false}
|
||||||
basicSetup={false}
|
basicSetup={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stringifyRanges(ranges: readonly SelectionRange[]): string {
|
||||||
|
return ranges.map(({ to, from }) => `${to}->${from}`).join('&')
|
||||||
|
}
|
||||||
|
@ -2,9 +2,7 @@ import { processMemory } from './MemoryPane'
|
|||||||
import { enginelessExecutor } from '../../../lib/testHelpers'
|
import { enginelessExecutor } from '../../../lib/testHelpers'
|
||||||
import { initPromise, parse } from '../../../lang/wasm'
|
import { initPromise, parse } from '../../../lang/wasm'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(() => initPromise)
|
||||||
await initPromise
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('processMemory', () => {
|
describe('processMemory', () => {
|
||||||
it('should grab the values and remove and geo data', async () => {
|
it('should grab the values and remove and geo data', async () => {
|
||||||
@ -28,7 +26,7 @@ describe('processMemory', () => {
|
|||||||
|> lineTo([0.98, 5.16], %)
|
|> lineTo([0.98, 5.16], %)
|
||||||
|> lineTo([2.15, 4.32], %)
|
|> lineTo([2.15, 4.32], %)
|
||||||
// |> rx(90, %)`
|
// |> rx(90, %)`
|
||||||
const ast = parse(code)
|
const ast = await parse(code)
|
||||||
const programMemory = await enginelessExecutor(ast, {
|
const programMemory = await enginelessExecutor(ast, {
|
||||||
root: {},
|
root: {},
|
||||||
return: null,
|
return: null,
|
||||||
|
@ -7,12 +7,7 @@ import React, { createContext, useEffect } from 'react'
|
|||||||
import useStateMachineCommands from '../hooks/useStateMachineCommands'
|
import useStateMachineCommands from '../hooks/useStateMachineCommands'
|
||||||
import { settingsMachine } from 'machines/settingsMachine'
|
import { settingsMachine } from 'machines/settingsMachine'
|
||||||
import { toast } from 'react-hot-toast'
|
import { toast } from 'react-hot-toast'
|
||||||
import {
|
import { getThemeColorForEngine, setThemeClass, Themes } from 'lib/theme'
|
||||||
getThemeColorForEngine,
|
|
||||||
getOppositeTheme,
|
|
||||||
setThemeClass,
|
|
||||||
Themes,
|
|
||||||
} from 'lib/theme'
|
|
||||||
import decamelize from 'decamelize'
|
import decamelize from 'decamelize'
|
||||||
import {
|
import {
|
||||||
AnyStateMachine,
|
AnyStateMachine,
|
||||||
@ -104,9 +99,6 @@ export const SettingsAuthProviderBase = ({
|
|||||||
{
|
{
|
||||||
context: loadedSettings,
|
context: loadedSettings,
|
||||||
actions: {
|
actions: {
|
||||||
//TODO: batch all these and if that's difficult to do from tsx,
|
|
||||||
// make it easy to do
|
|
||||||
|
|
||||||
setClientSideSceneUnits: (context, event) => {
|
setClientSideSceneUnits: (context, event) => {
|
||||||
const newBaseUnit =
|
const newBaseUnit =
|
||||||
event.type === 'set.modeling.defaultUnit'
|
event.type === 'set.modeling.defaultUnit'
|
||||||
@ -123,16 +115,6 @@ export const SettingsAuthProviderBase = ({
|
|||||||
color: getThemeColorForEngine(context.app.theme.current),
|
color: getThemeColorForEngine(context.app.theme.current),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const opposingTheme = getOppositeTheme(context.app.theme.current)
|
|
||||||
engineCommandManager.sendSceneCommand({
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd: {
|
|
||||||
type: 'set_default_system_properties',
|
|
||||||
color: getThemeColorForEngine(opposingTheme),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
setEngineEdges: (context) => {
|
setEngineEdges: (context) => {
|
||||||
engineCommandManager.sendSceneCommand({
|
engineCommandManager.sendSceneCommand({
|
||||||
|
@ -1,238 +0,0 @@
|
|||||||
import { hasNextSnippetField } from '@codemirror/autocomplete'
|
|
||||||
import { EditorView, ViewUpdate } from '@codemirror/view'
|
|
||||||
import { EditorSelection, SelectionRange } from '@codemirror/state'
|
|
||||||
import { engineCommandManager, sceneInfra } from 'lib/singletons'
|
|
||||||
import { ModelingMachineEvent } from 'machines/modelingMachine'
|
|
||||||
import { Selections, processCodeMirrorRanges, Selection } from 'lib/selections'
|
|
||||||
import { undo, redo } from '@codemirror/commands'
|
|
||||||
import { CommandBarMachineEvent } from 'machines/commandBarMachine'
|
|
||||||
import { addLineHighlight } from './highlightextension'
|
|
||||||
import { setDiagnostics, Diagnostic } from '@codemirror/lint'
|
|
||||||
|
|
||||||
export default class EditorManager {
|
|
||||||
private _editorView: EditorView | null = null
|
|
||||||
|
|
||||||
private _isShiftDown: boolean = false
|
|
||||||
private _selectionRanges: Selections = {
|
|
||||||
otherSelections: [],
|
|
||||||
codeBasedSelections: [],
|
|
||||||
}
|
|
||||||
|
|
||||||
private _lastSelectionEvent: number | null = null
|
|
||||||
private _lastSelection: string = ''
|
|
||||||
private _lastEvent: { event: string; time: number } | null = null
|
|
||||||
|
|
||||||
private _modelingSend: (eventInfo: ModelingMachineEvent) => void = () => {}
|
|
||||||
private _modelingEvent: ModelingMachineEvent | null = null
|
|
||||||
|
|
||||||
private _commandBarSend: (eventInfo: CommandBarMachineEvent) => void =
|
|
||||||
() => {}
|
|
||||||
|
|
||||||
private _convertToVariableEnabled: boolean = false
|
|
||||||
private _convertToVariableCallback: () => void = () => {}
|
|
||||||
|
|
||||||
private _highlightRange: [number, number] = [0, 0]
|
|
||||||
|
|
||||||
setEditorView(editorView: EditorView) {
|
|
||||||
this._editorView = editorView
|
|
||||||
}
|
|
||||||
|
|
||||||
get editorView(): EditorView | null {
|
|
||||||
return this._editorView
|
|
||||||
}
|
|
||||||
|
|
||||||
get isShiftDown(): boolean {
|
|
||||||
return this._isShiftDown
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsShiftDown(isShiftDown: boolean) {
|
|
||||||
this._isShiftDown = isShiftDown
|
|
||||||
}
|
|
||||||
|
|
||||||
set selectionRanges(selectionRanges: Selections) {
|
|
||||||
this._selectionRanges = selectionRanges
|
|
||||||
}
|
|
||||||
|
|
||||||
set lastSelectionEvent(time: number) {
|
|
||||||
this._lastSelectionEvent = time
|
|
||||||
}
|
|
||||||
|
|
||||||
set modelingSend(send: (eventInfo: ModelingMachineEvent) => void) {
|
|
||||||
this._modelingSend = send
|
|
||||||
}
|
|
||||||
|
|
||||||
set modelingEvent(event: ModelingMachineEvent) {
|
|
||||||
this._modelingEvent = event
|
|
||||||
}
|
|
||||||
|
|
||||||
setCommandBarSend(send: (eventInfo: CommandBarMachineEvent) => void) {
|
|
||||||
this._commandBarSend = send
|
|
||||||
}
|
|
||||||
|
|
||||||
commandBarSend(eventInfo: CommandBarMachineEvent): void {
|
|
||||||
return this._commandBarSend(eventInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
get highlightRange(): [number, number] {
|
|
||||||
return this._highlightRange
|
|
||||||
}
|
|
||||||
|
|
||||||
setHighlightRange(selection: Selection['range']): void {
|
|
||||||
this._highlightRange = selection
|
|
||||||
const editorView = this.editorView
|
|
||||||
const safeEnd = Math.min(
|
|
||||||
selection[1],
|
|
||||||
editorView?.state.doc.length || selection[1]
|
|
||||||
)
|
|
||||||
if (editorView) {
|
|
||||||
editorView.dispatch({
|
|
||||||
effects: addLineHighlight.of([selection[0], safeEnd]),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setDiagnostics(diagnostics: Diagnostic[]): void {
|
|
||||||
if (!this.editorView) return
|
|
||||||
this.editorView.dispatch(setDiagnostics(this.editorView.state, diagnostics))
|
|
||||||
}
|
|
||||||
|
|
||||||
undo() {
|
|
||||||
if (this._editorView) {
|
|
||||||
undo(this._editorView)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
redo() {
|
|
||||||
if (this._editorView) {
|
|
||||||
redo(this._editorView)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set convertToVariableEnabled(enabled: boolean) {
|
|
||||||
this._convertToVariableEnabled = enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
set convertToVariableCallback(callback: () => void) {
|
|
||||||
this._convertToVariableCallback = callback
|
|
||||||
}
|
|
||||||
|
|
||||||
convertToVariable() {
|
|
||||||
if (this._convertToVariableEnabled) {
|
|
||||||
this._convertToVariableCallback()
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
selectRange(selections: Selections) {
|
|
||||||
if (selections.codeBasedSelections.length === 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!this.editorView) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let codeBasedSelections = []
|
|
||||||
for (const selection of selections.codeBasedSelections) {
|
|
||||||
codeBasedSelections.push(
|
|
||||||
EditorSelection.range(selection.range[0], selection.range[1])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
codeBasedSelections.push(
|
|
||||||
EditorSelection.cursor(
|
|
||||||
selections.codeBasedSelections[
|
|
||||||
selections.codeBasedSelections.length - 1
|
|
||||||
].range[1]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
this.editorView.dispatch({
|
|
||||||
selection: EditorSelection.create(codeBasedSelections, 1),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
handleOnViewUpdate(viewUpdate: ViewUpdate): void {
|
|
||||||
// If we are just fucking around in a snippet, return early and don't
|
|
||||||
// trigger stuff below that might cause the component to re-render.
|
|
||||||
// Otherwise we will not be able to tab thru the snippet portions.
|
|
||||||
// We explicitly dont check HasPrevSnippetField because we always add
|
|
||||||
// a ${} to the end of the function so that's fine.
|
|
||||||
if (hasNextSnippetField(viewUpdate.view.state)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.editorView === null) {
|
|
||||||
this.setEditorView(viewUpdate.view)
|
|
||||||
}
|
|
||||||
const selString = stringifyRanges(
|
|
||||||
viewUpdate?.state?.selection?.ranges || []
|
|
||||||
)
|
|
||||||
|
|
||||||
if (selString === this._lastSelection) {
|
|
||||||
// onUpdate is noisy and is fired a lot by extensions
|
|
||||||
// since we're only interested in selections changes we can ignore most of these.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this._lastSelection = selString
|
|
||||||
|
|
||||||
if (
|
|
||||||
this._lastSelectionEvent &&
|
|
||||||
Date.now() - this._lastSelectionEvent < 150
|
|
||||||
) {
|
|
||||||
return // update triggered by scene selection
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sceneInfra.selected) {
|
|
||||||
return // mid drag
|
|
||||||
}
|
|
||||||
|
|
||||||
const ignoreEvents: ModelingMachineEvent['type'][] = [
|
|
||||||
'Equip Line tool',
|
|
||||||
'Equip tangential arc to',
|
|
||||||
]
|
|
||||||
|
|
||||||
if (!this._modelingEvent) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ignoreEvents.includes(this._modelingEvent.type)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const eventInfo = processCodeMirrorRanges({
|
|
||||||
codeMirrorRanges: viewUpdate.state.selection.ranges,
|
|
||||||
selectionRanges: this._selectionRanges,
|
|
||||||
isShiftDown: this._isShiftDown,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!eventInfo) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const deterministicEventInfo = {
|
|
||||||
...eventInfo,
|
|
||||||
engineEvents: eventInfo.engineEvents.map((e) => ({
|
|
||||||
...e,
|
|
||||||
cmd_id: 'static',
|
|
||||||
})),
|
|
||||||
}
|
|
||||||
const stringEvent = JSON.stringify(deterministicEventInfo)
|
|
||||||
if (
|
|
||||||
this._lastEvent &&
|
|
||||||
stringEvent === this._lastEvent.event &&
|
|
||||||
Date.now() - this._lastEvent.time < 500
|
|
||||||
) {
|
|
||||||
return // don't repeat events
|
|
||||||
}
|
|
||||||
|
|
||||||
this._lastEvent = { event: stringEvent, time: Date.now() }
|
|
||||||
this._modelingSend(eventInfo.modelingEvent)
|
|
||||||
eventInfo.engineEvents.forEach((event) =>
|
|
||||||
engineCommandManager.sendSceneCommand(event)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function stringifyRanges(ranges: readonly SelectionRange[]): string {
|
|
||||||
return ranges.map(({ to, from }) => `${to}->${from}`).join('&')
|
|
||||||
}
|
|
@ -21,7 +21,7 @@ import { LanguageServerClient } from 'editor/plugins/lsp'
|
|||||||
import { Marked } from '@ts-stack/markdown'
|
import { Marked } from '@ts-stack/markdown'
|
||||||
import { posToOffset } from 'editor/plugins/lsp/util'
|
import { posToOffset } from 'editor/plugins/lsp/util'
|
||||||
import { Program, ProgramMemory } from 'lang/wasm'
|
import { Program, ProgramMemory } from 'lang/wasm'
|
||||||
import { codeManager, editorManager, kclManager } from 'lib/singletons'
|
import { codeManager, kclManager } from 'lib/singletons'
|
||||||
import type { UnitLength } from 'wasm-lib/kcl/bindings/UnitLength'
|
import type { UnitLength } from 'wasm-lib/kcl/bindings/UnitLength'
|
||||||
import { UpdateUnitsResponse } from 'wasm-lib/kcl/bindings/UpdateUnitsResponse'
|
import { UpdateUnitsResponse } from 'wasm-lib/kcl/bindings/UpdateUnitsResponse'
|
||||||
import { UpdateCanExecuteResponse } from 'wasm-lib/kcl/bindings/UpdateCanExecuteResponse'
|
import { UpdateCanExecuteResponse } from 'wasm-lib/kcl/bindings/UpdateCanExecuteResponse'
|
||||||
@ -39,8 +39,6 @@ const CompletionItemKindMap = Object.fromEntries(
|
|||||||
) as Record<CompletionItemKind, string>
|
) as Record<CompletionItemKind, string>
|
||||||
|
|
||||||
const changesDelay = 600
|
const changesDelay = 600
|
||||||
let debounceTimer: ReturnType<typeof setTimeout> | null = null
|
|
||||||
const updateDelay = 100
|
|
||||||
|
|
||||||
export class LanguageServerPlugin implements PluginValue {
|
export class LanguageServerPlugin implements PluginValue {
|
||||||
public client: LanguageServerClient
|
public client: LanguageServerClient
|
||||||
@ -49,7 +47,6 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
public workspaceFolders: LSP.WorkspaceFolder[]
|
public workspaceFolders: LSP.WorkspaceFolder[]
|
||||||
private documentVersion: number
|
private documentVersion: number
|
||||||
private foldingRanges: LSP.FoldingRange[] | null = null
|
private foldingRanges: LSP.FoldingRange[] | null = null
|
||||||
private viewUpdate: ViewUpdate | null = null
|
|
||||||
private _defferer = deferExecution((code: string) => {
|
private _defferer = deferExecution((code: string) => {
|
||||||
try {
|
try {
|
||||||
// Update the state (not the editor) with the new code.
|
// Update the state (not the editor) with the new code.
|
||||||
@ -60,10 +57,6 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
},
|
},
|
||||||
contentChanges: [{ text: code }],
|
contentChanges: [{ text: code }],
|
||||||
})
|
})
|
||||||
|
|
||||||
if (this.viewUpdate) {
|
|
||||||
editorManager.handleOnViewUpdate(this.viewUpdate)
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
@ -87,27 +80,14 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
update(viewUpdate: ViewUpdate) {
|
update({ docChanged }: ViewUpdate) {
|
||||||
this.viewUpdate = viewUpdate
|
if (!docChanged) return
|
||||||
if (!viewUpdate.docChanged) {
|
|
||||||
// debounce the view update.
|
|
||||||
// otherwise it is laggy for typing.
|
|
||||||
if (debounceTimer) {
|
|
||||||
clearTimeout(debounceTimer)
|
|
||||||
}
|
|
||||||
|
|
||||||
debounceTimer = setTimeout(() => {
|
|
||||||
editorManager.handleOnViewUpdate(viewUpdate)
|
|
||||||
}, updateDelay)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const newCode = this.view.state.doc.toString()
|
const newCode = this.view.state.doc.toString()
|
||||||
|
|
||||||
codeManager.code = newCode
|
codeManager.code = newCode
|
||||||
codeManager.writeToFile()
|
codeManager.writeToFile()
|
||||||
kclManager.executeCode()
|
kclManager.executeCode()
|
||||||
|
|
||||||
this.sendChange({
|
this.sendChange({
|
||||||
documentText: newCode,
|
documentText: newCode,
|
||||||
})
|
})
|
||||||
@ -377,9 +357,15 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
try {
|
try {
|
||||||
switch (notification.method) {
|
switch (notification.method) {
|
||||||
case 'textDocument/publishDiagnostics':
|
case 'textDocument/publishDiagnostics':
|
||||||
//const params = notification.params as PublishDiagnosticsParams
|
const params = notification.params as PublishDiagnosticsParams
|
||||||
// this is sometimes slower than our actual typing.
|
this.processDiagnostics(params)
|
||||||
//this.processDiagnostics(params)
|
// Update the kcl errors pane.
|
||||||
|
/*if (!kclManager.isExecuting) {
|
||||||
|
kclManager.kclErrors = lspDiagnosticsToKclErrors(
|
||||||
|
this.view.state.doc,
|
||||||
|
params.diagnostics
|
||||||
|
)
|
||||||
|
}*/
|
||||||
break
|
break
|
||||||
case 'window/logMessage':
|
case 'window/logMessage':
|
||||||
console.log(
|
console.log(
|
||||||
@ -399,6 +385,17 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
// The server has updated the AST, we should update elsewhere.
|
// The server has updated the AST, we should update elsewhere.
|
||||||
let updatedAst = notification.params as Program
|
let updatedAst = notification.params as Program
|
||||||
console.log('[lsp]: Updated AST', updatedAst)
|
console.log('[lsp]: Updated AST', updatedAst)
|
||||||
|
// Update the ast when we are not already executing.
|
||||||
|
/* if (!kclManager.isExecuting) {
|
||||||
|
kclManager.ast = updatedAst
|
||||||
|
// Execute the ast.
|
||||||
|
console.log('[lsp]: executing ast')
|
||||||
|
await kclManager.executeAst(updatedAst)
|
||||||
|
console.log('[lsp]: executed ast', kclManager.kclErrors)
|
||||||
|
let diagnostics = kclErrorsToDiagnostics(kclManager.kclErrors)
|
||||||
|
this.view.dispatch(setDiagnostics(this.view.state, diagnostics))
|
||||||
|
console.log('[lsp]: updated diagnostics')
|
||||||
|
}*/
|
||||||
|
|
||||||
// Update the folding ranges, since the AST has changed.
|
// Update the folding ranges, since the AST has changed.
|
||||||
// This is a hack since codemirror does not support async foldService.
|
// This is a hack since codemirror does not support async foldService.
|
||||||
|
@ -7,7 +7,5 @@ export const VITE_KC_API_BASE_URL = import.meta.env.VITE_KC_API_BASE_URL
|
|||||||
export const VITE_KC_SITE_BASE_URL = import.meta.env.VITE_KC_SITE_BASE_URL
|
export const VITE_KC_SITE_BASE_URL = import.meta.env.VITE_KC_SITE_BASE_URL
|
||||||
export const VITE_KC_CONNECTION_TIMEOUT_MS = import.meta.env
|
export const VITE_KC_CONNECTION_TIMEOUT_MS = import.meta.env
|
||||||
.VITE_KC_CONNECTION_TIMEOUT_MS
|
.VITE_KC_CONNECTION_TIMEOUT_MS
|
||||||
export const VITE_KC_WASM_OVERRIDE_URL = import.meta.env
|
|
||||||
.VITE_KC_WASM_OVERRIDE_URL
|
|
||||||
export const TEST = import.meta.env.TEST
|
export const TEST = import.meta.env.TEST
|
||||||
export const DEV = import.meta.env.DEV
|
export const DEV = import.meta.env.DEV
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { editorManager, engineCommandManager } from 'lib/singletons'
|
import { useStore } from 'useStore'
|
||||||
|
import { engineCommandManager } from 'lib/singletons'
|
||||||
import { useModelingContext } from './useModelingContext'
|
import { useModelingContext } from './useModelingContext'
|
||||||
import { getEventForSelectWithPoint } from 'lib/selections'
|
import { getEventForSelectWithPoint } from 'lib/selections'
|
||||||
|
|
||||||
export function useEngineConnectionSubscriptions() {
|
export function useEngineConnectionSubscriptions() {
|
||||||
|
const { setHighlightRange, highlightRange } = useStore((s) => ({
|
||||||
|
setHighlightRange: s.setHighlightRange,
|
||||||
|
highlightRange: s.highlightRange,
|
||||||
|
}))
|
||||||
const { send, context } = useModelingContext()
|
const { send, context } = useModelingContext()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -16,13 +21,12 @@ export function useEngineConnectionSubscriptions() {
|
|||||||
if (data?.entity_id) {
|
if (data?.entity_id) {
|
||||||
const sourceRange =
|
const sourceRange =
|
||||||
engineCommandManager.artifactMap?.[data.entity_id]?.range
|
engineCommandManager.artifactMap?.[data.entity_id]?.range
|
||||||
editorManager.setHighlightRange(sourceRange)
|
setHighlightRange(sourceRange)
|
||||||
} else if (
|
} else if (
|
||||||
!editorManager.highlightRange ||
|
!highlightRange ||
|
||||||
(editorManager.highlightRange[0] !== 0 &&
|
(highlightRange[0] !== 0 && highlightRange[1] !== 0)
|
||||||
editorManager.highlightRange[1] !== 0)
|
|
||||||
) {
|
) {
|
||||||
editorManager.setHighlightRange([0, 0])
|
setHighlightRange([0, 0])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -39,5 +43,10 @@ export function useEngineConnectionSubscriptions() {
|
|||||||
unSubHover()
|
unSubHover()
|
||||||
unSubClick()
|
unSubClick()
|
||||||
}
|
}
|
||||||
}, [engineCommandManager, context?.sketchEnginePathId])
|
}, [
|
||||||
|
engineCommandManager,
|
||||||
|
setHighlightRange,
|
||||||
|
highlightRange,
|
||||||
|
context?.sketchEnginePathId,
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { editorManager } from 'lib/singletons'
|
import { useStore } from '../useStore'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
// Kurt's note: codeMirror styling overrides were needed to make this work
|
// Kurt's note: codeMirror styling overrides were needed to make this work
|
||||||
@ -6,17 +6,20 @@ import { useEffect } from 'react'
|
|||||||
// search for code-mirror-override in the repo to find the relevant styles
|
// search for code-mirror-override in the repo to find the relevant styles
|
||||||
|
|
||||||
export function useHotKeyListener() {
|
export function useHotKeyListener() {
|
||||||
|
const { setIsShiftDown } = useStore((s) => ({
|
||||||
|
setIsShiftDown: s.setIsShiftDown,
|
||||||
|
}))
|
||||||
const keyName = 'Shift'
|
const keyName = 'Shift'
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (event: KeyboardEvent) =>
|
const handleKeyDown = (event: KeyboardEvent) =>
|
||||||
event.key === keyName && editorManager.setIsShiftDown(true)
|
event.key === keyName && setIsShiftDown(true)
|
||||||
const handleKeyUp = (event: KeyboardEvent) =>
|
const handleKeyUp = (event: KeyboardEvent) =>
|
||||||
event.key === keyName && editorManager.setIsShiftDown(false)
|
event.key === keyName && setIsShiftDown(false)
|
||||||
window.addEventListener('keydown', handleKeyDown)
|
window.addEventListener('keydown', handleKeyDown)
|
||||||
window.addEventListener('keyup', handleKeyUp)
|
window.addEventListener('keyup', handleKeyUp)
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('keydown', handleKeyDown)
|
window.removeEventListener('keydown', handleKeyDown)
|
||||||
window.removeEventListener('keyup', handleKeyUp)
|
window.removeEventListener('keyup', handleKeyUp)
|
||||||
}
|
}
|
||||||
})
|
}, [setIsShiftDown])
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,9 @@ export function useSetupEngineManager(
|
|||||||
settings = {
|
settings = {
|
||||||
theme: Themes.System,
|
theme: Themes.System,
|
||||||
highlightEdges: true,
|
highlightEdges: true,
|
||||||
enableSSAO: true,
|
|
||||||
} as {
|
} as {
|
||||||
theme: Themes
|
theme: Themes
|
||||||
highlightEdges: boolean
|
highlightEdges: boolean
|
||||||
enableSSAO: boolean
|
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
|
@ -2,7 +2,7 @@ import {
|
|||||||
SetVarNameModal,
|
SetVarNameModal,
|
||||||
createSetVarNameModal,
|
createSetVarNameModal,
|
||||||
} from 'components/SetVarNameModal'
|
} from 'components/SetVarNameModal'
|
||||||
import { editorManager, kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { moveValueIntoNewVariable } from 'lang/modifyAst'
|
import { moveValueIntoNewVariable } from 'lang/modifyAst'
|
||||||
import { isNodeSafeToReplace } from 'lang/queryAst'
|
import { isNodeSafeToReplace } from 'lang/queryAst'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
@ -13,11 +13,6 @@ const getModalInfo = createSetVarNameModal(SetVarNameModal)
|
|||||||
export function useConvertToVariable() {
|
export function useConvertToVariable() {
|
||||||
const { context } = useModelingContext()
|
const { context } = useModelingContext()
|
||||||
const [enable, setEnabled] = useState(false)
|
const [enable, setEnabled] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
editorManager.convertToVariableEnabled = enable
|
|
||||||
}, [enable])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { isSafe, value } = isNodeSafeToReplace(
|
const { isSafe, value } = isNodeSafeToReplace(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
@ -50,7 +45,5 @@ export function useConvertToVariable() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
editorManager.convertToVariableCallback = handleClick
|
|
||||||
|
|
||||||
return { enable, handleClick }
|
return { enable, handleClick }
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,12 @@ import { KCLError } from './errors'
|
|||||||
import { createContext, useContext, useEffect, useState } from 'react'
|
import { createContext, useContext, useEffect, useState } from 'react'
|
||||||
import { type IndexLoaderData } from 'lib/types'
|
import { type IndexLoaderData } from 'lib/types'
|
||||||
import { useLoaderData } from 'react-router-dom'
|
import { useLoaderData } from 'react-router-dom'
|
||||||
|
import { useParams } from 'react-router-dom'
|
||||||
import { codeManager, kclManager } from 'lib/singletons'
|
import { codeManager, kclManager } from 'lib/singletons'
|
||||||
|
|
||||||
const KclContext = createContext({
|
const KclContext = createContext({
|
||||||
code: codeManager?.code || '',
|
code: codeManager?.code || '',
|
||||||
|
editorCode: codeManager?.code || '',
|
||||||
programMemory: kclManager?.programMemory,
|
programMemory: kclManager?.programMemory,
|
||||||
ast: kclManager?.ast,
|
ast: kclManager?.ast,
|
||||||
isExecuting: kclManager?.isExecuting,
|
isExecuting: kclManager?.isExecuting,
|
||||||
@ -28,6 +30,7 @@ export function KclContextProvider({
|
|||||||
const { code: loadedCode } = useLoaderData() as IndexLoaderData
|
const { code: loadedCode } = useLoaderData() as IndexLoaderData
|
||||||
// Both the code state and the editor state start off with the same code.
|
// Both the code state and the editor state start off with the same code.
|
||||||
const [code, setCode] = useState(loadedCode || codeManager.code)
|
const [code, setCode] = useState(loadedCode || codeManager.code)
|
||||||
|
const [editorCode, setEditorCode] = useState(code)
|
||||||
|
|
||||||
const [programMemory, setProgramMemory] = useState(kclManager.programMemory)
|
const [programMemory, setProgramMemory] = useState(kclManager.programMemory)
|
||||||
const [ast, setAst] = useState(kclManager.ast)
|
const [ast, setAst] = useState(kclManager.ast)
|
||||||
@ -39,6 +42,7 @@ export function KclContextProvider({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
codeManager.registerCallBacks({
|
codeManager.registerCallBacks({
|
||||||
setCode,
|
setCode,
|
||||||
|
setEditorCode,
|
||||||
})
|
})
|
||||||
kclManager.registerCallBacks({
|
kclManager.registerCallBacks({
|
||||||
setProgramMemory,
|
setProgramMemory,
|
||||||
@ -50,10 +54,15 @@ export function KclContextProvider({
|
|||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const params = useParams()
|
||||||
|
useEffect(() => {
|
||||||
|
codeManager.setParams(params)
|
||||||
|
}, [params])
|
||||||
return (
|
return (
|
||||||
<KclContext.Provider
|
<KclContext.Provider
|
||||||
value={{
|
value={{
|
||||||
code,
|
code,
|
||||||
|
editorCode,
|
||||||
programMemory,
|
programMemory,
|
||||||
ast,
|
ast,
|
||||||
isExecuting,
|
isExecuting,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { executeAst } from 'useStore'
|
import { executeAst } from 'useStore'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { KCLError, kclErrorsToDiagnostics } from './errors'
|
import { KCLError } from './errors'
|
||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
import { EngineCommandManager } from './std/engineConnection'
|
import { EngineCommandManager } from './std/engineConnection'
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ import {
|
|||||||
ExtrudeGroup,
|
ExtrudeGroup,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import { getNodeFromPath } from './queryAst'
|
import { getNodeFromPath } from './queryAst'
|
||||||
import { codeManager, editorManager } from 'lib/singletons'
|
import { codeManager } from 'lib/singletons'
|
||||||
|
|
||||||
export class KclManager {
|
export class KclManager {
|
||||||
private _ast: Program = {
|
private _ast: Program = {
|
||||||
@ -43,14 +43,16 @@ export class KclManager {
|
|||||||
const ast = this.safeParse(code)
|
const ast = this.safeParse(code)
|
||||||
if (!ast) return
|
if (!ast) return
|
||||||
try {
|
try {
|
||||||
|
parse(recast(ast)).then((newAst) => {
|
||||||
const fmtAndStringify = (ast: Program) =>
|
const fmtAndStringify = (ast: Program) =>
|
||||||
JSON.stringify(parse(recast(ast)))
|
JSON.stringify(newAst)
|
||||||
const isAstTheSame = fmtAndStringify(ast) === fmtAndStringify(this._ast)
|
const isAstTheSame = fmtAndStringify(ast) === fmtAndStringify(this._ast)
|
||||||
if (isAstTheSame) return
|
if (isAstTheSame) return
|
||||||
|
this.executeAst(ast)
|
||||||
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
this.executeAst(ast)
|
|
||||||
}, 600)
|
}, 600)
|
||||||
|
|
||||||
private _isExecutingCallback: (arg: boolean) => void = () => {}
|
private _isExecutingCallback: (arg: boolean) => void = () => {}
|
||||||
@ -90,8 +92,6 @@ export class KclManager {
|
|||||||
}
|
}
|
||||||
set kclErrors(kclErrors) {
|
set kclErrors(kclErrors) {
|
||||||
this._kclErrors = kclErrors
|
this._kclErrors = kclErrors
|
||||||
let diagnostics = kclErrorsToDiagnostics(kclErrors)
|
|
||||||
editorManager.setDiagnostics(diagnostics)
|
|
||||||
this._kclErrorsCallBack(kclErrors)
|
this._kclErrorsCallBack(kclErrors)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,9 +145,9 @@ export class KclManager {
|
|||||||
this._executeCallback = callback
|
this._executeCallback = callback
|
||||||
}
|
}
|
||||||
|
|
||||||
safeParse(code: string): Program | null {
|
async safeParse(code: string): Promise<Program | null> {
|
||||||
try {
|
try {
|
||||||
const ast = parse(code)
|
const ast = await parse(code)
|
||||||
this.kclErrors = []
|
this.kclErrors = []
|
||||||
return ast
|
return ast
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -347,16 +347,6 @@ export class KclManager {
|
|||||||
void this.engineCommandManager.setPlaneHidden(this.defaultPlanes.yz, true)
|
void this.engineCommandManager.setPlaneHidden(this.defaultPlanes.yz, true)
|
||||||
void this.engineCommandManager.setPlaneHidden(this.defaultPlanes.xz, true)
|
void this.engineCommandManager.setPlaneHidden(this.defaultPlanes.xz, true)
|
||||||
}
|
}
|
||||||
enterEditMode() {
|
|
||||||
enterEditMode(this.programMemory, this.engineCommandManager)
|
|
||||||
}
|
|
||||||
exitEditMode() {
|
|
||||||
this.engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: { type: 'edit_mode_exit' },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function enterEditMode(
|
function enterEditMode(
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import { KCLError } from './errors'
|
import { KCLError } from './errors'
|
||||||
import { initPromise, parse } from './wasm'
|
import { initPromise, parse } from './wasm'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(() => initPromise)
|
||||||
await initPromise
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('testing AST', () => {
|
describe('testing AST', () => {
|
||||||
test('5 + 6', () => {
|
test('5 + 6', async () => {
|
||||||
const result = parse('5 +6')
|
const result = await parse('5 +6')
|
||||||
delete (result as any).nonCodeMeta
|
delete (result as any).nonCodeMeta
|
||||||
expect(result.body).toEqual([
|
expect(result.body).toEqual([
|
||||||
{
|
{
|
||||||
@ -37,8 +35,8 @@ describe('testing AST', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('const myVar = 5', () => {
|
test('const myVar = 5', async () => {
|
||||||
const { body } = parse('const myVar = 5')
|
const { body } = await parse('const myVar = 5')
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -68,11 +66,11 @@ describe('testing AST', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('multi-line', () => {
|
test('multi-line', async () => {
|
||||||
const code = `const myVar = 5
|
const code = `const myVar = 5
|
||||||
const newVar = myVar + 1
|
const newVar = myVar + 1
|
||||||
`
|
`
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -143,8 +141,8 @@ const newVar = myVar + 1
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('testing function declaration', () => {
|
describe('testing function declaration', () => {
|
||||||
test('fn funcN = (a, b) => {return a + b}', () => {
|
test('fn funcN = (a, b) => {return a + b}', async () => {
|
||||||
const { body } = parse(
|
const { body } = await parse(
|
||||||
['fn funcN = (a, b) => {', ' return a + b', '}'].join('\n')
|
['fn funcN = (a, b) => {', ' return a + b', '}'].join('\n')
|
||||||
)
|
)
|
||||||
delete (body[0] as any).declarations[0].init.body.nonCodeMeta
|
delete (body[0] as any).declarations[0].init.body.nonCodeMeta
|
||||||
@ -226,10 +224,10 @@ describe('testing function declaration', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('call expression assignment', () => {
|
test('call expression assignment', async () => {
|
||||||
const code = `fn funcN = (a, b) => { return a + b }
|
const code = `fn funcN = (a, b) => { return a + b }
|
||||||
const myVar = funcN(1, 2)`
|
const myVar = funcN(1, 2)`
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
delete (body[0] as any).declarations[0].init.body.nonCodeMeta
|
delete (body[0] as any).declarations[0].init.body.nonCodeMeta
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
@ -359,14 +357,14 @@ const myVar = funcN(1, 2)`
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('testing pipe operator special', () => {
|
describe('testing pipe operator special', () => {
|
||||||
test('pipe operator with sketch', () => {
|
test('pipe operator with sketch', async () => {
|
||||||
let code = `const mySketch = startSketchAt([0, 0])
|
let code = `const mySketch = startSketchAt([0, 0])
|
||||||
|> lineTo([2, 3], %)
|
|> lineTo([2, 3], %)
|
||||||
|> lineTo([0, 1], %, "myPath")
|
|> lineTo([0, 1], %, "myPath")
|
||||||
|> lineTo([1, 1], %)
|
|> lineTo([1, 1], %)
|
||||||
|> rx(45, %)
|
|> rx(45, %)
|
||||||
`
|
`
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
delete (body[0] as any).declarations[0].init.nonCodeMeta
|
delete (body[0] as any).declarations[0].init.nonCodeMeta
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
@ -564,9 +562,9 @@ describe('testing pipe operator special', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('pipe operator with binary expression', () => {
|
test('pipe operator with binary expression', async () => {
|
||||||
let code = `const myVar = 5 + 6 |> myFunc(45, %)`
|
let code = `const myVar = 5 + 6 |> myFunc(45, %)`
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
delete (body as any)[0].declarations[0].init.nonCodeMeta
|
delete (body as any)[0].declarations[0].init.nonCodeMeta
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
@ -643,9 +641,9 @@ describe('testing pipe operator special', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('array expression', () => {
|
test('array expression', async () => {
|
||||||
let code = `const yo = [1, '2', three, 4 + 5]`
|
let code = `const yo = [1, '2', three, 4 + 5]`
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -715,12 +713,12 @@ describe('testing pipe operator special', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('object expression ast', () => {
|
test('object expression ast', async () => {
|
||||||
const code = [
|
const code = [
|
||||||
'const three = 3',
|
'const three = 3',
|
||||||
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
|
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -860,11 +858,11 @@ describe('testing pipe operator special', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('nested object expression ast', () => {
|
test('nested object expression ast', async () => {
|
||||||
const code = `const yo = {key: {
|
const code = `const yo = {key: {
|
||||||
key2: 'value'
|
key2: 'value'
|
||||||
}}`
|
}}`
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -930,9 +928,9 @@ describe('testing pipe operator special', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('object expression with array ast', () => {
|
test('object expression with array ast', async () => {
|
||||||
const code = `const yo = {key: [1, '2']}`
|
const code = `const yo = {key: [1, '2']}`
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -994,9 +992,9 @@ describe('testing pipe operator special', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('object memberExpression simple', () => {
|
test('object memberExpression simple', async () => {
|
||||||
const code = `const prop = yo.one.two`
|
const code = `const prop = yo.one.two`
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -1049,9 +1047,9 @@ describe('testing pipe operator special', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('object memberExpression with square braces', () => {
|
test('object memberExpression with square braces', async () => {
|
||||||
const code = `const prop = yo.one["two"]`
|
const code = `const prop = yo.one["two"]`
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -1105,9 +1103,9 @@ describe('testing pipe operator special', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
test('object memberExpression with two square braces literal and identifier', () => {
|
test('object memberExpression with two square braces literal and identifier', async () => {
|
||||||
const code = `const prop = yo["one"][two]`
|
const code = `const prop = yo["one"][two]`
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
@ -1164,9 +1162,9 @@ describe('testing pipe operator special', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('nests binary expressions correctly', () => {
|
describe('nests binary expressions correctly', () => {
|
||||||
it('works with the simple case', () => {
|
it('works with the simple case', async () => {
|
||||||
const code = `const yo = 1 + 2`
|
const code = `const yo = 1 + 2`
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
expect(body[0]).toEqual({
|
expect(body[0]).toEqual({
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -1207,10 +1205,10 @@ describe('nests binary expressions correctly', () => {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('should nest according to precedence with multiply first', () => {
|
it('should nest according to precedence with multiply first', async () => {
|
||||||
// should be binExp { binExp { lit-1 * lit-2 } + lit}
|
// should be binExp { binExp { lit-1 * lit-2 } + lit}
|
||||||
const code = `const yo = 1 * 2 + 3`
|
const code = `const yo = 1 * 2 + 3`
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
expect(body[0]).toEqual({
|
expect(body[0]).toEqual({
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -1264,10 +1262,10 @@ describe('nests binary expressions correctly', () => {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('should nest according to precedence with sum first', () => {
|
it('should nest according to precedence with sum first', async () => {
|
||||||
// should be binExp { lit-1 + binExp { lit-2 * lit-3 } }
|
// should be binExp { lit-1 + binExp { lit-2 * lit-3 } }
|
||||||
const code = `const yo = 1 + 2 * 3`
|
const code = `const yo = 1 + 2 * 3`
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
expect(body[0]).toEqual({
|
expect(body[0]).toEqual({
|
||||||
type: 'VariableDeclaration',
|
type: 'VariableDeclaration',
|
||||||
start: 0,
|
start: 0,
|
||||||
@ -1321,9 +1319,9 @@ describe('nests binary expressions correctly', () => {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('should nest properly with two operators of equal precedence', () => {
|
it('should nest properly with two operators of equal precedence', async () => {
|
||||||
const code = `const yo = 1 + 2 - 3`
|
const code = `const yo = 1 + 2 - 3`
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
expect((body[0] as any).declarations[0].init).toEqual({
|
expect((body[0] as any).declarations[0].init).toEqual({
|
||||||
type: 'BinaryExpression',
|
type: 'BinaryExpression',
|
||||||
start: 11,
|
start: 11,
|
||||||
@ -1358,9 +1356,9 @@ describe('nests binary expressions correctly', () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('should nest properly with two operators of equal (but higher) precedence', () => {
|
it('should nest properly with two operators of equal (but higher) precedence', async () => {
|
||||||
const code = `const yo = 1 * 2 / 3`
|
const code = `const yo = 1 * 2 / 3`
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
expect((body[0] as any).declarations[0].init).toEqual({
|
expect((body[0] as any).declarations[0].init).toEqual({
|
||||||
type: 'BinaryExpression',
|
type: 'BinaryExpression',
|
||||||
start: 11,
|
start: 11,
|
||||||
@ -1395,9 +1393,9 @@ describe('nests binary expressions correctly', () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('should nest properly with longer example', () => {
|
it('should nest properly with longer example', async () => {
|
||||||
const code = `const yo = 1 + 2 * (3 - 4) / 5 + 6`
|
const code = `const yo = 1 + 2 * (3 - 4) / 5 + 6`
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
const init = (body[0] as any).declarations[0].init
|
const init = (body[0] as any).declarations[0].init
|
||||||
expect(init).toEqual({
|
expect(init).toEqual({
|
||||||
type: 'BinaryExpression',
|
type: 'BinaryExpression',
|
||||||
@ -1445,7 +1443,7 @@ describe('nests binary expressions correctly', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('check nonCodeMeta data is attached to the AST correctly', () => {
|
describe('check nonCodeMeta data is attached to the AST correctly', () => {
|
||||||
it('comments between expressions', () => {
|
it('comments between expressions', async () => {
|
||||||
const code = `
|
const code = `
|
||||||
const yo = { a: { b: { c: '123' } } }
|
const yo = { a: { b: { c: '123' } } }
|
||||||
// this is a comment
|
// this is a comment
|
||||||
@ -1460,12 +1458,14 @@ const key = 'c'`
|
|||||||
value: 'this is a comment',
|
value: 'this is a comment',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
const { nonCodeMeta } = parse(code)
|
const { nonCodeMeta } = await parse(code)
|
||||||
expect(nonCodeMeta.nonCodeNodes[0][0]).toEqual(nonCodeMetaInstance)
|
expect(nonCodeMeta.nonCodeNodes[0][0]).toEqual(nonCodeMetaInstance)
|
||||||
|
|
||||||
// extra whitespace won't change it's position (0) or value (NB the start end would have changed though)
|
// extra whitespace won't change it's position (0) or value (NB the start end would have changed though)
|
||||||
const codeWithExtraStartWhitespace = '\n\n\n' + code
|
const codeWithExtraStartWhitespace = '\n\n\n' + code
|
||||||
const { nonCodeMeta: nonCodeMeta2 } = parse(codeWithExtraStartWhitespace)
|
const { nonCodeMeta: nonCodeMeta2 } = await parse(
|
||||||
|
codeWithExtraStartWhitespace
|
||||||
|
)
|
||||||
expect(nonCodeMeta2.nonCodeNodes[0][0].value).toStrictEqual(
|
expect(nonCodeMeta2.nonCodeNodes[0][0].value).toStrictEqual(
|
||||||
nonCodeMetaInstance.value
|
nonCodeMetaInstance.value
|
||||||
)
|
)
|
||||||
@ -1473,7 +1473,7 @@ const key = 'c'`
|
|||||||
nonCodeMetaInstance.start
|
nonCodeMetaInstance.start
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
it('comments nested within a block statement', () => {
|
it('comments nested within a block statement', async () => {
|
||||||
const code = `const mySketch = startSketchAt([0,0])
|
const code = `const mySketch = startSketchAt([0,0])
|
||||||
|> lineTo([0, 1], %, 'myPath')
|
|> lineTo([0, 1], %, 'myPath')
|
||||||
|> lineTo([1, 1], %) /* this is
|
|> lineTo([1, 1], %) /* this is
|
||||||
@ -1483,7 +1483,7 @@ const key = 'c'`
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
`
|
`
|
||||||
|
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
const indexOfSecondLineToExpression = 2
|
const indexOfSecondLineToExpression = 2
|
||||||
const sketchNonCodeMeta = (body as any)[0].declarations[0].init.nonCodeMeta
|
const sketchNonCodeMeta = (body as any)[0].declarations[0].init.nonCodeMeta
|
||||||
.nonCodeNodes
|
.nonCodeNodes
|
||||||
@ -1498,7 +1498,7 @@ const key = 'c'`
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('comments in a pipe expression', () => {
|
it('comments in a pipe expression', async () => {
|
||||||
const code = [
|
const code = [
|
||||||
'const mySk1 = startSketchAt([0, 0])',
|
'const mySk1 = startSketchAt([0, 0])',
|
||||||
' |> lineTo([1, 1], %)',
|
' |> lineTo([1, 1], %)',
|
||||||
@ -1508,7 +1508,7 @@ const key = 'c'`
|
|||||||
' |> rx(90, %)',
|
' |> rx(90, %)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
|
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
const sketchNonCodeMeta = (body[0] as any).declarations[0].init.nonCodeMeta
|
const sketchNonCodeMeta = (body[0] as any).declarations[0].init.nonCodeMeta
|
||||||
.nonCodeNodes[3][0]
|
.nonCodeNodes[3][0]
|
||||||
expect(sketchNonCodeMeta).toEqual({
|
expect(sketchNonCodeMeta).toEqual({
|
||||||
@ -1525,9 +1525,9 @@ const key = 'c'`
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('test UnaryExpression', () => {
|
describe('test UnaryExpression', () => {
|
||||||
it('should parse a unary expression in simple var dec situation', () => {
|
it('should parse a unary expression in simple var dec situation', async () => {
|
||||||
const code = `const myVar = -min(4, 100)`
|
const code = `const myVar = -min(4, 100)`
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
const myVarInit = (body?.[0] as any).declarations[0]?.init
|
const myVarInit = (body?.[0] as any).declarations[0]?.init
|
||||||
expect(myVarInit).toEqual({
|
expect(myVarInit).toEqual({
|
||||||
type: 'UnaryExpression',
|
type: 'UnaryExpression',
|
||||||
@ -1550,9 +1550,9 @@ describe('test UnaryExpression', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('testing nested call expressions', () => {
|
describe('testing nested call expressions', () => {
|
||||||
it('callExp in a binExp in a callExp', () => {
|
it('callExp in a binExp in a callExp', async () => {
|
||||||
const code = 'const myVar = min(100, 1 + legLen(5, 3))'
|
const code = 'const myVar = min(100, 1 + legLen(5, 3))'
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
const myVarInit = (body?.[0] as any).declarations[0]?.init
|
const myVarInit = (body?.[0] as any).declarations[0]?.init
|
||||||
expect(myVarInit).toEqual({
|
expect(myVarInit).toEqual({
|
||||||
type: 'CallExpression',
|
type: 'CallExpression',
|
||||||
@ -1587,8 +1587,8 @@ describe('testing nested call expressions', () => {
|
|||||||
|
|
||||||
describe('should recognise callExpresions in binaryExpressions', () => {
|
describe('should recognise callExpresions in binaryExpressions', () => {
|
||||||
const code = "xLineTo(segEndX('seg02', %) + 1, %)"
|
const code = "xLineTo(segEndX('seg02', %) + 1, %)"
|
||||||
it('should recognise the callExp', () => {
|
it('should recognise the callExp', async () => {
|
||||||
const { body } = parse(code)
|
const { body } = await parse(code)
|
||||||
const callExpArgs = (body?.[0] as any).expression?.arguments
|
const callExpArgs = (body?.[0] as any).expression?.arguments
|
||||||
expect(callExpArgs).toEqual([
|
expect(callExpArgs).toEqual([
|
||||||
{
|
{
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import { parse, initPromise } from './wasm'
|
import { parse, initPromise } from './wasm'
|
||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(() => initPromise)
|
||||||
await initPromise
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('testing artifacts', () => {
|
describe('testing artifacts', () => {
|
||||||
// Enable rotations #152
|
// Enable rotations #152
|
||||||
@ -14,7 +12,7 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
|> lineTo([-1.59, -1.54], %)
|
|> lineTo([-1.59, -1.54], %)
|
||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
// |> rx(45, %)`
|
// |> rx(45, %)`
|
||||||
const programMemory = await enginelessExecutor(parse(code))
|
const programMemory = await enginelessExecutor(await parse(code))
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const sketch001 = programMemory?.root?.mySketch001
|
const sketch001 = programMemory?.root?.mySketch001
|
||||||
expect(sketch001).toEqual({
|
expect(sketch001).toEqual({
|
||||||
@ -70,7 +68,7 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
// |> rx(45, %)
|
// |> rx(45, %)
|
||||||
|> extrude(2, %)`
|
|> extrude(2, %)`
|
||||||
const programMemory = await enginelessExecutor(parse(code))
|
const programMemory = await enginelessExecutor(await parse(code))
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const sketch001 = programMemory?.root?.mySketch001
|
const sketch001 = programMemory?.root?.mySketch001
|
||||||
expect(sketch001).toEqual({
|
expect(sketch001).toEqual({
|
||||||
@ -152,7 +150,7 @@ const sk2 = startSketchOn('XY')
|
|||||||
|> extrude(2, %)
|
|> extrude(2, %)
|
||||||
|
|
||||||
`
|
`
|
||||||
const programMemory = await enginelessExecutor(parse(code))
|
const programMemory = await enginelessExecutor(await parse(code))
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const geos = [programMemory?.root?.theExtrude, programMemory?.root?.sk2]
|
const geos = [programMemory?.root?.theExtrude, programMemory?.root?.sk2]
|
||||||
expect(geos).toEqual([
|
expect(geos).toEqual([
|
||||||
|
@ -5,7 +5,7 @@ import { bracket } from 'lib/exampleKcl'
|
|||||||
import { isTauri } from 'lib/isTauri'
|
import { isTauri } from 'lib/isTauri'
|
||||||
import { writeTextFile } from '@tauri-apps/plugin-fs'
|
import { writeTextFile } from '@tauri-apps/plugin-fs'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { editorManager } from 'lib/singletons'
|
import { Params } from 'react-router-dom'
|
||||||
|
|
||||||
const PERSIST_CODE_TOKEN = 'persistCode'
|
const PERSIST_CODE_TOKEN = 'persistCode'
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ export default class CodeManager {
|
|||||||
private _code: string = bracket
|
private _code: string = bracket
|
||||||
private _updateState: (arg: string) => void = () => {}
|
private _updateState: (arg: string) => void = () => {}
|
||||||
private _updateEditor: (arg: string) => void = () => {}
|
private _updateEditor: (arg: string) => void = () => {}
|
||||||
private _currentFilePath: string | null = null
|
private _params: Params<string> = {}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
if (isTauri()) {
|
if (isTauri()) {
|
||||||
@ -45,12 +45,19 @@ export default class CodeManager {
|
|||||||
return this._code
|
return this._code
|
||||||
}
|
}
|
||||||
|
|
||||||
registerCallBacks({ setCode }: { setCode: (arg: string) => void }) {
|
registerCallBacks({
|
||||||
|
setCode,
|
||||||
|
setEditorCode,
|
||||||
|
}: {
|
||||||
|
setCode: (arg: string) => void
|
||||||
|
setEditorCode: (arg: string) => void
|
||||||
|
}) {
|
||||||
this._updateState = setCode
|
this._updateState = setCode
|
||||||
|
this._updateEditor = setEditorCode
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCurrentFilePath(path: string) {
|
setParams(params: Params<string>) {
|
||||||
this._currentFilePath = path
|
this._params = params
|
||||||
}
|
}
|
||||||
|
|
||||||
// This updates the code state and calls the updateState function.
|
// This updates the code state and calls the updateState function.
|
||||||
@ -63,14 +70,11 @@ export default class CodeManager {
|
|||||||
|
|
||||||
// Update the code in the editor.
|
// Update the code in the editor.
|
||||||
updateCodeEditor(code: string): void {
|
updateCodeEditor(code: string): void {
|
||||||
const lastCode = this._code
|
if (this._code !== code) {
|
||||||
this.code = code
|
this.code = code
|
||||||
this._updateEditor(code)
|
this._updateEditor(code)
|
||||||
if (editorManager.editorView) {
|
|
||||||
editorManager.editorView.dispatch({
|
|
||||||
changes: { from: 0, to: lastCode.length, insert: code },
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
this._updateEditor(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the code, state, and the code the code mirror editor sees.
|
// Update the code, state, and the code the code mirror editor sees.
|
||||||
@ -87,8 +91,8 @@ export default class CodeManager {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Wait one event loop to give a chance for params to be set
|
// Wait one event loop to give a chance for params to be set
|
||||||
// Save the file to disk
|
// Save the file to disk
|
||||||
this._currentFilePath &&
|
this._params.id &&
|
||||||
writeTextFile(this._currentFilePath, this.code).catch((err) => {
|
writeTextFile(this._params.id, this.code).catch((err) => {
|
||||||
// TODO: add tracing per GH issue #254 (https://github.com/KittyCAD/modeling-app/issues/254)
|
// TODO: add tracing per GH issue #254 (https://github.com/KittyCAD/modeling-app/issues/254)
|
||||||
console.error('error saving file', err)
|
console.error('error saving file', err)
|
||||||
toast.error('Error saving file, please check file permissions')
|
toast.error('Error saving file, please check file permissions')
|
||||||
|
@ -4,9 +4,7 @@ import { parse, ProgramMemory, SketchGroup, initPromise } from './wasm'
|
|||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
import { KCLError } from './errors'
|
import { KCLError } from './errors'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(() => initPromise)
|
||||||
await initPromise
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('test executor', () => {
|
describe('test executor', () => {
|
||||||
it('test assigning two variables, the second summing with the first', async () => {
|
it('test assigning two variables, the second summing with the first', async () => {
|
||||||
@ -400,7 +398,7 @@ async function exe(
|
|||||||
code: string,
|
code: string,
|
||||||
programMemory: ProgramMemory = { root: {}, return: null }
|
programMemory: ProgramMemory = { root: {}, return: null }
|
||||||
) {
|
) {
|
||||||
const ast = parse(code)
|
const ast = await parse(code)
|
||||||
|
|
||||||
const result = await enginelessExecutor(ast, programMemory)
|
const result = await enginelessExecutor(ast, programMemory)
|
||||||
return result
|
return result
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import { getNodePathFromSourceRange, getNodeFromPath } from './queryAst'
|
import { getNodePathFromSourceRange, getNodeFromPath } from './queryAst'
|
||||||
import { Identifier, parse, initPromise, Parameter } from './wasm'
|
import { Identifier, parse, initPromise, Parameter } from './wasm'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(() => initPromise)
|
||||||
await initPromise
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('testing getNodePathFromSourceRange', () => {
|
describe('testing getNodePathFromSourceRange', () => {
|
||||||
it('test it gets the right path for a `lineTo` CallExpression within a SketchExpression', () => {
|
it('test it gets the right path for a `lineTo` CallExpression within a SketchExpression', async () => {
|
||||||
const code = `
|
const code = `
|
||||||
const myVar = 5
|
const myVar = 5
|
||||||
const sk3 = startSketchAt([0, 0])
|
const sk3 = startSketchAt([0, 0])
|
||||||
@ -21,14 +19,14 @@ const sk3 = startSketchAt([0, 0])
|
|||||||
lineToSubstringIndex + subStr.length,
|
lineToSubstringIndex + subStr.length,
|
||||||
]
|
]
|
||||||
|
|
||||||
const ast = parse(code)
|
const ast = await parse(code)
|
||||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
const { node } = getNodeFromPath<any>(ast, nodePath)
|
const { node } = getNodeFromPath<any>(ast, nodePath)
|
||||||
|
|
||||||
expect([node.start, node.end]).toEqual(sourceRange)
|
expect([node.start, node.end]).toEqual(sourceRange)
|
||||||
expect(node.type).toBe('CallExpression')
|
expect(node.type).toBe('CallExpression')
|
||||||
})
|
})
|
||||||
it('gets path right for function definition params', () => {
|
it('gets path right for function definition params', async () => {
|
||||||
const code = `fn cube = (pos, scale) => {
|
const code = `fn cube = (pos, scale) => {
|
||||||
const sg = startSketchAt(pos)
|
const sg = startSketchAt(pos)
|
||||||
|> line([0, scale], %)
|
|> line([0, scale], %)
|
||||||
@ -46,7 +44,7 @@ const b1 = cube([0,0], 10)`
|
|||||||
subStrIndex + 'pos'.length,
|
subStrIndex + 'pos'.length,
|
||||||
]
|
]
|
||||||
|
|
||||||
const ast = parse(code)
|
const ast = await parse(code)
|
||||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
const node = getNodeFromPath<Parameter>(ast, nodePath).node
|
const node = getNodeFromPath<Parameter>(ast, nodePath).node
|
||||||
|
|
||||||
@ -62,7 +60,7 @@ const b1 = cube([0,0], 10)`
|
|||||||
expect(node.type).toBe('Parameter')
|
expect(node.type).toBe('Parameter')
|
||||||
expect(node.identifier.name).toBe('pos')
|
expect(node.identifier.name).toBe('pos')
|
||||||
})
|
})
|
||||||
it('gets path right for deep within function definition body', () => {
|
it('gets path right for deep within function definition body', async () => {
|
||||||
const code = `fn cube = (pos, scale) => {
|
const code = `fn cube = (pos, scale) => {
|
||||||
const sg = startSketchAt(pos)
|
const sg = startSketchAt(pos)
|
||||||
|> line([0, scale], %)
|
|> line([0, scale], %)
|
||||||
@ -80,7 +78,7 @@ const b1 = cube([0,0], 10)`
|
|||||||
subStrIndex + 'scale'.length,
|
subStrIndex + 'scale'.length,
|
||||||
]
|
]
|
||||||
|
|
||||||
const ast = parse(code)
|
const ast = await parse(code)
|
||||||
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
const node = getNodeFromPath<Identifier>(ast, nodePath).node
|
const node = getNodeFromPath<Identifier>(ast, nodePath).node
|
||||||
expect(nodePath).toEqual([
|
expect(nodePath).toEqual([
|
||||||
|
@ -17,9 +17,7 @@ import {
|
|||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
import { getNodePathFromSourceRange } from './queryAst'
|
import { getNodePathFromSourceRange } from './queryAst'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(() => initPromise)
|
||||||
await initPromise
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('Testing createLiteral', () => {
|
describe('Testing createLiteral', () => {
|
||||||
it('should create a literal', () => {
|
it('should create a literal', () => {
|
||||||
|
81
src/lang/parser.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import {
|
||||||
|
ParserWorkerResponse,
|
||||||
|
WasmWorker,
|
||||||
|
WasmWorkerEventType,
|
||||||
|
ParserWorkerCallResponse,
|
||||||
|
} from 'lang/workers/types'
|
||||||
|
import Worker from 'lang/workers/parser?worker'
|
||||||
|
import { Program, wasmUrl } from 'lang/wasm'
|
||||||
|
import { KCLError } from 'lang/errors'
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
|
||||||
|
export default class Parser {
|
||||||
|
worker: any = new Worker({ name: 'parse' })
|
||||||
|
intitalized: boolean = false
|
||||||
|
mappings: Map<string, Program | KCLError> = new Map()
|
||||||
|
|
||||||
|
async parse(code: string): Promise<Program> {
|
||||||
|
this.ensureWorker()
|
||||||
|
const uuid = uuidv4()
|
||||||
|
this.worker.postMessage({
|
||||||
|
worker: WasmWorker.Parser,
|
||||||
|
eventType: WasmWorkerEventType.Call,
|
||||||
|
eventData: {
|
||||||
|
uuid,
|
||||||
|
code,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
let result = await this.waitForResult(uuid)
|
||||||
|
if (result instanceof KCLError) {
|
||||||
|
throw result
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForResult(uuid: string): Promise<Program | KCLError> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const result = this.mappings.get(uuid)
|
||||||
|
if (result) {
|
||||||
|
this.mappings.delete(uuid)
|
||||||
|
resolve(result)
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve(this.waitForResult(uuid))
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureWorker() {
|
||||||
|
if (!this.intitalized) {
|
||||||
|
this.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the worker.
|
||||||
|
start() {
|
||||||
|
if (this.intitalized) {
|
||||||
|
console.log('Worker already initialized')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.worker.postMessage({
|
||||||
|
worker: WasmWorker.Parser,
|
||||||
|
eventType: WasmWorkerEventType.Init,
|
||||||
|
eventData: {
|
||||||
|
wasmUrl: wasmUrl(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
this.worker.onmessage = function (r: ParserWorkerResponse) {
|
||||||
|
switch (r.eventType) {
|
||||||
|
case WasmWorkerEventType.Init:
|
||||||
|
this.intitalized = true
|
||||||
|
break
|
||||||
|
case WasmWorkerEventType.Call:
|
||||||
|
const c = r.response as ParserWorkerCallResponse
|
||||||
|
this.mappings.set(c.uuid, c.response)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,9 +15,7 @@ import {
|
|||||||
createPipeSubstitution,
|
createPipeSubstitution,
|
||||||
} from './modifyAst'
|
} from './modifyAst'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(() => initPromise)
|
||||||
await initPromise
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('findAllPreviousVariables', () => {
|
describe('findAllPreviousVariables', () => {
|
||||||
it('should find all previous variables', async () => {
|
it('should find all previous variables', async () => {
|
||||||
|
@ -1,58 +1,56 @@
|
|||||||
import { parse, Program, recast, initPromise } from './wasm'
|
import { parse, Program, recast, initPromise } from './wasm'
|
||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(() => initPromise)
|
||||||
await initPromise
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('recast', () => {
|
describe('recast', () => {
|
||||||
it('recasts a simple program', () => {
|
it('recasts a simple program', async () => {
|
||||||
const code = '1 + 2'
|
const code = '1 + 2'
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
it('variable declaration', () => {
|
it('variable declaration', async () => {
|
||||||
const code = 'const myVar = 5'
|
const code = 'const myVar = 5'
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
it("variable declaration that's binary with string", () => {
|
it("variable declaration that's binary with string", async () => {
|
||||||
const code = "const myVar = 5 + 'yo'"
|
const code = "const myVar = 5 + 'yo'"
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
const codeWithOtherQuotes = 'const myVar = 5 + "yo"'
|
const codeWithOtherQuotes = 'const myVar = 5 + "yo"'
|
||||||
const { ast: ast2 } = code2ast(codeWithOtherQuotes)
|
const { ast: ast2 } = await code2ast(codeWithOtherQuotes)
|
||||||
expect(recast(ast2).trim()).toBe(codeWithOtherQuotes)
|
expect(recast(ast2).trim()).toBe(codeWithOtherQuotes)
|
||||||
})
|
})
|
||||||
it('test assigning two variables, the second summing with the first', () => {
|
it('test assigning two variables, the second summing with the first', async () => {
|
||||||
const code = `const myVar = 5
|
const code = `const myVar = 5
|
||||||
const newVar = myVar + 1
|
const newVar = myVar + 1
|
||||||
`
|
`
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('test assigning a var by cont concatenating two strings string', () => {
|
it('test assigning a var by cont concatenating two strings string', async () => {
|
||||||
const code = fs.readFileSync(
|
const code = fs.readFileSync(
|
||||||
'./src/lang/testExamples/variableDeclaration.cado',
|
'./src/lang/testExamples/variableDeclaration.cado',
|
||||||
'utf-8'
|
'utf-8'
|
||||||
)
|
)
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.trim())
|
expect(recasted.trim()).toBe(code.trim())
|
||||||
})
|
})
|
||||||
it('test with function call', () => {
|
it('test with function call', async () => {
|
||||||
const code = `const myVar = "hello"
|
const code = `const myVar = "hello"
|
||||||
log(5, myVar)
|
log(5, myVar)
|
||||||
`
|
`
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('function declaration with call', () => {
|
it('function declaration with call', async () => {
|
||||||
const code = [
|
const code = [
|
||||||
'fn funcN = (a, b) => {',
|
'fn funcN = (a, b) => {',
|
||||||
' return a + b',
|
' return a + b',
|
||||||
@ -60,22 +58,22 @@ log(5, myVar)
|
|||||||
'const theVar = 60',
|
'const theVar = 60',
|
||||||
'const magicNum = funcN(9, theVar)',
|
'const magicNum = funcN(9, theVar)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
it('recast sketch declaration', () => {
|
it('recast sketch declaration', async () => {
|
||||||
let code = `const mySketch = startSketchAt([0, 0])
|
let code = `const mySketch = startSketchAt([0, 0])
|
||||||
|> lineTo([0, 1], %, "myPath")
|
|> lineTo([0, 1], %, "myPath")
|
||||||
|> lineTo([1, 1], %)
|
|> lineTo([1, 1], %)
|
||||||
|> lineTo([1, 0], %, "rightPath")
|
|> lineTo([1, 0], %, "rightPath")
|
||||||
|> close(%)
|
|> close(%)
|
||||||
`
|
`
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('sketch piped into callExpression', () => {
|
it('sketch piped into callExpression', async () => {
|
||||||
const code = [
|
const code = [
|
||||||
'const mySk1 = startSketchAt([0, 0])',
|
'const mySk1 = startSketchAt([0, 0])',
|
||||||
' |> lineTo([1, 1], %)',
|
' |> lineTo([1, 1], %)',
|
||||||
@ -83,11 +81,11 @@ log(5, myVar)
|
|||||||
' |> lineTo([1, 1], %)',
|
' |> lineTo([1, 1], %)',
|
||||||
' |> rx(90, %)',
|
' |> rx(90, %)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.trim())
|
expect(recasted.trim()).toBe(code.trim())
|
||||||
})
|
})
|
||||||
it('recast BinaryExpression piped into CallExpression', () => {
|
it('recast BinaryExpression piped into CallExpression', async () => {
|
||||||
const code = [
|
const code = [
|
||||||
'fn myFn = (a) => {',
|
'fn myFn = (a) => {',
|
||||||
' return a + 1',
|
' return a + 1',
|
||||||
@ -95,49 +93,49 @@ log(5, myVar)
|
|||||||
'const myVar = 5 + 1',
|
'const myVar = 5 + 1',
|
||||||
' |> myFn(%)',
|
' |> myFn(%)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
it('recast nested binary expression', () => {
|
it('recast nested binary expression', async () => {
|
||||||
const code = ['const myVar = 1 + 2 * 5'].join('\n')
|
const code = ['const myVar = 1 + 2 * 5'].join('\n')
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.trim())
|
expect(recasted.trim()).toBe(code.trim())
|
||||||
})
|
})
|
||||||
it('recast nested binary expression with parans', () => {
|
it('recast nested binary expression with parans', async () => {
|
||||||
const code = ['const myVar = 1 + (1 + 2) * 5'].join('\n')
|
const code = ['const myVar = 1 + (1 + 2) * 5'].join('\n')
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.trim())
|
expect(recasted.trim()).toBe(code.trim())
|
||||||
})
|
})
|
||||||
it('unnecessary paran wrap will be remove', () => {
|
it('unnecessary paran wrap will be remove', async () => {
|
||||||
const code = ['const myVar = 1 + (2 * 5)'].join('\n')
|
const code = ['const myVar = 1 + (2 * 5)'].join('\n')
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.replace('(', '').replace(')', ''))
|
expect(recasted.trim()).toBe(code.replace('(', '').replace(')', ''))
|
||||||
})
|
})
|
||||||
it('complex nested binary expression', () => {
|
it('complex nested binary expression', async () => {
|
||||||
const code = ['1 * ((2 + 3) / 4 + 5)'].join('\n')
|
const code = ['1 * ((2 + 3) / 4 + 5)'].join('\n')
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.trim())
|
expect(recasted.trim()).toBe(code.trim())
|
||||||
})
|
})
|
||||||
it('multiplied paren expressions', () => {
|
it('multiplied paren expressions', async () => {
|
||||||
const code = ['3 + (1 + 2) * (3 + 4)'].join('\n')
|
const code = ['3 + (1 + 2) * (3 + 4)'].join('\n')
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.trim())
|
expect(recasted.trim()).toBe(code.trim())
|
||||||
})
|
})
|
||||||
it('recast array declaration', () => {
|
it('recast array declaration', async () => {
|
||||||
const code = ['const three = 3', "const yo = [1, '2', three, 4 + 5]"].join(
|
const code = ['const three = 3', "const yo = [1, '2', three, 4 + 5]"].join(
|
||||||
'\n'
|
'\n'
|
||||||
)
|
)
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.trim())
|
expect(recasted.trim()).toBe(code.trim())
|
||||||
})
|
})
|
||||||
it('recast long array declaration', () => {
|
it('recast long array declaration', async () => {
|
||||||
const code = [
|
const code = [
|
||||||
'const three = 3',
|
'const three = 3',
|
||||||
'const yo = [',
|
'const yo = [',
|
||||||
@ -148,11 +146,11 @@ log(5, myVar)
|
|||||||
" 'hey oooooo really long long long'",
|
" 'hey oooooo really long long long'",
|
||||||
']',
|
']',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code.trim())
|
expect(recasted.trim()).toBe(code.trim())
|
||||||
})
|
})
|
||||||
it('recast long object execution', () => {
|
it('recast long object execution', async () => {
|
||||||
const code = `const three = 3
|
const code = `const three = 3
|
||||||
const yo = {
|
const yo = {
|
||||||
aStr: 'str',
|
aStr: 'str',
|
||||||
@ -161,43 +159,43 @@ const yo = {
|
|||||||
binExp: 4 + 5
|
binExp: 4 + 5
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('recast short object execution', () => {
|
it('recast short object execution', async () => {
|
||||||
const code = `const yo = { key: 'val' }
|
const code = `const yo = { key: 'val' }
|
||||||
`
|
`
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('recast object execution with member expression', () => {
|
it('recast object execution with member expression', async () => {
|
||||||
const code = `const yo = { a: { b: { c: '123' } } }
|
const code = `const yo = { a: { b: { c: '123' } } }
|
||||||
const key = 'c'
|
const key = 'c'
|
||||||
const myVar = yo.a['b'][key]
|
const myVar = yo.a['b'][key]
|
||||||
const key2 = 'b'
|
const key2 = 'b'
|
||||||
const myVar2 = yo['a'][key2].c
|
const myVar2 = yo['a'][key2].c
|
||||||
`
|
`
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('testing recasting with comments and whitespace', () => {
|
describe('testing recasting with comments and whitespace', () => {
|
||||||
it('code with comments', () => {
|
it('code with comments', async () => {
|
||||||
const code = `const yo = { a: { b: { c: '123' } } }
|
const code = `const yo = { a: { b: { c: '123' } } }
|
||||||
// this is a comment
|
// this is a comment
|
||||||
const key = 'c'
|
const key = 'c'
|
||||||
`
|
`
|
||||||
|
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
|
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('code with comment and extra lines', () => {
|
it('code with comment and extra lines', async () => {
|
||||||
const code = `const yo = 'c'
|
const code = `const yo = 'c'
|
||||||
|
|
||||||
/* this is
|
/* this is
|
||||||
@ -205,23 +203,23 @@ a
|
|||||||
comment */
|
comment */
|
||||||
const yo = 'bing'
|
const yo = 'bing'
|
||||||
`
|
`
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('comments at the start and end', () => {
|
it('comments at the start and end', async () => {
|
||||||
const code = `// this is a comment
|
const code = `// this is a comment
|
||||||
const yo = { a: { b: { c: '123' } } }
|
const yo = { a: { b: { c: '123' } } }
|
||||||
const key = 'c'
|
const key = 'c'
|
||||||
|
|
||||||
// this is also a comment
|
// this is also a comment
|
||||||
`
|
`
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('comments in a fn block', () => {
|
it('comments in a fn block', async () => {
|
||||||
const code = `fn myFn = () => {
|
const code = `fn myFn = async () => {
|
||||||
// this is a comment
|
// this is a comment
|
||||||
const yo = { a: { b: { c: '123' } } }
|
const yo = { a: { b: { c: '123' } } }
|
||||||
|
|
||||||
@ -231,11 +229,11 @@ const key = 'c'
|
|||||||
// this is also a comment
|
// this is also a comment
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('comments in a pipe expression', () => {
|
it('comments in a pipe expression', async () => {
|
||||||
const code = [
|
const code = [
|
||||||
'const mySk1 = startSketchAt([0, 0])',
|
'const mySk1 = startSketchAt([0, 0])',
|
||||||
' |> lineTo([1, 1], %)',
|
' |> lineTo([1, 1], %)',
|
||||||
@ -244,11 +242,11 @@ const key = 'c'
|
|||||||
' // a comment',
|
' // a comment',
|
||||||
' |> rx(90, %)',
|
' |> rx(90, %)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
it('comments sprinkled in all over the place', () => {
|
it('comments sprinkled in all over the place', async () => {
|
||||||
const code = `
|
const code = `
|
||||||
/* comment at start */
|
/* comment at start */
|
||||||
|
|
||||||
@ -266,11 +264,11 @@ const mySk1 = startSketchAt([0, 0])
|
|||||||
|
|
||||||
|
|
||||||
|> rx(45, %)
|
|> rx(45, %)
|
||||||
/*
|
/*
|
||||||
one more for good measure
|
one more for good measure
|
||||||
*/
|
*/
|
||||||
`
|
`
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(`/* comment at start */
|
expect(recasted).toBe(`/* comment at start */
|
||||||
|
|
||||||
@ -285,43 +283,43 @@ const mySk1 = startSketchAt([0, 0])
|
|||||||
// and another with just white space between others below
|
// and another with just white space between others below
|
||||||
|> ry(45, %)
|
|> ry(45, %)
|
||||||
|> rx(45, %)
|
|> rx(45, %)
|
||||||
/* one more for good measure */
|
/* one more for good measure */
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('testing call Expressions in BinaryExpressions and UnaryExpressions', () => {
|
describe('testing call Expressions in BinaryExpressions and UnaryExpressions', () => {
|
||||||
it('nested callExpression in binaryExpression', () => {
|
it('nested callExpression in binaryExpression', async () => {
|
||||||
const code = 'const myVar = 2 + min(100, legLen(5, 3))'
|
const code = 'const myVar = 2 + min(100, legLen(5, 3))'
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
it('nested callExpression in unaryExpression', () => {
|
it('nested callExpression in unaryExpression', async () => {
|
||||||
const code = 'const myVar = -min(100, legLen(5, 3))'
|
const code = 'const myVar = -min(100, legLen(5, 3))'
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
it('with unaryExpression in callExpression', () => {
|
it('with unaryExpression in callExpression', async () => {
|
||||||
const code = 'const myVar = min(5, -legLen(5, 4))'
|
const code = 'const myVar = min(5, -legLen(5, 4))'
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
it('with unaryExpression in sketch situation', () => {
|
it('with unaryExpression in sketch situation', async () => {
|
||||||
const code = [
|
const code = [
|
||||||
'const part001 = startSketchAt([0, 0])',
|
'const part001 = startSketchAt([0, 0])',
|
||||||
' |> line([-2.21, -legLen(5, min(3, 999))], %)',
|
' |> line([-2.21, -legLen(5, min(3, 999))], %)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted.trim()).toBe(code)
|
expect(recasted.trim()).toBe(code)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('it recasts wrapped object expressions in pipe bodies with correct indentation', () => {
|
describe('it recasts wrapped object expressions in pipe bodies with correct indentation', () => {
|
||||||
it('with a single line', () => {
|
it('with a single line', async () => {
|
||||||
const code = `const part001 = startSketchAt([-0.01, -0.08])
|
const code = `const part001 = startSketchAt([-0.01, -0.08])
|
||||||
|> line([0.62, 4.15], %, 'seg01')
|
|> line([0.62, 4.15], %, 'seg01')
|
||||||
|> line([2.77, -1.24], %)
|
|> line([2.77, -1.24], %)
|
||||||
@ -332,35 +330,35 @@ describe('it recasts wrapped object expressions in pipe bodies with correct inde
|
|||||||
}, %)
|
}, %)
|
||||||
|> line([-0.42, -1.72], %)
|
|> line([-0.42, -1.72], %)
|
||||||
`
|
`
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
it('recasts wrapped object expressions NOT in pipe body correctly', () => {
|
it('recasts wrapped object expressions NOT in pipe body correctly', async () => {
|
||||||
const code = `angledLineThatIntersects({
|
const code = `angledLineThatIntersects({
|
||||||
angle: 201,
|
angle: 201,
|
||||||
offset: -1.35,
|
offset: -1.35,
|
||||||
intersectTag: 'seg01'
|
intersectTag: 'seg01'
|
||||||
}, %)
|
}, %)
|
||||||
`
|
`
|
||||||
const { ast } = code2ast(code)
|
const { ast } = await code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('it recasts binary expression using brackets where needed', () => {
|
describe('it recasts binary expression using brackets where needed', () => {
|
||||||
it('when there are two minus in a row', () => {
|
it('when there are two minus in a row', async () => {
|
||||||
const code = `const part001 = 1 - (def - abc)
|
const code = `const part001 = 1 - (def - abc)
|
||||||
`
|
`
|
||||||
const recasted = recast(code2ast(code).ast)
|
const recasted = recast((await code2ast(code)).ast)
|
||||||
expect(recasted).toBe(code)
|
expect(recasted).toBe(code)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// helpers
|
// helpers
|
||||||
|
|
||||||
function code2ast(code: string): { ast: Program } {
|
async function code2ast(code: string): Promise<{ ast: Program }> {
|
||||||
const ast = parse(code)
|
const ast = await parse(code)
|
||||||
return { ast }
|
return { ast }
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { Models } from '@kittycad/lib'
|
|||||||
import { exportSave } from 'lib/exportSave'
|
import { exportSave } from 'lib/exportSave'
|
||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
import { getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { Themes, getThemeColorForEngine, getOppositeTheme } from 'lib/theme'
|
import { Themes, getThemeColorForEngine } from 'lib/theme'
|
||||||
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
||||||
|
|
||||||
let lastMessage = ''
|
let lastMessage = ''
|
||||||
@ -941,7 +941,6 @@ export class EngineCommandManager {
|
|||||||
settings = {
|
settings = {
|
||||||
theme: Themes.Dark,
|
theme: Themes.Dark,
|
||||||
highlightEdges: true,
|
highlightEdges: true,
|
||||||
enableSSAO: true,
|
|
||||||
},
|
},
|
||||||
}: {
|
}: {
|
||||||
setMediaStream: (stream: MediaStream) => void
|
setMediaStream: (stream: MediaStream) => void
|
||||||
@ -954,7 +953,6 @@ export class EngineCommandManager {
|
|||||||
settings?: {
|
settings?: {
|
||||||
theme: Themes
|
theme: Themes
|
||||||
highlightEdges: boolean
|
highlightEdges: boolean
|
||||||
enableSSAO: boolean
|
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
this.makeDefaultPlanes = makeDefaultPlanes
|
this.makeDefaultPlanes = makeDefaultPlanes
|
||||||
@ -971,8 +969,7 @@ export class EngineCommandManager {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const additionalSettings = settings.enableSSAO ? '&post_effect=ssao' : ''
|
const url = `${VITE_KC_API_WS_MODELING_URL}?video_res_width=${width}&video_res_height=${height}`
|
||||||
const url = `${VITE_KC_API_WS_MODELING_URL}?video_res_width=${width}&video_res_height=${height}${additionalSettings}`
|
|
||||||
this.engineConnection = new EngineConnection({
|
this.engineConnection = new EngineConnection({
|
||||||
engineCommandManager: this,
|
engineCommandManager: this,
|
||||||
url,
|
url,
|
||||||
@ -992,18 +989,6 @@ export class EngineCommandManager {
|
|||||||
color: getThemeColorForEngine(settings.theme),
|
color: getThemeColorForEngine(settings.theme),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// Sets the default line colors
|
|
||||||
const opposingTheme = getOppositeTheme(settings.theme)
|
|
||||||
this.sendSceneCommand({
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd: {
|
|
||||||
type: 'set_default_system_properties',
|
|
||||||
color: getThemeColorForEngine(opposingTheme),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// Set the edge lines visibility
|
// Set the edge lines visibility
|
||||||
this.sendSceneCommand({
|
this.sendSceneCommand({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
@ -1186,10 +1171,7 @@ export class EngineCommandManager {
|
|||||||
type: 'receive-reliable',
|
type: 'receive-reliable',
|
||||||
data: message,
|
data: message,
|
||||||
id,
|
id,
|
||||||
cmd_type:
|
cmd_type: command?.commandType || this.lastArtifactMap[id]?.commandType,
|
||||||
command?.commandType ||
|
|
||||||
this.lastArtifactMap[id]?.commandType ||
|
|
||||||
sceneCommand?.commandType,
|
|
||||||
})
|
})
|
||||||
Object.values(this.subscriptions[modelingResponse.type] || {}).forEach(
|
Object.values(this.subscriptions[modelingResponse.type] || {}).forEach(
|
||||||
(callback) => callback(modelingResponse)
|
(callback) => callback(modelingResponse)
|
||||||
@ -1341,17 +1323,6 @@ export class EngineCommandManager {
|
|||||||
this.lastArtifactMap = this.artifactMap
|
this.lastArtifactMap = this.artifactMap
|
||||||
this.artifactMap = {}
|
this.artifactMap = {}
|
||||||
await this.initPlanes()
|
await this.initPlanes()
|
||||||
await this.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'make_axes_gizmo',
|
|
||||||
clobber: false,
|
|
||||||
// If true, axes gizmo will be placed in the corner of the screen.
|
|
||||||
// If false, it will be placed at the origin of the scene.
|
|
||||||
gizmo_mode: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
subscribeTo<T extends ModelTypes>({
|
subscribeTo<T extends ModelTypes>({
|
||||||
event,
|
event,
|
||||||
|
@ -25,9 +25,7 @@ const eachQuad: [number, [number, number]][] = [
|
|||||||
[675, [1, -1]],
|
[675, [1, -1]],
|
||||||
]
|
]
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(() => initPromise)
|
||||||
await initPromise
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('testing getYComponent', () => {
|
describe('testing getYComponent', () => {
|
||||||
it('should return the vertical component of a vector correctly when given angles in each quadrant (and with angles < 0, or > 360)', () => {
|
it('should return the vertical component of a vector correctly when given angles in each quadrant (and with angles < 0, or > 360)', () => {
|
||||||
@ -102,11 +100,11 @@ describe('testing changeSketchArguments', () => {
|
|||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> ${line}
|
|> ${line}
|
||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
// |> rx(45, %)
|
// |> rx(45, %)
|
||||||
`
|
`
|
||||||
const code = genCode(lineToChange)
|
const code = genCode(lineToChange)
|
||||||
const expectedCode = genCode(lineAfterChange)
|
const expectedCode = genCode(lineAfterChange)
|
||||||
const ast = parse(code)
|
const ast = await parse(code)
|
||||||
const programMemory = await enginelessExecutor(ast)
|
const programMemory = await enginelessExecutor(ast)
|
||||||
const sourceStart = code.indexOf(lineToChange)
|
const sourceStart = code.indexOf(lineToChange)
|
||||||
const { modifiedAst } = changeSketchArguments(
|
const { modifiedAst } = changeSketchArguments(
|
||||||
@ -130,7 +128,7 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
// |> rx(45, %)
|
// |> rx(45, %)
|
||||||
|> lineTo([-1.59, -1.54], %)
|
|> lineTo([-1.59, -1.54], %)
|
||||||
|> lineTo([0.46, -5.82], %)`
|
|> lineTo([0.46, -5.82], %)`
|
||||||
const ast = parse(code)
|
const ast = await parse(code)
|
||||||
const programMemory = await enginelessExecutor(ast)
|
const programMemory = await enginelessExecutor(ast)
|
||||||
const sourceStart = code.indexOf(lineToChange)
|
const sourceStart = code.indexOf(lineToChange)
|
||||||
expect(sourceStart).toBe(95)
|
expect(sourceStart).toBe(95)
|
||||||
@ -192,7 +190,7 @@ describe('testing addTagForSketchOnFace', () => {
|
|||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
`
|
`
|
||||||
const code = genCode(originalLine)
|
const code = genCode(originalLine)
|
||||||
const ast = parse(code)
|
const ast = await parse(code)
|
||||||
const programMemory = await enginelessExecutor(ast)
|
const programMemory = await enginelessExecutor(ast)
|
||||||
const sourceStart = code.indexOf(originalLine)
|
const sourceStart = code.indexOf(originalLine)
|
||||||
const sourceRange: [number, number] = [
|
const sourceRange: [number, number] = [
|
||||||
|
@ -8,9 +8,7 @@ import { getSketchSegmentFromSourceRange } from './sketchConstraints'
|
|||||||
import { Selection } from 'lib/selections'
|
import { Selection } from 'lib/selections'
|
||||||
import { enginelessExecutor } from '../../lib/testHelpers'
|
import { enginelessExecutor } from '../../lib/testHelpers'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(() => initPromise)
|
||||||
await initPromise
|
|
||||||
})
|
|
||||||
|
|
||||||
// testing helper function
|
// testing helper function
|
||||||
async function testingSwapSketchFnCall({
|
async function testingSwapSketchFnCall({
|
||||||
@ -30,7 +28,7 @@ async function testingSwapSketchFnCall({
|
|||||||
type: 'default',
|
type: 'default',
|
||||||
range: [startIndex, startIndex + callToSwap.length],
|
range: [startIndex, startIndex + callToSwap.length],
|
||||||
}
|
}
|
||||||
const ast = parse(inputCode)
|
const ast = await parse(inputCode)
|
||||||
const programMemory = await enginelessExecutor(ast)
|
const programMemory = await enginelessExecutor(ast)
|
||||||
const selections = {
|
const selections = {
|
||||||
codeBasedSelections: [range],
|
codeBasedSelections: [range],
|
||||||
@ -353,7 +351,7 @@ const part001 = startSketchOn('XY')
|
|||||||
|> line([2.14, 1.35], %) // normal-segment
|
|> line([2.14, 1.35], %) // normal-segment
|
||||||
|> xLine(3.54, %)`
|
|> xLine(3.54, %)`
|
||||||
it('normal case works', async () => {
|
it('normal case works', async () => {
|
||||||
const programMemory = await enginelessExecutor(parse(code))
|
const programMemory = await enginelessExecutor(await parse(code))
|
||||||
const index = code.indexOf('// normal-segment') - 7
|
const index = code.indexOf('// normal-segment') - 7
|
||||||
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
|
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
|
||||||
programMemory.root['part001'] as SketchGroup,
|
programMemory.root['part001'] as SketchGroup,
|
||||||
@ -367,7 +365,7 @@ const part001 = startSketchOn('XY')
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('verify it works when the segment is in the `start` property', async () => {
|
it('verify it works when the segment is in the `start` property', async () => {
|
||||||
const programMemory = await enginelessExecutor(parse(code))
|
const programMemory = await enginelessExecutor(await parse(code))
|
||||||
const index = code.indexOf('// segment-in-start') - 7
|
const index = code.indexOf('// segment-in-start') - 7
|
||||||
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
|
const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange(
|
||||||
programMemory.root['part001'] as SketchGroup,
|
programMemory.root['part001'] as SketchGroup,
|
||||||
|
@ -11,59 +11,57 @@ import { ToolTip } from '../../useStore'
|
|||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { enginelessExecutor } from '../../lib/testHelpers'
|
import { enginelessExecutor } from '../../lib/testHelpers'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(() => initPromise)
|
||||||
await initPromise
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('testing getConstraintType', () => {
|
describe('testing getConstraintType', () => {
|
||||||
const helper = getConstraintTypeFromSourceHelper
|
const helper = getConstraintTypeFromSourceHelper
|
||||||
it('testing line', () => {
|
it('testing line', async () => {
|
||||||
expect(helper(`line([5, myVar], %)`)).toBe('yRelative')
|
expect(await helper(`line([5, myVar], %)`)).toBe('yRelative')
|
||||||
expect(helper(`line([myVar, 5], %)`)).toBe('xRelative')
|
expect(await helper(`line([myVar, 5], %)`)).toBe('xRelative')
|
||||||
})
|
})
|
||||||
it('testing lineTo', () => {
|
it('testing lineTo', async () => {
|
||||||
expect(helper(`lineTo([5, myVar], %)`)).toBe('yAbsolute')
|
expect(await helper(`lineTo([5, myVar], %)`)).toBe('yAbsolute')
|
||||||
expect(helper(`lineTo([myVar, 5], %)`)).toBe('xAbsolute')
|
expect(await helper(`lineTo([myVar, 5], %)`)).toBe('xAbsolute')
|
||||||
})
|
})
|
||||||
it('testing angledLine', () => {
|
it('testing angledLine', async () => {
|
||||||
expect(helper(`angledLine([5, myVar], %)`)).toBe('length')
|
expect(await helper(`angledLine([5, myVar], %)`)).toBe('length')
|
||||||
expect(helper(`angledLine([myVar, 5], %)`)).toBe('angle')
|
expect(await helper(`angledLine([myVar, 5], %)`)).toBe('angle')
|
||||||
})
|
})
|
||||||
it('testing angledLineOfXLength', () => {
|
it('testing angledLineOfXLength', async () => {
|
||||||
expect(helper(`angledLineOfXLength([5, myVar], %)`)).toBe('xRelative')
|
expect(await helper(`angledLineOfXLength([5, myVar], %)`)).toBe('xRelative')
|
||||||
expect(helper(`angledLineOfXLength([myVar, 5], %)`)).toBe('angle')
|
expect(await helper(`angledLineOfXLength([myVar, 5], %)`)).toBe('angle')
|
||||||
})
|
})
|
||||||
it('testing angledLineToX', () => {
|
it('testing angledLineToX', async () => {
|
||||||
expect(helper(`angledLineToX([5, myVar], %)`)).toBe('xAbsolute')
|
expect(await helper(`angledLineToX([5, myVar], %)`)).toBe('xAbsolute')
|
||||||
expect(helper(`angledLineToX([myVar, 5], %)`)).toBe('angle')
|
expect(await helper(`angledLineToX([myVar, 5], %)`)).toBe('angle')
|
||||||
})
|
})
|
||||||
it('testing angledLineOfYLength', () => {
|
it('testing angledLineOfYLength', async () => {
|
||||||
expect(helper(`angledLineOfYLength([5, myVar], %)`)).toBe('yRelative')
|
expect(await helper(`angledLineOfYLength([5, myVar], %)`)).toBe('yRelative')
|
||||||
expect(helper(`angledLineOfYLength([myVar, 5], %)`)).toBe('angle')
|
expect(await helper(`angledLineOfYLength([myVar, 5], %)`)).toBe('angle')
|
||||||
})
|
})
|
||||||
it('testing angledLineToY', () => {
|
it('testing angledLineToY', async () => {
|
||||||
expect(helper(`angledLineToY([5, myVar], %)`)).toBe('yAbsolute')
|
expect(await helper(`angledLineToY([5, myVar], %)`)).toBe('yAbsolute')
|
||||||
expect(helper(`angledLineToY([myVar, 5], %)`)).toBe('angle')
|
expect(await helper(`angledLineToY([myVar, 5], %)`)).toBe('angle')
|
||||||
})
|
})
|
||||||
const helper2 = getConstraintTypeFromSourceHelper2
|
const helper2 = getConstraintTypeFromSourceHelper2
|
||||||
it('testing xLine', () => {
|
it('testing xLine', async () => {
|
||||||
expect(helper2(`xLine(5, %)`)).toBe('yRelative')
|
expect(await helper2(`xLine(5, %)`)).toBe('yRelative')
|
||||||
})
|
})
|
||||||
it('testing yLine', () => {
|
it('testing yLine', async () => {
|
||||||
expect(helper2(`yLine(5, %)`)).toBe('xRelative')
|
expect(await helper2(`yLine(5, %)`)).toBe('xRelative')
|
||||||
})
|
})
|
||||||
it('testing xLineTo', () => {
|
it('testing xLineTo', async () => {
|
||||||
expect(helper2(`xLineTo(5, %)`)).toBe('yAbsolute')
|
expect(await helper2(`xLineTo(5, %)`)).toBe('yAbsolute')
|
||||||
})
|
})
|
||||||
it('testing yLineTo', () => {
|
it('testing yLineTo', async () => {
|
||||||
expect(helper2(`yLineTo(5, %)`)).toBe('xAbsolute')
|
expect(await helper2(`yLineTo(5, %)`)).toBe('xAbsolute')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function getConstraintTypeFromSourceHelper(
|
async function getConstraintTypeFromSourceHelper(
|
||||||
code: string
|
code: string
|
||||||
): ReturnType<typeof getConstraintType> {
|
): Promise<ReturnType<typeof getConstraintType>> {
|
||||||
const ast = parse(code)
|
const ast = await parse(code)
|
||||||
const args = (ast.body[0] as any).expression.arguments[0].elements as [
|
const args = (ast.body[0] as any).expression.arguments[0].elements as [
|
||||||
Value,
|
Value,
|
||||||
Value
|
Value
|
||||||
@ -71,10 +69,10 @@ function getConstraintTypeFromSourceHelper(
|
|||||||
const fnName = (ast.body[0] as any).expression.callee.name as ToolTip
|
const fnName = (ast.body[0] as any).expression.callee.name as ToolTip
|
||||||
return getConstraintType(args, fnName)
|
return getConstraintType(args, fnName)
|
||||||
}
|
}
|
||||||
function getConstraintTypeFromSourceHelper2(
|
async function getConstraintTypeFromSourceHelper2(
|
||||||
code: string
|
code: string
|
||||||
): ReturnType<typeof getConstraintType> {
|
): Promise<ReturnType<typeof getConstraintType>> {
|
||||||
const ast = parse(code)
|
const ast = await parse(code)
|
||||||
const arg = (ast.body[0] as any).expression.arguments[0] as Value
|
const arg = (ast.body[0] as any).expression.arguments[0] as Value
|
||||||
const fnName = (ast.body[0] as any).expression.callee.name as ToolTip
|
const fnName = (ast.body[0] as any).expression.callee.name as ToolTip
|
||||||
return getConstraintType(arg, fnName)
|
return getConstraintType(arg, fnName)
|
||||||
@ -199,7 +197,7 @@ const part001 = startSketchOn('XY')
|
|||||||
|> yLine(segLen('seg01', %), %) // ln-yLineTo-free should convert to yLine
|
|> yLine(segLen('seg01', %), %) // ln-yLineTo-free should convert to yLine
|
||||||
`
|
`
|
||||||
it('should transform the ast', async () => {
|
it('should transform the ast', async () => {
|
||||||
const ast = parse(inputScript)
|
const ast = await parse(inputScript)
|
||||||
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((ln) => ln.includes('//'))
|
.filter((ln) => ln.includes('//'))
|
||||||
@ -286,7 +284,7 @@ const part001 = startSketchOn('XY')
|
|||||||
|> xLineTo(myVar3, %) // select for horizontal constraint 10
|
|> xLineTo(myVar3, %) // select for horizontal constraint 10
|
||||||
|> angledLineToY([301, myVar], %) // select for vertical constraint 10
|
|> angledLineToY([301, myVar], %) // select for vertical constraint 10
|
||||||
`
|
`
|
||||||
const ast = parse(inputScript)
|
const ast = await parse(inputScript)
|
||||||
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((ln) => ln.includes('// select for horizontal constraint'))
|
.filter((ln) => ln.includes('// select for horizontal constraint'))
|
||||||
@ -344,7 +342,7 @@ const part001 = startSketchOn('XY')
|
|||||||
|> angledLineToX([333, myVar3], %) // select for horizontal constraint 10
|
|> angledLineToX([333, myVar3], %) // select for horizontal constraint 10
|
||||||
|> yLineTo(myVar, %) // select for vertical constraint 10
|
|> yLineTo(myVar, %) // select for vertical constraint 10
|
||||||
`
|
`
|
||||||
const ast = parse(inputScript)
|
const ast = await parse(inputScript)
|
||||||
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((ln) => ln.includes('// select for vertical constraint'))
|
.filter((ln) => ln.includes('// select for vertical constraint'))
|
||||||
@ -435,7 +433,7 @@ async function helperThing(
|
|||||||
linesOfInterest: string[],
|
linesOfInterest: string[],
|
||||||
constraint: ConstraintType
|
constraint: ConstraintType
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const ast = parse(inputScript)
|
const ast = await parse(inputScript)
|
||||||
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((ln) =>
|
.filter((ln) =>
|
||||||
@ -467,7 +465,7 @@ async function helperThing(
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('testing getConstraintLevelFromSourceRange', () => {
|
describe('testing getConstraintLevelFromSourceRange', () => {
|
||||||
it('should divide up lines into free, partial and fully contrained', () => {
|
it('should divide up lines into free, partial and fully contrained', async () => {
|
||||||
const code = `const baseLength = 3
|
const code = `const baseLength = 3
|
||||||
const baseThick = 1
|
const baseThick = 1
|
||||||
const armThick = 0.5
|
const armThick = 0.5
|
||||||
@ -497,7 +495,7 @@ const part001 = startSketchOn('XY')
|
|||||||
|> line([-1.49, 1.06], %) // free
|
|> line([-1.49, 1.06], %) // free
|
||||||
|> xLine(-3.43 + 0, %) // full
|
|> xLine(-3.43 + 0, %) // full
|
||||||
|> angledLineOfXLength([243 + 0, 1.2 + 0], %) // full`
|
|> angledLineOfXLength([243 + 0, 1.2 + 0], %) // full`
|
||||||
const ast = parse(code)
|
const ast = await parse(code)
|
||||||
const constraintLevels: ReturnType<
|
const constraintLevels: ReturnType<
|
||||||
typeof getConstraintLevelFromSourceRange
|
typeof getConstraintLevelFromSourceRange
|
||||||
>['level'][] = ['full', 'partial', 'free']
|
>['level'][] = ['full', 'partial', 'free']
|
||||||
|