Compare commits
4 Commits
v0.22.1
...
achalmers/
Author | SHA1 | Date | |
---|---|---|---|
c6f43cb607 | |||
9df69fb1c2 | |||
24f69a2b6f | |||
cf6f474dc6 |
2
.github/workflows/playwright.yml
vendored
2
.github/workflows/playwright.yml
vendored
@ -150,7 +150,7 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn
|
run: yarn
|
||||||
- name: Cache Playwright Browsers
|
- name: Cache Playwright Browsers
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cache/ms-playwright
|
~/.cache/ms-playwright
|
||||||
|
@ -17,7 +17,6 @@ import {
|
|||||||
TEST_SETTINGS_CORRUPTED,
|
TEST_SETTINGS_CORRUPTED,
|
||||||
TEST_SETTINGS_ONBOARDING_EXPORT,
|
TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||||
TEST_SETTINGS_ONBOARDING_START,
|
TEST_SETTINGS_ONBOARDING_START,
|
||||||
TEST_CODE_GIZMO,
|
|
||||||
} from './storageStates'
|
} from './storageStates'
|
||||||
import * as TOML from '@iarna/toml'
|
import * as TOML from '@iarna/toml'
|
||||||
import { LineInputsType } from 'lang/std/sketchcombos'
|
import { LineInputsType } from 'lang/std/sketchcombos'
|
||||||
@ -332,15 +331,15 @@ test('if you click the format button it formats your code', async ({
|
|||||||
// check no error to begin with
|
// check no error to begin with
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await page.click('.cm-content')
|
||||||
await page.keyboard.type(`const sketch001 = startSketchOn('XY')
|
await page.keyboard.type(`const sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, -10], %)
|
|> startProfileAt([-10, -10], %)
|
||||||
|> line([20, 0], %)
|
|> line([20, 0], %)
|
||||||
|> line([0, 20], %)
|
|> line([0, 20], %)
|
||||||
|> line([-20, 0], %)
|
|> line([-20, 0], %)
|
||||||
|> close(%)`)
|
|> close(%)`)
|
||||||
await page.locator('#code-pane button:first-child').click()
|
await page.click('#code-pane button:first-child')
|
||||||
await page.locator('button:has-text("Format code")').click()
|
await page.click('button:has-text("Format code")')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const sketch001 = startSketchOn('XY')
|
.toHaveText(`const sketch001 = startSketchOn('XY')
|
||||||
@ -388,7 +387,7 @@ test('if you use the format keyboard binding it formats your code', async ({
|
|||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
// focus the editor
|
// focus the editor
|
||||||
await u.codeLocator.click()
|
await page.click('.cm-line')
|
||||||
|
|
||||||
// Hit alt+shift+f to format the code
|
// Hit alt+shift+f to format the code
|
||||||
await page.keyboard.press('Alt+Shift+KeyF')
|
await page.keyboard.press('Alt+Shift+KeyF')
|
||||||
@ -426,7 +425,7 @@ test('if you write invalid kcl you get inlined errors', async ({ page }) => {
|
|||||||
const topAng = 30
|
const topAng = 30
|
||||||
const bottomAng = 25
|
const bottomAng = 25
|
||||||
*/
|
*/
|
||||||
await u.codeLocator.click()
|
await page.click('.cm-content')
|
||||||
await page.keyboard.type('$ error')
|
await page.keyboard.type('$ error')
|
||||||
|
|
||||||
// press arrows to clear autocomplete
|
// press arrows to clear autocomplete
|
||||||
@ -523,7 +522,7 @@ fn squareHole = (l, w) => {
|
|||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
// Click on the bottom of the code editor to add a new line
|
// Click on the bottom of the code editor to add a new line
|
||||||
await u.codeLocator.click()
|
await page.click('.cm-content')
|
||||||
await page.keyboard.press('ArrowDown')
|
await page.keyboard.press('ArrowDown')
|
||||||
await page.keyboard.press('ArrowDown')
|
await page.keyboard.press('ArrowDown')
|
||||||
await page.keyboard.press('ArrowDown')
|
await page.keyboard.press('ArrowDown')
|
||||||
@ -788,7 +787,7 @@ test('Auto complete works', async ({ page }) => {
|
|||||||
// tests clicking on an option, selection the first option
|
// tests clicking on an option, selection the first option
|
||||||
// and arrowing down to an option
|
// and arrowing down to an option
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await page.click('.cm-content')
|
||||||
await page.keyboard.type('const sketch001 = start')
|
await page.keyboard.type('const sketch001 = start')
|
||||||
|
|
||||||
// expect there to be six auto complete options
|
// expect there to be six auto complete options
|
||||||
@ -930,7 +929,7 @@ test('Project settings can be opened with keybinding from the editor', async ({
|
|||||||
.waitFor({ state: 'visible' })
|
.waitFor({ state: 'visible' })
|
||||||
|
|
||||||
// Put the cursor in the editor
|
// Put the cursor in the editor
|
||||||
await page.locator('.cm-content').click()
|
await page.click('.cm-content')
|
||||||
|
|
||||||
// Open the settings modal with the browser keyboard shortcut
|
// Open the settings modal with the browser keyboard shortcut
|
||||||
await page.keyboard.press('Meta+Shift+,')
|
await page.keyboard.press('Meta+Shift+,')
|
||||||
@ -979,7 +978,7 @@ test('Project and user settings can be reset', async ({ page }) => {
|
|||||||
.waitFor({ state: 'visible' })
|
.waitFor({ state: 'visible' })
|
||||||
|
|
||||||
// Put the cursor in the editor
|
// Put the cursor in the editor
|
||||||
await page.locator('.cm-content').click()
|
await page.click('.cm-content')
|
||||||
|
|
||||||
// Open the settings modal with the browser keyboard shortcut
|
// Open the settings modal with the browser keyboard shortcut
|
||||||
await page.keyboard.press('Meta+Shift+,')
|
await page.keyboard.press('Meta+Shift+,')
|
||||||
@ -1558,7 +1557,7 @@ test.describe('Command bar tests', () => {
|
|||||||
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
|
|
||||||
// Put the cursor in the code editor
|
// Put the cursor in the code editor
|
||||||
await page.locator('.cm-content').click()
|
await page.click('.cm-content')
|
||||||
|
|
||||||
// Now try the same, but with the keyboard shortcut, check focus
|
// Now try the same, but with the keyboard shortcut, check focus
|
||||||
await page.keyboard.press('Meta+K')
|
await page.keyboard.press('Meta+K')
|
||||||
@ -3480,24 +3479,14 @@ test.describe('Testing segment overlays', () => {
|
|||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await page.getByText('xLineTo(5 + 9 - 5, %)').click()
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
|
||||||
await page.waitForTimeout(500)
|
|
||||||
|
|
||||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(13)
|
|
||||||
|
|
||||||
const clickUnconstrained = _clickUnconstrained(page)
|
|
||||||
const clickConstrained = _clickConstrained(page)
|
|
||||||
|
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await u.sendCustomCmd({
|
await u.sendCustomCmd({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd_id: uuidv4(),
|
cmd_id: uuidv4(),
|
||||||
cmd: {
|
cmd: {
|
||||||
type: 'default_camera_look_at',
|
type: 'default_camera_look_at',
|
||||||
vantage: { x: 80, y: -1350, z: 510 },
|
vantage: { x: 0, y: -1250, z: 580 },
|
||||||
center: { x: 80, y: 0, z: 510 },
|
center: { x: 0, y: 0, z: 0 },
|
||||||
up: { x: 0, y: 0, z: 1 },
|
up: { x: 0, y: 0, z: 1 },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -3512,6 +3501,27 @@ test.describe('Testing segment overlays', () => {
|
|||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
await page.getByText('xLineTo(5 + 9 - 5, %)').click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
|
await expect(page.getByTestId('segment-overlay')).toHaveCount(13)
|
||||||
|
|
||||||
|
const clickUnconstrained = _clickUnconstrained(page)
|
||||||
|
const clickConstrained = _clickConstrained(page)
|
||||||
|
|
||||||
|
// Drag the sketch into view
|
||||||
|
await page.mouse.move(600, 64)
|
||||||
|
await page.mouse.down({ button: 'middle' })
|
||||||
|
await page.mouse.move(600, 450, { steps: 10 })
|
||||||
|
await page.mouse.up({ button: 'middle' })
|
||||||
|
|
||||||
|
await page.mouse.move(600, 64)
|
||||||
|
await page.mouse.down({ button: 'middle' })
|
||||||
|
await page.mouse.move(600, 120, { steps: 10 })
|
||||||
|
await page.mouse.up({ button: 'middle' })
|
||||||
|
|
||||||
let ang = 0
|
let ang = 0
|
||||||
|
|
||||||
const line = await u.getBoundingBox(`[data-overlay-index="${0}"]`)
|
const line = await u.getBoundingBox(`[data-overlay-index="${0}"]`)
|
||||||
@ -3560,8 +3570,11 @@ test.describe('Testing segment overlays', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
await page.mouse.move(700, 250)
|
await page.mouse.move(700, 250)
|
||||||
await page.mouse.wheel(0, 25)
|
for (let i = 0; i < 5; i++) {
|
||||||
await page.waitForTimeout(100)
|
await page.mouse.wheel(0, 100)
|
||||||
|
await page.waitForTimeout(25)
|
||||||
|
}
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
let lineTo = await u.getBoundingBox(`[data-overlay-index="2"]`)
|
let lineTo = await u.getBoundingBox(`[data-overlay-index="2"]`)
|
||||||
ang = await u.getAngle(`[data-overlay-index="2"]`)
|
ang = await u.getAngle(`[data-overlay-index="2"]`)
|
||||||
@ -3643,8 +3656,12 @@ const part001 = startSketchOn('XZ')
|
|||||||
const clickUnconstrained = _clickUnconstrained(page)
|
const clickUnconstrained = _clickUnconstrained(page)
|
||||||
|
|
||||||
await page.mouse.move(700, 250)
|
await page.mouse.move(700, 250)
|
||||||
await page.mouse.wheel(0, 25)
|
for (let i = 0; i < 7; i++) {
|
||||||
await page.waitForTimeout(100)
|
await page.mouse.wheel(0, 100)
|
||||||
|
await page.waitForTimeout(25)
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.waitForTimeout(300)
|
||||||
|
|
||||||
let ang = 0
|
let ang = 0
|
||||||
|
|
||||||
@ -4845,25 +4862,25 @@ test.describe('Testing Gizmo', () => {
|
|||||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testDescription: 'right view',
|
testDescription: '+x view',
|
||||||
clickPosition: { x: 929, y: 417 },
|
clickPosition: { x: 929, y: 417 },
|
||||||
expectedCameraPosition: { x: 5660.02, y: -152, z: 26 },
|
expectedCameraPosition: { x: 5660.02, y: -152, z: 26 },
|
||||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testDescription: 'left view',
|
testDescription: '-x view',
|
||||||
clickPosition: { x: 974, y: 397 },
|
clickPosition: { x: 974, y: 397 },
|
||||||
expectedCameraPosition: { x: -4060.02, y: -152, z: 26 },
|
expectedCameraPosition: { x: -4060.02, y: -152, z: 26 },
|
||||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testDescription: 'back view',
|
testDescription: '+y view',
|
||||||
clickPosition: { x: 967, y: 421 },
|
clickPosition: { x: 967, y: 421 },
|
||||||
expectedCameraPosition: { x: 800, y: 4708.02, z: 26 },
|
expectedCameraPosition: { x: 800, y: 4708.02, z: 26 },
|
||||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testDescription: 'front view',
|
testDescription: '-y view',
|
||||||
clickPosition: { x: 935, y: 393 },
|
clickPosition: { x: 935, y: 393 },
|
||||||
expectedCameraPosition: { x: 800, y: -5012.02, z: 26 },
|
expectedCameraPosition: { x: 800, y: -5012.02, z: 26 },
|
||||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||||
@ -4875,11 +4892,34 @@ test.describe('Testing Gizmo', () => {
|
|||||||
expectedCameraTarget,
|
expectedCameraTarget,
|
||||||
testDescription,
|
testDescription,
|
||||||
} of cases) {
|
} of cases) {
|
||||||
test(`check ${testDescription}`, async ({ page, browserName }) => {
|
test(`check ${testDescription}`, async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript((TEST_CODE_GIZMO) => {
|
await page.addInitScript(async (KCL_DEFAULT_LENGTH) => {
|
||||||
localStorage.setItem('persistCode', TEST_CODE_GIZMO)
|
localStorage.setItem(
|
||||||
}, TEST_CODE_GIZMO)
|
'persistCode',
|
||||||
|
`const part001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([20, 0], %)
|
||||||
|
|> line([7.13, 4 + 0], %)
|
||||||
|
|> angledLine({ angle: 3 + 0, length: 3.14 + 0 }, %)
|
||||||
|
|> lineTo([20.14 + 0, -0.14 + 0], %)
|
||||||
|
|> xLineTo(29 + 0, %)
|
||||||
|
|> yLine(-3.14 + 0, %, 'a')
|
||||||
|
|> xLine(1.63, %)
|
||||||
|
|> angledLineOfXLength({ angle: 3 + 0, length: 3.14 }, %)
|
||||||
|
|> angledLineOfYLength({ angle: 30, length: 3 + 0 }, %)
|
||||||
|
|> angledLineToX({ angle: 22.14 + 0, to: 12 }, %)
|
||||||
|
|> angledLineToY({ angle: 30, to: 11.14 }, %)
|
||||||
|
|> angledLineThatIntersects({
|
||||||
|
angle: 3.14,
|
||||||
|
intersectTag: 'a',
|
||||||
|
offset: 0
|
||||||
|
}, %)
|
||||||
|
|> tangentialArcTo([13.14 + 0, 13.14], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5 + 7, %)
|
||||||
|
`
|
||||||
|
)
|
||||||
|
}, KCL_DEFAULT_LENGTH)
|
||||||
await page.setViewportSize({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
await page.goto('/')
|
await page.goto('/')
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
@ -4916,11 +4956,11 @@ test.describe('Testing Gizmo', () => {
|
|||||||
})
|
})
|
||||||
await u.waitForCmdReceive('default_camera_get_settings')
|
await u.waitForCmdReceive('default_camera_get_settings')
|
||||||
|
|
||||||
await u.clearCommandLogs()
|
await page.waitForTimeout(400)
|
||||||
await page.mouse.move(clickPosition.x, clickPosition.y)
|
await page.mouse.move(clickPosition.x, clickPosition.y)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
await u.clearCommandLogs()
|
||||||
await page.mouse.click(clickPosition.x, clickPosition.y)
|
await page.mouse.click(clickPosition.x, clickPosition.y)
|
||||||
await page.mouse.move(0, 0)
|
|
||||||
await u.waitForCmdReceive('default_camera_look_at')
|
await u.waitForCmdReceive('default_camera_look_at')
|
||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
|
|
||||||
@ -4932,6 +4972,7 @@ test.describe('Testing Gizmo', () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
await u.waitForCmdReceive('default_camera_get_settings')
|
await u.waitForCmdReceive('default_camera_get_settings')
|
||||||
|
await page.waitForTimeout(400)
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
// position
|
// position
|
||||||
@ -4957,103 +4998,6 @@ test.describe('Testing Gizmo', () => {
|
|||||||
])
|
])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
test('Context menu', async ({ page }) => {
|
|
||||||
const testCase = {
|
|
||||||
testDescription: 'Right view',
|
|
||||||
expectedCameraPosition: { x: 5660.02, y: -152, z: 26 },
|
|
||||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test prelude taken from the above test
|
|
||||||
const u = await getUtils(page)
|
|
||||||
await page.addInitScript((TEST_CODE_GIZMO) => {
|
|
||||||
localStorage.setItem('persistCode', TEST_CODE_GIZMO)
|
|
||||||
}, TEST_CODE_GIZMO)
|
|
||||||
await page.setViewportSize({ width: 1000, height: 500 })
|
|
||||||
await page.goto('/')
|
|
||||||
await u.waitForAuthSkipAppStart()
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
// wait for execution done
|
|
||||||
await u.openDebugPanel()
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
||||||
await u.sendCustomCmd({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_look_at',
|
|
||||||
vantage: {
|
|
||||||
x: 3000,
|
|
||||||
y: 3000,
|
|
||||||
z: 3000,
|
|
||||||
},
|
|
||||||
center: {
|
|
||||||
x: 800,
|
|
||||||
y: -152,
|
|
||||||
z: 26,
|
|
||||||
},
|
|
||||||
up: { x: 0, y: 0, z: 1 },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await u.clearCommandLogs()
|
|
||||||
await u.sendCustomCmd({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_get_settings',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
await u.waitForCmdReceive('default_camera_get_settings')
|
|
||||||
|
|
||||||
// Now find and select the correct
|
|
||||||
// view from the context menu
|
|
||||||
await u.clearCommandLogs()
|
|
||||||
const gizmo = page.locator('[aria-label*=gizmo]')
|
|
||||||
await gizmo.click({ button: 'right' })
|
|
||||||
const buttonToTest = page.getByRole('button', {
|
|
||||||
name: testCase.testDescription,
|
|
||||||
})
|
|
||||||
await expect(buttonToTest).toBeVisible()
|
|
||||||
await buttonToTest.click()
|
|
||||||
|
|
||||||
// Now assert we've moved to the correct view
|
|
||||||
// Taken from the above test
|
|
||||||
await u.waitForCmdReceive('default_camera_look_at')
|
|
||||||
|
|
||||||
await u.sendCustomCmd({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_get_settings',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
await u.waitForCmdReceive('default_camera_get_settings')
|
|
||||||
await page.waitForTimeout(400)
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
// position
|
|
||||||
expect(page.getByTestId('cam-x-position')).toHaveValue(
|
|
||||||
testCase.expectedCameraPosition.x.toString()
|
|
||||||
),
|
|
||||||
expect(page.getByTestId('cam-y-position')).toHaveValue(
|
|
||||||
testCase.expectedCameraPosition.y.toString()
|
|
||||||
),
|
|
||||||
expect(page.getByTestId('cam-z-position')).toHaveValue(
|
|
||||||
testCase.expectedCameraPosition.z.toString()
|
|
||||||
),
|
|
||||||
// target
|
|
||||||
expect(page.getByTestId('cam-x-target')).toHaveValue(
|
|
||||||
testCase.expectedCameraTarget.x.toString()
|
|
||||||
),
|
|
||||||
expect(page.getByTestId('cam-y-target')).toHaveValue(
|
|
||||||
testCase.expectedCameraTarget.y.toString()
|
|
||||||
),
|
|
||||||
expect(page.getByTestId('cam-z-target')).toHaveValue(
|
|
||||||
testCase.expectedCameraTarget.z.toString()
|
|
||||||
),
|
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Successful export shows a success toast', async ({ page }) => {
|
test('Successful export shows a success toast', async ({ page }) => {
|
||||||
|
@ -50,25 +50,3 @@ export const TEST_SETTINGS_CORRUPTED = {
|
|||||||
textWrapping: true,
|
textWrapping: true,
|
||||||
},
|
},
|
||||||
} satisfies Partial<SaveSettingsPayload>
|
} satisfies Partial<SaveSettingsPayload>
|
||||||
|
|
||||||
export const TEST_CODE_GIZMO = `const part001 = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([20, 0], %)
|
|
||||||
|> line([7.13, 4 + 0], %)
|
|
||||||
|> angledLine({ angle: 3 + 0, length: 3.14 + 0 }, %)
|
|
||||||
|> lineTo([20.14 + 0, -0.14 + 0], %)
|
|
||||||
|> xLineTo(29 + 0, %)
|
|
||||||
|> yLine(-3.14 + 0, %, 'a')
|
|
||||||
|> xLine(1.63, %)
|
|
||||||
|> angledLineOfXLength({ angle: 3 + 0, length: 3.14 }, %)
|
|
||||||
|> angledLineOfYLength({ angle: 30, length: 3 + 0 }, %)
|
|
||||||
|> angledLineToX({ angle: 22.14 + 0, to: 12 }, %)
|
|
||||||
|> angledLineToY({ angle: 30, to: 11.14 }, %)
|
|
||||||
|> angledLineThatIntersects({
|
|
||||||
angle: 3.14,
|
|
||||||
intersectTag: 'a',
|
|
||||||
offset: 0
|
|
||||||
}, %)
|
|
||||||
|> tangentialArcTo([13.14 + 0, 13.14], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(5 + 7, %)
|
|
||||||
`
|
|
||||||
|
@ -12,16 +12,14 @@ async function waitForPageLoad(page: Page) {
|
|||||||
// wait for 'Loading stream...' spinner
|
// wait for 'Loading stream...' spinner
|
||||||
await page.getByTestId('loading-stream').waitFor()
|
await page.getByTestId('loading-stream').waitFor()
|
||||||
// wait for all spinners to be gone
|
// wait for all spinners to be gone
|
||||||
await page
|
await page.getByTestId('loading').waitFor({ state: 'detached' })
|
||||||
.getByTestId('loading')
|
|
||||||
.waitFor({ state: 'detached', timeout: 20_000 })
|
|
||||||
|
|
||||||
await page.getByTestId('start-sketch').waitFor()
|
await page.getByTestId('start-sketch').waitFor()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeCurrentCode(page: Page) {
|
async function removeCurrentCode(page: Page) {
|
||||||
const hotkey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
const hotkey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
||||||
await page.locator('.cm-content').click()
|
await page.click('.cm-content')
|
||||||
await page.keyboard.down(hotkey)
|
await page.keyboard.down(hotkey)
|
||||||
await page.keyboard.press('a')
|
await page.keyboard.press('a')
|
||||||
await page.keyboard.up(hotkey)
|
await page.keyboard.up(hotkey)
|
||||||
@ -30,12 +28,12 @@ async function removeCurrentCode(page: Page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function sendCustomCmd(page: Page, cmd: EngineCommand) {
|
async function sendCustomCmd(page: Page, cmd: EngineCommand) {
|
||||||
await page.getByTestId('custom-cmd-input').fill(JSON.stringify(cmd))
|
await page.fill('[data-testid="custom-cmd-input"]', JSON.stringify(cmd))
|
||||||
await page.getByTestId('custom-cmd-send-button').click()
|
await page.click('[data-testid="custom-cmd-send-button"]')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clearCommandLogs(page: Page) {
|
async function clearCommandLogs(page: Page) {
|
||||||
await page.getByTestId('clear-commands').click()
|
await page.click('[data-testid="clear-commands"]')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function expectCmdLog(page: Page, locatorStr: string) {
|
async function expectCmdLog(page: Page, locatorStr: string) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "untitled-app",
|
"name": "untitled-app",
|
||||||
"version": "0.22.1",
|
"version": "0.22.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.16.0",
|
"@codemirror/autocomplete": "^6.16.0",
|
||||||
|
109
src-tauri/Cargo.lock
generated
109
src-tauri/Cargo.lock
generated
@ -200,7 +200,7 @@ dependencies = [
|
|||||||
"tauri-plugin-shell",
|
"tauri-plugin-shell",
|
||||||
"tauri-plugin-updater",
|
"tauri-plugin-updater",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml 0.8.14",
|
"toml 0.8.13",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -766,7 +766,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "8a969e13a7589e9e3e4207e153bae624ade2b5622fb4684a4923b23ec3d57719"
|
checksum = "8a969e13a7589e9e3e4207e153bae624ade2b5622fb4684a4923b23ec3d57719"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"toml 0.8.14",
|
"toml 0.8.13",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1373,7 +1373,7 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
"memchr",
|
"memchr",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
"toml 0.8.14",
|
"toml 0.8.13",
|
||||||
"vswhom",
|
"vswhom",
|
||||||
"winreg 0.52.0",
|
"winreg 0.52.0",
|
||||||
]
|
]
|
||||||
@ -2578,6 +2578,8 @@ dependencies = [
|
|||||||
"gltf-json",
|
"gltf-json",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"kittycad",
|
"kittycad",
|
||||||
|
"kittycad-execution-plan-macros",
|
||||||
|
"kittycad-execution-plan-traits",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"mime_guess",
|
"mime_guess",
|
||||||
"parse-display",
|
"parse-display",
|
||||||
@ -2590,7 +2592,7 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite",
|
"tokio-tungstenite",
|
||||||
"toml 0.8.14",
|
"toml 0.8.13",
|
||||||
"tower-lsp",
|
"tower-lsp",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
"url",
|
"url",
|
||||||
@ -2653,6 +2655,28 @@ dependencies = [
|
|||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kittycad-execution-plan-macros"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0611fc9b9786175da21d895ffa0f65039e19c9111e94a41b7af999e3b95f045f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.66",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kittycad-execution-plan-traits"
|
||||||
|
version = "0.1.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "123cb47e2780ea8ef3aa67b4db237a27b388d3d3b96db457e274aa4565723151"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"thiserror",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kuchikiki"
|
name = "kuchikiki"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
@ -3329,9 +3353,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parse-display"
|
name = "parse-display"
|
||||||
version = "0.9.1"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "914a1c2265c98e2446911282c6ac86d8524f495792c38c5bd884f80499c7538a"
|
checksum = "06af5f9333eb47bd9ba8462d612e37a8328a5cb80b13f0af4de4c3b89f52dee5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parse-display-derive",
|
"parse-display-derive",
|
||||||
"regex",
|
"regex",
|
||||||
@ -3340,9 +3364,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parse-display-derive"
|
name = "parse-display-derive"
|
||||||
version = "0.9.1"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2ae7800a4c974efd12df917266338e79a7a74415173caf7e70aa0a0707345281"
|
checksum = "dc9252f259500ee570c75adcc4e317fa6f57a1e47747d622e0bf838002a7b790"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -3711,9 +3735,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.85"
|
version = "1.0.83"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
|
checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@ -4275,19 +4299,6 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustls"
|
|
||||||
version = "0.23.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ebbbdb961df0ad3f2652da8f3fdc4b36122f568f968f45ad3316f26c025c677b"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
"rustls-pki-types",
|
|
||||||
"rustls-webpki 0.102.3",
|
|
||||||
"subtle",
|
|
||||||
"zeroize",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-native-certs"
|
name = "rustls-native-certs"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@ -5031,7 +5042,7 @@ dependencies = [
|
|||||||
"cfg-expr",
|
"cfg-expr",
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"toml 0.8.14",
|
"toml 0.8.13",
|
||||||
"version-compare",
|
"version-compare",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -5184,7 +5195,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"tauri-winres",
|
"tauri-winres",
|
||||||
"toml 0.8.14",
|
"toml 0.8.13",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -5242,7 +5253,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"toml 0.8.14",
|
"toml 0.8.13",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -5519,7 +5530,7 @@ dependencies = [
|
|||||||
"serde_with",
|
"serde_with",
|
||||||
"swift-rs",
|
"swift-rs",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"toml 0.8.14",
|
"toml 0.8.13",
|
||||||
"url",
|
"url",
|
||||||
"urlpattern",
|
"urlpattern",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
@ -5653,9 +5664,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.38.0"
|
version = "1.37.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
|
checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -5673,9 +5684,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "2.3.0"
|
version = "2.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -5703,30 +5714,19 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-rustls"
|
|
||||||
version = "0.26.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
|
||||||
dependencies = [
|
|
||||||
"rustls 0.23.7",
|
|
||||||
"rustls-pki-types",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-tungstenite"
|
name = "tokio-tungstenite"
|
||||||
version = "0.23.0"
|
version = "0.21.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "becd34a233e7e31a3dbf7c7241b38320f57393dcae8e7324b0167d21b8e320b0"
|
checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"log",
|
"log",
|
||||||
"rustls 0.23.7",
|
"rustls 0.22.4",
|
||||||
"rustls-native-certs",
|
"rustls-native-certs",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls 0.26.0",
|
"tokio-rustls 0.25.0",
|
||||||
"tungstenite",
|
"tungstenite",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -5758,14 +5758,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.8.14"
|
version = "0.8.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
|
checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
"toml_edit 0.22.14",
|
"toml_edit 0.22.13",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5814,9 +5814,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.22.14"
|
version = "0.22.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
|
checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.2.6",
|
"indexmap 2.2.6",
|
||||||
"serde",
|
"serde",
|
||||||
@ -6035,9 +6035,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tungstenite"
|
name = "tungstenite"
|
||||||
version = "0.23.0"
|
version = "0.21.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8"
|
checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -6046,10 +6046,11 @@ dependencies = [
|
|||||||
"httparse",
|
"httparse",
|
||||||
"log",
|
"log",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"rustls 0.23.7",
|
"rustls 0.22.4",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"sha1",
|
"sha1",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"url",
|
||||||
"utf-8",
|
"utf-8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -74,5 +74,5 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"productName": "Zoo Modeling App",
|
"productName": "Zoo Modeling App",
|
||||||
"version": "0.22.1"
|
"version": "0.22.0"
|
||||||
}
|
}
|
||||||
|
@ -444,7 +444,7 @@ export class CameraControls {
|
|||||||
this.handleEnd()
|
this.handleEnd()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.engineCommandManager.sendSceneCommand({
|
this.throttledEngCmd({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd: {
|
cmd: {
|
||||||
type: 'default_camera_zoom',
|
type: 'default_camera_zoom',
|
||||||
@ -456,11 +456,11 @@ export class CameraControls {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Else "clientToEngine" (Sketch Mode) or forceUpdate
|
const isTrackpad = Math.abs(event.deltaY) <= 1 || event.deltaY % 1 === 0
|
||||||
|
|
||||||
// From onMouseMove zoom handling which seems to be really smooth
|
const zoomSpeed = isTrackpad ? 0.02 : 0.1 // Reduced zoom speed for trackpad
|
||||||
this.pendingZoom = this.pendingZoom ? this.pendingZoom : 1
|
this.pendingZoom = this.pendingZoom ? this.pendingZoom : 1
|
||||||
this.pendingZoom *= 1 + event.deltaY * 0.01
|
this.pendingZoom *= 1 + (event.deltaY > 0 ? zoomSpeed : -zoomSpeed)
|
||||||
this.handleEnd()
|
this.handleEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,199 +0,0 @@
|
|||||||
import toast from 'react-hot-toast'
|
|
||||||
import { ActionIcon, ActionIconProps } from './ActionIcon'
|
|
||||||
import { RefObject, useEffect, useMemo, useRef, useState } from 'react'
|
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
|
||||||
import { Dialog } from '@headlessui/react'
|
|
||||||
|
|
||||||
interface ContextMenuProps
|
|
||||||
extends Omit<React.HTMLAttributes<HTMLUListElement>, 'children'> {
|
|
||||||
items?: React.ReactElement[]
|
|
||||||
menuTargetElement?: RefObject<HTMLElement>
|
|
||||||
}
|
|
||||||
|
|
||||||
const DefaultContextMenuItems = [
|
|
||||||
<ContextMenuItemRefresh />,
|
|
||||||
<ContextMenuItemCopy />,
|
|
||||||
// add more default context menu items here
|
|
||||||
]
|
|
||||||
|
|
||||||
export function ContextMenu({
|
|
||||||
items = DefaultContextMenuItems,
|
|
||||||
menuTargetElement,
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: ContextMenuProps) {
|
|
||||||
const dialogRef = useRef<HTMLDivElement>(null)
|
|
||||||
const [open, setOpen] = useState(false)
|
|
||||||
const [windowSize, setWindowSize] = useState({
|
|
||||||
width: globalThis?.window?.innerWidth,
|
|
||||||
height: globalThis?.window?.innerHeight,
|
|
||||||
})
|
|
||||||
const [position, setPosition] = useState({ x: 0, y: 0 })
|
|
||||||
useHotkeys('esc', () => setOpen(false), {
|
|
||||||
enabled: open,
|
|
||||||
})
|
|
||||||
|
|
||||||
const dialogPositionStyle = useMemo(() => {
|
|
||||||
if (!dialogRef.current)
|
|
||||||
return {
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
right: 'auto',
|
|
||||||
bottom: 'auto',
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
top:
|
|
||||||
position.y + dialogRef.current.clientHeight > windowSize.height
|
|
||||||
? 'auto'
|
|
||||||
: position.y,
|
|
||||||
left:
|
|
||||||
position.x + dialogRef.current.clientWidth > windowSize.width
|
|
||||||
? 'auto'
|
|
||||||
: position.x,
|
|
||||||
right:
|
|
||||||
position.x + dialogRef.current.clientWidth > windowSize.width
|
|
||||||
? windowSize.width - position.x
|
|
||||||
: 'auto',
|
|
||||||
bottom:
|
|
||||||
position.y + dialogRef.current.clientHeight > windowSize.height
|
|
||||||
? windowSize.height - position.y
|
|
||||||
: 'auto',
|
|
||||||
}
|
|
||||||
}, [position, windowSize, dialogRef.current])
|
|
||||||
|
|
||||||
// Listen for window resize to update context menu position
|
|
||||||
useEffect(() => {
|
|
||||||
const handleResize = () => {
|
|
||||||
setWindowSize({
|
|
||||||
width: globalThis?.window?.innerWidth,
|
|
||||||
height: globalThis?.window?.innerHeight,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
globalThis?.window?.addEventListener('resize', handleResize)
|
|
||||||
return () => {
|
|
||||||
globalThis?.window?.removeEventListener('resize', handleResize)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
// Add context menu listener to target once mounted
|
|
||||||
useEffect(() => {
|
|
||||||
const handleContextMenu = (e: MouseEvent) => {
|
|
||||||
console.log('context menu', e)
|
|
||||||
e.preventDefault()
|
|
||||||
setPosition({ x: e.x, y: e.y })
|
|
||||||
setOpen(true)
|
|
||||||
}
|
|
||||||
menuTargetElement?.current?.addEventListener(
|
|
||||||
'contextmenu',
|
|
||||||
handleContextMenu
|
|
||||||
)
|
|
||||||
return () => {
|
|
||||||
menuTargetElement?.current?.removeEventListener(
|
|
||||||
'contextmenu',
|
|
||||||
handleContextMenu
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}, [menuTargetElement?.current])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={open} onClose={() => setOpen(false)}>
|
|
||||||
<div
|
|
||||||
className="fixed inset-0 z-50 w-screen h-screen"
|
|
||||||
onContextMenu={(e) => e.preventDefault()}
|
|
||||||
>
|
|
||||||
<Dialog.Backdrop className="fixed z-10 inset-0" />
|
|
||||||
<Dialog.Panel
|
|
||||||
ref={dialogRef}
|
|
||||||
className={`w-48 fixed bg-chalkboard-10 dark:bg-chalkboard-90
|
|
||||||
border border-solid border-chalkboard-10 dark:border-chalkboard-90 rounded
|
|
||||||
shadow-lg backdrop:fixed backdrop:inset-0 backdrop:bg-primary ${className}`}
|
|
||||||
style={{
|
|
||||||
...dialogPositionStyle,
|
|
||||||
...props.style,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ul
|
|
||||||
{...props}
|
|
||||||
className="relative flex flex-col gap-0.5 items-stretch content-stretch"
|
|
||||||
onClick={() => setOpen(false)}
|
|
||||||
>
|
|
||||||
{...items}
|
|
||||||
</ul>
|
|
||||||
</Dialog.Panel>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ContextMenuDivider() {
|
|
||||||
return <hr className="border-chalkboard-20 dark:border-chalkboard-80" />
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ContextMenuItemProps {
|
|
||||||
children: React.ReactNode
|
|
||||||
icon?: ActionIconProps['icon']
|
|
||||||
onClick?: () => void
|
|
||||||
hotkey?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ContextMenuItem({
|
|
||||||
children,
|
|
||||||
icon,
|
|
||||||
onClick,
|
|
||||||
hotkey,
|
|
||||||
}: ContextMenuItemProps) {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
className="flex items-center gap-2 py-1 px-2 cursor-pointer hover:bg-chalkboard-20 dark:hover:bg-chalkboard-80 border-none text-left"
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
|
||||||
{icon && <ActionIcon icon={icon} bgClassName="!bg-transparent" />}
|
|
||||||
<div className="flex-1">{children}</div>
|
|
||||||
{hotkey && (
|
|
||||||
<kbd className="px-1.5 py-0.5 rounded bg-primary/10 text-primary dark:bg-chalkboard-80 dark:text-chalkboard-40">
|
|
||||||
{hotkey}
|
|
||||||
</kbd>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ContextMenuItemRefresh() {
|
|
||||||
return (
|
|
||||||
<ContextMenuItem
|
|
||||||
icon="arrowRotateRight"
|
|
||||||
onClick={() => globalThis?.window?.location.reload()}
|
|
||||||
>
|
|
||||||
Refresh
|
|
||||||
</ContextMenuItem>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ContextMenuItemCopyProps {
|
|
||||||
toBeCopiedContent?: string
|
|
||||||
toBeCopiedLabel?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ContextMenuItemCopy({
|
|
||||||
toBeCopiedContent = globalThis.window?.getSelection()?.toString(),
|
|
||||||
toBeCopiedLabel = 'selection',
|
|
||||||
}: ContextMenuItemCopyProps) {
|
|
||||||
return (
|
|
||||||
<ContextMenuItem
|
|
||||||
icon="clipboardPlus"
|
|
||||||
onClick={() => {
|
|
||||||
if (toBeCopiedContent) {
|
|
||||||
globalThis?.navigator?.clipboard
|
|
||||||
.writeText(toBeCopiedContent)
|
|
||||||
.then(() => toast.success(`Copied ${toBeCopiedLabel} to clipboard`))
|
|
||||||
.catch(() =>
|
|
||||||
toast.error(`Failed to copy ${toBeCopiedLabel} to clipboard`)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Copy
|
|
||||||
</ContextMenuItem>
|
|
||||||
)
|
|
||||||
}
|
|
@ -18,8 +18,6 @@ import { useLspContext } from './LspProvider'
|
|||||||
import useHotkeyWrapper from 'lib/hotkeyWrapper'
|
import useHotkeyWrapper from 'lib/hotkeyWrapper'
|
||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import { DeleteConfirmationDialog } from './ProjectCard/DeleteProjectDialog'
|
import { DeleteConfirmationDialog } from './ProjectCard/DeleteProjectDialog'
|
||||||
import { ContextMenu, ContextMenuItem } from './ContextMenu'
|
|
||||||
import usePlatform from 'hooks/usePlatform'
|
|
||||||
|
|
||||||
function getIndentationCSS(level: number) {
|
function getIndentationCSS(level: number) {
|
||||||
return `calc(1rem * ${level + 1})`
|
return `calc(1rem * ${level + 1})`
|
||||||
@ -127,7 +125,6 @@ const FileTreeItem = ({
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const [isConfirmingDelete, setIsConfirmingDelete] = useState(false)
|
const [isConfirmingDelete, setIsConfirmingDelete] = useState(false)
|
||||||
const isCurrentFile = fileOrDir.path === currentFile?.path
|
const isCurrentFile = fileOrDir.path === currentFile?.path
|
||||||
const itemRef = useRef(null)
|
|
||||||
|
|
||||||
const isRenaming = fileContext.itemsBeingRenamed.includes(fileOrDir.path)
|
const isRenaming = fileContext.itemsBeingRenamed.includes(fileOrDir.path)
|
||||||
const removeCurrentItemFromRenaming = useCallback(
|
const removeCurrentItemFromRenaming = useCallback(
|
||||||
@ -188,7 +185,7 @@ const FileTreeItem = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="contents" ref={itemRef}>
|
<>
|
||||||
{fileOrDir.children === undefined ? (
|
{fileOrDir.children === undefined ? (
|
||||||
<li
|
<li
|
||||||
className={
|
className={
|
||||||
@ -324,41 +321,7 @@ const FileTreeItem = ({
|
|||||||
setIsOpen={setIsConfirmingDelete}
|
setIsOpen={setIsConfirmingDelete}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<FileTreeContextMenu
|
</>
|
||||||
itemRef={itemRef}
|
|
||||||
onRename={addCurrentItemToRenaming}
|
|
||||||
onDelete={() => setIsConfirmingDelete(true)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FileTreeContextMenuProps {
|
|
||||||
itemRef: React.RefObject<HTMLElement>
|
|
||||||
onRename: () => void
|
|
||||||
onDelete: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
function FileTreeContextMenu({
|
|
||||||
itemRef,
|
|
||||||
onRename,
|
|
||||||
onDelete,
|
|
||||||
}: FileTreeContextMenuProps) {
|
|
||||||
const platform = usePlatform()
|
|
||||||
const metaKey = platform === 'macos' ? '⌘' : 'Ctrl'
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ContextMenu
|
|
||||||
menuTargetElement={itemRef}
|
|
||||||
items={[
|
|
||||||
<ContextMenuItem onClick={onRename} hotkey="Enter">
|
|
||||||
Rename
|
|
||||||
</ContextMenuItem>,
|
|
||||||
<ContextMenuItem onClick={onDelete} hotkey={metaKey + ' + Del'}>
|
|
||||||
Delete
|
|
||||||
</ContextMenuItem>,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,12 +19,6 @@ import {
|
|||||||
Intersection,
|
Intersection,
|
||||||
Object3D,
|
Object3D,
|
||||||
} from 'three'
|
} from 'three'
|
||||||
import {
|
|
||||||
ContextMenu,
|
|
||||||
ContextMenuDivider,
|
|
||||||
ContextMenuItem,
|
|
||||||
ContextMenuItemRefresh,
|
|
||||||
} from './ContextMenu'
|
|
||||||
|
|
||||||
const CANVAS_SIZE = 80
|
const CANVAS_SIZE = 80
|
||||||
const FRUSTUM_SIZE = 0.5
|
const FRUSTUM_SIZE = 0.5
|
||||||
@ -44,17 +38,8 @@ enum AxisNames {
|
|||||||
NEG_Y = '-y',
|
NEG_Y = '-y',
|
||||||
NEG_Z = '-z',
|
NEG_Z = '-z',
|
||||||
}
|
}
|
||||||
const axisNamesSemantic: Record<AxisNames, string> = {
|
|
||||||
[AxisNames.X]: 'Right',
|
|
||||||
[AxisNames.Y]: 'Back',
|
|
||||||
[AxisNames.Z]: 'Top',
|
|
||||||
[AxisNames.NEG_X]: 'Left',
|
|
||||||
[AxisNames.NEG_Y]: 'Front',
|
|
||||||
[AxisNames.NEG_Z]: 'Bottom',
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Gizmo() {
|
export default function Gizmo() {
|
||||||
const wrapperRef = useRef<HTMLDivElement | null>(null)
|
|
||||||
const canvasRef = useRef<HTMLCanvasElement | null>(null)
|
const canvasRef = useRef<HTMLCanvasElement | null>(null)
|
||||||
const raycasterIntersect = useRef<Intersection<Object3D> | null>(null)
|
const raycasterIntersect = useRef<Intersection<Object3D> | null>(null)
|
||||||
const cameraPassiveUpdateTimer = useRef(0)
|
const cameraPassiveUpdateTimer = useRef(0)
|
||||||
@ -115,43 +100,9 @@ export default function Gizmo() {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="grid place-content-center rounded-full overflow-hidden border border-solid border-primary/50 pointer-events-none">
|
||||||
<div
|
<canvas ref={canvasRef} />
|
||||||
ref={wrapperRef}
|
</div>
|
||||||
aria-label="View orientation gizmo"
|
|
||||||
className="grid place-content-center rounded-full overflow-hidden border border-solid border-primary/50 pointer-events-auto"
|
|
||||||
>
|
|
||||||
<canvas ref={canvasRef} />
|
|
||||||
<ContextMenu
|
|
||||||
menuTargetElement={wrapperRef}
|
|
||||||
items={[
|
|
||||||
...Object.entries(axisNamesSemantic).map(
|
|
||||||
([axisName, axisSemantic]) => (
|
|
||||||
<ContextMenuItem
|
|
||||||
key={axisName}
|
|
||||||
onClick={() => {
|
|
||||||
sceneInfra.camControls.updateCameraToAxis(
|
|
||||||
axisName as AxisNames
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{axisSemantic} view
|
|
||||||
</ContextMenuItem>
|
|
||||||
)
|
|
||||||
),
|
|
||||||
<ContextMenuItem
|
|
||||||
onClick={() => {
|
|
||||||
sceneInfra.camControls.resetCameraPosition()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Reset view
|
|
||||||
</ContextMenuItem>,
|
|
||||||
<ContextMenuDivider />,
|
|
||||||
<ContextMenuItemRefresh />,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,6 +312,8 @@ class EngineConnection extends EventTarget {
|
|||||||
if (next.type === EngineConnectionStateType.Disconnecting) {
|
if (next.type === EngineConnectionStateType.Disconnecting) {
|
||||||
const sub = next.value
|
const sub = next.value
|
||||||
if (sub.type === DisconnectingType.Error) {
|
if (sub.type === DisconnectingType.Error) {
|
||||||
|
console.log(sub)
|
||||||
|
|
||||||
// Record the last step we failed at.
|
// Record the last step we failed at.
|
||||||
// (Check the current state that we're about to override that
|
// (Check the current state that we're about to override that
|
||||||
// it was a Connecting state.)
|
// it was a Connecting state.)
|
||||||
@ -754,6 +756,8 @@ class EngineConnection extends EventTarget {
|
|||||||
// when assuming we're the only consumer or that all messages will
|
// when assuming we're the only consumer or that all messages will
|
||||||
// be carefully formatted here.
|
// be carefully formatted here.
|
||||||
|
|
||||||
|
console.log(event)
|
||||||
|
|
||||||
if (typeof event.data !== 'string') {
|
if (typeof event.data !== 'string') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -774,6 +778,7 @@ class EngineConnection extends EventTarget {
|
|||||||
`Error in response to request ${message.request_id}:\n${errorsString}
|
`Error in response to request ${message.request_id}:\n${errorsString}
|
||||||
failed cmd type was ${artifactThatFailed?.commandType}`
|
failed cmd type was ${artifactThatFailed?.commandType}`
|
||||||
)
|
)
|
||||||
|
console.log(artifactThatFailed)
|
||||||
} else {
|
} else {
|
||||||
console.error(`Error from server:\n${errorsString}`)
|
console.error(`Error from server:\n${errorsString}`)
|
||||||
}
|
}
|
||||||
@ -864,6 +869,7 @@ class EngineConnection extends EventTarget {
|
|||||||
this.pc
|
this.pc
|
||||||
?.createOffer()
|
?.createOffer()
|
||||||
.then((offer: RTCSessionDescriptionInit) => {
|
.then((offer: RTCSessionDescriptionInit) => {
|
||||||
|
console.log(offer)
|
||||||
this.state = {
|
this.state = {
|
||||||
type: EngineConnectionStateType.Connecting,
|
type: EngineConnectionStateType.Connecting,
|
||||||
value: {
|
value: {
|
||||||
@ -935,6 +941,7 @@ class EngineConnection extends EventTarget {
|
|||||||
|
|
||||||
case 'trickle_ice':
|
case 'trickle_ice':
|
||||||
let candidate = resp.data?.candidate
|
let candidate = resp.data?.candidate
|
||||||
|
console.log('trickle_ice: using this candidate: ', candidate)
|
||||||
void this.pc?.addIceCandidate(candidate as RTCIceCandidateInit)
|
void this.pc?.addIceCandidate(candidate as RTCIceCandidateInit)
|
||||||
break
|
break
|
||||||
|
|
||||||
@ -1337,6 +1344,8 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
this.engineConnection?.addEventListener(
|
this.engineConnection?.addEventListener(
|
||||||
EngineConnectionEvents.NewTrack,
|
EngineConnectionEvents.NewTrack,
|
||||||
(({ detail: { mediaStream } }: CustomEvent<NewTrackArgs>) => {
|
(({ detail: { mediaStream } }: CustomEvent<NewTrackArgs>) => {
|
||||||
|
console.log('received track', mediaStream)
|
||||||
|
|
||||||
mediaStream.getVideoTracks()[0].addEventListener('mute', () => {
|
mediaStream.getVideoTracks()[0].addEventListener('mute', () => {
|
||||||
console.error(
|
console.error(
|
||||||
'video track mute: check webrtc internals -> inbound rtp'
|
'video track mute: check webrtc internals -> inbound rtp'
|
||||||
@ -1667,6 +1676,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
command.type === 'modeling_cmd_req' &&
|
command.type === 'modeling_cmd_req' &&
|
||||||
command.cmd.type !== lastMessage
|
command.cmd.type !== lastMessage
|
||||||
) {
|
) {
|
||||||
|
console.log('sending command', command.cmd.type)
|
||||||
lastMessage = command.cmd.type
|
lastMessage = command.cmd.type
|
||||||
}
|
}
|
||||||
if (command.type === 'modeling_cmd_batch_req') {
|
if (command.type === 'modeling_cmd_batch_req') {
|
||||||
|
54
src/wasm-lib/Cargo.lock
generated
54
src/wasm-lib/Cargo.lock
generated
@ -1138,6 +1138,16 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http-body"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"http 1.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httparse"
|
name = "httparse"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
@ -1162,7 +1172,7 @@ dependencies = [
|
|||||||
"futures-util",
|
"futures-util",
|
||||||
"h2",
|
"h2",
|
||||||
"http 0.2.12",
|
"http 0.2.12",
|
||||||
"http-body",
|
"http-body 0.4.6",
|
||||||
"httparse",
|
"httparse",
|
||||||
"httpdate",
|
"httpdate",
|
||||||
"itoa",
|
"itoa",
|
||||||
@ -1174,6 +1184,21 @@ dependencies = [
|
|||||||
"want",
|
"want",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"http 1.1.0",
|
||||||
|
"http-body 1.0.0",
|
||||||
|
"httpdate",
|
||||||
|
"pin-project-lite",
|
||||||
|
"smallvec",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-rustls"
|
name = "hyper-rustls"
|
||||||
version = "0.24.2"
|
version = "0.24.2"
|
||||||
@ -1182,7 +1207,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 0.2.12",
|
"http 0.2.12",
|
||||||
"hyper",
|
"hyper 0.14.29",
|
||||||
"rustls 0.21.12",
|
"rustls 0.21.12",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls 0.24.1",
|
"tokio-rustls 0.24.1",
|
||||||
@ -1434,6 +1459,15 @@ dependencies = [
|
|||||||
"syn 2.0.66",
|
"syn 2.0.66",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kcl-test-server"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"hyper 1.3.1",
|
||||||
|
"kcl-lib",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad"
|
name = "kittycad"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@ -2095,8 +2129,8 @@ dependencies = [
|
|||||||
"futures-util",
|
"futures-util",
|
||||||
"h2",
|
"h2",
|
||||||
"http 0.2.12",
|
"http 0.2.12",
|
||||||
"http-body",
|
"http-body 0.4.6",
|
||||||
"hyper",
|
"hyper 0.14.29",
|
||||||
"hyper-rustls",
|
"hyper-rustls",
|
||||||
"ipnet",
|
"ipnet",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
@ -2165,7 +2199,7 @@ dependencies = [
|
|||||||
"futures",
|
"futures",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"http 0.2.12",
|
"http 0.2.12",
|
||||||
"hyper",
|
"hyper 0.14.29",
|
||||||
"parking_lot 0.11.2",
|
"parking_lot 0.11.2",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"reqwest-middleware",
|
"reqwest-middleware",
|
||||||
@ -2975,9 +3009,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.8.14"
|
version = "0.8.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
|
checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
@ -2996,9 +3030,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.22.14"
|
version = "0.22.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
|
checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.2.5",
|
"indexmap 2.2.5",
|
||||||
"serde",
|
"serde",
|
||||||
@ -3444,7 +3478,7 @@ dependencies = [
|
|||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"futures",
|
"futures",
|
||||||
"gloo-utils",
|
"gloo-utils",
|
||||||
"hyper",
|
"hyper 0.14.29",
|
||||||
"image",
|
"image",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"kcl-lib",
|
"kcl-lib",
|
||||||
|
@ -17,7 +17,7 @@ kcl-lib = { path = "kcl" }
|
|||||||
kittycad = { workspace = true }
|
kittycad = { workspace = true }
|
||||||
serde_json = "1.0.116"
|
serde_json = "1.0.116"
|
||||||
tokio = { version = "1.38.0", features = ["sync"] }
|
tokio = { version = "1.38.0", features = ["sync"] }
|
||||||
toml = "0.8.14"
|
toml = "0.8.13"
|
||||||
uuid = { version = "1.8.0", features = ["v4", "js", "serde"] }
|
uuid = { version = "1.8.0", features = ["v4", "js", "serde"] }
|
||||||
wasm-bindgen = "0.2.91"
|
wasm-bindgen = "0.2.91"
|
||||||
wasm-bindgen-futures = "0.4.42"
|
wasm-bindgen-futures = "0.4.42"
|
||||||
@ -65,6 +65,7 @@ members = [
|
|||||||
"derive-docs",
|
"derive-docs",
|
||||||
"kcl",
|
"kcl",
|
||||||
"kcl-macros",
|
"kcl-macros",
|
||||||
|
"kcl-test-server",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
|
24
src/wasm-lib/grackle/Cargo.toml
Normal file
24
src/wasm-lib/grackle/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "grackle"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
description = "A new executor for KCL which compiles to Execution Plans"
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
image = { version = "0.25.1", default-features = false, features = ["png"] }
|
||||||
|
kcl-lib = { path = "../kcl" }
|
||||||
|
kittycad = { workspace = true }
|
||||||
|
kittycad-execution-plan = { workspace = true }
|
||||||
|
kittycad-execution-plan-traits = { workspace = true }
|
||||||
|
kittycad-execution-plan-macros = { workspace = true }
|
||||||
|
kittycad-modeling-cmds = { workspace = true }
|
||||||
|
kittycad-modeling-session = { workspace = true }
|
||||||
|
thiserror = "1.0.61"
|
||||||
|
tokio = { version = "1.37.0", features = ["macros", "rt"] }
|
||||||
|
twenty-twenty = "0.8.0"
|
||||||
|
uuid = "1.8"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
pretty_assertions = "1"
|
||||||
|
serde_json = "1.0.116"
|
9
src/wasm-lib/kcl-test-server/Cargo.toml
Normal file
9
src/wasm-lib/kcl-test-server/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "kcl-test-server"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
hyper = { version = "1.3.1", features = ["server"] }
|
||||||
|
kcl-lib = { path = "../kcl" }
|
||||||
|
tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] }
|
67
src/wasm-lib/kcl-test-server/src/main.rs
Normal file
67
src/wasm-lib/kcl-test-server/src/main.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
|
use hyper::header::CONTENT_TYPE;
|
||||||
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
|
use hyper::{Body, Error, Response, Server};
|
||||||
|
use kcl_lib::executor::ExecutorContext;
|
||||||
|
use kcl_lib::settings::types::UnitLength;
|
||||||
|
|
||||||
|
use crate::new_context;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
let listen_on = std::env::args().skip(1).next().unwrap();
|
||||||
|
start(listen_on).await?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn start(listen_on: SocketAddr) -> anyhow::Result<()> {
|
||||||
|
let state: ExecutorContext = new_context(UnitLength::Mm).await?;
|
||||||
|
// In hyper, a `MakeService` is basically your server.
|
||||||
|
// It makes a `Service` for each connection, which manages the connection.
|
||||||
|
let make_service = make_service_fn(
|
||||||
|
// This closure is run for each connection.
|
||||||
|
move |_| {
|
||||||
|
let state = state.clone();
|
||||||
|
async move {
|
||||||
|
// This is the `Service` which handles the connection.
|
||||||
|
// `service_fn` converts a function which returns a Response
|
||||||
|
// into a `Service`.
|
||||||
|
Ok::<_, Error>(service_fn(move |req| {
|
||||||
|
// Return a response.
|
||||||
|
|
||||||
|
async move {
|
||||||
|
let whole_body = hyper::body::to_bytes(req.into_body()).await?;
|
||||||
|
let Ok(kcl_src_code) = String::from_utf8(whole_body.into()) else {
|
||||||
|
return Ok(bad_request("Body was not UTF-8".to_owned()));
|
||||||
|
};
|
||||||
|
let parser = match kcl_lib::token::lexer(&kcl_src_code) {
|
||||||
|
Ok(t) => kcl_lib::parser::Parser::new(t),
|
||||||
|
Err(e) => return Ok(bad_request(format!("tokenization error: {e}"))),
|
||||||
|
};
|
||||||
|
let program = match parser.ast() {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => return Ok(bad_request(format!("Parse error: {e}"))),
|
||||||
|
};
|
||||||
|
let png_bytes: Vec<u8> = todo!();
|
||||||
|
let mut resp = Response::new(Body::from(png_bytes));
|
||||||
|
resp.headers_mut().insert(CONTENT_TYPE, "image/png".parse().unwrap());
|
||||||
|
Ok::<_, Error>(resp)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let server = Server::bind(&listen_on).serve(make_service);
|
||||||
|
println!("Listening on {listen_on}");
|
||||||
|
if let Err(e) = server.await {
|
||||||
|
eprintln!("Server error: {e}");
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bad_request(msg: String) -> Response<Body> {
|
||||||
|
let mut resp = Response::new(Body::from(msg));
|
||||||
|
*resp.status_mut() = hyper::StatusCode::BAD_REQUEST;
|
||||||
|
resp
|
||||||
|
}
|
@ -35,7 +35,7 @@ serde = { version = "1.0.203", features = ["derive"] }
|
|||||||
serde_json = "1.0.116"
|
serde_json = "1.0.116"
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
thiserror = "1.0.61"
|
thiserror = "1.0.61"
|
||||||
toml = "0.8.14"
|
toml = "0.8.13"
|
||||||
# TODO: change this to a cargo release once 8.1.1 comes out
|
# TODO: change this to a cargo release once 8.1.1 comes out
|
||||||
ts-rs = { git = "https://github.com/Aleph-Alpha/ts-rs", features = ["uuid-impl", "url-impl", "chrono-impl", "no-serde-warnings"] }
|
ts-rs = { git = "https://github.com/Aleph-Alpha/ts-rs", features = ["uuid-impl", "url-impl", "chrono-impl", "no-serde-warnings"] }
|
||||||
url = { version = "2.5.0", features = ["serde"] }
|
url = { version = "2.5.0", features = ["serde"] }
|
||||||
|
@ -4,8 +4,6 @@ use kcl_lib::{
|
|||||||
settings::types::UnitLength,
|
settings::types::UnitLength,
|
||||||
};
|
};
|
||||||
|
|
||||||
// mod server;
|
|
||||||
|
|
||||||
async fn new_context(units: UnitLength) -> Result<ExecutorContext> {
|
async fn new_context(units: UnitLength) -> Result<ExecutorContext> {
|
||||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||||
let http_client = reqwest::Client::builder()
|
let http_client = reqwest::Client::builder()
|
||||||
@ -1959,3 +1957,23 @@ async fn serial_test_neg_xz_plane() {
|
|||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/neg_xz_plane.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/neg_xz_plane.png", &result, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn start_server() {
|
||||||
|
let server_url = "0.0.0.0:3333";
|
||||||
|
tokio::task::spawn(server::start(server_url.parse().unwrap()));
|
||||||
|
let code = r#"const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> lineTo([100, 100], %)
|
||||||
|
|> lineTo([100, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5 + 7, %)
|
||||||
|
"#;
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
client
|
||||||
|
.post(format!("http://{server_url}"))
|
||||||
|
.body(code)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
1
src/wasm-lib/tests/executor/server.rs
Normal file
1
src/wasm-lib/tests/executor/server.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
Reference in New Issue
Block a user