Compare commits
7 Commits
parser-in-
...
v0.18.1
Author | SHA1 | Date | |
---|---|---|---|
2d0ac249df | |||
3d73b82c23 | |||
0b235dc1cd | |||
0857415793 | |||
1da4fd03ef | |||
39d84c12ab | |||
537d86c8ff |
@ -1,3 +1,3 @@
|
||||
module.exports = {
|
||||
presets: ["@babel/preset-env"],
|
||||
presets: ['@babel/preset-env'],
|
||||
}
|
||||
|
@ -104,6 +104,7 @@ test('Basic sketch', async ({ page }) => {
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> line([${commonPoints.num1}, 0], %)
|
||||
|> line([0, ${commonPoints.num1}], %)`)
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||
@ -328,9 +329,7 @@ test('if you write invalid kcl you get inlined errors', async ({ page }) => {
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
})
|
||||
|
||||
/* 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 ({
|
||||
test('if your kcl gets an error from the engine it is inlined', async ({
|
||||
page,
|
||||
}) => {
|
||||
const u = getUtils(page)
|
||||
@ -349,7 +348,7 @@ const sketch001 = startSketchOn(box, "revolveAxis")
|
||||
|> startProfileAt([5, 10], %)
|
||||
|> line([0, -10], %)
|
||||
|> line([2, 0], %)
|
||||
|> line([0, 10], %)
|
||||
|> line([0, -10], %)
|
||||
|> close(%)
|
||||
|> revolve({
|
||||
axis: getEdge('revolveAxis', box),
|
||||
@ -364,7 +363,7 @@ angle: 90
|
||||
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
u.openDebugPanel()
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
|
||||
@ -378,7 +377,7 @@ angle: 90
|
||||
'sketch profile must lie entirely on one side of the revolution axis'
|
||||
)
|
||||
).toBeVisible()
|
||||
})*/
|
||||
})
|
||||
|
||||
test('executes on load', async ({ page }) => {
|
||||
const u = getUtils(page)
|
||||
@ -736,7 +735,7 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
||||
await u.openDebugPanel()
|
||||
|
||||
const xAxisClick = () =>
|
||||
page.mouse.click(700, 250).then(() => page.waitForTimeout(100))
|
||||
page.mouse.click(700, 253).then(() => page.waitForTimeout(100))
|
||||
const emptySpaceClick = () =>
|
||||
page.mouse.click(728, 343).then(() => page.waitForTimeout(100))
|
||||
const topHorzSegmentClick = () =>
|
||||
@ -761,6 +760,7 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt(${commonPoints.startAt}, %)`)
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
@ -768,12 +768,14 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> line([${commonPoints.num1}, 0], %)`)
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> line([${commonPoints.num1}, 0], %)
|
||||
|> line([0, ${commonPoints.num1}], %)`)
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||
@ -786,10 +788,14 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
||||
await page.getByRole('button', { name: 'Line' }).click()
|
||||
|
||||
await u.closeDebugPanel()
|
||||
const selectionSequence = async () => {
|
||||
const selectionSequence = async (isSecondTime = false) => {
|
||||
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
|
||||
|
||||
await page.mouse.move(startXPx + PUR * 15, 500 - PUR * 10)
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.move(
|
||||
startXPx + PUR * 15,
|
||||
isSecondTime ? 430 : 500 - PUR * 10
|
||||
)
|
||||
|
||||
await expect(page.getByTestId('hover-highlight')).toBeVisible()
|
||||
// bg-yellow-200 is more brittle than hover-highlight, but is closer to the user experience
|
||||
@ -799,7 +805,10 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
||||
// check mousing off, than mousing onto another line
|
||||
await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 15) // mouse off
|
||||
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
|
||||
await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 20) // mouse onto another line
|
||||
await page.mouse.move(
|
||||
startXPx + PUR * 10,
|
||||
isSecondTime ? 295 : 500 - PUR * 20
|
||||
) // mouse onto another line
|
||||
await expect(page.getByTestId('hover-highlight')).toBeVisible()
|
||||
|
||||
// now check clicking works including axis
|
||||
@ -809,6 +818,7 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
||||
await page.keyboard.down('Shift')
|
||||
const absYButton = page.getByRole('button', { name: 'ABS Y' })
|
||||
await expect(absYButton).toBeDisabled()
|
||||
await page.waitForTimeout(100)
|
||||
await xAxisClick()
|
||||
await page.keyboard.up('Shift')
|
||||
await absYButton.and(page.locator(':not([disabled])')).waitFor()
|
||||
@ -817,10 +827,12 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
||||
// clear selection by clicking on nothing
|
||||
await emptySpaceClick()
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
// same selection but click the axis first
|
||||
await xAxisClick()
|
||||
await expect(absYButton).toBeDisabled()
|
||||
await page.keyboard.down('Shift')
|
||||
await page.waitForTimeout(100)
|
||||
await topHorzSegmentClick()
|
||||
|
||||
await page.keyboard.up('Shift')
|
||||
@ -833,6 +845,7 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
||||
await page.getByText(` |> line([-${commonPoints.num2}, 0], %)`).click()
|
||||
await page.keyboard.down('Shift')
|
||||
await expect(absYButton).toBeDisabled()
|
||||
await page.waitForTimeout(100)
|
||||
await xAxisClick()
|
||||
await page.keyboard.up('Shift')
|
||||
await expect(absYButton).not.toBeDisabled()
|
||||
@ -875,11 +888,16 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
// enter sketch again
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
await u.doAndWaitForCmd(
|
||||
() => page.getByRole('button', { name: 'Edit Sketch' }).click(),
|
||||
'default_camera_get_settings'
|
||||
)
|
||||
await page.waitForTimeout(150)
|
||||
|
||||
await page.waitForTimeout(300) // wait for animation
|
||||
|
||||
// hover again and check it works
|
||||
await selectionSequence()
|
||||
await selectionSequence(true)
|
||||
})
|
||||
|
||||
test.describe('Command bar tests', () => {
|
||||
@ -1065,6 +1083,7 @@ test('Can add multiple sketches', async ({ page }) => {
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
|> line([${commonPoints.num1}, 0], %)
|
||||
|> line([0, ${commonPoints.num1}], %)`)
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||
const finalCodeFirstSketch = `const part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt(${commonPoints.startAt}, %)
|
||||
@ -1080,24 +1099,33 @@ test('Can add multiple sketches', async ({ page }) => {
|
||||
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
|
||||
await u.updateCamPosition([0, 100, 100])
|
||||
await u.updateCamPosition([100, 100, 100])
|
||||
await page.waitForTimeout(250)
|
||||
|
||||
// start a new sketch
|
||||
await u.clearCommandLogs()
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(673, 384)
|
||||
await page.waitForTimeout(400)
|
||||
await page.mouse.click(650, 450)
|
||||
|
||||
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
||||
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)
|
||||
const startAt2 = '[0.93,-1.25]'
|
||||
const startAt2 =
|
||||
process.platform === 'darwin' ? '[9.75, -13.16]' : '[0.93, -1.25]'
|
||||
await expect(
|
||||
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||
).toBe(
|
||||
`${finalCodeFirstSketch}
|
||||
const part002 = startSketchOn('XY')
|
||||
const part002 = startSketchOn('${plane}')
|
||||
|> startProfileAt(${startAt2}, %)`.replace(/\s/g, '')
|
||||
)
|
||||
await page.waitForTimeout(100)
|
||||
@ -1106,12 +1134,12 @@ const part002 = startSketchOn('XY')
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
const num2 = 0.94
|
||||
const num2 = process.platform === 'darwin' ? 9.84 : 0.94
|
||||
await expect(
|
||||
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||
).toBe(
|
||||
`${finalCodeFirstSketch}
|
||||
const part002 = startSketchOn('XY')
|
||||
const part002 = startSketchOn('${plane}')
|
||||
|> startProfileAt(${startAt2}, %)
|
||||
|> line([${num2}, 0], %)`.replace(/\s/g, '')
|
||||
)
|
||||
@ -1121,21 +1149,29 @@ const part002 = startSketchOn('XY')
|
||||
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||
).toBe(
|
||||
`${finalCodeFirstSketch}
|
||||
const part002 = startSketchOn('XY')
|
||||
const part002 = startSketchOn('${plane}')
|
||||
|> startProfileAt(${startAt2}, %)
|
||||
|> line([${num2}, 0], %)
|
||||
|> line([0, ${roundOff(num2 - 0.01)}], %)`.replace(/\s/g, '')
|
||||
|> line([0, ${roundOff(
|
||||
num2 + (process.platform === 'darwin' ? 0.01 : -0.01)
|
||||
)}], %)`.replace(/\s/g, '')
|
||||
)
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||
await expect(
|
||||
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||
).toBe(
|
||||
`${finalCodeFirstSketch}
|
||||
const part002 = startSketchOn('XY')
|
||||
const part002 = startSketchOn('${plane}')
|
||||
|> startProfileAt(${startAt2}, %)
|
||||
|> line([${num2}, 0], %)
|
||||
|> line([0, ${roundOff(num2 - 0.01)}], %)
|
||||
|> line([-1.87, 0], %)`.replace(/\s/g, '')
|
||||
|> line([0, ${roundOff(
|
||||
num2 + (process.platform === 'darwin' ? 0.01 : -0.01)
|
||||
)}], %)
|
||||
|> line([-${process.platform === 'darwin' ? 19.59 : 1.87}, 0], %)`.replace(
|
||||
/\s/g,
|
||||
''
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
@ -1339,10 +1375,12 @@ test('Deselecting line tool should mean nothing happens on click', async ({
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(700, 300)
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(750, 300)
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||
@ -1367,16 +1405,16 @@ test('Can edit segments by dragging their handles', async ({ page }) => {
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
|
||||
const startPX = [652, 418]
|
||||
const lineEndPX = [794, 416]
|
||||
const arcEndPX = [893, 318]
|
||||
const startPX = [665, 458]
|
||||
const lineEndPX = [842, 458]
|
||||
const arcEndPX = [971, 342]
|
||||
|
||||
const dragPX = 30
|
||||
|
||||
await page.getByText('startProfileAt([4.61, -14.01], %)').click()
|
||||
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
await page.waitForTimeout(100)
|
||||
await page.waitForTimeout(400)
|
||||
let prevContent = await page.locator('.cm-content').innerText()
|
||||
|
||||
const step5 = { steps: 5 }
|
||||
@ -1386,7 +1424,7 @@ test('Can edit segments by dragging their handles', async ({ page }) => {
|
||||
await page.mouse.down()
|
||||
await page.mouse.move(startPX[0] + dragPX, startPX[1] - dragPX, step5)
|
||||
await page.mouse.up()
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||
prevContent = await page.locator('.cm-content').innerText()
|
||||
|
||||
@ -1414,9 +1452,9 @@ test('Can edit segments by dragging their handles', async ({ page }) => {
|
||||
// expect the code to have changed
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt([7.01, -11.79], %)
|
||||
|> line([14.69, 2.73], %)
|
||||
|> tangentialArcTo([27.6, -3.25], %)`)
|
||||
|> startProfileAt([6.44, -12.07], %)
|
||||
|> line([14.04, 2.03], %)
|
||||
|> tangentialArcTo([27.19, -4.2], %)`)
|
||||
})
|
||||
|
||||
const doSnapAtDifferentScales = async (
|
||||
@ -1535,38 +1573,46 @@ test('Sketch on face', async ({ page }) => {
|
||||
).not.toBeDisabled()
|
||||
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
await page.waitForTimeout(300)
|
||||
|
||||
let previousCodeContent = await page.locator('.cm-content').innerText()
|
||||
|
||||
await page.mouse.click(793, 133)
|
||||
await u.openAndClearDebugPanel()
|
||||
await u.doAndWaitForCmd(
|
||||
() => page.mouse.click(793, 133),
|
||||
'default_camera_get_settings',
|
||||
true
|
||||
)
|
||||
await page.waitForTimeout(150)
|
||||
|
||||
const firstClickPosition = [612, 238]
|
||||
const secondClickPosition = [661, 242]
|
||||
const thirdClickPosition = [609, 267]
|
||||
|
||||
await page.waitForTimeout(300)
|
||||
|
||||
await page.mouse.click(firstClickPosition[0], firstClickPosition[1])
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(secondClickPosition[0], secondClickPosition[1])
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(thirdClickPosition[0], thirdClickPosition[1])
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(firstClickPosition[0], firstClickPosition[1])
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|
||||
|> startProfileAt([1.03, 1.03], %)
|
||||
|> line([4.18, -0.35], %)
|
||||
|> line([-4.44, -2.13], %)
|
||||
|> startProfileAt([-12.83, 6.7], %)
|
||||
|> line([2.87, -0.23], %)
|
||||
|> line([-3.05, -1.47], %)
|
||||
|> close(%)`)
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
@ -1576,9 +1622,14 @@ test('Sketch on face', async ({ page }) => {
|
||||
await u.updateCamPosition([1049, 239, 686])
|
||||
await u.closeDebugPanel()
|
||||
|
||||
await page.getByText('startProfileAt([1.03, 1.03], %)').click()
|
||||
await page.getByText('startProfileAt([-12.83, 6.7], %)').click()
|
||||
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
await u.doAndWaitForCmd(
|
||||
() => page.getByRole('button', { name: 'Edit Sketch' }).click(),
|
||||
'default_camera_get_settings',
|
||||
true
|
||||
)
|
||||
await page.waitForTimeout(150)
|
||||
await page.setViewportSize({ width: 1200, height: 1200 })
|
||||
await u.openAndClearDebugPanel()
|
||||
await u.updateCamPosition([452, -152, 1166])
|
||||
@ -1598,11 +1649,11 @@ test('Sketch on face', async ({ page }) => {
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|
||||
|> startProfileAt([1.03, 1.03], %)
|
||||
|> line([${process?.env?.CI ? 2.74 : 2.93}, -${
|
||||
process?.env?.CI ? 0.24 : 0.2
|
||||
|> startProfileAt([-12.83, 6.7], %)
|
||||
|> line([${process?.env?.CI ? 2.28 : 2.28}, -${
|
||||
process?.env?.CI ? 0.07 : 0.07
|
||||
}], %)
|
||||
|> line([-4.44, -2.13], %)
|
||||
|> line([-3.05, -1.47], %)
|
||||
|> close(%)`)
|
||||
|
||||
// exit sketch
|
||||
@ -1610,7 +1661,7 @@ test('Sketch on face', async ({ page }) => {
|
||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
|
||||
await page.getByText('startProfileAt([1.03, 1.03], %)').click()
|
||||
await page.getByText('startProfileAt([-12.83, 6.7], %)').click()
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Extrude' })).not.toBeDisabled()
|
||||
await page.getByRole('button', { name: 'Extrude' }).click()
|
||||
@ -1624,11 +1675,11 @@ test('Sketch on face', async ({ page }) => {
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|
||||
|> startProfileAt([1.03, 1.03], %)
|
||||
|> line([${process?.env?.CI ? 2.74 : 2.93}, -${
|
||||
process?.env?.CI ? 0.24 : 0.2
|
||||
|> startProfileAt([-12.83, 6.7], %)
|
||||
|> line([${process?.env?.CI ? 2.28 : 2.28}, -${
|
||||
process?.env?.CI ? 0.07 : 0.07
|
||||
}], %)
|
||||
|> line([-4.44, -2.13], %)
|
||||
|> line([-3.05, -1.47], %)
|
||||
|> close(%)
|
||||
|> extrude(5 + 7, %)`)
|
||||
})
|
||||
@ -1661,11 +1712,11 @@ test('Can code mod a line length', async ({ page }) => {
|
||||
|
||||
// enter sketch again
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
await page.waitForTimeout(300) // wait for animation
|
||||
await page.waitForTimeout(350) // wait for animation
|
||||
|
||||
const startXPx = 500
|
||||
await page.mouse.move(startXPx + PUR * 15, 250 - PUR * 10)
|
||||
await page.mouse.click(615, 133)
|
||||
await page.mouse.click(615, 102)
|
||||
await page.getByRole('button', { name: 'length', exact: true }).click()
|
||||
await page.getByText('Add constraining value').click()
|
||||
|
||||
@ -1673,3 +1724,42 @@ 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, %)`
|
||||
)
|
||||
})
|
||||
|
||||
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: 50 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 51 KiB |
@ -6,7 +6,7 @@ import { PNG } from 'pngjs'
|
||||
|
||||
async function waitForPageLoad(page: Page) {
|
||||
// wait for 'Loading stream...' spinner
|
||||
await page.getByTestId('loading-stream').waitFor()
|
||||
// await page.getByTestId('loading-stream').waitFor()
|
||||
// wait for all spinners to be gone
|
||||
await page.getByTestId('loading').waitFor({ state: 'detached' })
|
||||
|
||||
|
@ -57,7 +57,7 @@ echo "New version number without 'v': $new_version_number"
|
||||
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" '.package.version=$v' src-tauri/tauri.conf.json --indent 2)" > src-tauri/tauri.conf.json
|
||||
echo "$(jq --arg v "$new_version_number" '.version=$v' src-tauri/tauri.conf.json --indent 2)" > src-tauri/tauri.conf.json
|
||||
|
||||
git add package.json src-tauri/tauri.conf.json
|
||||
git commit -m "Cut release $new_version"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "untitled-app",
|
||||
"version": "0.17.3",
|
||||
"version": "0.18.1",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.16.0",
|
||||
@ -8,7 +8,7 @@
|
||||
"@fortawesome/free-brands-svg-icons": "^6.5.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@headlessui/react": "^1.7.18",
|
||||
"@headlessui/react": "^1.7.19",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@kittycad/lib": "^0.0.58",
|
||||
"@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",
|
||||
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
||||
"fmt": "prettier --write ./src && prettier --write ./e2e",
|
||||
"fmt-check": "prettier --check ./src && prettier --check ./e2e",
|
||||
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e",
|
||||
"fmt-check": "prettier --check ./src *.ts *.json *.js ./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": "(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",
|
||||
@ -132,6 +132,7 @@
|
||||
"@types/wicg-file-system-access": "^2023.10.5",
|
||||
"@types/ws": "^8.5.10",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"@vitest/web-worker": "^1.5.0",
|
||||
"@wdio/cli": "^8.24.3",
|
||||
"@wdio/globals": "^8.36.0",
|
||||
"@wdio/local-runner": "^8.36.0",
|
||||
|
@ -49,8 +49,6 @@ export default defineConfig({
|
||||
// use: { ...devices['Desktop Chrome'] },
|
||||
// },
|
||||
|
||||
|
||||
|
||||
/* Test against mobile viewports. */
|
||||
// {
|
||||
// name: 'Mobile Chrome',
|
||||
@ -78,4 +76,4 @@ export default defineConfig({
|
||||
// url: 'http://127.0.0.1:3000',
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
@ -55,5 +55,5 @@
|
||||
}
|
||||
},
|
||||
"productName": "Zoo Modeling App",
|
||||
"version": "0.17.3"
|
||||
"version": "0.18.1"
|
||||
}
|
||||
|
@ -246,13 +246,31 @@ export class CameraControls {
|
||||
camSettings.center.y,
|
||||
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) {
|
||||
this.camera.fov = camSettings.fov_y
|
||||
} else if (
|
||||
this.camera instanceof OrthographicCamera &&
|
||||
camSettings.ortho_scale
|
||||
) {
|
||||
this.camera.zoom = camSettings.ortho_scale
|
||||
const distanceToTarget = new Vector3(
|
||||
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()
|
||||
}
|
||||
@ -965,10 +983,10 @@ export class CameraControls {
|
||||
// Pure function helpers
|
||||
|
||||
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_far = 1000 + nearFarRatio * (100000 - 1000)
|
||||
return { z_near: 0.1, z_far }
|
||||
// const z_far = 1000 + nearFarRatio * (100000 - 1000)
|
||||
return { z_near: 0.1, z_far: 1000 }
|
||||
}
|
||||
|
||||
function convertThreeCamValuesToEngineCam({
|
||||
@ -1043,3 +1061,62 @@ function _getInteractionType(
|
||||
if (enableZoom && interactionGuards.zoom.dragCallback(event)) return 'zoom'
|
||||
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,7 +3,6 @@ import { useModelingContext } from 'hooks/useModelingContext'
|
||||
|
||||
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { useStore } from 'useStore'
|
||||
import { ARROWHEAD, DEBUG_SHOW_BOTH_SCENES } from './sceneInfra'
|
||||
import { ReactCameraProperties } from './CameraControls'
|
||||
import { throttle } from 'lib/utils'
|
||||
@ -47,10 +46,6 @@ export const ClientSideScene = ({
|
||||
const canvasRef = useRef<HTMLDivElement>(null)
|
||||
const { state, send, context } = useModelingContext()
|
||||
const { hideClient, hideServer } = useShouldHideScene()
|
||||
const { setHighlightRange } = useStore((s) => ({
|
||||
setHighlightRange: s.setHighlightRange,
|
||||
highlightRange: s.highlightRange,
|
||||
}))
|
||||
|
||||
// Listen for changes to the camera controls setting
|
||||
// and update the client-side scene's controls accordingly.
|
||||
@ -69,7 +64,6 @@ export const ClientSideScene = ({
|
||||
const canvas = canvasRef.current
|
||||
canvas.appendChild(sceneInfra.renderer.domElement)
|
||||
sceneInfra.animate()
|
||||
sceneInfra.setHighlightCallback(setHighlightRange)
|
||||
canvas.addEventListener('mousemove', sceneInfra.onMouseMove, false)
|
||||
canvas.addEventListener('mousedown', sceneInfra.onMouseDown, false)
|
||||
canvas.addEventListener('mouseup', sceneInfra.onMouseUp, false)
|
||||
|
@ -57,6 +57,7 @@ import {
|
||||
kclManager,
|
||||
sceneInfra,
|
||||
codeManager,
|
||||
editorManager,
|
||||
} from 'lib/singletons'
|
||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||
import { executeAst, useStore } from 'useStore'
|
||||
@ -214,8 +215,9 @@ export class SceneEntities {
|
||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||
const baseXColor = 0x000055
|
||||
const baseYColor = 0x550000
|
||||
const xAxisGeometry = new BoxGeometry(100000, 0.3, 0.01)
|
||||
const yAxisGeometry = new BoxGeometry(0.3, 100000, 0.01)
|
||||
const axisPixelWidth = 1.6
|
||||
const xAxisGeometry = new BoxGeometry(100000, axisPixelWidth, 0.01)
|
||||
const yAxisGeometry = new BoxGeometry(axisPixelWidth, 100000, 0.01)
|
||||
const xAxisMaterial = new MeshBasicMaterial({
|
||||
color: baseXColor,
|
||||
depthTest: false,
|
||||
@ -1323,30 +1325,31 @@ export class SceneEntities {
|
||||
selected.material.color = defaultPlaneColor(type)
|
||||
},
|
||||
onClick: async (args) => {
|
||||
const checkExtrudeFaceClick = async (): Promise<boolean> => {
|
||||
const checkExtrudeFaceClick = async (): Promise<
|
||||
['face' | 'plane' | 'other', string]
|
||||
> => {
|
||||
const { streamDimensions } = useStore.getState()
|
||||
const { entity_id } = await sendSelectEventToEngine(
|
||||
args?.mouseEvent,
|
||||
document.getElementById('video-stream') as HTMLVideoElement,
|
||||
streamDimensions
|
||||
)
|
||||
if (!entity_id) return false
|
||||
if (!entity_id) return ['other', '']
|
||||
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]
|
||||
if (artifact?.commandType !== 'solid3d_get_extrusion_face_info')
|
||||
return false
|
||||
const faceInfo: Models['FaceIsPlanar_type'] = (
|
||||
await this.engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'face_is_planar',
|
||||
object_id: entity_id,
|
||||
},
|
||||
})
|
||||
)?.data?.data
|
||||
return ['other', entity_id]
|
||||
|
||||
const faceInfo = await getFaceDetails(entity_id)
|
||||
if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis)
|
||||
return false
|
||||
const { z_axis, origin, y_axis } = faceInfo
|
||||
return ['other', entity_id]
|
||||
const { z_axis, y_axis, origin } = faceInfo
|
||||
const pathToNode = getNodePathFromSourceRange(
|
||||
kclManager.ast,
|
||||
artifact.range
|
||||
@ -1366,12 +1369,15 @@ export class SceneEntities {
|
||||
artifact?.additionalData?.type === 'cap'
|
||||
? artifact.additionalData.info
|
||||
: 'none',
|
||||
faceId: entity_id,
|
||||
},
|
||||
})
|
||||
return true
|
||||
return ['face', entity_id]
|
||||
}
|
||||
|
||||
if (await checkExtrudeFaceClick()) return
|
||||
const faceResult = await checkExtrudeFaceClick()
|
||||
console.log('faceResult', faceResult)
|
||||
if (faceResult[0] === 'face') return
|
||||
|
||||
if (!args || !args.intersects?.[0]) return
|
||||
if (args.mouseEvent.which !== 1) return
|
||||
@ -1397,6 +1403,7 @@ export class SceneEntities {
|
||||
plane: planeString,
|
||||
zAxis,
|
||||
yAxis,
|
||||
planeId: faceResult[1],
|
||||
},
|
||||
})
|
||||
},
|
||||
@ -1423,7 +1430,7 @@ export class SceneEntities {
|
||||
parent.userData.pathToNode,
|
||||
'CallExpression'
|
||||
).node
|
||||
sceneInfra.highlightCallback([node.start, node.end])
|
||||
editorManager.setHighlightRange([node.start, node.end])
|
||||
const yellow = 0xffff00
|
||||
colorSegment(selected, yellow)
|
||||
const extraSegmentGroup = parent.getObjectByName(EXTRA_SEGMENT_HANDLE)
|
||||
@ -1459,10 +1466,10 @@ export class SceneEntities {
|
||||
}
|
||||
return
|
||||
}
|
||||
sceneInfra.highlightCallback([0, 0])
|
||||
editorManager.setHighlightRange([0, 0])
|
||||
},
|
||||
onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => {
|
||||
sceneInfra.highlightCallback([0, 0])
|
||||
editorManager.setHighlightRange([0, 0])
|
||||
const parent = getParentGroup(selected, [
|
||||
STRAIGHT_SEGMENT,
|
||||
TANGENTIAL_ARC_TO_SEGMENT,
|
||||
@ -1680,7 +1687,7 @@ export async function getSketchOrientationDetails(
|
||||
sketchPathToNode: PathToNode
|
||||
): Promise<{
|
||||
quat: Quaternion
|
||||
sketchDetails: SketchDetails
|
||||
sketchDetails: SketchDetails & { faceId?: string }
|
||||
}> {
|
||||
const sketchGroup = sketchGroupFromPathToNode({
|
||||
pathToNode: sketchPathToNode,
|
||||
@ -1696,20 +1703,13 @@ export async function getSketchOrientationDetails(
|
||||
zAxis: [zAxis.x, zAxis.y, zAxis.z],
|
||||
yAxis: [sketchGroup.yAxis.x, sketchGroup.yAxis.y, sketchGroup.yAxis.z],
|
||||
origin: [0, 0, 0],
|
||||
faceId: sketchGroup.on.id,
|
||||
},
|
||||
}
|
||||
}
|
||||
if (sketchGroup.on.type === 'face') {
|
||||
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
|
||||
const faceInfo = await getFaceDetails(sketchGroup.on.faceId)
|
||||
|
||||
if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis)
|
||||
throw new Error('faceInfo')
|
||||
const { z_axis, y_axis, origin } = faceInfo
|
||||
@ -1724,6 +1724,7 @@ export async function getSketchOrientationDetails(
|
||||
zAxis: [z_axis.x, z_axis.y, z_axis.z],
|
||||
yAxis: [y_axis.x, y_axis.y, y_axis.z],
|
||||
origin: [origin.x, origin.y, origin.z],
|
||||
faceId: sketchGroup.on.faceId,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -1732,6 +1733,46 @@ 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 {
|
||||
const dummyCam = new PerspectiveCamera()
|
||||
dummyCam.up.set(0, 0, 1)
|
||||
|
@ -24,7 +24,6 @@ import {
|
||||
import { compareVec2Epsilon2 } from 'lang/std/sketch'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
import * as TWEEN from '@tweenjs/tween.js'
|
||||
import { SourceRange } from 'lang/wasm'
|
||||
import { Axis } from 'lib/selections'
|
||||
import { type BaseUnit } from 'lib/settings/settingsTypes'
|
||||
import { CameraControls } from './CameraControls'
|
||||
@ -149,10 +148,6 @@ export class SceneInfra {
|
||||
onMouseLeave: () => {},
|
||||
})
|
||||
}
|
||||
highlightCallback: (a: SourceRange) => void = () => {}
|
||||
setHighlightCallback(cb: (a: SourceRange) => void) {
|
||||
this.highlightCallback = cb
|
||||
}
|
||||
|
||||
modelingSend: SendType = (() => {}) as any
|
||||
setSend(send: SendType) {
|
||||
|
@ -1,11 +1,9 @@
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { editorManager, kclManager } from 'lib/singletons'
|
||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useStore } from 'useStore'
|
||||
|
||||
export function AstExplorer() {
|
||||
const setHighlightRange = useStore((s) => s.setHighlightRange)
|
||||
const { context } = useModelingContext()
|
||||
const pathToNode = getNodePathFromSourceRange(
|
||||
// TODO maybe need to have callback to make sure it stays in sync
|
||||
@ -42,7 +40,7 @@ export function AstExplorer() {
|
||||
<div
|
||||
className="h-full relative"
|
||||
onMouseLeave={(e) => {
|
||||
setHighlightRange([0, 0])
|
||||
editorManager.setHighlightRange([0, 0])
|
||||
}}
|
||||
>
|
||||
<pre className="text-xs">
|
||||
@ -88,7 +86,6 @@ function DisplayObj({
|
||||
filterKeys: string[]
|
||||
node: any
|
||||
}) {
|
||||
const setHighlightRange = useStore((s) => s.setHighlightRange)
|
||||
const { send } = useModelingContext()
|
||||
const ref = useRef<HTMLPreElement>(null)
|
||||
const [hasCursor, setHasCursor] = useState(false)
|
||||
@ -112,12 +109,12 @@ function DisplayObj({
|
||||
hasCursor ? 'bg-violet-100/80 dark:bg-violet-100/25' : ''
|
||||
}`}
|
||||
onMouseEnter={(e) => {
|
||||
setHighlightRange([obj?.start || 0, obj.end])
|
||||
editorManager.setHighlightRange([obj?.start || 0, obj.end])
|
||||
e.stopPropagation()
|
||||
}}
|
||||
onMouseMove={(e) => {
|
||||
e.stopPropagation()
|
||||
setHighlightRange([obj?.start || 0, obj.end])
|
||||
editorManager.setHighlightRange([obj?.start || 0, obj.end])
|
||||
}}
|
||||
onClick={(e) => {
|
||||
send({
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useMachine } from '@xstate/react'
|
||||
import { editorManager } from 'lib/singletons'
|
||||
import { commandBarMachine } from 'machines/commandBarMachine'
|
||||
import { createContext } from 'react'
|
||||
import { createContext, useEffect } from 'react'
|
||||
import { EventFrom, StateFrom } from 'xstate'
|
||||
|
||||
type CommandsContextType = {
|
||||
@ -30,6 +31,10 @@ export const CommandBarProvider = ({
|
||||
},
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
editorManager.setCommandBarSend(commandBarSend)
|
||||
})
|
||||
|
||||
return (
|
||||
<CommandsContext.Provider
|
||||
value={{
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
sceneInfra,
|
||||
engineCommandManager,
|
||||
codeManager,
|
||||
editorManager,
|
||||
} from 'lib/singletons'
|
||||
import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance'
|
||||
import {
|
||||
@ -53,10 +54,9 @@ import { exportFromEngine } from 'lib/exportFromEngine'
|
||||
import { Models } from '@kittycad/lib/dist/types/src'
|
||||
import toast from 'react-hot-toast'
|
||||
import { EditorSelection } from '@uiw/react-codemirror'
|
||||
import { Vector3 } from 'three'
|
||||
import { quaternionFromUpNForward } from 'clientSideScene/helpers'
|
||||
import { CoreDumpManager } from 'lib/coredump'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls'
|
||||
|
||||
type MachineContext<T extends AnyStateMachine> = {
|
||||
state: StateFrom<T>
|
||||
@ -98,17 +98,6 @@ export const ModelingMachineProvider = ({
|
||||
)
|
||||
useHotkeys('meta + shift + .', () => coreDump(coreDumpManager, true))
|
||||
|
||||
const {
|
||||
isShiftDown,
|
||||
editorView,
|
||||
setLastCodeMirrorSelectionUpdatedFromScene,
|
||||
} = useStore((s) => ({
|
||||
isShiftDown: s.isShiftDown,
|
||||
editorView: s.editorView,
|
||||
setLastCodeMirrorSelectionUpdatedFromScene:
|
||||
s.setLastCodeMirrorSelectionUpdatedFromScene,
|
||||
}))
|
||||
|
||||
// Settings machine setup
|
||||
// const retrievedSettings = useRef(
|
||||
// localStorage?.getItem(MODELING_PERSIST_KEY) || '{}'
|
||||
@ -135,29 +124,33 @@ export const ModelingMachineProvider = ({
|
||||
'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
|
||||
const setSelections = event.data
|
||||
if (!editorView) return {}
|
||||
if (!editorManager.editorView) return {}
|
||||
const dispatchSelection = (selection?: EditorSelection) => {
|
||||
if (!selection) return // TODO less of hack for the below please
|
||||
setLastCodeMirrorSelectionUpdatedFromScene(Date.now())
|
||||
setTimeout(() => editorView.dispatch({ selection }))
|
||||
editorManager.lastSelectionEvent = Date.now()
|
||||
setTimeout(() => {
|
||||
if (editorManager.editorView) {
|
||||
editorManager.editorView.dispatch({ selection })
|
||||
}
|
||||
})
|
||||
}
|
||||
let selections: Selections = {
|
||||
codeBasedSelections: [],
|
||||
otherSelections: [],
|
||||
}
|
||||
if (setSelections.selectionType === 'singleCodeCursor') {
|
||||
if (!setSelections.selection && isShiftDown) {
|
||||
} else if (!setSelections.selection && !isShiftDown) {
|
||||
if (!setSelections.selection && editorManager.isShiftDown) {
|
||||
} else if (!setSelections.selection && !editorManager.isShiftDown) {
|
||||
selections = {
|
||||
codeBasedSelections: [],
|
||||
otherSelections: [],
|
||||
}
|
||||
} else if (setSelections.selection && !isShiftDown) {
|
||||
} else if (setSelections.selection && !editorManager.isShiftDown) {
|
||||
selections = {
|
||||
codeBasedSelections: [setSelections.selection],
|
||||
otherSelections: [],
|
||||
}
|
||||
} else if (setSelections.selection && isShiftDown) {
|
||||
} else if (setSelections.selection && editorManager.isShiftDown) {
|
||||
selections = {
|
||||
codeBasedSelections: [
|
||||
...selectionRanges.codeBasedSelections,
|
||||
@ -180,6 +173,7 @@ export const ModelingMachineProvider = ({
|
||||
engineCommandManager.sendSceneCommand(event)
|
||||
)
|
||||
updateSceneObjectColors()
|
||||
|
||||
return {
|
||||
selectionRanges: selections,
|
||||
}
|
||||
@ -192,7 +186,7 @@ export const ModelingMachineProvider = ({
|
||||
}
|
||||
|
||||
if (setSelections.selectionType === 'otherSelection') {
|
||||
if (isShiftDown) {
|
||||
if (editorManager.isShiftDown) {
|
||||
selections = {
|
||||
codeBasedSelections: selectionRanges.codeBasedSelections,
|
||||
otherSelections: [setSelections.selection],
|
||||
@ -324,16 +318,9 @@ export const ModelingMachineProvider = ({
|
||||
)
|
||||
await kclManager.executeAstMock(modifiedAst)
|
||||
|
||||
const forward = new Vector3(...data.zAxis)
|
||||
const up = new Vector3(...data.yAxis)
|
||||
|
||||
let target = new Vector3(...data.position).multiplyScalar(
|
||||
sceneInfra._baseUnitMultiplier
|
||||
)
|
||||
const quaternion = quaternionFromUpNForward(up, forward)
|
||||
await sceneInfra.camControls.tweenCameraToQuaternion(
|
||||
quaternion,
|
||||
target
|
||||
await letEngineAnimateAndSyncCamAfter(
|
||||
engineCommandManager,
|
||||
data.faceId
|
||||
)
|
||||
|
||||
return {
|
||||
@ -348,6 +335,7 @@ export const ModelingMachineProvider = ({
|
||||
data.plane
|
||||
)
|
||||
await kclManager.updateAst(modifiedAst, false)
|
||||
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
||||
const quat = await getSketchQuaternion(pathToNode, data.zAxis)
|
||||
await sceneInfra.camControls.tweenCameraToQuaternion(quat)
|
||||
return {
|
||||
@ -364,9 +352,9 @@ export const ModelingMachineProvider = ({
|
||||
sourceRange
|
||||
)
|
||||
const info = await getSketchOrientationDetails(sketchPathToNode || [])
|
||||
await sceneInfra.camControls.tweenCameraToQuaternion(
|
||||
info.quat,
|
||||
new Vector3(...info.sketchDetails.origin)
|
||||
await letEngineAnimateAndSyncCamAfter(
|
||||
engineCommandManager,
|
||||
info?.sketchDetails?.faceId || ''
|
||||
)
|
||||
return {
|
||||
sketchPathToNode: sketchPathToNode || [],
|
||||
@ -516,6 +504,19 @@ export const ModelingMachineProvider = ({
|
||||
})
|
||||
}, [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({
|
||||
machineId: 'modeling',
|
||||
state: modelingState,
|
||||
|
@ -1,13 +1,8 @@
|
||||
import { undo, redo } from '@codemirror/commands'
|
||||
import ReactCodeMirror from '@uiw/react-codemirror'
|
||||
import { TEST } from 'env'
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { useConvertToVariable } from 'hooks/useToolbarGuards'
|
||||
import { Themes, getSystemTheme } from 'lib/theme'
|
||||
import { useEffect, useMemo, useRef } from 'react'
|
||||
import { useStore } from 'useStore'
|
||||
import { processCodeMirrorRanges } from 'lib/selections'
|
||||
import { useEffect, useMemo } from 'react'
|
||||
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search'
|
||||
import { lineHighlightField } from 'editor/highlightextension'
|
||||
import { roundOff } from 'lib/utils'
|
||||
@ -29,7 +24,7 @@ import {
|
||||
historyKeymap,
|
||||
history,
|
||||
} from '@codemirror/commands'
|
||||
import { lintGutter, lintKeymap, linter } from '@codemirror/lint'
|
||||
import { lintGutter, lintKeymap } from '@codemirror/lint'
|
||||
import {
|
||||
foldGutter,
|
||||
foldKeymap,
|
||||
@ -39,25 +34,20 @@ import {
|
||||
syntaxHighlighting,
|
||||
defaultHighlightStyle,
|
||||
} from '@codemirror/language'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
import interact from '@replit/codemirror-interact'
|
||||
import { engineCommandManager, sceneInfra, kclManager } from 'lib/singletons'
|
||||
import { useKclContext } from 'lang/KclProvider'
|
||||
import { ModelingMachineEvent } from 'machines/modelingMachine'
|
||||
import { kclManager, editorManager, codeManager } from 'lib/singletons'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { isTauri } from 'lib/isTauri'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { paths } from 'lib/paths'
|
||||
import makeUrlPathRelative from 'lib/makeUrlPathRelative'
|
||||
import { useLspContext } from 'components/LspProvider'
|
||||
import { Prec, EditorState, Extension, SelectionRange } from '@codemirror/state'
|
||||
import { Prec, EditorState, Extension } from '@codemirror/state'
|
||||
import {
|
||||
closeBrackets,
|
||||
closeBracketsKeymap,
|
||||
completionKeymap,
|
||||
hasNextSnippetField,
|
||||
} from '@codemirror/autocomplete'
|
||||
import { kclErrorsToDiagnostics } from 'lang/errors'
|
||||
|
||||
export const editorShortcutMeta = {
|
||||
formatCode: {
|
||||
@ -77,13 +67,6 @@ export const KclEditorPane = () => {
|
||||
context.app.theme.current === Themes.System
|
||||
? getSystemTheme()
|
||||
: 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 navigate = useNavigate()
|
||||
|
||||
@ -96,90 +79,15 @@ export const KclEditorPane = () => {
|
||||
|
||||
useHotkeys('mod+z', (e) => {
|
||||
e.preventDefault()
|
||||
if (editorView) {
|
||||
undo(editorView)
|
||||
}
|
||||
editorManager.undo()
|
||||
})
|
||||
useHotkeys('mod+shift+z', (e) => {
|
||||
e.preventDefault()
|
||||
if (editorView) {
|
||||
redo(editorView)
|
||||
}
|
||||
editorManager.redo()
|
||||
})
|
||||
|
||||
const {
|
||||
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 textWrapping = context.textEditor.textWrapping
|
||||
const cursorBlinking = context.textEditor.blinkingCursor
|
||||
|
||||
const editorExtensions = useMemo(() => {
|
||||
const extensions = [
|
||||
@ -202,7 +110,7 @@ export const KclEditorPane = () => {
|
||||
{
|
||||
key: 'Meta-k',
|
||||
run: () => {
|
||||
commandBarSend({ type: 'Open' })
|
||||
editorManager.commandBarSend({ type: 'Open' })
|
||||
return false
|
||||
},
|
||||
},
|
||||
@ -216,11 +124,7 @@ export const KclEditorPane = () => {
|
||||
{
|
||||
key: editorShortcutMeta.convertToVariable.codeMirror,
|
||||
run: () => {
|
||||
if (convertEnabled) {
|
||||
convertCallback()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return editorManager.convertToVariable()
|
||||
},
|
||||
},
|
||||
]),
|
||||
@ -233,9 +137,6 @@ export const KclEditorPane = () => {
|
||||
if (!TEST) {
|
||||
extensions.push(
|
||||
lintGutter(),
|
||||
linter((_view: EditorView) => {
|
||||
return kclErrorsToDiagnostics(errors)
|
||||
}),
|
||||
lineNumbers(),
|
||||
highlightActiveLineGutter(),
|
||||
highlightSpecialChars(),
|
||||
@ -288,13 +189,10 @@ export const KclEditorPane = () => {
|
||||
}
|
||||
|
||||
return extensions
|
||||
}, [
|
||||
kclLSP,
|
||||
copilotLSP,
|
||||
textWrapping.current,
|
||||
cursorBlinking.current,
|
||||
convertCallback,
|
||||
])
|
||||
}, [kclLSP, copilotLSP, textWrapping.current, cursorBlinking.current])
|
||||
|
||||
let debounceTimer: ReturnType<typeof setTimeout> | null = null
|
||||
const updateDelay = 100
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -302,18 +200,26 @@ export const KclEditorPane = () => {
|
||||
className={'absolute inset-0 ' + (cursorBlinking.current ? 'blink' : '')}
|
||||
>
|
||||
<ReactCodeMirror
|
||||
value={editorCode}
|
||||
value={codeManager.code}
|
||||
extensions={editorExtensions}
|
||||
onUpdate={onUpdate}
|
||||
theme={theme}
|
||||
onCreateEditor={(_editorView) => setEditorView(_editorView)}
|
||||
onCreateEditor={(_editorView) =>
|
||||
editorManager.setEditorView(_editorView)
|
||||
}
|
||||
onUpdate={(view: ViewUpdate) => {
|
||||
// debounce the view update.
|
||||
// otherwise it is laggy for typing.
|
||||
if (debounceTimer) {
|
||||
clearTimeout(debounceTimer)
|
||||
}
|
||||
|
||||
debounceTimer = setTimeout(() => {
|
||||
editorManager.handleOnViewUpdate(view)
|
||||
}, updateDelay)
|
||||
}}
|
||||
indentWithTab={false}
|
||||
basicSetup={false}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function stringifyRanges(ranges: readonly SelectionRange[]): string {
|
||||
return ranges.map(({ to, from }) => `${to}->${from}`).join('&')
|
||||
}
|
||||
|
@ -2,7 +2,9 @@ import { processMemory } from './MemoryPane'
|
||||
import { enginelessExecutor } from '../../../lib/testHelpers'
|
||||
import { initPromise, parse } from '../../../lang/wasm'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
})
|
||||
|
||||
describe('processMemory', () => {
|
||||
it('should grab the values and remove and geo data', async () => {
|
||||
|
238
src/editor/manager.ts
Normal file
@ -0,0 +1,238 @@
|
||||
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 { posToOffset } from 'editor/plugins/lsp/util'
|
||||
import { Program, ProgramMemory } from 'lang/wasm'
|
||||
import { codeManager, kclManager } from 'lib/singletons'
|
||||
import { codeManager, editorManager, kclManager } from 'lib/singletons'
|
||||
import type { UnitLength } from 'wasm-lib/kcl/bindings/UnitLength'
|
||||
import { UpdateUnitsResponse } from 'wasm-lib/kcl/bindings/UpdateUnitsResponse'
|
||||
import { UpdateCanExecuteResponse } from 'wasm-lib/kcl/bindings/UpdateCanExecuteResponse'
|
||||
@ -57,6 +57,9 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
},
|
||||
contentChanges: [{ text: code }],
|
||||
})
|
||||
if (editorManager.editorView) {
|
||||
//editorManager.handleOnViewUpdate(editorManager.editorView)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
@ -357,15 +360,9 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
try {
|
||||
switch (notification.method) {
|
||||
case 'textDocument/publishDiagnostics':
|
||||
const params = notification.params as PublishDiagnosticsParams
|
||||
this.processDiagnostics(params)
|
||||
// Update the kcl errors pane.
|
||||
/*if (!kclManager.isExecuting) {
|
||||
kclManager.kclErrors = lspDiagnosticsToKclErrors(
|
||||
this.view.state.doc,
|
||||
params.diagnostics
|
||||
)
|
||||
}*/
|
||||
//const params = notification.params as PublishDiagnosticsParams
|
||||
// this is sometimes slower than our actual typing.
|
||||
//this.processDiagnostics(params)
|
||||
break
|
||||
case 'window/logMessage':
|
||||
console.log(
|
||||
@ -385,17 +382,6 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
// The server has updated the AST, we should update elsewhere.
|
||||
let updatedAst = notification.params as Program
|
||||
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.
|
||||
// This is a hack since codemirror does not support async foldService.
|
||||
|
@ -1,14 +1,9 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useStore } from 'useStore'
|
||||
import { engineCommandManager } from 'lib/singletons'
|
||||
import { editorManager, engineCommandManager } from 'lib/singletons'
|
||||
import { useModelingContext } from './useModelingContext'
|
||||
import { getEventForSelectWithPoint } from 'lib/selections'
|
||||
|
||||
export function useEngineConnectionSubscriptions() {
|
||||
const { setHighlightRange, highlightRange } = useStore((s) => ({
|
||||
setHighlightRange: s.setHighlightRange,
|
||||
highlightRange: s.highlightRange,
|
||||
}))
|
||||
const { send, context } = useModelingContext()
|
||||
|
||||
useEffect(() => {
|
||||
@ -21,12 +16,13 @@ export function useEngineConnectionSubscriptions() {
|
||||
if (data?.entity_id) {
|
||||
const sourceRange =
|
||||
engineCommandManager.artifactMap?.[data.entity_id]?.range
|
||||
setHighlightRange(sourceRange)
|
||||
editorManager.setHighlightRange(sourceRange)
|
||||
} else if (
|
||||
!highlightRange ||
|
||||
(highlightRange[0] !== 0 && highlightRange[1] !== 0)
|
||||
!editorManager.highlightRange ||
|
||||
(editorManager.highlightRange[0] !== 0 &&
|
||||
editorManager.highlightRange[1] !== 0)
|
||||
) {
|
||||
setHighlightRange([0, 0])
|
||||
editorManager.setHighlightRange([0, 0])
|
||||
}
|
||||
},
|
||||
})
|
||||
@ -43,10 +39,5 @@ export function useEngineConnectionSubscriptions() {
|
||||
unSubHover()
|
||||
unSubClick()
|
||||
}
|
||||
}, [
|
||||
engineCommandManager,
|
||||
setHighlightRange,
|
||||
highlightRange,
|
||||
context?.sketchEnginePathId,
|
||||
])
|
||||
}, [engineCommandManager, context?.sketchEnginePathId])
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useStore } from '../useStore'
|
||||
import { editorManager } from 'lib/singletons'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
// Kurt's note: codeMirror styling overrides were needed to make this work
|
||||
@ -6,20 +6,17 @@ import { useEffect } from 'react'
|
||||
// search for code-mirror-override in the repo to find the relevant styles
|
||||
|
||||
export function useHotKeyListener() {
|
||||
const { setIsShiftDown } = useStore((s) => ({
|
||||
setIsShiftDown: s.setIsShiftDown,
|
||||
}))
|
||||
const keyName = 'Shift'
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) =>
|
||||
event.key === keyName && setIsShiftDown(true)
|
||||
event.key === keyName && editorManager.setIsShiftDown(true)
|
||||
const handleKeyUp = (event: KeyboardEvent) =>
|
||||
event.key === keyName && setIsShiftDown(false)
|
||||
event.key === keyName && editorManager.setIsShiftDown(false)
|
||||
window.addEventListener('keydown', handleKeyDown)
|
||||
window.addEventListener('keyup', handleKeyUp)
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown)
|
||||
window.removeEventListener('keyup', handleKeyUp)
|
||||
}
|
||||
}, [setIsShiftDown])
|
||||
})
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import {
|
||||
SetVarNameModal,
|
||||
createSetVarNameModal,
|
||||
} from 'components/SetVarNameModal'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { editorManager, kclManager } from 'lib/singletons'
|
||||
import { moveValueIntoNewVariable } from 'lang/modifyAst'
|
||||
import { isNodeSafeToReplace } from 'lang/queryAst'
|
||||
import { useEffect, useState } from 'react'
|
||||
@ -13,6 +13,11 @@ const getModalInfo = createSetVarNameModal(SetVarNameModal)
|
||||
export function useConvertToVariable() {
|
||||
const { context } = useModelingContext()
|
||||
const [enable, setEnabled] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
editorManager.convertToVariableEnabled = enable
|
||||
}, [enable])
|
||||
|
||||
useEffect(() => {
|
||||
const { isSafe, value } = isNodeSafeToReplace(
|
||||
kclManager.ast,
|
||||
@ -45,5 +50,7 @@ export function useConvertToVariable() {
|
||||
}
|
||||
}
|
||||
|
||||
editorManager.convertToVariableCallback = handleClick
|
||||
|
||||
return { enable, handleClick }
|
||||
}
|
||||
|
@ -2,12 +2,10 @@ import { KCLError } from './errors'
|
||||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
import { type IndexLoaderData } from 'lib/types'
|
||||
import { useLoaderData } from 'react-router-dom'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { codeManager, kclManager } from 'lib/singletons'
|
||||
|
||||
const KclContext = createContext({
|
||||
code: codeManager?.code || '',
|
||||
editorCode: codeManager?.code || '',
|
||||
programMemory: kclManager?.programMemory,
|
||||
ast: kclManager?.ast,
|
||||
isExecuting: kclManager?.isExecuting,
|
||||
@ -30,7 +28,6 @@ export function KclContextProvider({
|
||||
const { code: loadedCode } = useLoaderData() as IndexLoaderData
|
||||
// Both the code state and the editor state start off with the same code.
|
||||
const [code, setCode] = useState(loadedCode || codeManager.code)
|
||||
const [editorCode, setEditorCode] = useState(code)
|
||||
|
||||
const [programMemory, setProgramMemory] = useState(kclManager.programMemory)
|
||||
const [ast, setAst] = useState(kclManager.ast)
|
||||
@ -42,7 +39,6 @@ export function KclContextProvider({
|
||||
useEffect(() => {
|
||||
codeManager.registerCallBacks({
|
||||
setCode,
|
||||
setEditorCode,
|
||||
})
|
||||
kclManager.registerCallBacks({
|
||||
setProgramMemory,
|
||||
@ -54,15 +50,10 @@ export function KclContextProvider({
|
||||
})
|
||||
}, [])
|
||||
|
||||
const params = useParams()
|
||||
useEffect(() => {
|
||||
codeManager.setParams(params)
|
||||
}, [params])
|
||||
return (
|
||||
<KclContext.Provider
|
||||
value={{
|
||||
code,
|
||||
editorCode,
|
||||
programMemory,
|
||||
ast,
|
||||
isExecuting,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { executeAst } from 'useStore'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { KCLError } from './errors'
|
||||
import { KCLError, kclErrorsToDiagnostics } from './errors'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import { EngineCommandManager } from './std/engineConnection'
|
||||
|
||||
@ -17,7 +17,7 @@ import {
|
||||
ExtrudeGroup,
|
||||
} from 'lang/wasm'
|
||||
import { getNodeFromPath } from './queryAst'
|
||||
import { codeManager } from 'lib/singletons'
|
||||
import { codeManager, editorManager } from 'lib/singletons'
|
||||
|
||||
export class KclManager {
|
||||
private _ast: Program = {
|
||||
@ -90,6 +90,8 @@ export class KclManager {
|
||||
}
|
||||
set kclErrors(kclErrors) {
|
||||
this._kclErrors = kclErrors
|
||||
let diagnostics = kclErrorsToDiagnostics(kclErrors)
|
||||
editorManager.setDiagnostics(diagnostics)
|
||||
this._kclErrorsCallBack(kclErrors)
|
||||
}
|
||||
|
||||
@ -345,6 +347,16 @@ export class KclManager {
|
||||
void this.engineCommandManager.setPlaneHidden(this.defaultPlanes.yz, 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(
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { KCLError } from './errors'
|
||||
import { initPromise, parse } from './wasm'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
})
|
||||
|
||||
describe('testing AST', () => {
|
||||
test('5 + 6', () => {
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { parse, initPromise } from './wasm'
|
||||
import { enginelessExecutor } from '../lib/testHelpers'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
})
|
||||
|
||||
describe('testing artifacts', () => {
|
||||
// Enable rotations #152
|
||||
|
@ -5,7 +5,7 @@ import { bracket } from 'lib/exampleKcl'
|
||||
import { isTauri } from 'lib/isTauri'
|
||||
import { writeTextFile } from '@tauri-apps/plugin-fs'
|
||||
import toast from 'react-hot-toast'
|
||||
import { Params } from 'react-router-dom'
|
||||
import { editorManager } from 'lib/singletons'
|
||||
|
||||
const PERSIST_CODE_TOKEN = 'persistCode'
|
||||
|
||||
@ -13,7 +13,7 @@ export default class CodeManager {
|
||||
private _code: string = bracket
|
||||
private _updateState: (arg: string) => void = () => {}
|
||||
private _updateEditor: (arg: string) => void = () => {}
|
||||
private _params: Params<string> = {}
|
||||
private _currentFilePath: string | null = null
|
||||
|
||||
constructor() {
|
||||
if (isTauri()) {
|
||||
@ -45,19 +45,12 @@ export default class CodeManager {
|
||||
return this._code
|
||||
}
|
||||
|
||||
registerCallBacks({
|
||||
setCode,
|
||||
setEditorCode,
|
||||
}: {
|
||||
setCode: (arg: string) => void
|
||||
setEditorCode: (arg: string) => void
|
||||
}) {
|
||||
registerCallBacks({ setCode }: { setCode: (arg: string) => void }) {
|
||||
this._updateState = setCode
|
||||
this._updateEditor = setEditorCode
|
||||
}
|
||||
|
||||
setParams(params: Params<string>) {
|
||||
this._params = params
|
||||
updateCurrentFilePath(path: string) {
|
||||
this._currentFilePath = path
|
||||
}
|
||||
|
||||
// This updates the code state and calls the updateState function.
|
||||
@ -70,11 +63,14 @@ export default class CodeManager {
|
||||
|
||||
// Update the code in the editor.
|
||||
updateCodeEditor(code: string): void {
|
||||
if (this._code !== code) {
|
||||
this.code = code
|
||||
this._updateEditor(code)
|
||||
}
|
||||
const lastCode = this._code
|
||||
this.code = code
|
||||
this._updateEditor(code)
|
||||
if (editorManager.editorView) {
|
||||
editorManager.editorView.dispatch({
|
||||
changes: { from: 0, to: lastCode.length, insert: code },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Update the code, state, and the code the code mirror editor sees.
|
||||
@ -91,8 +87,8 @@ export default class CodeManager {
|
||||
setTimeout(() => {
|
||||
// Wait one event loop to give a chance for params to be set
|
||||
// Save the file to disk
|
||||
this._params.id &&
|
||||
writeTextFile(this._params.id, this.code).catch((err) => {
|
||||
this._currentFilePath &&
|
||||
writeTextFile(this._currentFilePath, this.code).catch((err) => {
|
||||
// TODO: add tracing per GH issue #254 (https://github.com/KittyCAD/modeling-app/issues/254)
|
||||
console.error('error saving file', err)
|
||||
toast.error('Error saving file, please check file permissions')
|
||||
|
@ -4,7 +4,9 @@ import { parse, ProgramMemory, SketchGroup, initPromise } from './wasm'
|
||||
import { enginelessExecutor } from '../lib/testHelpers'
|
||||
import { KCLError } from './errors'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
})
|
||||
|
||||
describe('test executor', () => {
|
||||
it('test assigning two variables, the second summing with the first', async () => {
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { getNodePathFromSourceRange, getNodeFromPath } from './queryAst'
|
||||
import { Identifier, parse, initPromise, Parameter } from './wasm'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
})
|
||||
|
||||
describe('testing getNodePathFromSourceRange', () => {
|
||||
it('test it gets the right path for a `lineTo` CallExpression within a SketchExpression', () => {
|
||||
|
@ -17,7 +17,9 @@ import {
|
||||
import { enginelessExecutor } from '../lib/testHelpers'
|
||||
import { getNodePathFromSourceRange } from './queryAst'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
})
|
||||
|
||||
describe('Testing createLiteral', () => {
|
||||
it('should create a literal', () => {
|
||||
|
@ -15,7 +15,9 @@ import {
|
||||
createPipeSubstitution,
|
||||
} from './modifyAst'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
})
|
||||
|
||||
describe('findAllPreviousVariables', () => {
|
||||
it('should find all previous variables', async () => {
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { parse, Program, recast, initPromise } from './wasm'
|
||||
import fs from 'node:fs'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
})
|
||||
|
||||
describe('recast', () => {
|
||||
it('recasts a simple program', () => {
|
||||
|
@ -1171,7 +1171,10 @@ export class EngineCommandManager {
|
||||
type: 'receive-reliable',
|
||||
data: message,
|
||||
id,
|
||||
cmd_type: command?.commandType || this.lastArtifactMap[id]?.commandType,
|
||||
cmd_type:
|
||||
command?.commandType ||
|
||||
this.lastArtifactMap[id]?.commandType ||
|
||||
sceneCommand?.commandType,
|
||||
})
|
||||
Object.values(this.subscriptions[modelingResponse.type] || {}).forEach(
|
||||
(callback) => callback(modelingResponse)
|
||||
|
@ -25,7 +25,9 @@ const eachQuad: [number, [number, number]][] = [
|
||||
[675, [1, -1]],
|
||||
]
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
})
|
||||
|
||||
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)', () => {
|
||||
|
@ -8,7 +8,9 @@ import { getSketchSegmentFromSourceRange } from './sketchConstraints'
|
||||
import { Selection } from 'lib/selections'
|
||||
import { enginelessExecutor } from '../../lib/testHelpers'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
})
|
||||
|
||||
// testing helper function
|
||||
async function testingSwapSketchFnCall({
|
||||
|
@ -11,7 +11,9 @@ import { ToolTip } from '../../useStore'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { enginelessExecutor } from '../../lib/testHelpers'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
})
|
||||
|
||||
describe('testing getConstraintType', () => {
|
||||
const helper = getConstraintTypeFromSourceHelper
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { parse, initPromise } from '../wasm'
|
||||
import { enginelessExecutor } from '../../lib/testHelpers'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
})
|
||||
|
||||
describe('testing angledLineThatIntersects', () => {
|
||||
it('angledLineThatIntersects should intersect with another line', async () => {
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { lexer, initPromise } from './wasm'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
})
|
||||
|
||||
describe('testing lexer', () => {
|
||||
it('async lexer works too', async () => {
|
||||
|
@ -25,6 +25,7 @@ import { AppInfo } from 'wasm-lib/kcl/bindings/AppInfo'
|
||||
import { CoreDumpManager } from 'lib/coredump'
|
||||
import openWindow from 'lib/openWindow'
|
||||
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
||||
import { TEST } from 'env'
|
||||
|
||||
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||
export type { Value } from '../wasm-lib/kcl/bindings/Value'
|
||||
@ -95,10 +96,15 @@ export const wasmUrl = () => {
|
||||
|
||||
// Initialise the wasm module.
|
||||
const initialise = async () => {
|
||||
const fullUrl = wasmUrl()
|
||||
const input = await fetch(fullUrl)
|
||||
const buffer = await input.arrayBuffer()
|
||||
return init(buffer)
|
||||
try {
|
||||
const fullUrl = wasmUrl()
|
||||
const input = await fetch(fullUrl)
|
||||
const buffer = await input.arrayBuffer()
|
||||
return await init(buffer)
|
||||
} catch (e) {
|
||||
console.log('Error initialising WASM', e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
export const initPromise = initialise()
|
||||
@ -153,10 +159,6 @@ export const executor = async (
|
||||
return _programMemory
|
||||
}
|
||||
|
||||
const getSettingsState = import('components/SettingsAuthProvider').then(
|
||||
(module) => module.getSettingsState
|
||||
)
|
||||
|
||||
export const _executor = async (
|
||||
node: Program,
|
||||
programMemory: ProgramMemory = { root: {}, return: null },
|
||||
@ -164,8 +166,14 @@ export const _executor = async (
|
||||
isMock: boolean
|
||||
): Promise<ProgramMemory> => {
|
||||
try {
|
||||
const baseUnit =
|
||||
(await getSettingsState)()?.modeling.defaultUnit.current || 'mm'
|
||||
let baseUnit = 'mm'
|
||||
if (!TEST) {
|
||||
const getSettingsState = import('components/SettingsAuthProvider').then(
|
||||
(module) => module.getSettingsState
|
||||
)
|
||||
baseUnit =
|
||||
(await getSettingsState)()?.modeling.defaultUnit.current || 'mm'
|
||||
}
|
||||
const memory: ProgramMemory = await execute_wasm(
|
||||
JSON.stringify(node),
|
||||
JSON.stringify(programMemory),
|
||||
|
@ -103,6 +103,7 @@ export const fileLoader: LoaderFunction = async ({
|
||||
// Update both the state and the editor's code.
|
||||
// We explicitly do not write to the file here since we are loading from
|
||||
// the file system and not the editor.
|
||||
codeManager.updateCurrentFilePath(currentFilePath)
|
||||
codeManager.updateCodeStateEditor(code)
|
||||
kclManager.executeCode(true)
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { SceneEntities } from 'clientSideScene/sceneEntities'
|
||||
import { SceneInfra } from 'clientSideScene/sceneInfra'
|
||||
import EditorManager from 'editor/manager'
|
||||
import { KclManager } from 'lang/KclSingleton'
|
||||
import CodeManager from 'lang/codeManager'
|
||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||
@ -8,6 +9,7 @@ export const codeManager = new CodeManager()
|
||||
|
||||
export const engineCommandManager = new EngineCommandManager()
|
||||
|
||||
// This needs to be after codeManager is created.
|
||||
export const kclManager = new KclManager(engineCommandManager)
|
||||
engineCommandManager.getAstCb = () => kclManager.ast
|
||||
|
||||
@ -15,3 +17,18 @@ export const sceneInfra = new SceneInfra(engineCommandManager)
|
||||
engineCommandManager.camControlsCameraChange = sceneInfra.onCameraChange
|
||||
|
||||
export const sceneEntitiesManager = new SceneEntities(engineCommandManager)
|
||||
|
||||
// This needs to be after sceneInfra and engineCommandManager are is created.
|
||||
export const editorManager = new EditorManager()
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
;(window as any).engineCommandManager = engineCommandManager
|
||||
;(window as any).kclManager = kclManager
|
||||
;(window as any).sceneInfra = sceneInfra
|
||||
;(window as any).sceneEntitiesManager = sceneEntitiesManager
|
||||
;(window as any).editorManager = editorManager
|
||||
;(window as any).enableMousePositionLogs = () =>
|
||||
document.addEventListener('mousemove', (e) =>
|
||||
console.log(`await page.mouse.click(${e.clientX}, ${e.clientY})`)
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { Coords2d } from 'lang/std/sketch'
|
||||
import { isPointsCCW, initPromise } from 'lang/wasm'
|
||||
|
||||
beforeAll(() => initPromise)
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
})
|
||||
|
||||
describe('test isPointsCW', () => {
|
||||
test('basic test', () => {
|
||||
|
@ -16,6 +16,54 @@ export type CommandBarContext = {
|
||||
argumentsToSubmit: { [x: string]: unknown }
|
||||
}
|
||||
|
||||
export type CommandBarMachineEvent =
|
||||
| { type: 'Open' }
|
||||
| { type: 'Close' }
|
||||
| { type: 'Clear' }
|
||||
| {
|
||||
type: 'Select command'
|
||||
data: { command: Command }
|
||||
}
|
||||
| { type: 'Deselect command' }
|
||||
| { type: 'Submit command'; data: { [x: string]: unknown } }
|
||||
| {
|
||||
type: 'Add argument'
|
||||
data: { argument: CommandArgumentWithName<unknown> }
|
||||
}
|
||||
| {
|
||||
type: 'Remove argument'
|
||||
data: { [x: string]: CommandArgumentWithName<unknown> }
|
||||
}
|
||||
| {
|
||||
type: 'Edit argument'
|
||||
data: { arg: CommandArgumentWithName<unknown> }
|
||||
}
|
||||
| {
|
||||
type: 'Add commands'
|
||||
data: { commands: Command[] }
|
||||
}
|
||||
| {
|
||||
type: 'Remove commands'
|
||||
data: { commands: Command[] }
|
||||
}
|
||||
| { type: 'Submit argument'; data: { [x: string]: unknown } }
|
||||
| {
|
||||
type: 'done.invoke.validateArguments'
|
||||
data: { [x: string]: unknown }
|
||||
}
|
||||
| {
|
||||
type: 'error.platform.validateArguments'
|
||||
data: { message: string; arg: CommandArgumentWithName<unknown> }
|
||||
}
|
||||
| {
|
||||
type: 'Find and select command'
|
||||
data: { name: string; ownerMachine: string }
|
||||
}
|
||||
| {
|
||||
type: 'Change current argument'
|
||||
data: { [x: string]: CommandArgumentWithName<unknown> }
|
||||
}
|
||||
|
||||
export const commandBarMachine = createMachine(
|
||||
{
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QGED2BbdBDAdhABAEJYBOAxMgDaqxgDaADALqKgAONAlgC6eo6sQAD0QBaAJwA6AGwAmAKwBmBoukAWafIAcDcSoA0IAJ6JZDaZIDs8hgzV6AjA61a1DWQF8PhtJlwFiciowUkYWJBAOWB4+AQiRBFF5CwdpcVkHS1lpVyU5QxNEh1lFGTUsrUUtOQd5SwZLLx8MbDwiUkkqGkgyAHk2MBwwwSiY-kEEswZJbPltM0U3eXLZAsRFeQcrerUHRbTFvfkmkF9WgI6u2ggyADFONv98WkowAGNufDeW-2GI0d443iiHESkktUUilkskqdiUWjWCAcDC0kjUqnElkc6lkoK0JzOT0CnWo1zIAEEIARvn48LA-uwuIC4qAEqIHJjJPJcYtxFoYdJFFjVsZEG5pqCquJxJoGvJxOUCT82sSrj0AEpgdCoABuYC+yog9OYIyZsQmYmhWzMmTqLnU0kyikRGVRbj5lg2SmUam5StpFxIkgAymBXh8HlADQGyKHw58aecGZEzUDWYgchY7CisWoSlpQQ5EVDLJJxKkdIpyypUop-ed2kHCW0Xu9uD1kwDzcCEJCtlUNgWhZZ1OlnaKEBpZJItHVzDZ5xlpPWiZdDc8w22Ow5wozosyLUiVNMSg5ytVKmfrIipzO564z2otPVpI1vKd18SAOJYbgACzAEhI3wUgoAAV3QQZuFgCg-1wGAvjAkgSCgkCSHAyCcG4TtUxZYRED2TQZGSOw80qaQ7ARCc9mmZYSksextGkNJBRXFUOh-f9AOA0CIKgmCABE4E3D5oyTE1-lww8clKBjZF2Bh5SFZwXUUyRVDzJwNE2LQzzYwNJE4gCgJwKNeMw6DJHJAB3LAYlM-AHjYMDuFjMCACN0B4NCMKgnD927dMkWSUs8yY8RISfWQshvcsrDlapshULJPHfZsDKM7iHPM-jJAANSwShOAgX9IzICB+DASQHh1VAAGsqp1Qrit-MByXQvisP8sY8ISZwbCsDllBLSwz1qRFBQsRZ5WRIVkui-TG0M39jJ4jqLNgfLmpK3hTLIQCSFQIM2EoX8ADMjvQSQmqKna2vWvyJL3HrD1SEoyzSdJUkUm9dnUtQn10aK6hkxbiU1HVODAay3M87zE1+J6UwCtN8IQUauR0OZ0ksFwUUqRFPWmUdouPXR3TBjoIahmHKQIHKuqRrtUYSaVShhLF+TkeTzBFQosVKDRM2RVInEdSmg2p6GyE1bU9R8zrsKZqSexFqQHWlHNXHzCbFhnX1+UydFkVxiXJClmGAFEIG8hmld3ZGXp7TR5C5RSCxdjlQV1tR9bqaVdhsSFxDN5AALeOrgPa3ysJgiqcCqmr6sa7bWujxXjQd5nesQZYthsblIQYJx6hUic9AsdEFT2HQzByVLmgDJaw-eSOHPTjbysq6qcFqhrrtT9sO-4ugd1NFGc4QXEPo5eVLGsFxlnKREXGnZI9HcT1mOikO0s-S5w7bqNh9j-bkKOyQTvOy6B9utOHtj7qD1V8pSyrHJZ3STY4QmjQ0R2Ww0oSjuF3u+HAqAIBwEEOlRs48nZBVENkKQNprC42qBoJ0LothaXMJ9aElRXDLj3k3VUpJIBwOfgg4oMx8y41SPUOY8p5DFk2GiKoxsoRVmhGoM2cY2zAQRngChgU0a7HZtFH0ilRp1D5usVh6JXCKD2CUdI8lQ7rlbB8chkkJ6HkxFsBeulbQZAUCwrYCiqzIhyE+fqZtMomTMg-aCwiWYEV2NOBUCghQ6EFGzYsvpwQUT5MxawFECx2JWllRxMdLI2TsrtKMTkXIuMnmeCiZYwrePMFWCKv1XZKVGroWwmxNARK4g4hWG0tp3wSSk16lQpC41ULjV8uxUjSBvFCNEDTdCOkWCY44xCGzgzAJDaGdTnaZHBADYcqg5DpCovzeRno5izA0BeUOh8o5OPgDo+BaNvqV0xNFaE7gdYTnnvkmUChdKEMyF4LwQA */
|
||||
@ -227,53 +275,7 @@ export const commandBarMachine = createMachine(
|
||||
},
|
||||
},
|
||||
schema: {
|
||||
events: {} as
|
||||
| { type: 'Open' }
|
||||
| { type: 'Close' }
|
||||
| { type: 'Clear' }
|
||||
| {
|
||||
type: 'Select command'
|
||||
data: { command: Command }
|
||||
}
|
||||
| { type: 'Deselect command' }
|
||||
| { type: 'Submit command'; data: { [x: string]: unknown } }
|
||||
| {
|
||||
type: 'Add argument'
|
||||
data: { argument: CommandArgumentWithName<unknown> }
|
||||
}
|
||||
| {
|
||||
type: 'Remove argument'
|
||||
data: { [x: string]: CommandArgumentWithName<unknown> }
|
||||
}
|
||||
| {
|
||||
type: 'Edit argument'
|
||||
data: { arg: CommandArgumentWithName<unknown> }
|
||||
}
|
||||
| {
|
||||
type: 'Add commands'
|
||||
data: { commands: Command[] }
|
||||
}
|
||||
| {
|
||||
type: 'Remove commands'
|
||||
data: { commands: Command[] }
|
||||
}
|
||||
| { type: 'Submit argument'; data: { [x: string]: unknown } }
|
||||
| {
|
||||
type: 'done.invoke.validateArguments'
|
||||
data: { [x: string]: unknown }
|
||||
}
|
||||
| {
|
||||
type: 'error.platform.validateArguments'
|
||||
data: { message: string; arg: CommandArgumentWithName<unknown> }
|
||||
}
|
||||
| {
|
||||
type: 'Find and select command'
|
||||
data: { name: string; ownerMachine: string }
|
||||
}
|
||||
| {
|
||||
type: 'Change current argument'
|
||||
data: { [x: string]: CommandArgumentWithName<unknown> }
|
||||
},
|
||||
events: {} as CommandBarMachineEvent,
|
||||
},
|
||||
preserveActionOrder: true,
|
||||
},
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
sceneInfra,
|
||||
sceneEntitiesManager,
|
||||
engineCommandManager,
|
||||
editorManager,
|
||||
} from 'lib/singletons'
|
||||
import {
|
||||
horzVertInfo,
|
||||
@ -101,12 +102,14 @@ export type ModelingMachineEvent =
|
||||
| {
|
||||
type: 'defaultPlane'
|
||||
plane: DefaultPlaneStr
|
||||
planeId: string
|
||||
}
|
||||
| {
|
||||
type: 'extrudeFace'
|
||||
position: [number, number, number]
|
||||
extrudeSegmentPathToNode: PathToNode
|
||||
cap: 'start' | 'end' | 'none'
|
||||
faceId: string
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -152,7 +155,7 @@ export type MoveDesc = { line: number; snippet: string }
|
||||
|
||||
export const modelingMachine = createMachine(
|
||||
{
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QFkD2EwBsCWA7KAxAMICGuAxlgNoAMAuoqAA6qzYAu2qujIAHogC0AdgCsAZgB04gEyjhADnEA2GgoUAWJQBoQAT0QBGGuICckmoZkbTM42YWKAvk91oMOfAQDKYdgAJYLDByTm5aBiQQFjYwniiBBFtpGhlxDRphGg1ZURlhXQMEcRLDSVF5UwV84XEK8Rc3dCw8Ql8AgFtUAFcgwPYSdjAI3hiOLnjQIsFRQzLVDUNlRbSJWeVdRJLkjIUJdPzlVWVGkHcW-ElsCEwwAgBRXCGAJ0CAaz9yAAsRqLG43iJQQyZTCSRWZSmZYKKxzUEaQqIGSmDSSHI5ayGcQ0KqQ07nTxQK43O6PF7vT4-QyRZiscbcQFCaw0NFLSyiNTYhSZREIDIySS2YTKWYgmHiGEnVxnZqE4m3B58djPboYX602ITRkIGbySTImiiSGiDSiGG8uYc-UKZQKUymDk2DSLfGy1ry0l8FjPdjq6J0gEJJnqa3MmR2J1m3nCYTzMTpEXc-Km10ed3eD7sb7EMiUTAZyl+-5aoMIOY28GZZ3c7lmWQWwww8GNmFYyx7BSpi5EgtZr6SXvfACSJIIyBIH0CYCgHTAT38ADcwD7sOQSJgiwGS6BElZTGVGzJNMLNKkCvojIspNiaKl6vklBou3LB-3XyOFePJ0EZ3OAl9UGebAAC9uAGDd6FGLcGVLKxhFRVQbUNOZDmEGQLXEOZylNLIlkww08mfdNM2+AcSK+D87iIbhYGVEg8H8ACgNAp5138CBsFo3Nhkgv5oMmfgjDkVFsjSHFH33dReTSKELAqDlslEfcsSIy5XzIylKOIGi6IYpcVzXTB2M4gYKB4ml-U1GCdyE21BTyPYlkWNQNHQi8EHDWpykxY8YxBE1VJ7ciNL7LTqNwWjnno3B-AAQQAIW8fwAA1NysgTdyPUQ0XEMRDBsTDkTSaSHVRB1QW5O9TFqBppQJYjKRC4dR3CyLorixL-AATTS+kMqEvZ9VcpY5EMI0jTcoouQUSQ0PktlHGdQKmrfciwp0qKGLIKBbl6wMbLLdJryUao9kUQxhFMaSOTBOQzpjI1Luq5b1PfFqNva258HYH5eI1PrtSxXLpBFVR91kTDRGk5RG1myFFku6sVGEF7grehVWt0mKmGXHHcA48hukwEhXg4rizL27dBLLcMZpsUxCoyRtjmkmFUVjKpqrQ50NDEVHGvRqiPoYpiQLA9dMD0fx12wKBcEp6zqYhAUOy0CRTQyZRxAtdZ9QdXz8iNEp+b7Fb1oirHF2XThDMl6WcDlhX+ppy6cqPS71jMWYLWUORyjtS6bWqKpahN0jBe0i3Npi2BcBIJh-HYVAUqdwHMKkEEJTUarqjMaTRP1OQjiOQ1HHDMPVs096o-a2P48T5Oer+yyAdg9OLGFTCY1y4wEXciMwRNMulIujRllqpo0zUtG1urtqGLAABHbo2K+qAftT2DkSkWZecurI8gkc8igu7EcpMW9DVB0wK7NufLaYEmJeoZvi0V3dZDBVJM7GhmEb7k+DMv6lBhvaTIahRC3wjpjaO-hnhgC6Eufw5BhZPFgJvA6VgtCSBhtYCamgxo6Hco2TCFgli2GyCUNQvsoGzwVAAJTAIIMAfAQjdCGBgpWdQpBYlUFYeSYgNjuShMocoXM6gwmRL3WhVcFT3GXtgBOAAZPAYAG6oAghZN+ztGyyX2Mse06spLuSUNlG61hwwmhFHMGRoVRzyO6IoxO20-zYDYiTcgDdOGJDsH7DIphDQOhKGPbWxDs76nqMcXEhoJ4yinkFAWdDSQKITvA0I21bjqM0VBdK2pPLZTsC5ZEzoOShJPjISwaJkTqG-kA2J9Vp6NVigAd3ov+QCYtWJGTJqZSg-g8AADNUAEAgNwMAVxcALlQB8SQMB2CCFFixcCghBmoG8UibEqJnTLBxLUe03dpKLHMHsI8aRyFFxRnVN0jTTYtLaYxDpSy2I9O4v03AQyCDLmeIBSQTBibsCGc8Dosy-ALMeeLTAKz3lrNfvxPJ2JspzGdHMdIDlqrSQlAKbINBVAmDyMKQwt87kcCtgZZ5JlXmrJGWMiZUyZlzMEPpG264oVDPWR5Qq4JcowhMKCQwQppJHikI4Q+dhShoSJa0klTLVzkvJn0qlXyfl-MGIC4FDKZWGVZTCrRcLSxnJVoaMQ+4xCaEFa5cEoNUgSlOkoSV9yEpJWSm8j5ozcDjLwHS8ZDKSAACNYCCD4Nq9lZzRHCgqd-NYCgSq+3BBkRY48QT5HqdchJtypUBEdSlF1wylXPF+f8tVIL5l+oDUG1ZIau45RtXUAJQ8SpLGkGaEU+FeY2HtSSrNXUc3UvdbS6Z3rQWlsEHoYNsLcn6swqIuo8gKi2CAaaXk2JQRok5hyM0CKnxXPiStSQxLM2dW7Yq543z80qoBYBdVQ7-UjrHbqidB0zlf0OLlEEWsE1LqchYSERxLFYgldu7su793S3wJkqlbqPWTIHcWwQGSmEVvHa3R9VbsQosPoYww10jSCgRZYMazaYwdoCPBnteaC2qsvbB+Dd6cnIepmcmaWQViSOzhda6F1mx1CyHIQ0IJLmTyA+pEDa8fo9sg-2+loLRNfFo3xB9DHIZuzMP-Cp+RoaDRxeKlQF0rDEf8DJsjJ7lWFqowymTcn-r7UU424weLJTHkmkiZYApf1aBRGIB6UpBMvmCiBnGzw8YEyJiTYy8q1EQZpZ6mDDKAtBdXCF54ggXlmUsy3azPik32SUNYX2Yo5Csz9j3MeIpDFyBTTu4TGb-BxbnMF4mpMKVmSM6eijF6gWwdq-jBLDXktNcoGl7ReSsv61kGPcMtoCv9xjGCYrRwlIOnK7fFR7qskPBSc4-Arj3HPE8UnENl1RFVHysxusl0ymIBzmiEw+UbTyBBI2ZbqismSCHLgDgBAQ1pFESdWY3IURyCuu5W0ZQ0h2jMCYGMtgKtCeCittRScNGvfe+wT71I6MZaRPkAUHIjxOSsDioHRQodoi1hIWwRoAmGie6txHmBJAADlk4AAVUB4HYLAAgsUIAQH6CTAILB2chqOGVK+SlXLcmWLyI45gcjVWqpkdQEoacI9QEjpn-hWfs856QMy2T5P0d3JocwJgwcVJRB2aN7lcKrtAaUI8Wtb5vY++y4wja1ZZBUHxnEAChIlFJ+G44qRliEsA75xqzvUdUHRwbzHZZkKSDtDibIWsI35QtFI2aewEwQgKt5uJsPGoABUXFPDcUZDxDd1uOOUc9unwvnTn0UEpDkvteQojDWhVYWQcVjwEwX8Pptuj42TlxH0r4ADyuBe1Qa9Xu7wRfBDD9GYIMf7BJ-yyQ3HoGM18VmCRTkDkVuihmhZFCflLlahKFyrfRh6SwOq6R-u1o-gOlQDwFznncCQimR2mot-eAnCJ8iw2U6gjYVgSgdm6KDGjgoiEgikkI1UuiFc-guAycAyJAlAPgwQoQ7EYAGBRMAuxM7q7KwIWEuiOQMYWg8IDo0uIIieVgR0T0wo7aYe7oZA2AHQgwL+ScNWxBdwEm0WMyHBXBQwggScggGBlA7KFQB43Iig2K38PIwOR40gjB2IR41UWgnYbBlwIh3B+ADc-gLCJkL+sA5EM+km4y+hYhEh5hhYW+VMiQvs2Uwe+Qx2Jor6n6M0RwV+UI6gHmOhPmDUps8Oa2DiTiaSv+mS9ejh78WOYg+oxgf8sY1q9YxCY0nehSiujkcgLg0oaBGA8AUQDSUAGOThQg3IZQuQigXu6gWgF2ZYkIZCt4mgvG5Orky01wtw5R8ROo0I5QoI1UEopox4vIYB5QXIY0hUaGN8uhaa3wvRzs-haIZ0vK3uKI1006VCmQeEewlgtizUPRseFRZYGQGcWhl0R4OEVQDYKIcajBWmuUJgMOg+pEIGiyEKYWvSEW0KSxgMQcaI-iigsYNQqQGKyQqKN4Mu+QoewRNy7x1WmqcqPxOa-xsE6gAobIlg3Cvs+4gqVoqQiwbM82sw+mWazqqy6JmC4OFgkB28vsUBJUpCpoFUfCqgZoMg5Jh6aJJxfRuiOO1xmE3IrkS6OKBScgFQxqmJcx8JCx-YIGpGVJfJOido2UWst4OQDgeQDM10agieYOFQqg4adQ+mhmypVmpxuiLI5x40MIDMxc0MKgs0+4IcQxrJrxIRiJ9yXW9WoWKWCqfxKpAJOGh2R+8aUIJQ0k50s0-Kew+4UpUIKuWS1JDG9gHcJQewNg6gtovInJzYtokido2hXJ8xu6YRdOyOHAqZPiY0ZQmQmZJoVQQc0uyQSw76ISASY8spA+Xp-YFZau9OaBQK64NZSINgYIe8sgAStY4Y0YlSNgduhUNosgyZlZGuWuaCY5HkpoGckBWg9g32jR4a1ocwWIjYLBMITuKO25xgSkOCGszIEo++vuZY4BzYYBbYjYZot8JeW2ZeO2e2qA25d0wC1Qrk42MMY8Fo4pD51USkjgmQ9opZcpu6y+o+Aw4+5EU+t5to5gawasZOOWROiAVipO-KeCj0ew+epRu6d+0Rj+mAIFEgoiyw8gOEEgjYOKGeTYaEDoumqKOIt+P+pGlZz+hhQQKC+MyCgE7qzwzFmgD57Fs6wp3FxCSkNpKgvMsw55SgPZtF6k9Folg5e6GaL+ABAkQ2+qMMoiV4IIKISBdoFoIBieMIRZ+EuyKBaB-gUhYA25wI-uiYamrkLm4J7kvMGcE0UINQWsDMy0NhPBycKq7quFUg1UkI2QDooJalRQvsoiJg6gwoQ8XI-etFCVhhvBJhtEZh5Et51Us0RoIoPG+UmEtgrMLIJcSezINoR4+RTgQAA */
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QFkD2EwBsCWA7KAxAMICGuAxlgNoAMAuoqAA6qzYAu2qujIAHogC0AdgCsAZgB04gEyjhADnEA2GgoUAWJQBoQAT0QBGGuICckmoZkbTM42YWKAvk91oMOfAQDKYdgAJYLDByTm5aBiQQFjYwniiBBEFTU2VJYWFxDUNDbPFxCQ1dAwQZO0lDW3EcuQzlBRoNFzd0LDxCXwCAW1QAVyDA9hJ2MAjeGI4ueNBE5JTJVUNNBUNlLQ05YsQsjQWNLVE5Yw0aZXFmkHc2-ElsCEwwAgBRXBGAJ0CAaz9yAAsxqITOK8WYyZTCCpg1JaKw5cFFfSIGSmXYaLKyPI0UwKZSmC5XTxQW73R4vd5fH7-QyRZisSbcEFCaw0STZRaiNTiNQ0YRbBAnGSSWzCZSiKz1QziFbKfGtQnEh7PPjsN69DAA2mxKaMpKieSSZE0US40QaUQrPk5DkGnEKFIcmz7QyyjztBWkvgsN7sDXROnAhJM9Q25llXIo818jKGBZiLKihoyYRml3XIneb7sP7EMiUTAZym+oHawMIHI4io8-YNBpmWSWpYx8srSWWBTm1PygtZ36Sbt-ACSJIIyBI30CYCgXTAr38ADcwN7sOQSJgi-6SzMjMimwoZJoRZoaEnLXlpDQL3IJEmlE1XJc5W7+73n0PFaPx0EpzOAr9UG9sAAL24IY13ocYNwZUsrGTBY1FUMUYPBGRLWqGNTXkSwzmMPUZE7J9Mz+PtCN+N9HiIbhYBVEg8H8P8AOA15V38CBsCo3NRnAwFIOmfht1NCwNi5bEslMJYFD5GQzDSI09QvM0xMlfCbmfYjKTI4hKOo2iFyXFdMBYtihgoTiaT9LUoK3MswQUIVDnbVZsjUDZJKTKRDmyA8MjBU1lPTEi1J7DSKNwKi3ho3B-AAQQAIW8fwAA11ws3jEisdtWXEMRw2qZEpMk0wBMK8EGmPURTEyc57wJAjKUCwdhxCsKIuiuL-AATWS+lUu3DLrDsZQjmNY0UMRBAuWDYQrzFVRHH2Pz6pfEjgq08LaLIKAHi6gMrMlNFpCUPd20UQxhFMSSOQhORjoyY0zoqhbVNfRrVpah58HYf4uM1bqdUlLLpFFVQxNkapREk5QlnSXFsjO6sVGER6AuexUmu0yKmEXTHcFY8hekwEgPlY9iTO2zc+Os-dWRSXKTiWVRlEklZdmESo7W8-YNDEJG6pR8jXto+igJA1dMD0fxV2wKBcDJyyKfFQV22WQpTVOcRLRmg1Cq8pNjXyHme0WlbQvR+dF04fSxYlnBpdlnrrLOzL9zOmazDFS1BtESRzQq7F6mRRwqpaV0VOR5aXpNtbItgXASCYfx2FQRK7b+6opDBKU1AqvczEkxpBTKY1lGLo1HDKA2iL5zTI5amO44TpPOu+8zfugtOLBFaoMiy45JNyCFTTL8rTo0NYg4fEP-N58PUYFyKwAAR16Zj3qgT6U+g5F3P7s7hCNOQsstTIWTREwLyNIG8Wqx9Q+n9SI+a2imEJ0XqGb4s5bS2QIWPDPDHKrII9LSmABiYaokMUg8jUKICuS176zxrrRN4YAegLn8OQOe7BYAb12tYWykNrAjU0P-HQY0ljVAsKsWwjR8jwTwtfSei0jbDgAEpgEEGAPgIRegjBwfLAoUhJSLGmmIRmY1UhpHKt3c0VgxKNFgcwxUTwl7YHjgAGTwGABuqAwJmQ-vbJYqRvb5DWPaLI6g+RKC9pdfqchR6IQUVXZRvRVEJw2j+bAzFCbkAbnwxIdg5CsixEaQq+RR7qzIVnA0eoVCnD9kaceNVb6GycSo+OyDQgbQeNo3REEUo6jKGIA0xhNDIn2ByCJJQrCWFZAHRMW9MgKKigAdxor+f8wsmIGWJsZSg-g8AADNUAEAgNwMAtxcBzlQN8SQMB2CCCFoxUCghBmoD8UiLkqJR4nAqmYCqmRJLZHMO2fcUkqFyHBE01pHA6IdKWcxHpHF+m4CGQQRcbx-ySCYATdgQy3hdFmX4BZdyRaYBWS8tZ78eIFK5F7HITo9r2QqpJKUgpGinAvAUJMkMrltLNnpB5RknmrJGWMiZUyZlzMELpC2q5wVDPWaUXKFQsorBMOCSoJ4xplCUJIRwhxDj-VOvQ4OaYmEtLxTS5chKSZ9JJe8z53zhh-IBVSqV+l6WQr0dC0sZzFZGjEGJMQmhXIbAqEDY8UojpKFxTc2K8UErPNeaM3A4y8AUvGVSkgAAjWAgg+CasZWctIIoZCXgPpGblqRBS5EaI5WQg1GkMLFapCVdq2qOvlW8D5bwvk-JVYC+ZPq-UBtWUGrumUrUFFMEaLQBVVjSHNKKaojRkxX1FV2AKaaAj2o6k64ZLq3WTOmZ6oFxbBB6EDVC-JurwGNvkHqWwIDkzgzGlycE1M7QcnNLCu8HbaqG27a1eK7V+1vOzYq-N-5VVjt9ROqd2qZ1WTOT-bFWUwRnDZHycBuw4nF0LpKKatqAhZK0SSwd5KR2FsEKBh9eTW7PorVyHIWLyqFUMBdY0QpYWWH-k2jIwGJb4GyVmnNeblXXug7Bst06EMUzObZPe2R9zVLtKdC6p0KiSkwnIfelzk2drqke1en0z0QfdVBqlInfhwe4k++jYMnZmFhhsY8vJuX1C9mrLuKhTpWEI9Js9Crc1Kt+ZRqTM414yZo4+uj-i53GBMDI+oXNRolDBGa-9WgURiFujKATB6iJHsxm8bGuN8aE0MrKsDELSWusg5SoFIWwvLgi28QQjyTKyZ+jtejYJBTawxINGymxuV7i9j3Ueop7QH0I8lmc4WCZEyJSZIzF6TNXv+dB+rONUtNYyy1yg2WW65f8fluyShrDFf9qu9zGQISVeLmhgViSb5T0Nho11OTnhpLcfgDxXi3g+MTkGs6aRsT9xocukBfJs5BOqFoUUU1IYKAUZtrRicdE7ZcekkIxlNofdQDooNutilYiyKzS19YyH-xDVNRzigHJyDe5onJkgBy4A4AQEHKg+VSjFA0FEchzpjQlAaKUICa1ZTOlJFHW3PuYHR5j9g2PqTwdG0iJMgoOQsbWNU3EUZHbhIkLYY0NajR08BzoyQAA5JOAAFVAeAsEECihACAgxCYBBYMroNxddg1uGkThoaw+TF3MGiX2e9EdSkl2juX-hFfK9gDmEyuS5N2aMJocwJgpJ7ixAcCxY094xhsBA-I6UzgKIx1jxlxgG0HD3rEw4AfLRSSkKPUNDNjx8+j8z1ntmOdliNDGO0wTwlhssAiKpO50jtnjOKGwKgFEABV3GvE8QZbxDdvuuPezkvX+xMo8nbIVU4bnEAojh2UAoe9TiZ4Ub0HGSd2LemfAAeVwHFodHrJBRW8C3wQS-RmCFX+wDfMtaNF-+rZQ4mRFL7AKGoPk5oWSpEqM5TISgsoKLYZk4jUujO3a7Q-gHSUAeAqu6u-gGS-22SYBeAfCVS2QXs6gjY+4LaWI6m-ijgkimyYuFUhisC-guAScAyJAlAPgwQoQLEYAZB+M2uBMrqjKggdgu4KI1Oj2bas2iAg0aQKwUkaI90IoNgC0ZA2AXQwwIBic-gSqrq2+CW4yYhEhIwggicggZBlAjKeou4I+6Kv8PIZuVMuUtC+4FUWgr2AWNwShkh+ADc-gnCRkIBsAJE8hEmMy1hKhahzhhYV+5MiQnsFg+WZ0Kwpo7636UMxcX+qQ6gPmFh94JBGA8AUQSSUA7OfhQgDQMYsg8gSgs0ywlSRgZqo8lQooO6-8Z2C0dwDwaRn8Qgawt+4Iuymg5oXMliKwxiag-8xhlg7aE8KaJENR9s0RrIx07K+8qe3KyIQo4e2IdQxcp0jiM8YAgxf0Jw6cZhNOzRwRDYKIFQzG66+QSeq2jCqa1y7SDEoKUWvSMWQyKx0EtorIOyigrMSYe8E+TKtg0gZoXIpwuISYzolh62QWZx+KtK3Sg2NxqAdxu06gMaqglgAig0Ykrk1ox4nkuQS2YohGvamaEK0J8sdoUgnIsiKgdgF4JO7mICoed0OIWEpcIqfRgmh6IJvap6qy+JaUdo3O+4aISw+c36pwXshceohqsJvRKR4qIJoG-aHJXuhUCwPxaIDghwN23KHItkOc7YxoPIg0BQBmlmom7JHu1+Ze92w0KwICxcYi7mZwIaYksxjRZoUegJkpeKPWjWkWmWcqeJxp6RZY7YdpoozkjQqQ+QkkJ06QbMw8IpqQduDOsppQ9gHc+Q7YNg6g9QL+7RlOx8SwHImmcZQOjOMe7ACZdgYoyZ+OaZtoZunxqwn64SNao84pa2TC-eDOkgJB-yq4pZNgEIXMmQhotYZQUYNSYelQEe+4ZwDJEpqkbZhZsuCuSurwSROWfphC6cSguQbKoMZwUYg0NoOQkoSwwhKweeHACZOE5gawpozIUoZguQDY7RzYKGbYHYLpqkbe+2Heh2x2UJvptRpQ5oP8qKGwo8YIGJlogpewFU5UjgPIKQ05LZqkx+K+Qwa+JEm+F59Q5gEgg8esO6yIL+o8rIEChCNJxov+f20p8Z-59sB8aQ15K6cYfJ1pvUWRKwjgqQU0vxlF-+AOaOwBthQQGCOM6C-4rqbwpZLRew8gZozFJuGsNamUaw2UR5SgzZJxAUf+sBgBe+ZxIB8BvE+iBSkMaQeQUIvZhip4-8fKHF6UGBD075JExBpB5ByxtFOoLB+QCw5oYa-UawflfIXM6cI00adQdYohmOyhUhScsh7lq5AFqwUgFUuIIZ8g-OhhMkVqIog8E0iMLpHhMV9hfAjhQlAxHl0EFU6QRcPGuQ1QtgTMLIJcZezIOI+4LgLgQAA */
|
||||
id: 'Modeling',
|
||||
|
||||
tsTypes: {} as import('./modelingMachine.typegen').Typegen0,
|
||||
@ -525,7 +528,11 @@ export const modelingMachine = createMachine(
|
||||
'engineToClient cam sync direction',
|
||||
],
|
||||
|
||||
entry: ['add axis n grid', 'conditionally equip line tool'],
|
||||
entry: [
|
||||
'add axis n grid',
|
||||
'conditionally equip line tool',
|
||||
'clientToEngine cam sync direction',
|
||||
],
|
||||
},
|
||||
|
||||
'Sketch no face': {
|
||||
@ -549,8 +556,6 @@ export const modelingMachine = createMachine(
|
||||
actions: 'set new sketch metadata',
|
||||
},
|
||||
},
|
||||
|
||||
entry: 'clientToEngine cam sync direction',
|
||||
},
|
||||
|
||||
'animating to existing sketch': {
|
||||
@ -564,8 +569,6 @@ export const modelingMachine = createMachine(
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
entry: 'clientToEngine cam sync direction',
|
||||
},
|
||||
},
|
||||
|
||||
@ -800,7 +803,7 @@ export const modelingMachine = createMachine(
|
||||
sketchDetails.origin
|
||||
)
|
||||
},
|
||||
'AST extrude': (_, event) => {
|
||||
'AST extrude': async (_, event) => {
|
||||
if (!event.data) return
|
||||
const { selection, distance } = event.data
|
||||
let ast = kclManager.ast
|
||||
@ -829,10 +832,12 @@ export const modelingMachine = createMachine(
|
||||
? distance.variableIdentifierAst
|
||||
: distance.valueAst
|
||||
)
|
||||
// TODO not handling focusPath correctly I think
|
||||
kclManager.updateAst(modifiedAst, true, {
|
||||
const selections = await kclManager.updateAst(modifiedAst, true, {
|
||||
focusPath: pathToExtrudeArg,
|
||||
})
|
||||
if (selections) {
|
||||
editorManager.selectRange(selections)
|
||||
}
|
||||
},
|
||||
'conditionally equip line tool': (_, { type }) => {
|
||||
if (type === 'done.invoke.animate-to-face') {
|
||||
@ -863,7 +868,52 @@ export const modelingMachine = createMachine(
|
||||
})()
|
||||
},
|
||||
'animate after sketch': () => {
|
||||
sceneEntitiesManager.animateAfterSketch()
|
||||
engineCommandManager
|
||||
.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_disable_sketch_mode',
|
||||
},
|
||||
})
|
||||
.then(async () => {
|
||||
// there doesn't appear to be an animation, but if there was one we could add a wait here
|
||||
|
||||
await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_set_perspective',
|
||||
},
|
||||
})
|
||||
sceneInfra.camControls.syncDirection = 'engineToClient'
|
||||
await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_set_perspective',
|
||||
},
|
||||
})
|
||||
await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_look_at',
|
||||
center: { x: 0, y: 0, z: 0 },
|
||||
vantage: sceneInfra.camControls.camera.position,
|
||||
up: { x: 0, y: 0, z: 1 },
|
||||
},
|
||||
})
|
||||
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',
|
||||
},
|
||||
})
|
||||
})
|
||||
},
|
||||
'tear down client sketch': () => {
|
||||
if (sceneEntitiesManager.activeSegments) {
|
||||
@ -980,18 +1030,10 @@ export const modelingMachine = createMachine(
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'set_selection_filter',
|
||||
filter: ['face'],
|
||||
},
|
||||
}),
|
||||
'set selection filter to defaults': () =>
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'set_selection_filter',
|
||||
filter: ['face', 'edge', 'solid2d'],
|
||||
filter: ['face', 'plane'],
|
||||
},
|
||||
}),
|
||||
'set selection filter to defaults': () => kclManager.enterEditMode(),
|
||||
},
|
||||
// end actions
|
||||
}
|
||||
|
@ -1,13 +1,11 @@
|
||||
import { create } from 'zustand'
|
||||
import { persist } from 'zustand/middleware'
|
||||
import { addLineHighlight, EditorView } from './editor/highlightextension'
|
||||
import {
|
||||
Program,
|
||||
_executor,
|
||||
ProgramMemory,
|
||||
programMemoryInit,
|
||||
} from './lang/wasm'
|
||||
import { Selection } from 'lib/selections'
|
||||
import { enginelessExecutor } from './lib/testHelpers'
|
||||
import { EngineCommandManager } from './lang/std/engineConnection'
|
||||
import { KCLError } from './lang/errors'
|
||||
@ -55,12 +53,6 @@ export type PaneType =
|
||||
| 'lspMessages'
|
||||
|
||||
export interface StoreState {
|
||||
editorView: EditorView | null
|
||||
setEditorView: (editorView: EditorView) => void
|
||||
highlightRange: [number, number]
|
||||
setHighlightRange: (range: Selection['range']) => void
|
||||
isShiftDown: boolean
|
||||
setIsShiftDown: (isShiftDown: boolean) => void
|
||||
mediaStream?: MediaStream
|
||||
setMediaStream: (mediaStream: MediaStream) => void
|
||||
isStreamReady: boolean
|
||||
@ -92,34 +84,12 @@ export interface StoreState {
|
||||
path: string
|
||||
}[]
|
||||
setHomeMenuItems: (items: { name: string; path: string }[]) => void
|
||||
lastCodeMirrorSelectionUpdatedFromScene: number
|
||||
setLastCodeMirrorSelectionUpdatedFromScene: (time: number) => void
|
||||
}
|
||||
|
||||
export const useStore = create<StoreState>()(
|
||||
persist(
|
||||
(set, get) => {
|
||||
return {
|
||||
editorView: null,
|
||||
setEditorView: (editorView) => {
|
||||
set({ editorView })
|
||||
},
|
||||
highlightRange: [0, 0],
|
||||
setHighlightRange: (selection) => {
|
||||
set({ highlightRange: selection })
|
||||
const editorView = get().editorView
|
||||
const safeEnd = Math.min(
|
||||
selection[1],
|
||||
editorView?.state.doc.length || selection[1]
|
||||
)
|
||||
if (editorView) {
|
||||
editorView.dispatch({
|
||||
effects: addLineHighlight.of([selection[0], safeEnd]),
|
||||
})
|
||||
}
|
||||
},
|
||||
isShiftDown: false,
|
||||
setIsShiftDown: (isShiftDown) => set({ isShiftDown }),
|
||||
setMediaStream: (mediaStream) => set({ mediaStream }),
|
||||
isStreamReady: false,
|
||||
setIsStreamReady: (isStreamReady) => set({ isStreamReady }),
|
||||
@ -159,9 +129,6 @@ export const useStore = create<StoreState>()(
|
||||
setHomeShowMenu: (showHomeMenu) => set({ showHomeMenu }),
|
||||
homeMenuItems: [],
|
||||
setHomeMenuItems: (homeMenuItems) => set({ homeMenuItems }),
|
||||
lastCodeMirrorSelectionUpdatedFromScene: Date.now(),
|
||||
setLastCodeMirrorSelectionUpdatedFromScene: (time) =>
|
||||
set({ lastCodeMirrorSelectionUpdatedFromScene: time }),
|
||||
}
|
||||
},
|
||||
{
|
||||
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 109 KiB |
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 143 KiB |
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 191 KiB |
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 191 KiB |
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 109 KiB |
@ -4,13 +4,14 @@
|
||||
"paths": {
|
||||
"/*": ["src/*"]
|
||||
},
|
||||
"types": ["vite/client", "@types/wicg-file-system-access", "node", "@wdio/globals/types"],
|
||||
"target": "esnext",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
"types": [
|
||||
"vite/client",
|
||||
"@types/wicg-file-system-access",
|
||||
"node",
|
||||
"@wdio/globals/types"
|
||||
],
|
||||
"target": "esnext",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
@ -25,10 +26,6 @@
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"src",
|
||||
"e2e",
|
||||
"./*.ts"
|
||||
],
|
||||
"include": ["src", "e2e", "./*.ts"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
|
@ -5,7 +5,5 @@
|
||||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": [
|
||||
"vite.config.ts"
|
||||
]
|
||||
}
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
|
@ -1,15 +1,9 @@
|
||||
import react from '@vitejs/plugin-react'
|
||||
import viteTsconfigPaths from 'vite-tsconfig-paths'
|
||||
import eslint from 'vite-plugin-eslint'
|
||||
import dns from 'dns'
|
||||
import { defineConfig, configDefaults } from 'vitest/config'
|
||||
import version from 'vite-plugin-package-version'
|
||||
|
||||
// Only needed because we run Node < 17
|
||||
// and we want to open `localhost` not `127.0.0.1` on server start
|
||||
// reference: https://vitejs.dev/config/server-options.html#server-host
|
||||
dns.setDefaultResultOrder('verbatim')
|
||||
|
||||
const config = defineConfig({
|
||||
server: {
|
||||
open: true,
|
||||
@ -25,32 +19,38 @@ const config = defineConfig({
|
||||
forks: {
|
||||
maxForks: 2,
|
||||
minForks: 1,
|
||||
}
|
||||
},
|
||||
},
|
||||
setupFiles: 'src/setupTests.ts',
|
||||
setupFiles: ['src/setupTests.ts', '@vitest/web-worker'],
|
||||
environment: 'happy-dom',
|
||||
coverage: {
|
||||
provider: 'istanbul' // or 'v8'
|
||||
provider: 'istanbul', // or 'v8'
|
||||
},
|
||||
exclude: [...configDefaults.exclude, '**/e2e/playwright/**/*'],
|
||||
deps: {
|
||||
inline: ['vitest-canvas-mock']
|
||||
}
|
||||
optimizer: {
|
||||
web: {
|
||||
include: ['vitest-canvas-mock'],
|
||||
},
|
||||
},
|
||||
},
|
||||
clearMocks: true,
|
||||
restoreMocks: true,
|
||||
mockReset: true,
|
||||
reporters: process.env.GITHUB_ACTIONS
|
||||
? ['dot', 'github-actions']
|
||||
: ['verbose', 'hanging-process'],
|
||||
testTimeout: 1000,
|
||||
hookTimeout: 1000,
|
||||
teardownTimeout: 1000,
|
||||
},
|
||||
build: {
|
||||
outDir: 'build',
|
||||
},
|
||||
plugins: [
|
||||
react(),
|
||||
viteTsconfigPaths(),
|
||||
eslint(),
|
||||
version(),
|
||||
],
|
||||
plugins: [react(), viteTsconfigPaths(), eslint(), version()],
|
||||
worker: {
|
||||
plugins: () => [
|
||||
viteTsconfigPaths(),
|
||||
],
|
||||
}
|
||||
plugins: () => [viteTsconfigPaths()],
|
||||
},
|
||||
})
|
||||
|
||||
export default config
|
||||
|
70
yarn.lock
@ -1675,10 +1675,10 @@
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
|
||||
"@headlessui/react@^1.7.18":
|
||||
version "1.7.18"
|
||||
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.7.18.tgz#30af4634d2215b2ca1aa29d07f33d02bea82d9d7"
|
||||
integrity sha512-4i5DOrzwN4qSgNsL4Si61VMkUcWbcSKueUV7sFhpHzQcSShdlHENE5+QBntMSRvHt8NyoFO2AGG8si9lq+w4zQ==
|
||||
"@headlessui/react@^1.7.19":
|
||||
version "1.7.19"
|
||||
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.7.19.tgz#91c78cf5fcb254f4a0ebe96936d48421caf75f40"
|
||||
integrity sha512-Ll+8q3OlMJfJbAKM/+/Y2q6PPYbryqNTXDbryx7SXLIDamkF6iQFbriYHga0dY44PvDhvvBWCx1Xj4U5+G4hOw==
|
||||
dependencies:
|
||||
"@tanstack/react-virtual" "^3.0.0-beta.60"
|
||||
client-only "^0.0.1"
|
||||
@ -1703,9 +1703,9 @@
|
||||
integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
|
||||
|
||||
"@humanwhocodes/object-schema@^2.0.2":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917"
|
||||
integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
|
||||
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
|
||||
|
||||
"@iarna/toml@^2.2.5":
|
||||
version "2.2.5"
|
||||
@ -2360,19 +2360,14 @@
|
||||
integrity sha512-awNxydYSU+E2vL7EiOAMtiSOfL5gUM5X4YSE2A92qpxDJQ/rXz6oMPYBFDcDywlUmvIDI6zsqgq17cGm5CITQw==
|
||||
|
||||
"@types/eslint@^8.4.5":
|
||||
version "8.44.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.1.tgz#d1811559bb6bcd1a76009e3f7883034b78a0415e"
|
||||
integrity sha512-XpNDc4Z5Tb4x+SW1MriMVeIsMoONHCkWFMkR/aPJbzEsxqHy+4Glu/BqTdPrApfDeMaXbtNh6bseNgl5KaWrSg==
|
||||
version "8.56.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.10.tgz#eb2370a73bf04a901eeba8f22595c7ee0f7eb58d"
|
||||
integrity sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==
|
||||
dependencies:
|
||||
"@types/estree" "*"
|
||||
"@types/json-schema" "*"
|
||||
|
||||
"@types/estree@*":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194"
|
||||
integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==
|
||||
|
||||
"@types/estree@1.0.5", "@types/estree@^1.0.0":
|
||||
"@types/estree@*", "@types/estree@1.0.5", "@types/estree@^1.0.0":
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4"
|
||||
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
|
||||
@ -2417,7 +2412,12 @@
|
||||
expect "^29.0.0"
|
||||
pretty-format "^29.0.0"
|
||||
|
||||
"@types/json-schema@*", "@types/json-schema@^7.0.9":
|
||||
"@types/json-schema@*":
|
||||
version "7.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
||||
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
|
||||
|
||||
"@types/json-schema@^7.0.9":
|
||||
version "7.0.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
|
||||
integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==
|
||||
@ -2764,6 +2764,13 @@
|
||||
loupe "^2.3.7"
|
||||
pretty-format "^29.7.0"
|
||||
|
||||
"@vitest/web-worker@^1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/web-worker/-/web-worker-1.5.0.tgz#9de0e20b4a9cfed2a4958d757bd0594a76a2d4e9"
|
||||
integrity sha512-WxX5VAgp8knJGTZknTDUICAVtNiDsBDQN1z/ldsDFqhRUFp09WLhRxWnKqCQc+CXk1W+kpUZmWDjxaD3i8QBmA==
|
||||
dependencies:
|
||||
debug "^4.3.4"
|
||||
|
||||
"@wdio/cli@^8.24.3":
|
||||
version "8.24.3"
|
||||
resolved "https://registry.yarnpkg.com/@wdio/cli/-/cli-8.24.3.tgz#305adc66cd2264ed4ef4549266bbb327826e10ef"
|
||||
@ -5080,11 +5087,12 @@ find-up@^6.3.0:
|
||||
path-exists "^5.0.0"
|
||||
|
||||
flat-cache@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
|
||||
integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee"
|
||||
integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==
|
||||
dependencies:
|
||||
flatted "^3.1.0"
|
||||
flatted "^3.2.9"
|
||||
keyv "^4.5.3"
|
||||
rimraf "^3.0.2"
|
||||
|
||||
flat@^5.0.2:
|
||||
@ -5092,10 +5100,10 @@ flat@^5.0.2:
|
||||
resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
|
||||
integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
|
||||
|
||||
flatted@^3.1.0:
|
||||
version "3.2.7"
|
||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
|
||||
integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
|
||||
flatted@^3.2.9:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
|
||||
integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==
|
||||
|
||||
flux@^4.0.1:
|
||||
version "4.0.4"
|
||||
@ -5436,9 +5444,9 @@ globals@^11.1.0:
|
||||
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
|
||||
|
||||
globals@^13.19.0:
|
||||
version "13.20.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82"
|
||||
integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==
|
||||
version "13.24.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171"
|
||||
integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==
|
||||
dependencies:
|
||||
type-fest "^0.20.2"
|
||||
|
||||
@ -8482,9 +8490,9 @@ tinybench@^2.5.1:
|
||||
integrity sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==
|
||||
|
||||
tinypool@^0.8.3:
|
||||
version "0.8.3"
|
||||
resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.8.3.tgz#e17d0a5315a7d425f875b05f7af653c225492d39"
|
||||
integrity sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==
|
||||
version "0.8.4"
|
||||
resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.8.4.tgz#e217fe1270d941b39e98c625dcecebb1408c9aa8"
|
||||
integrity sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==
|
||||
|
||||
tinyspy@^2.2.0:
|
||||
version "2.2.1"
|
||||
|