Merge branch 'main' into achalmers/kw-fn-sketches
@ -18,6 +18,7 @@ export class ToolbarFixture {
|
|||||||
filletButton!: Locator
|
filletButton!: Locator
|
||||||
chamferButton!: Locator
|
chamferButton!: Locator
|
||||||
shellButton!: Locator
|
shellButton!: Locator
|
||||||
|
revolveButton!: Locator
|
||||||
offsetPlaneButton!: Locator
|
offsetPlaneButton!: Locator
|
||||||
startSketchBtn!: Locator
|
startSketchBtn!: Locator
|
||||||
lineBtn!: Locator
|
lineBtn!: Locator
|
||||||
@ -47,6 +48,7 @@ export class ToolbarFixture {
|
|||||||
this.filletButton = page.getByTestId('fillet3d')
|
this.filletButton = page.getByTestId('fillet3d')
|
||||||
this.chamferButton = page.getByTestId('chamfer3d')
|
this.chamferButton = page.getByTestId('chamfer3d')
|
||||||
this.shellButton = page.getByTestId('shell')
|
this.shellButton = page.getByTestId('shell')
|
||||||
|
this.revolveButton = page.getByTestId('revolve')
|
||||||
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
||||||
this.startSketchBtn = page.getByTestId('sketch')
|
this.startSketchBtn = page.getByTestId('sketch')
|
||||||
this.lineBtn = page.getByTestId('line')
|
this.lineBtn = page.getByTestId('line')
|
||||||
|
@ -1078,7 +1078,7 @@ sketch002 = startSketchOn('XZ')
|
|||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByText('Unable to sweep with the provided selection')
|
page.getByText('Unable to sweep with the current selection. Reason:')
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -1183,7 +1183,7 @@ extrude001 = extrude(-12, sketch001)
|
|||||||
currentArgKey: 'radius',
|
currentArgKey: 'radius',
|
||||||
currentArgValue: '5',
|
currentArgValue: '5',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Selection: '1 face',
|
Selection: '1 segment',
|
||||||
Radius: '',
|
Radius: '',
|
||||||
},
|
},
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
@ -1192,7 +1192,7 @@ extrude001 = extrude(-12, sketch001)
|
|||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
commandName: 'Fillet',
|
commandName: 'Fillet',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Selection: '1 face',
|
Selection: '1 segment',
|
||||||
Radius: '5',
|
Radius: '5',
|
||||||
},
|
},
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
@ -1398,7 +1398,7 @@ extrude001 = extrude(sketch001, length = -12)
|
|||||||
currentArgKey: 'length',
|
currentArgKey: 'length',
|
||||||
currentArgValue: '5',
|
currentArgValue: '5',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Selection: '1 face',
|
Selection: '1 segment',
|
||||||
Length: '',
|
Length: '',
|
||||||
},
|
},
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
@ -1407,7 +1407,7 @@ extrude001 = extrude(sketch001, length = -12)
|
|||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
commandName: 'Chamfer',
|
commandName: 'Chamfer',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Selection: '1 face',
|
Selection: '1 segment',
|
||||||
Length: '5',
|
Length: '5',
|
||||||
},
|
},
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
@ -1846,8 +1846,176 @@ sweep001 = sweep({ path = sketch002 }, sketch001)
|
|||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByText('Unable to shell with the provided selection')
|
page.getByText('Unable to shell with the current selection. Reason:')
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test.describe('Revolve point and click workflows', () => {
|
||||||
|
test('Base case workflow, auto spam continue in command bar', async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `
|
||||||
|
sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([-100.0, 100.0], %)
|
||||||
|
|> angledLine([0, 200.0], %, $rectangleSegmentA001)
|
||||||
|
|> angledLine([segAng(rectangleSegmentA001) - 90, 200], %, $rectangleSegmentB001)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001),
|
||||||
|
-segLen(rectangleSegmentA001)
|
||||||
|
], %, $rectangleSegmentC001)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(200, sketch001)
|
||||||
|
sketch002 = startSketchOn(extrude001, rectangleSegmentA001)
|
||||||
|
|> startProfileAt([-66.77, 84.81], %)
|
||||||
|
|> angledLine([180, 27.08], %, $rectangleSegmentA002)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA002) - 90,
|
||||||
|
27.8
|
||||||
|
], %, $rectangleSegmentB002)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA002),
|
||||||
|
-segLen(rectangleSegmentA002)
|
||||||
|
], %, $rectangleSegmentC002)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
`
|
||||||
|
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, initialCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
|
||||||
|
// select line of code
|
||||||
|
const codeToSelecton = `segAng(rectangleSegmentA002) - 90,`
|
||||||
|
// revolve
|
||||||
|
await page.getByText(codeToSelecton).click()
|
||||||
|
await toolbar.revolveButton.click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
|
||||||
|
const newCodeToFind = `revolve001 = revolve({ angle = 360, axis = 'X' }, sketch002)`
|
||||||
|
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
||||||
|
})
|
||||||
|
test('revolve surface around edge from an extruded solid2d', async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `
|
||||||
|
sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([-102.57, 101.72], %)
|
||||||
|
|> angledLine([0, 202.6], %, $rectangleSegmentA001)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001) - 90,
|
||||||
|
202.6
|
||||||
|
], %, $rectangleSegmentB001)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001),
|
||||||
|
-segLen(rectangleSegmentA001)
|
||||||
|
], %, $rectangleSegmentC001)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(50, sketch001)
|
||||||
|
sketch002 = startSketchOn(extrude001, rectangleSegmentA001)
|
||||||
|
|> circle({
|
||||||
|
center = [-11.34, 10.0],
|
||||||
|
radius = 8.69
|
||||||
|
}, %)
|
||||||
|
`
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, initialCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
|
||||||
|
// select line of code
|
||||||
|
const codeToSelecton = `center = [-11.34, 10.0]`
|
||||||
|
// revolve
|
||||||
|
await page.getByText(codeToSelecton).click()
|
||||||
|
await toolbar.revolveButton.click()
|
||||||
|
await page.getByText('Edge', { exact: true }).click()
|
||||||
|
const lineCodeToSelection = `|> angledLine([0, 202.6], %, $rectangleSegmentA001)`
|
||||||
|
await page.getByText(lineCodeToSelection).click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
|
||||||
|
const newCodeToFind = `revolve001 = revolve({angle = 360, axis = getOppositeEdge(rectangleSegmentA001)}, sketch002) `
|
||||||
|
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
||||||
|
})
|
||||||
|
test('revolve sketch circle around line segment from startProfileAt sketch', async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `
|
||||||
|
sketch002 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-2.02, 1.79], %)
|
||||||
|
|> xLine(2.6, %)
|
||||||
|
sketch001 = startSketchOn('-XY')
|
||||||
|
|> startProfileAt([-0.48, 1.25], %)
|
||||||
|
|> angledLine([0, 2.38], %, $rectangleSegmentA001)
|
||||||
|
|> angledLine([segAng(rectangleSegmentA001) - 90, 2.4], %, $rectangleSegmentB001)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001),
|
||||||
|
-segLen(rectangleSegmentA001)
|
||||||
|
], %, $rectangleSegmentC001)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(5, sketch001)
|
||||||
|
sketch003 = startSketchOn(extrude001, 'START')
|
||||||
|
|> circle({
|
||||||
|
center = [-0.69, 0.56],
|
||||||
|
radius = 0.28
|
||||||
|
}, %)
|
||||||
|
`
|
||||||
|
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, initialCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
|
||||||
|
// select line of code
|
||||||
|
const codeToSelecton = `center = [-0.69, 0.56]`
|
||||||
|
// revolve
|
||||||
|
await page.getByText(codeToSelecton).click()
|
||||||
|
await toolbar.revolveButton.click()
|
||||||
|
await page.getByText('Edge', { exact: true }).click()
|
||||||
|
const lineCodeToSelection = `|> xLine(2.6, %)`
|
||||||
|
await page.getByText(lineCodeToSelection).click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
|
||||||
|
const newCodeToFind = `revolve001 = revolve({ angle = 360, axis = seg01 }, sketch003)`
|
||||||
|
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -572,7 +572,7 @@ test(
|
|||||||
fs.utimesSync(`${dir}/lego/main.kcl`, _1995, _1995)
|
fs.utimesSync(`${dir}/lego/main.kcl`, _1995, _1995)
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 600 })
|
||||||
|
|
||||||
page.on('console', console.log)
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
@ -886,7 +886,7 @@ test.describe('Sketch tests', () => {
|
|||||||
// sketch selection should already have been made. "Selection: 1 face" only show up when the selection has been made already
|
// sketch selection should already have been made. "Selection: 1 face" only show up when the selection has been made already
|
||||||
// otherwise the cmdbar would be waiting for a selection.
|
// otherwise the cmdbar would be waiting for a selection.
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'selection : 1 face', exact: false })
|
page.getByRole('button', { name: 'selection : 1 segment', exact: false })
|
||||||
).toBeVisible({
|
).toBeVisible({
|
||||||
timeout: 10_000,
|
timeout: 10_000,
|
||||||
})
|
})
|
||||||
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 145 KiB After Width: | Height: | Size: 145 KiB |
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 129 KiB |
@ -69,7 +69,6 @@ test.describe('Testing in-app sample loading', () => {
|
|||||||
await confirmButton.click()
|
await confirmButton.click()
|
||||||
|
|
||||||
await editor.expectEditor.toContain('// ' + newSample.title)
|
await editor.expectEditor.toContain('// ' + newSample.title)
|
||||||
await expect(unitsToast('in')).toBeVisible()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -158,7 +157,6 @@ test.describe('Testing in-app sample loading', () => {
|
|||||||
await editor.expectEditor.toContain('// ' + sampleOne.title)
|
await editor.expectEditor.toContain('// ' + sampleOne.title)
|
||||||
await expect(newlyCreatedFile(sampleOne.file)).toBeVisible()
|
await expect(newlyCreatedFile(sampleOne.file)).toBeVisible()
|
||||||
await expect(projectMenuButton).toContainText(sampleOne.file)
|
await expect(projectMenuButton).toContainText(sampleOne.file)
|
||||||
await expect(unitsToast('in')).toBeVisible()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step(`Now overwrite the current file`, async () => {
|
await test.step(`Now overwrite the current file`, async () => {
|
||||||
@ -188,7 +186,6 @@ test.describe('Testing in-app sample loading', () => {
|
|||||||
await expect(newlyCreatedFile(sampleOne.file)).toBeVisible()
|
await expect(newlyCreatedFile(sampleOne.file)).toBeVisible()
|
||||||
await expect(newlyCreatedFile(sampleTwo.file)).not.toBeVisible()
|
await expect(newlyCreatedFile(sampleTwo.file)).not.toBeVisible()
|
||||||
await expect(projectMenuButton).toContainText(sampleOne.file)
|
await expect(projectMenuButton).toContainText(sampleOne.file)
|
||||||
await expect(unitsToast('mm')).toBeVisible()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
39
src/App.tsx
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useMemo, useRef } from 'react'
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { useHotKeyListener } from './hooks/useHotKeyListener'
|
import { useHotKeyListener } from './hooks/useHotKeyListener'
|
||||||
import { Stream } from './components/Stream'
|
import { Stream } from './components/Stream'
|
||||||
import { AppHeader } from './components/AppHeader'
|
import { AppHeader } from './components/AppHeader'
|
||||||
@ -24,7 +24,12 @@ import { UnitsMenu } from 'components/UnitsMenu'
|
|||||||
import { CameraProjectionToggle } from 'components/CameraProjectionToggle'
|
import { CameraProjectionToggle } from 'components/CameraProjectionToggle'
|
||||||
import { useCreateFileLinkQuery } from 'hooks/useCreateFileLinkQueryWatcher'
|
import { useCreateFileLinkQuery } from 'hooks/useCreateFileLinkQueryWatcher'
|
||||||
import { maybeWriteToDisk } from 'lib/telemetry'
|
import { maybeWriteToDisk } from 'lib/telemetry'
|
||||||
|
import { takeScreenshotOfVideoStreamCanvas } from 'lib/screenshot'
|
||||||
|
import { writeProjectThumbnailFile } from 'lib/desktop'
|
||||||
|
import { useRouteLoaderData } from 'react-router-dom'
|
||||||
|
import { useEngineCommands } from 'components/EngineCommands'
|
||||||
import { commandBarActor } from 'machines/commandBarMachine'
|
import { commandBarActor } from 'machines/commandBarMachine'
|
||||||
|
import { useToken } from 'machines/appMachine'
|
||||||
maybeWriteToDisk()
|
maybeWriteToDisk()
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
@ -54,14 +59,20 @@ export function App() {
|
|||||||
|
|
||||||
const projectName = project?.name || null
|
const projectName = project?.name || null
|
||||||
const projectPath = project?.path || null
|
const projectPath = project?.path || null
|
||||||
|
|
||||||
|
const [commands] = useEngineCommands()
|
||||||
|
const [capturedCanvas, setCapturedCanvas] = useState(false)
|
||||||
|
const loaderData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
||||||
|
const lastCommandType = commands[commands.length - 1]?.type
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onProjectOpen({ name: projectName, path: projectPath }, file || null)
|
onProjectOpen({ name: projectName, path: projectPath }, file || null)
|
||||||
}, [projectName, projectPath])
|
}, [projectName, projectPath])
|
||||||
|
|
||||||
useHotKeyListener()
|
useHotKeyListener()
|
||||||
|
|
||||||
const { auth, settings } = useSettingsAuthContext()
|
const { settings } = useSettingsAuthContext()
|
||||||
const token = auth?.context?.token
|
const token = useToken()
|
||||||
|
|
||||||
const coreDumpManager = useMemo(
|
const coreDumpManager = useMemo(
|
||||||
() => new CoreDumpManager(engineCommandManager, codeManager, token),
|
() => new CoreDumpManager(engineCommandManager, codeManager, token),
|
||||||
@ -91,6 +102,28 @@ export function App() {
|
|||||||
|
|
||||||
useEngineConnectionSubscriptions()
|
useEngineConnectionSubscriptions()
|
||||||
|
|
||||||
|
// Generate thumbnail.png when loading the app
|
||||||
|
useEffect(() => {
|
||||||
|
if (!capturedCanvas && lastCommandType === 'execution-done') {
|
||||||
|
setTimeout(() => {
|
||||||
|
const projectDirectoryWithoutEndingSlash = loaderData?.project?.path
|
||||||
|
if (!projectDirectoryWithoutEndingSlash) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const dataUrl: string = takeScreenshotOfVideoStreamCanvas()
|
||||||
|
// zoom to fit command does not wait, wait 500ms to see if zoom to fit finishes
|
||||||
|
writeProjectThumbnailFile(dataUrl, projectDirectoryWithoutEndingSlash)
|
||||||
|
.then(() => {})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(
|
||||||
|
`Failed to generate thumbnail for ${projectDirectoryWithoutEndingSlash}`
|
||||||
|
)
|
||||||
|
console.error(e)
|
||||||
|
})
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
}, [lastCommandType])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative h-full flex flex-col" ref={ref}>
|
<div className="relative h-full flex flex-col" ref={ref}>
|
||||||
<AppHeader
|
<AppHeader
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
|
import { useAuthState } from 'machines/appMachine'
|
||||||
import Loading from './components/Loading'
|
import Loading from './components/Loading'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
|
||||||
|
|
||||||
// Wrapper around protected routes, used in src/Router.tsx
|
// Wrapper around protected routes, used in src/Router.tsx
|
||||||
export const Auth = ({ children }: React.PropsWithChildren) => {
|
export const Auth = ({ children }: React.PropsWithChildren) => {
|
||||||
const { auth } = useSettingsAuthContext()
|
const authState = useAuthState()
|
||||||
const isLoggingIn = auth?.state.matches('checkIfLoggedIn')
|
const isLoggingIn = authState.matches('checkIfLoggedIn')
|
||||||
|
|
||||||
return isLoggingIn ? (
|
return isLoggingIn ? (
|
||||||
<Loading>
|
<Loading>
|
||||||
|
@ -37,7 +37,6 @@ import { KclContextProvider } from 'lang/KclProvider'
|
|||||||
import { ASK_TO_OPEN_QUERY_PARAM, BROWSER_PROJECT_NAME } from 'lib/constants'
|
import { ASK_TO_OPEN_QUERY_PARAM, BROWSER_PROJECT_NAME } from 'lib/constants'
|
||||||
import { CoreDumpManager } from 'lib/coredump'
|
import { CoreDumpManager } from 'lib/coredump'
|
||||||
import { codeManager, engineCommandManager } from 'lib/singletons'
|
import { codeManager, engineCommandManager } from 'lib/singletons'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
|
||||||
import useHotkeyWrapper from 'lib/hotkeyWrapper'
|
import useHotkeyWrapper from 'lib/hotkeyWrapper'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { coreDump } from 'lang/wasm'
|
import { coreDump } from 'lang/wasm'
|
||||||
@ -47,6 +46,7 @@ import { reportRejection } from 'lib/trap'
|
|||||||
import { RouteProvider } from 'components/RouteProvider'
|
import { RouteProvider } from 'components/RouteProvider'
|
||||||
import { ProjectsContextProvider } from 'components/ProjectsContextProvider'
|
import { ProjectsContextProvider } from 'components/ProjectsContextProvider'
|
||||||
import { OpenInDesktopAppHandler } from 'components/OpenInDesktopAppHandler'
|
import { OpenInDesktopAppHandler } from 'components/OpenInDesktopAppHandler'
|
||||||
|
import { useToken } from 'machines/appMachine'
|
||||||
|
|
||||||
const createRouter = isDesktop() ? createHashRouter : createBrowserRouter
|
const createRouter = isDesktop() ? createHashRouter : createBrowserRouter
|
||||||
|
|
||||||
@ -203,8 +203,7 @@ export const Router = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function CoreDump() {
|
function CoreDump() {
|
||||||
const { auth } = useSettingsAuthContext()
|
const token = useToken()
|
||||||
const token = auth?.context?.token
|
|
||||||
const coreDumpManager = useMemo(
|
const coreDumpManager = useMemo(
|
||||||
() => new CoreDumpManager(engineCommandManager, codeManager, token),
|
() => new CoreDumpManager(engineCommandManager, codeManager, token),
|
||||||
[]
|
[]
|
||||||
|
@ -2,11 +2,11 @@ import { Toolbar } from '../Toolbar'
|
|||||||
import UserSidebarMenu from 'components/UserSidebarMenu'
|
import UserSidebarMenu from 'components/UserSidebarMenu'
|
||||||
import { type IndexLoaderData } from 'lib/types'
|
import { type IndexLoaderData } from 'lib/types'
|
||||||
import ProjectSidebarMenu from './ProjectSidebarMenu'
|
import ProjectSidebarMenu from './ProjectSidebarMenu'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
|
||||||
import styles from './AppHeader.module.css'
|
import styles from './AppHeader.module.css'
|
||||||
import { RefreshButton } from 'components/RefreshButton'
|
import { RefreshButton } from 'components/RefreshButton'
|
||||||
import { CommandBarOpenButton } from './CommandBarOpenButton'
|
import { CommandBarOpenButton } from './CommandBarOpenButton'
|
||||||
import { isDesktop } from 'lib/isDesktop'
|
import { isDesktop } from 'lib/isDesktop'
|
||||||
|
import { useUser } from 'machines/appMachine'
|
||||||
|
|
||||||
interface AppHeaderProps extends React.PropsWithChildren {
|
interface AppHeaderProps extends React.PropsWithChildren {
|
||||||
showToolbar?: boolean
|
showToolbar?: boolean
|
||||||
@ -24,8 +24,7 @@ export const AppHeader = ({
|
|||||||
style,
|
style,
|
||||||
enableMenu = false,
|
enableMenu = false,
|
||||||
}: AppHeaderProps) => {
|
}: AppHeaderProps) => {
|
||||||
const { auth } = useSettingsAuthContext()
|
const user = useUser()
|
||||||
const user = auth?.context?.user
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header
|
<header
|
||||||
|
@ -30,6 +30,7 @@ import {
|
|||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import { markOnce } from 'lib/performance'
|
import { markOnce } from 'lib/performance'
|
||||||
import { commandBarActor } from 'machines/commandBarMachine'
|
import { commandBarActor } from 'machines/commandBarMachine'
|
||||||
|
import { useToken } from 'machines/appMachine'
|
||||||
|
|
||||||
type MachineContext<T extends AnyStateMachine> = {
|
type MachineContext<T extends AnyStateMachine> = {
|
||||||
state: StateFrom<T>
|
state: StateFrom<T>
|
||||||
@ -47,7 +48,8 @@ export const FileMachineProvider = ({
|
|||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}) => {
|
}) => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { settings, auth } = useSettingsAuthContext()
|
const { settings } = useSettingsAuthContext()
|
||||||
|
const token = useToken()
|
||||||
const projectData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
const projectData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
||||||
const { project, file } = projectData
|
const { project, file } = projectData
|
||||||
const [kclSamples, setKclSamples] = React.useState<KclSamplesManifestItem[]>(
|
const [kclSamples, setKclSamples] = React.useState<KclSamplesManifestItem[]>(
|
||||||
@ -297,7 +299,7 @@ export const FileMachineProvider = ({
|
|||||||
const kclCommandMemo = useMemo(
|
const kclCommandMemo = useMemo(
|
||||||
() =>
|
() =>
|
||||||
kclCommands({
|
kclCommands({
|
||||||
authToken: auth?.context?.token ?? '',
|
authToken: token ?? '',
|
||||||
projectData,
|
projectData,
|
||||||
settings: {
|
settings: {
|
||||||
defaultUnit: settings?.context?.modeling.defaultUnit.current ?? 'mm',
|
defaultUnit: settings?.context?.modeling.defaultUnit.current ?? 'mm',
|
||||||
|
@ -27,6 +27,7 @@ import { PROJECT_ENTRYPOINT } from 'lib/constants'
|
|||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
import { isDesktop } from 'lib/isDesktop'
|
import { isDesktop } from 'lib/isDesktop'
|
||||||
import { codeManager } from 'lib/singletons'
|
import { codeManager } from 'lib/singletons'
|
||||||
|
import { useToken } from 'machines/appMachine'
|
||||||
|
|
||||||
function getWorkspaceFolders(): LSP.WorkspaceFolder[] {
|
function getWorkspaceFolders(): LSP.WorkspaceFolder[] {
|
||||||
return []
|
return []
|
||||||
@ -69,8 +70,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
const [isKclLspReady, setIsKclLspReady] = useState(false)
|
const [isKclLspReady, setIsKclLspReady] = useState(false)
|
||||||
const [isCopilotLspReady, setIsCopilotLspReady] = useState(false)
|
const [isCopilotLspReady, setIsCopilotLspReady] = useState(false)
|
||||||
|
|
||||||
const { auth } = useSettingsAuthContext()
|
const token = useToken()
|
||||||
const token = auth?.context.token
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
// So this is a bit weird, we need to initialize the lsp server and client.
|
// So this is a bit weird, we need to initialize the lsp server and client.
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import { useEngineCommands } from './EngineCommands'
|
import { useEngineCommands } from './EngineCommands'
|
||||||
import { Spinner } from './Spinner'
|
import { Spinner } from './Spinner'
|
||||||
import { CustomIcon } from './CustomIcon'
|
import { CustomIcon } from './CustomIcon'
|
||||||
|
|
||||||
export const ModelStateIndicator = () => {
|
export const ModelStateIndicator = () => {
|
||||||
const [commands] = useEngineCommands()
|
const [commands] = useEngineCommands()
|
||||||
|
|
||||||
const lastCommandType = commands[commands.length - 1]?.type
|
const lastCommandType = commands[commands.length - 1]?.type
|
||||||
|
|
||||||
let className = 'w-6 h-6 '
|
let className = 'w-6 h-6 '
|
||||||
|
@ -89,6 +89,7 @@ import { Node } from 'wasm-lib/kcl/bindings/Node'
|
|||||||
import { promptToEditFlow } from 'lib/promptToEdit'
|
import { promptToEditFlow } from 'lib/promptToEdit'
|
||||||
import { kclEditorActor } from 'machines/kclEditorMachine'
|
import { kclEditorActor } from 'machines/kclEditorMachine'
|
||||||
import { commandBarActor } from 'machines/commandBarMachine'
|
import { commandBarActor } from 'machines/commandBarMachine'
|
||||||
|
import { useToken } from 'machines/appMachine'
|
||||||
|
|
||||||
type MachineContext<T extends AnyStateMachine> = {
|
type MachineContext<T extends AnyStateMachine> = {
|
||||||
state: StateFrom<T>
|
state: StateFrom<T>
|
||||||
@ -110,7 +111,6 @@ export const ModelingMachineProvider = ({
|
|||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
auth,
|
|
||||||
settings: {
|
settings: {
|
||||||
context: {
|
context: {
|
||||||
app: { theme, enableSSAO, allowOrbitInSketchMode },
|
app: { theme, enableSSAO, allowOrbitInSketchMode },
|
||||||
@ -127,7 +127,7 @@ export const ModelingMachineProvider = ({
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { context, send: fileMachineSend } = useFileContext()
|
const { context, send: fileMachineSend } = useFileContext()
|
||||||
const { file } = useLoaderData() as IndexLoaderData
|
const { file } = useLoaderData() as IndexLoaderData
|
||||||
const token = auth?.context?.token
|
const token = useToken()
|
||||||
const streamRef = useRef<HTMLDivElement>(null)
|
const streamRef = useRef<HTMLDivElement>(null)
|
||||||
const persistedContext = useMemo(() => getPersistedContext(), [])
|
const persistedContext = useMemo(() => getPersistedContext(), [])
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import { FormEvent, useEffect, useRef, useState } from 'react'
|
|||||||
import { PATHS } from 'lib/paths'
|
import { PATHS } from 'lib/paths'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { ActionButton } from '../ActionButton'
|
import { ActionButton } from '../ActionButton'
|
||||||
import { FILE_EXT } from 'lib/constants'
|
import { FILE_EXT, PROJECT_IMAGE_NAME } from 'lib/constants'
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
import Tooltip from '../Tooltip'
|
import Tooltip from '../Tooltip'
|
||||||
import { DeleteConfirmationDialog } from './DeleteProjectDialog'
|
import { DeleteConfirmationDialog } from './DeleteProjectDialog'
|
||||||
@ -29,7 +29,7 @@ function ProjectCard({
|
|||||||
const [isConfirmingDelete, setIsConfirmingDelete] = useState(false)
|
const [isConfirmingDelete, setIsConfirmingDelete] = useState(false)
|
||||||
const [numberOfFiles, setNumberOfFiles] = useState(1)
|
const [numberOfFiles, setNumberOfFiles] = useState(1)
|
||||||
const [numberOfFolders, setNumberOfFolders] = useState(0)
|
const [numberOfFolders, setNumberOfFolders] = useState(0)
|
||||||
// const [imageUrl, setImageUrl] = useState('')
|
const [imageUrl, setImageUrl] = useState('')
|
||||||
|
|
||||||
let inputRef = useRef<HTMLInputElement>(null)
|
let inputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
@ -53,18 +53,21 @@ function ProjectCard({
|
|||||||
setNumberOfFolders(project.directory_count)
|
setNumberOfFolders(project.directory_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
// async function setupImageUrl() {
|
async function setupImageUrl() {
|
||||||
// const projectImagePath = await join(project.file.path, PROJECT_IMAGE_NAME)
|
const projectImagePath = window.electron.path.join(
|
||||||
// if (await exists(projectImagePath)) {
|
project.path,
|
||||||
// const imageData = await readFile(projectImagePath)
|
PROJECT_IMAGE_NAME
|
||||||
// const blob = new Blob([imageData], { type: 'image/jpg' })
|
)
|
||||||
// const imageUrl = URL.createObjectURL(blob)
|
if (await window.electron.exists(projectImagePath)) {
|
||||||
// setImageUrl(imageUrl)
|
const imageData = await window.electron.readFile(projectImagePath)
|
||||||
// }
|
const blob = new Blob([imageData], { type: 'image/png' })
|
||||||
// }
|
const imageUrl = URL.createObjectURL(blob)
|
||||||
|
setImageUrl(imageUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void getNumberOfFiles()
|
void getNumberOfFiles()
|
||||||
// void setupImageUrl()
|
void setupImageUrl()
|
||||||
}, [project.kcl_file_count, project.directory_count])
|
}, [project.kcl_file_count, project.directory_count])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -84,7 +87,7 @@ function ProjectCard({
|
|||||||
to={`${PATHS.FILE}/${encodeURIComponent(project.default_file)}`}
|
to={`${PATHS.FILE}/${encodeURIComponent(project.default_file)}`}
|
||||||
className="flex flex-col flex-1 !no-underline !text-chalkboard-110 dark:!text-chalkboard-10 group-hover:!hue-rotate-0 min-h-[5em] divide-y divide-primary/40 dark:divide-chalkboard-80 group-hover:!divide-primary"
|
className="flex flex-col flex-1 !no-underline !text-chalkboard-110 dark:!text-chalkboard-10 group-hover:!hue-rotate-0 min-h-[5em] divide-y divide-primary/40 dark:divide-chalkboard-80 group-hover:!divide-primary"
|
||||||
>
|
>
|
||||||
{/* <div className="h-36 relative overflow-hidden bg-gradient-to-b from-transparent to-primary/10 rounded-t-sm">
|
<div className="h-36 relative overflow-hidden bg-gradient-to-b from-transparent to-primary/10 rounded-t-sm">
|
||||||
{imageUrl && (
|
{imageUrl && (
|
||||||
<img
|
<img
|
||||||
src={imageUrl}
|
src={imageUrl}
|
||||||
@ -92,7 +95,7 @@ function ProjectCard({
|
|||||||
className="h-full w-full transition-transform group-hover:scale-105 object-cover"
|
className="h-full w-full transition-transform group-hover:scale-105 object-cover"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div> */}
|
</div>
|
||||||
<div className="pb-2 flex flex-col flex-grow flex-auto gap-2 rounded-b-sm">
|
<div className="pb-2 flex flex-col flex-grow flex-auto gap-2 rounded-b-sm">
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
<ProjectCardRenameForm
|
<ProjectCardRenameForm
|
||||||
|
@ -20,6 +20,7 @@ import { useSelector } from '@xstate/react'
|
|||||||
import { copyFileShareLink } from 'lib/links'
|
import { copyFileShareLink } from 'lib/links'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import { DEV } from 'env'
|
import { DEV } from 'env'
|
||||||
|
import { useToken } from 'machines/appMachine'
|
||||||
|
|
||||||
const ProjectSidebarMenu = ({
|
const ProjectSidebarMenu = ({
|
||||||
project,
|
project,
|
||||||
@ -103,7 +104,8 @@ function ProjectMenuPopover({
|
|||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const filePath = useAbsoluteFilePath()
|
const filePath = useAbsoluteFilePath()
|
||||||
const { settings, auth } = useSettingsAuthContext()
|
const { settings } = useSettingsAuthContext()
|
||||||
|
const token = useToken()
|
||||||
const machineManager = useContext(MachineManagerContext)
|
const machineManager = useContext(MachineManagerContext)
|
||||||
const commands = useSelector(commandBarActor, commandsSelector)
|
const commands = useSelector(commandBarActor, commandsSelector)
|
||||||
|
|
||||||
@ -194,7 +196,7 @@ function ProjectMenuPopover({
|
|||||||
disabled: !DEV,
|
disabled: !DEV,
|
||||||
onClick: async () => {
|
onClick: async () => {
|
||||||
await copyFileShareLink({
|
await copyFileShareLink({
|
||||||
token: auth?.context.token || '',
|
token: token ?? '',
|
||||||
code: codeManager.code,
|
code: codeManager.code,
|
||||||
name: project?.name || '',
|
name: project?.name || '',
|
||||||
units: settings.context.modeling.defaultUnit.current,
|
units: settings.context.modeling.defaultUnit.current,
|
||||||
|
@ -8,10 +8,10 @@ import Tooltip from './Tooltip'
|
|||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
import { toSync } from 'lib/utils'
|
import { toSync } from 'lib/utils'
|
||||||
|
import { useToken } from 'machines/appMachine'
|
||||||
|
|
||||||
export const RefreshButton = ({ children }: React.PropsWithChildren) => {
|
export const RefreshButton = ({ children }: React.PropsWithChildren) => {
|
||||||
const { auth } = useSettingsAuthContext()
|
const token = useToken()
|
||||||
const token = auth?.context?.token
|
|
||||||
const coreDumpManager = useMemo(
|
const coreDumpManager = useMemo(
|
||||||
() => new CoreDumpManager(engineCommandManager, codeManager, token),
|
() => new CoreDumpManager(engineCommandManager, codeManager, token),
|
||||||
[]
|
[]
|
||||||
|
@ -2,10 +2,12 @@ import { useEffect, useState, createContext, ReactNode } from 'react'
|
|||||||
import { useNavigation, useLocation } from 'react-router-dom'
|
import { useNavigation, useLocation } from 'react-router-dom'
|
||||||
import { PATHS } from 'lib/paths'
|
import { PATHS } from 'lib/paths'
|
||||||
import { markOnce } from 'lib/performance'
|
import { markOnce } from 'lib/performance'
|
||||||
|
import { useAuthNavigation } from 'hooks/useAuthNavigation'
|
||||||
|
|
||||||
export const RouteProviderContext = createContext({})
|
export const RouteProviderContext = createContext({})
|
||||||
|
|
||||||
export function RouteProvider({ children }: { children: ReactNode }) {
|
export function RouteProvider({ children }: { children: ReactNode }) {
|
||||||
|
useAuthNavigation()
|
||||||
const [first, setFirstState] = useState(true)
|
const [first, setFirstState] = useState(true)
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
|
@ -2,10 +2,7 @@ import { trap } from 'lib/trap'
|
|||||||
import { useMachine, useSelector } from '@xstate/react'
|
import { useMachine, useSelector } from '@xstate/react'
|
||||||
import { useNavigate, useRouteLoaderData, useLocation } from 'react-router-dom'
|
import { useNavigate, useRouteLoaderData, useLocation } from 'react-router-dom'
|
||||||
import { PATHS, BROWSER_PATH } from 'lib/paths'
|
import { PATHS, BROWSER_PATH } from 'lib/paths'
|
||||||
import { authMachine, TOKEN_PERSIST_KEY } from '../machines/authMachine'
|
|
||||||
import withBaseUrl from '../lib/withBaseURL'
|
|
||||||
import React, { createContext, useEffect, useState } from 'react'
|
import React, { createContext, useEffect, useState } from 'react'
|
||||||
import useStateMachineCommands from '../hooks/useStateMachineCommands'
|
|
||||||
import { settingsMachine } from 'machines/settingsMachine'
|
import { settingsMachine } from 'machines/settingsMachine'
|
||||||
import { toast } from 'react-hot-toast'
|
import { toast } from 'react-hot-toast'
|
||||||
import {
|
import {
|
||||||
@ -16,7 +13,6 @@ import {
|
|||||||
} from 'lib/theme'
|
} from 'lib/theme'
|
||||||
import decamelize from 'decamelize'
|
import decamelize from 'decamelize'
|
||||||
import { Actor, AnyStateMachine, ContextFrom, Prop, StateFrom } from 'xstate'
|
import { Actor, AnyStateMachine, ContextFrom, Prop, StateFrom } from 'xstate'
|
||||||
import { authCommandBarConfig } from 'lib/commandBarConfigs/authCommandConfig'
|
|
||||||
import {
|
import {
|
||||||
kclManager,
|
kclManager,
|
||||||
sceneInfra,
|
sceneInfra,
|
||||||
@ -50,7 +46,6 @@ type MachineContext<T extends AnyStateMachine> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SettingsAuthContextType = {
|
type SettingsAuthContextType = {
|
||||||
auth: MachineContext<typeof authMachine>
|
|
||||||
settings: MachineContext<typeof settingsMachine>
|
settings: MachineContext<typeof settingsMachine>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,40 +365,9 @@ export const SettingsAuthProviderBase = ({
|
|||||||
)
|
)
|
||||||
}, [settingsState.context.textEditor.blinkingCursor.current])
|
}, [settingsState.context.textEditor.blinkingCursor.current])
|
||||||
|
|
||||||
// Auth machine setup
|
|
||||||
const [authState, authSend, authActor] = useMachine(
|
|
||||||
authMachine.provide({
|
|
||||||
actions: {
|
|
||||||
goToSignInPage: () => {
|
|
||||||
navigate(PATHS.SIGN_IN)
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
logout()
|
|
||||||
},
|
|
||||||
goToIndexPage: () => {
|
|
||||||
if (location.pathname.includes(PATHS.SIGN_IN)) {
|
|
||||||
navigate(PATHS.INDEX)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
useStateMachineCommands({
|
|
||||||
machineId: 'auth',
|
|
||||||
state: authState,
|
|
||||||
send: authSend,
|
|
||||||
commandBarConfig: authCommandBarConfig,
|
|
||||||
actor: authActor,
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsAuthContext.Provider
|
<SettingsAuthContext.Provider
|
||||||
value={{
|
value={{
|
||||||
auth: {
|
|
||||||
state: authState,
|
|
||||||
context: authState.context,
|
|
||||||
send: authSend,
|
|
||||||
},
|
|
||||||
settings: {
|
settings: {
|
||||||
state: settingsState,
|
state: settingsState,
|
||||||
context: settingsState.context,
|
context: settingsState.context,
|
||||||
@ -417,12 +381,3 @@ export const SettingsAuthProviderBase = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default SettingsAuthProvider
|
export default SettingsAuthProvider
|
||||||
|
|
||||||
export async function logout() {
|
|
||||||
localStorage.removeItem(TOKEN_PERSIST_KEY)
|
|
||||||
if (isDesktop()) return Promise.resolve(null)
|
|
||||||
return fetch(withBaseUrl('/logout'), {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'include',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -4,12 +4,12 @@ import { useLocation, useNavigate } from 'react-router-dom'
|
|||||||
import { Fragment, useMemo, useState } from 'react'
|
import { Fragment, useMemo, useState } from 'react'
|
||||||
import { PATHS } from 'lib/paths'
|
import { PATHS } from 'lib/paths'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
|
||||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||||
import Tooltip from './Tooltip'
|
import Tooltip from './Tooltip'
|
||||||
import usePlatform from 'hooks/usePlatform'
|
import usePlatform from 'hooks/usePlatform'
|
||||||
import { isDesktop } from 'lib/isDesktop'
|
import { isDesktop } from 'lib/isDesktop'
|
||||||
import { CustomIcon } from './CustomIcon'
|
import { CustomIcon } from './CustomIcon'
|
||||||
|
import { authActor } from 'machines/appMachine'
|
||||||
|
|
||||||
type User = Models['User_type']
|
type User = Models['User_type']
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
|
|||||||
const displayedName = getDisplayName(user)
|
const displayedName = getDisplayName(user)
|
||||||
const [imageLoadFailed, setImageLoadFailed] = useState(false)
|
const [imageLoadFailed, setImageLoadFailed] = useState(false)
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const send = useSettingsAuthContext()?.auth?.send
|
const send = authActor.send
|
||||||
|
|
||||||
// We filter this memoized list so that no orphan "break" elements are rendered.
|
// We filter this memoized list so that no orphan "break" elements are rendered.
|
||||||
const userMenuItems = useMemo<(ActionButtonProps | 'break')[]>(
|
const userMenuItems = useMemo<(ActionButtonProps | 'break')[]>(
|
||||||
|
29
src/hooks/useAuthNavigation.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { PATHS } from 'lib/paths'
|
||||||
|
import { useAuthState } from 'machines/appMachine'
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple hook that listens to the auth state of the app and navigates
|
||||||
|
* accordingly.
|
||||||
|
*/
|
||||||
|
export function useAuthNavigation() {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const location = useLocation()
|
||||||
|
const authState = useAuthState()
|
||||||
|
|
||||||
|
// Subscribe to the auth state of the app and navigate accordingly.
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
authState.matches('loggedIn') &&
|
||||||
|
location.pathname.includes(PATHS.SIGN_IN)
|
||||||
|
) {
|
||||||
|
navigate(PATHS.INDEX)
|
||||||
|
} else if (
|
||||||
|
authState.matches('loggedOut') &&
|
||||||
|
!location.pathname.includes(PATHS.SIGN_IN)
|
||||||
|
) {
|
||||||
|
navigate(PATHS.SIGN_IN)
|
||||||
|
}
|
||||||
|
}, [authState])
|
||||||
|
}
|
@ -5,7 +5,11 @@ import {
|
|||||||
PathToNode,
|
PathToNode,
|
||||||
Identifier,
|
Identifier,
|
||||||
topLevelRange,
|
topLevelRange,
|
||||||
|
PipeExpression,
|
||||||
|
CallExpression,
|
||||||
|
VariableDeclarator,
|
||||||
} from './wasm'
|
} from './wasm'
|
||||||
|
import { ProgramMemory } from 'lang/wasm'
|
||||||
import {
|
import {
|
||||||
findAllPreviousVariables,
|
findAllPreviousVariables,
|
||||||
isNodeSafeToReplace,
|
isNodeSafeToReplace,
|
||||||
@ -25,9 +29,11 @@ import {
|
|||||||
createCallExpression,
|
createCallExpression,
|
||||||
createLiteral,
|
createLiteral,
|
||||||
createPipeSubstitution,
|
createPipeSubstitution,
|
||||||
|
createCallExpressionStdLib,
|
||||||
} from './modifyAst'
|
} from './modifyAst'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
import { codeRefFromRange } from './std/artifactGraph'
|
import { codeRefFromRange } from './std/artifactGraph'
|
||||||
|
import { addCallExpressionsToPipe, addCloseToPipe } from 'lang/std/sketch'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await initPromise
|
await initPromise
|
||||||
@ -680,3 +686,115 @@ myNestedVar = [
|
|||||||
expect(pathToNode).toEqual(pathToNode2)
|
expect(pathToNode).toEqual(pathToNode2)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Testing specific sketch getNodeFromPath workflow', () => {
|
||||||
|
it('should parse the code', () => {
|
||||||
|
const openSketch = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([0.02, 0.22], %)
|
||||||
|
|> xLine(0.39, %)
|
||||||
|
|> line([0.02, -0.17], %)
|
||||||
|
|> yLine(-0.15, %)
|
||||||
|
|> line([-0.21, -0.02], %)
|
||||||
|
|> xLine(-0.15, %)
|
||||||
|
|> line([-0.02, 0.21], %)
|
||||||
|
|> line([-0.08, 0.05], %)`
|
||||||
|
const ast = assertParse(openSketch)
|
||||||
|
expect(ast.start).toEqual(0)
|
||||||
|
expect(ast.end).toEqual(227)
|
||||||
|
})
|
||||||
|
it('should find the location to add new lineTo', () => {
|
||||||
|
const openSketch = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([0.02, 0.22], %)
|
||||||
|
|> xLine(0.39, %)
|
||||||
|
|> line([0.02, -0.17], %)
|
||||||
|
|> yLine(-0.15, %)
|
||||||
|
|> line([-0.21, -0.02], %)
|
||||||
|
|> xLine(-0.15, %)
|
||||||
|
|> line([-0.02, 0.21], %)
|
||||||
|
|> line([-0.08, 0.05], %)`
|
||||||
|
const ast = assertParse(openSketch)
|
||||||
|
|
||||||
|
const sketchSnippet = `startProfileAt([0.02, 0.22], %)`
|
||||||
|
const sketchRange = topLevelRange(
|
||||||
|
openSketch.indexOf(sketchSnippet),
|
||||||
|
openSketch.indexOf(sketchSnippet) + sketchSnippet.length
|
||||||
|
)
|
||||||
|
const sketchPathToNode = getNodePathFromSourceRange(ast, sketchRange)
|
||||||
|
const modifiedAst = addCallExpressionsToPipe({
|
||||||
|
node: ast,
|
||||||
|
programMemory: ProgramMemory.empty(),
|
||||||
|
pathToNode: sketchPathToNode,
|
||||||
|
expressions: [
|
||||||
|
createCallExpressionStdLib(
|
||||||
|
'lineTo', // We are forcing lineTo!
|
||||||
|
[
|
||||||
|
createArrayExpression([
|
||||||
|
createCallExpressionStdLib('profileStartX', [
|
||||||
|
createPipeSubstitution(),
|
||||||
|
]),
|
||||||
|
createCallExpressionStdLib('profileStartY', [
|
||||||
|
createPipeSubstitution(),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
createPipeSubstitution(),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
if (err(modifiedAst)) throw modifiedAst
|
||||||
|
const recasted = recast(modifiedAst)
|
||||||
|
const expectedCode = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([0.02, 0.22], %)
|
||||||
|
|> xLine(0.39, %)
|
||||||
|
|> line([0.02, -0.17], %)
|
||||||
|
|> yLine(-0.15, %)
|
||||||
|
|> line([-0.21, -0.02], %)
|
||||||
|
|> xLine(-0.15, %)
|
||||||
|
|> line([-0.02, 0.21], %)
|
||||||
|
|> line([-0.08, 0.05], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
`
|
||||||
|
expect(recasted).toEqual(expectedCode)
|
||||||
|
})
|
||||||
|
it('it should find the location to add close', () => {
|
||||||
|
const openSketch = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([0.02, 0.22], %)
|
||||||
|
|> xLine(0.39, %)
|
||||||
|
|> line([0.02, -0.17], %)
|
||||||
|
|> yLine(-0.15, %)
|
||||||
|
|> line([-0.21, -0.02], %)
|
||||||
|
|> xLine(-0.15, %)
|
||||||
|
|> line([-0.02, 0.21], %)
|
||||||
|
|> line([-0.08, 0.05], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
`
|
||||||
|
const ast = assertParse(openSketch)
|
||||||
|
const sketchSnippet = `startProfileAt([0.02, 0.22], %)`
|
||||||
|
const sketchRange = topLevelRange(
|
||||||
|
openSketch.indexOf(sketchSnippet),
|
||||||
|
openSketch.indexOf(sketchSnippet) + sketchSnippet.length
|
||||||
|
)
|
||||||
|
const sketchPathToNode = getNodePathFromSourceRange(ast, sketchRange)
|
||||||
|
const modifiedAst = addCloseToPipe({
|
||||||
|
node: ast,
|
||||||
|
programMemory: ProgramMemory.empty(),
|
||||||
|
pathToNode: sketchPathToNode,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (err(modifiedAst)) throw modifiedAst
|
||||||
|
const recasted = recast(modifiedAst)
|
||||||
|
const expectedCode = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([0.02, 0.22], %)
|
||||||
|
|> xLine(0.39, %)
|
||||||
|
|> line([0.02, -0.17], %)
|
||||||
|
|> yLine(-0.15, %)
|
||||||
|
|> line([-0.21, -0.02], %)
|
||||||
|
|> xLine(-0.15, %)
|
||||||
|
|> line([-0.02, 0.21], %)
|
||||||
|
|> line([-0.08, 0.05], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
`
|
||||||
|
expect(recasted).toEqual(expectedCode)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -22,6 +22,7 @@ import {
|
|||||||
topLevelRange,
|
topLevelRange,
|
||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
|
recast,
|
||||||
} from './wasm'
|
} from './wasm'
|
||||||
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
||||||
import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
|
import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
|
||||||
@ -79,7 +80,28 @@ export function getNodeFromPath<T>(
|
|||||||
deepPath: successfulPaths,
|
deepPath: successfulPaths,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new Error('not an object')
|
const stackTraceError = new Error()
|
||||||
|
const sourceCode = recast(node)
|
||||||
|
const levels = stackTraceError.stack?.split('\n')
|
||||||
|
const aFewFunctionNames: string[] = []
|
||||||
|
let tree = ''
|
||||||
|
levels?.forEach((val, index) => {
|
||||||
|
const fnName = val.trim().split(' ')[1]
|
||||||
|
const ending = index === levels.length - 1 ? ' ' : ' > '
|
||||||
|
tree += fnName + ending
|
||||||
|
if (index < 3) {
|
||||||
|
aFewFunctionNames.push(fnName)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const error = new Error(
|
||||||
|
`Failed to stopAt ${stopAt}, ${aFewFunctionNames
|
||||||
|
.filter((a) => a)
|
||||||
|
.join(' > ')}`
|
||||||
|
)
|
||||||
|
console.error(tree)
|
||||||
|
console.error(sourceCode)
|
||||||
|
console.error(error.stack)
|
||||||
|
return error
|
||||||
}
|
}
|
||||||
parent = currentNode
|
parent = currentNode
|
||||||
parentEdge = pathItem[0]
|
parentEdge = pathItem[0]
|
||||||
|
@ -1999,7 +1999,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
// TODO: Previously was never caught, we are not rejecting these pendingCommands but this needs to be handled at some point.
|
// TODO: Previously was never caught, we are not rejecting these pendingCommands but this needs to be handled at some point.
|
||||||
/*noop*/
|
/*noop*/
|
||||||
return null
|
return e
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
default_project_settings,
|
default_project_settings,
|
||||||
base64_decode,
|
base64_decode,
|
||||||
clear_scene_and_bust_cache,
|
clear_scene_and_bust_cache,
|
||||||
|
change_kcl_settings,
|
||||||
reloadModule,
|
reloadModule,
|
||||||
} from 'lib/wasm_lib_wrapper'
|
} from 'lib/wasm_lib_wrapper'
|
||||||
|
|
||||||
@ -56,6 +57,7 @@ import { ArtifactGraph as RustArtifactGraph } from 'wasm-lib/kcl/bindings/Artifa
|
|||||||
import { Artifact } from './std/artifactGraph'
|
import { Artifact } from './std/artifactGraph'
|
||||||
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
||||||
import { NumericSuffix } from 'wasm-lib/kcl/bindings/NumericSuffix'
|
import { NumericSuffix } from 'wasm-lib/kcl/bindings/NumericSuffix'
|
||||||
|
import { MetaSettings } from 'wasm-lib/kcl/bindings/MetaSettings'
|
||||||
|
|
||||||
export type { Artifact } from 'wasm-lib/kcl/bindings/Artifact'
|
export type { Artifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||||
export type { ArtifactCommand } from 'wasm-lib/kcl/bindings/Artifact'
|
export type { ArtifactCommand } from 'wasm-lib/kcl/bindings/Artifact'
|
||||||
@ -848,3 +850,17 @@ export function base64Decode(base64: string): ArrayBuffer | Error {
|
|||||||
return new Error('Caught error decoding base64 string: ' + e)
|
return new Error('Caught error decoding base64 string: ' + e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Change the meta settings for the kcl file.
|
||||||
|
/// Returns the new kcl string with the updated settings.
|
||||||
|
export function changeKclSettings(
|
||||||
|
kcl: string,
|
||||||
|
settings: MetaSettings
|
||||||
|
): string | Error {
|
||||||
|
try {
|
||||||
|
return change_kcl_settings(kcl, JSON.stringify(settings))
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Caught error changing kcl settings: ' + e)
|
||||||
|
return new Error('Caught error changing kcl settings: ' + e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
import { StateMachineCommandSetConfig } from 'lib/commandTypes'
|
import { Command } from 'lib/commandTypes'
|
||||||
import { authMachine } from 'machines/authMachine'
|
import { authActor } from 'machines/appMachine'
|
||||||
|
import { ACTOR_IDS } from 'machines/machineConstants'
|
||||||
|
|
||||||
type AuthCommandSchema = {}
|
export const authCommands: Command[] = [
|
||||||
|
{
|
||||||
export const authCommandBarConfig: StateMachineCommandSetConfig<
|
groupId: ACTOR_IDS.AUTH,
|
||||||
typeof authMachine,
|
name: 'log-out',
|
||||||
AuthCommandSchema
|
displayName: 'Log out',
|
||||||
> = {
|
|
||||||
'Log in': {
|
|
||||||
hide: 'both',
|
|
||||||
},
|
|
||||||
'Log out': {
|
|
||||||
args: [],
|
|
||||||
icon: 'arrowLeft',
|
icon: 'arrowLeft',
|
||||||
|
needsReview: false,
|
||||||
|
onSubmit: () => authActor.send({ type: 'Log out' }),
|
||||||
},
|
},
|
||||||
}
|
]
|
||||||
|
@ -308,7 +308,6 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
description:
|
description:
|
||||||
'Create a 3D body by moving a sketch region along an arbitrary path.',
|
'Create a 3D body by moving a sketch region along an arbitrary path.',
|
||||||
icon: 'sweep',
|
icon: 'sweep',
|
||||||
status: 'development',
|
|
||||||
needsReview: false,
|
needsReview: false,
|
||||||
args: {
|
args: {
|
||||||
target: {
|
target: {
|
||||||
@ -317,8 +316,6 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
required: true,
|
required: true,
|
||||||
skip: true,
|
skip: true,
|
||||||
multiple: false,
|
multiple: false,
|
||||||
warningMessage:
|
|
||||||
'The sweep workflow is new and under tested. Please break it and report issues.',
|
|
||||||
},
|
},
|
||||||
trajectory: {
|
trajectory: {
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
@ -368,7 +365,6 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
Revolve: {
|
Revolve: {
|
||||||
description: 'Create a 3D body by rotating a sketch region about an axis.',
|
description: 'Create a 3D body by rotating a sketch region about an axis.',
|
||||||
icon: 'revolve',
|
icon: 'revolve',
|
||||||
status: 'development',
|
|
||||||
needsReview: true,
|
needsReview: true,
|
||||||
args: {
|
args: {
|
||||||
selection: {
|
selection: {
|
||||||
@ -377,8 +373,6 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
multiple: false, // TODO: multiple selection
|
multiple: false, // TODO: multiple selection
|
||||||
required: true,
|
required: true,
|
||||||
skip: true,
|
skip: true,
|
||||||
warningMessage:
|
|
||||||
'The revolve workflow is new and under tested. Please break it and report issues.',
|
|
||||||
},
|
},
|
||||||
axisOrEdge: {
|
axisOrEdge: {
|
||||||
inputType: 'options',
|
inputType: 'options',
|
||||||
|
19
src/lib/commandBarConfigs/validators.test.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { parseEngineErrorMessage } from './validators'
|
||||||
|
|
||||||
|
describe('parseEngineErrorMessage', () => {
|
||||||
|
it('takes an engine error string and parses its json message', () => {
|
||||||
|
const engineError =
|
||||||
|
'engine error: [{"error_code":"internal_engine","message":"Trajectory curve must be G1 continuous (with continuous tangents)"}]'
|
||||||
|
const message = parseEngineErrorMessage(engineError)
|
||||||
|
expect(message).toEqual(
|
||||||
|
'Trajectory curve must be G1 continuous (with continuous tangents)'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('retuns undefined on strings with different formats', () => {
|
||||||
|
const s1 = 'engine error: []'
|
||||||
|
const s2 = 'blabla'
|
||||||
|
expect(parseEngineErrorMessage(s1)).toBeUndefined()
|
||||||
|
expect(parseEngineErrorMessage(s2)).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
@ -3,6 +3,7 @@ import { engineCommandManager } from 'lib/singletons'
|
|||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
import { CommandBarContext } from 'machines/commandBarMachine'
|
import { CommandBarContext } from 'machines/commandBarMachine'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
|
import { ApiError_type } from '@kittycad/lib/dist/types/src/models'
|
||||||
|
|
||||||
export const disableDryRunWithRetry = async (numberOfRetries = 3) => {
|
export const disableDryRunWithRetry = async (numberOfRetries = 3) => {
|
||||||
for (let tries = 0; tries < numberOfRetries; tries++) {
|
for (let tries = 0; tries < numberOfRetries; tries++) {
|
||||||
@ -46,6 +47,20 @@ function isSelections(selections: unknown): selections is Selections {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function parseEngineErrorMessage(engineError: string) {
|
||||||
|
const parts = engineError.split('engine error: ')
|
||||||
|
if (parts.length < 2) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const errors = JSON.parse(parts[1]) as ApiError_type[]
|
||||||
|
if (!errors[0]) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors[0].message
|
||||||
|
}
|
||||||
|
|
||||||
export const revolveAxisValidator = async ({
|
export const revolveAxisValidator = async ({
|
||||||
data,
|
data,
|
||||||
context,
|
context,
|
||||||
@ -83,7 +98,7 @@ export const revolveAxisValidator = async ({
|
|||||||
value: 360,
|
value: 360,
|
||||||
}
|
}
|
||||||
|
|
||||||
const revolveAboutEdgeCommand = async () => {
|
const command = async () => {
|
||||||
return await engineCommandManager.sendSceneCommand({
|
return await engineCommandManager.sendSceneCommand({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd_id: uuidv4(),
|
cmd_id: uuidv4(),
|
||||||
@ -92,17 +107,18 @@ export const revolveAxisValidator = async ({
|
|||||||
angle: angleInDegrees,
|
angle: angleInDegrees,
|
||||||
edge_id: edgeSelection,
|
edge_id: edgeSelection,
|
||||||
target: sketchSelection,
|
target: sketchSelection,
|
||||||
tolerance: 0.0001,
|
// Gotcha: Playwright will fail with larger tolerances, need to use a smaller one.
|
||||||
|
tolerance: 1e-7,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const attemptRevolve = await dryRunWrapper(revolveAboutEdgeCommand)
|
const result = await dryRunWrapper(command)
|
||||||
if (attemptRevolve?.success) {
|
if (result?.success) {
|
||||||
return true
|
return true
|
||||||
} else {
|
|
||||||
// return error message for the toast
|
|
||||||
return 'Unable to revolve with selected edge'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const reason = parseEngineErrorMessage(result) || 'unknown'
|
||||||
|
return `Unable to revolve with the current selection. Reason: ${reason}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loftValidator = async ({
|
export const loftValidator = async ({
|
||||||
@ -128,7 +144,7 @@ export const loftValidator = async ({
|
|||||||
return 'Unable to loft, selection contains less than two solid2ds'
|
return 'Unable to loft, selection contains less than two solid2ds'
|
||||||
}
|
}
|
||||||
|
|
||||||
const loftCommand = async () => {
|
const command = async () => {
|
||||||
// TODO: check what to do with these
|
// TODO: check what to do with these
|
||||||
const DEFAULT_V_DEGREE = 2
|
const DEFAULT_V_DEGREE = 2
|
||||||
const DEFAULT_TOLERANCE = 2
|
const DEFAULT_TOLERANCE = 2
|
||||||
@ -145,13 +161,13 @@ export const loftValidator = async ({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const attempt = await dryRunWrapper(loftCommand)
|
const result = await dryRunWrapper(command)
|
||||||
if (attempt?.success) {
|
if (result?.success) {
|
||||||
return true
|
return true
|
||||||
} else {
|
|
||||||
// return error message for the toast
|
|
||||||
return 'Unable to loft with selected sketches'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const reason = parseEngineErrorMessage(result) || 'unknown'
|
||||||
|
return `Unable to loft with the current selection. Reason: ${reason}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export const shellValidator = async ({
|
export const shellValidator = async ({
|
||||||
@ -180,7 +196,7 @@ export const shellValidator = async ({
|
|||||||
return "Unable to shell, couldn't find the solid"
|
return "Unable to shell, couldn't find the solid"
|
||||||
}
|
}
|
||||||
|
|
||||||
const shellCommand = async () => {
|
const command = async () => {
|
||||||
// TODO: figure out something better than an arbitrarily small value
|
// TODO: figure out something better than an arbitrarily small value
|
||||||
const DEFAULT_THICKNESS: Models['LengthUnit_type'] = 1e-9
|
const DEFAULT_THICKNESS: Models['LengthUnit_type'] = 1e-9
|
||||||
const DEFAULT_HOLLOW = false
|
const DEFAULT_HOLLOW = false
|
||||||
@ -200,12 +216,13 @@ export const shellValidator = async ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const attemptShell = await dryRunWrapper(shellCommand)
|
const result = await dryRunWrapper(command)
|
||||||
if (attemptShell?.success) {
|
if (result?.success) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Unable to shell with the provided selection'
|
const reason = parseEngineErrorMessage(result) || 'unknown'
|
||||||
|
return `Unable to shell with the current selection. Reason: ${reason}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export const sweepValidator = async ({
|
export const sweepValidator = async ({
|
||||||
@ -241,7 +258,7 @@ export const sweepValidator = async ({
|
|||||||
}
|
}
|
||||||
const target = targetArtifact.pathId
|
const target = targetArtifact.pathId
|
||||||
|
|
||||||
const sweepCommand = async () => {
|
const command = async () => {
|
||||||
// TODO: second look on defaults here
|
// TODO: second look on defaults here
|
||||||
const DEFAULT_TOLERANCE: Models['LengthUnit_type'] = 1e-7
|
const DEFAULT_TOLERANCE: Models['LengthUnit_type'] = 1e-7
|
||||||
const DEFAULT_SECTIONAL = false
|
const DEFAULT_SECTIONAL = false
|
||||||
@ -261,10 +278,11 @@ export const sweepValidator = async ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const attemptSweep = await dryRunWrapper(sweepCommand)
|
const result = await dryRunWrapper(command)
|
||||||
if (attemptSweep?.success) {
|
if (result?.success) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Unable to sweep with the provided selection'
|
const reason = parseEngineErrorMessage(result) || 'unknown'
|
||||||
|
return `Unable to sweep with the current selection. Reason: ${reason}`
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ export const FILE_EXT = '.kcl'
|
|||||||
/** Default file to open when a project is opened */
|
/** Default file to open when a project is opened */
|
||||||
export const PROJECT_ENTRYPOINT = `main${FILE_EXT}` as const
|
export const PROJECT_ENTRYPOINT = `main${FILE_EXT}` as const
|
||||||
/** Thumbnail file name */
|
/** Thumbnail file name */
|
||||||
export const PROJECT_IMAGE_NAME = `main.jpg` as const
|
export const PROJECT_IMAGE_NAME = `thumbnail.png` as const
|
||||||
/** The localStorage key for last-opened projects */
|
/** The localStorage key for last-opened projects */
|
||||||
export const FILE_PERSIST_KEY = `${PROJECT_FOLDER}-last-opened` as const
|
export const FILE_PERSIST_KEY = `${PROJECT_FOLDER}-last-opened` as const
|
||||||
/** The default name given to new kcl files in a project */
|
/** The default name given to new kcl files in a project */
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
PROJECT_ENTRYPOINT,
|
PROJECT_ENTRYPOINT,
|
||||||
PROJECT_FOLDER,
|
PROJECT_FOLDER,
|
||||||
|
PROJECT_IMAGE_NAME,
|
||||||
PROJECT_SETTINGS_FILE_NAME,
|
PROJECT_SETTINGS_FILE_NAME,
|
||||||
SETTINGS_FILE_NAME,
|
SETTINGS_FILE_NAME,
|
||||||
TELEMETRY_FILE_NAME,
|
TELEMETRY_FILE_NAME,
|
||||||
@ -625,3 +626,19 @@ export const getUser = async (
|
|||||||
}
|
}
|
||||||
return Promise.reject(new Error('unreachable'))
|
return Promise.reject(new Error('unreachable'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const writeProjectThumbnailFile = async (
|
||||||
|
dataUrl: string,
|
||||||
|
projectDirectoryPath: string
|
||||||
|
) => {
|
||||||
|
const filePath = window.electron.path.join(
|
||||||
|
projectDirectoryPath,
|
||||||
|
PROJECT_IMAGE_NAME
|
||||||
|
)
|
||||||
|
const data = atob(dataUrl.substring('data:image/png;base64,'.length))
|
||||||
|
const asArray = new Uint8Array(data.length)
|
||||||
|
for (let i = 0, len = data.length; i < len; ++i) {
|
||||||
|
asArray[i] = data.charCodeAt(i)
|
||||||
|
}
|
||||||
|
return window.electron.writeFile(filePath, asArray)
|
||||||
|
}
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
import { CommandBarOverwriteWarning } from 'components/CommandBarOverwriteWarning'
|
import { CommandBarOverwriteWarning } from 'components/CommandBarOverwriteWarning'
|
||||||
import { Command, CommandArgumentOption } from './commandTypes'
|
import { Command, CommandArgumentOption } from './commandTypes'
|
||||||
import { codeManager, kclManager } from './singletons'
|
import { kclManager } from './singletons'
|
||||||
import { isDesktop } from './isDesktop'
|
import { isDesktop } from './isDesktop'
|
||||||
import { FILE_EXT, PROJECT_SETTINGS_FILE_NAME } from './constants'
|
import { FILE_EXT } from './constants'
|
||||||
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
|
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
|
||||||
import { parseProjectSettings } from 'lang/wasm'
|
import { reportRejection } from './trap'
|
||||||
import { err, reportRejection } from './trap'
|
|
||||||
import { projectConfigurationToSettingsPayload } from './settings/settingsUtils'
|
|
||||||
import { copyFileShareLink } from './links'
|
|
||||||
import { IndexLoaderData } from './types'
|
import { IndexLoaderData } from './types'
|
||||||
|
|
||||||
interface OnSubmitProps {
|
interface OnSubmitProps {
|
||||||
@ -68,56 +65,23 @@ export function kclCommands(commandProps: KclCommandConfig): Command[] {
|
|||||||
const sampleCodeUrl = `https://raw.githubusercontent.com/KittyCAD/kcl-samples/main/${encodeURIComponent(
|
const sampleCodeUrl = `https://raw.githubusercontent.com/KittyCAD/kcl-samples/main/${encodeURIComponent(
|
||||||
projectPathPart
|
projectPathPart
|
||||||
)}/${encodeURIComponent(primaryKclFile)}`
|
)}/${encodeURIComponent(primaryKclFile)}`
|
||||||
const sampleSettingsFileUrl = `https://raw.githubusercontent.com/KittyCAD/kcl-samples/main/${encodeURIComponent(
|
|
||||||
projectPathPart
|
|
||||||
)}/${PROJECT_SETTINGS_FILE_NAME}`
|
|
||||||
|
|
||||||
Promise.allSettled([fetch(sampleCodeUrl), fetch(sampleSettingsFileUrl)])
|
fetch(sampleCodeUrl)
|
||||||
.then((results) => {
|
.then(async (codeResponse): Promise<OnSubmitProps> => {
|
||||||
const a =
|
if (!codeResponse.ok) {
|
||||||
'value' in results[0] ? results[0].value : results[0].reason
|
console.error(
|
||||||
const b =
|
'Failed to fetch sample code:',
|
||||||
'value' in results[1] ? results[1].value : results[1].reason
|
codeResponse.statusText
|
||||||
return [a, b]
|
)
|
||||||
})
|
return Promise.reject(new Error('Failed to fetch sample code'))
|
||||||
.then(
|
|
||||||
async ([
|
|
||||||
codeResponse,
|
|
||||||
settingsResponse,
|
|
||||||
]): Promise<OnSubmitProps> => {
|
|
||||||
if (!codeResponse.ok) {
|
|
||||||
console.error(
|
|
||||||
'Failed to fetch sample code:',
|
|
||||||
codeResponse.statusText
|
|
||||||
)
|
|
||||||
return Promise.reject(new Error('Failed to fetch sample code'))
|
|
||||||
}
|
|
||||||
const code = await codeResponse.text()
|
|
||||||
|
|
||||||
// It's possible that a sample doesn't have a project.toml
|
|
||||||
// associated with it.
|
|
||||||
let projectSettingsPayload: ReturnType<
|
|
||||||
typeof projectConfigurationToSettingsPayload
|
|
||||||
> = {}
|
|
||||||
if (settingsResponse.ok) {
|
|
||||||
const parsedProjectSettings = parseProjectSettings(
|
|
||||||
await settingsResponse.text()
|
|
||||||
)
|
|
||||||
if (!err(parsedProjectSettings)) {
|
|
||||||
projectSettingsPayload =
|
|
||||||
projectConfigurationToSettingsPayload(parsedProjectSettings)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
sampleName: data.sample.split('/')[0] + FILE_EXT,
|
|
||||||
code,
|
|
||||||
method: data.method,
|
|
||||||
sampleUnits:
|
|
||||||
projectSettingsPayload.modeling?.defaultUnit || 'mm',
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
const code = await codeResponse.text()
|
||||||
|
return {
|
||||||
|
sampleName: data.sample.split('/')[0] + FILE_EXT,
|
||||||
|
code,
|
||||||
|
method: data.method,
|
||||||
|
}
|
||||||
|
})
|
||||||
.then((props) => {
|
.then((props) => {
|
||||||
if (props?.code) {
|
if (props?.code) {
|
||||||
commandProps.specialPropsForSampleCommand
|
commandProps.specialPropsForSampleCommand
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
function takeScreenshotOfVideoStreamCanvas() {
|
export function takeScreenshotOfVideoStreamCanvas() {
|
||||||
const canvas = document.querySelector('[data-engine]')
|
const canvas = document.querySelector('[data-engine]')
|
||||||
const video = document.getElementById('video-stream')
|
const video = document.getElementById('video-stream')
|
||||||
if (
|
if (
|
||||||
|
@ -577,10 +577,9 @@ export function getSelectionTypeDisplayText(
|
|||||||
.map(
|
.map(
|
||||||
// Hack for showing "face" instead of "extrude-wall" in command bar text
|
// Hack for showing "face" instead of "extrude-wall" in command bar text
|
||||||
([type, count]) =>
|
([type, count]) =>
|
||||||
`${count} ${type
|
`${count} ${type.replace('wall', 'face').replace('solid2d', 'face')}${
|
||||||
.replace('wall', 'face')
|
count > 1 ? 's' : ''
|
||||||
.replace('solid2d', 'face')
|
}`
|
||||||
.replace('segment', 'face')}${count > 1 ? 's' : ''}`
|
|
||||||
)
|
)
|
||||||
.toArray()
|
.toArray()
|
||||||
.join(', ')
|
.join(', ')
|
||||||
|
@ -103,7 +103,7 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
data: { name: 'Revolve', groupId: 'modeling' },
|
data: { name: 'Revolve', groupId: 'modeling' },
|
||||||
}),
|
}),
|
||||||
icon: 'revolve',
|
icon: 'revolve',
|
||||||
status: DEV || IS_NIGHTLY_OR_DEBUG ? 'available' : 'kcl-only',
|
status: 'available',
|
||||||
title: 'Revolve',
|
title: 'Revolve',
|
||||||
hotkey: 'R',
|
hotkey: 'R',
|
||||||
description:
|
description:
|
||||||
@ -124,7 +124,7 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
data: { name: 'Sweep', groupId: 'modeling' },
|
data: { name: 'Sweep', groupId: 'modeling' },
|
||||||
}),
|
}),
|
||||||
icon: 'sweep',
|
icon: 'sweep',
|
||||||
status: DEV || IS_NIGHTLY_OR_DEBUG ? 'available' : 'kcl-only',
|
status: 'available',
|
||||||
title: 'Sweep',
|
title: 'Sweep',
|
||||||
hotkey: 'W',
|
hotkey: 'W',
|
||||||
description:
|
description:
|
||||||
|
@ -26,6 +26,7 @@ import {
|
|||||||
default_project_settings as DefaultProjectSettings,
|
default_project_settings as DefaultProjectSettings,
|
||||||
base64_decode as Base64Decode,
|
base64_decode as Base64Decode,
|
||||||
clear_scene_and_bust_cache as ClearSceneAndBustCache,
|
clear_scene_and_bust_cache as ClearSceneAndBustCache,
|
||||||
|
change_kcl_settings as ChangeKclSettings,
|
||||||
} from '../wasm-lib/pkg/wasm_lib'
|
} from '../wasm-lib/pkg/wasm_lib'
|
||||||
|
|
||||||
type ModuleType = typeof import('../wasm-lib/pkg/wasm_lib')
|
type ModuleType = typeof import('../wasm-lib/pkg/wasm_lib')
|
||||||
@ -110,3 +111,6 @@ export const clear_scene_and_bust_cache: typeof ClearSceneAndBustCache = (
|
|||||||
) => {
|
) => {
|
||||||
return getModule().clear_scene_and_bust_cache(...args)
|
return getModule().clear_scene_and_bust_cache(...args)
|
||||||
}
|
}
|
||||||
|
export const change_kcl_settings: typeof ChangeKclSettings = (...args) => {
|
||||||
|
return getModule().change_kcl_settings(...args)
|
||||||
|
}
|
||||||
|
30
src/machines/appMachine.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { ActorRefFrom, createActor, setup } from 'xstate'
|
||||||
|
import { authMachine } from './authMachine'
|
||||||
|
import { useSelector } from '@xstate/react'
|
||||||
|
import { ACTOR_IDS } from './machineConstants'
|
||||||
|
|
||||||
|
const appMachine = setup({
|
||||||
|
actors: {
|
||||||
|
[ACTOR_IDS.AUTH]: authMachine,
|
||||||
|
},
|
||||||
|
}).createMachine({
|
||||||
|
/** @xstate-layout N4IgpgJg5mDOIC5gF8A0IB2B7CdGgAoBbAQwGMALASwzAEp8QAHLWKgFyqw0YA9EAjACZ0AT0FDkU5EA */
|
||||||
|
id: 'modeling-app',
|
||||||
|
invoke: [
|
||||||
|
{
|
||||||
|
src: ACTOR_IDS.AUTH,
|
||||||
|
systemId: ACTOR_IDS.AUTH,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const appActor = createActor(appMachine).start()
|
||||||
|
|
||||||
|
export const authActor = appActor.system.get(ACTOR_IDS.AUTH) as ActorRefFrom<
|
||||||
|
typeof authMachine
|
||||||
|
>
|
||||||
|
export const useAuthState = () => useSelector(authActor, (state) => state)
|
||||||
|
export const useToken = () =>
|
||||||
|
useSelector(authActor, (state) => state.context.token)
|
||||||
|
export const useUser = () =>
|
||||||
|
useSelector(authActor, (state) => state.context.user)
|
@ -15,6 +15,8 @@ import {
|
|||||||
} from 'lib/desktop'
|
} from 'lib/desktop'
|
||||||
import { COOKIE_NAME } from 'lib/constants'
|
import { COOKIE_NAME } from 'lib/constants'
|
||||||
import { markOnce } from 'lib/performance'
|
import { markOnce } from 'lib/performance'
|
||||||
|
import { ACTOR_IDS } from './machineConstants'
|
||||||
|
import withBaseUrl from '../lib/withBaseURL'
|
||||||
|
|
||||||
const SKIP_AUTH = VITE_KC_SKIP_AUTH === 'true' && DEV
|
const SKIP_AUTH = VITE_KC_SKIP_AUTH === 'true' && DEV
|
||||||
|
|
||||||
@ -50,7 +52,7 @@ export type Events =
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const TOKEN_PERSIST_KEY = 'TOKEN_PERSIST_KEY'
|
export const TOKEN_PERSIST_KEY = 'TOKEN_PERSIST_KEY'
|
||||||
const persistedToken =
|
export const persistedToken =
|
||||||
VITE_KC_DEV_TOKEN ||
|
VITE_KC_DEV_TOKEN ||
|
||||||
getCookie(COOKIE_NAME) ||
|
getCookie(COOKIE_NAME) ||
|
||||||
localStorage?.getItem(TOKEN_PERSIST_KEY) ||
|
localStorage?.getItem(TOKEN_PERSIST_KEY) ||
|
||||||
@ -69,18 +71,17 @@ export const authMachine = setup({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
|
||||||
goToIndexPage: () => {},
|
|
||||||
goToSignInPage: () => {},
|
|
||||||
},
|
|
||||||
actors: {
|
actors: {
|
||||||
getUser: fromPromise(({ input }: { input: { token?: string } }) =>
|
getUser: fromPromise(({ input }: { input: { token?: string } }) =>
|
||||||
getUser(input)
|
getUser(input)
|
||||||
),
|
),
|
||||||
|
logout: fromPromise(async () =>
|
||||||
|
isDesktop() ? writeTokenFile('') : logout()
|
||||||
|
),
|
||||||
},
|
},
|
||||||
}).createMachine({
|
}).createMachine({
|
||||||
/** @xstate-layout N4IgpgJg5mDOIC5QEECuAXAFgOgMabFwGsBJAMwBkB7KGCEgOwGIIqGxsBLBgNyqI75CRALQAbGnRHcA2gAYAuolAAHKrE7pObZSAAeiAIwAWQ9gBspuQCYAnAGYAHPYCsx+4ccAaEAE9E1q7YcoZyxrYR1m7mcrYAvnE+aFh4BMTk1LSQjExgAE55VHnYKmIAhuhkRQC2qcLikpDSDPJKSCBqGlo67QYI9gDs5tge5o6h5vau7oY+-v3mA9jWco4u5iu21ua2YcYJSRg4Eln0zJkABFQYrbqdmtoMun2GA7YjxuPmLqvGNh5zRCfJaOcyLUzuAYuFyGcwHEDJY6NCAAeQwTEuskUd3UDx6oD6Im2wUcAzkMJ2cjBxlMgIWLmwZLWljecjJTjh8IYVAgcF0iJxXUez0QIgGxhJZIpu2ptL8AWwtje1nCW2iq1shns8MRdXSlGRjEFeKevUQjkcy3sqwGHimbg83nlCF22GMytVUWMMUc8USCKO2BOdCN7Xu3VNBKMKsVFp2hm2vu+1id83slkVrgTxhcW0pNJ1geDkDR6GNEZFCAT1kZZLk9cMLltb0WdPMjewjjC1mzOZCtk5CSAA */
|
/** @xstate-layout N4IgpgJg5mDOIC5QAoC2BDAxgCwJYDswBKAOhzEwGsBJAMwBkB7KGCa-AYgkcJIIDdGlMGWwVKAWgA2zVhIIBtAAwBdRKAAOjWLgAuuHupAAPRAGYArAEYSADgu2AnGYBMLpVYBsZz7YA0IACeiG6OJM62tmZKLgDsno5KtvEAvikBaFh4hKTkVHRMLJDsHGAATmWMZSQaUui6tFWoouLSspDy+MpqSCBaOvqGvaYIljb2Tq7uXj7+QYgALFYW4clWy1ZmVgsWsZtpGRg4BMQkMkVsnIUABIwArrrdRv16BvhGI74LJBYW7o5WKJmKILObBUZeEgJP4LTxKMwIhZmBYLA4gTLHHJnWQEKAAeQeXB4IgEQhEGOyp3OUFxBN0CFJmHqb26T16L0G72GiCsSg8PyszkBCViTiUjgC4Jcnhc4SUsQcvgsoL2VjRFJOpGptMJ5Uq1Vq9UaZWaGqx2vw+IeDPwgiZnNZqme2leQ1An1s31+-0BCJBYJCLm+lk8CRl9hRyos6qOlK17QgdI4N0UTvZLs5Hx58NsJARuys0tDSl+AYQthsgNi0TMqt2LjVaPwjAgcCMZuIzoGbyzCAknkliH7Maympa+QYCfYXddXPdixcg4QvKUdk2u2iLkcsXhCRHmKpU7nfQzPe5CAsMpIXi8MvFKM8VliS5c1jzj53W3isNFqPS6NjMcLStXQZ0zc8ohsJI-kcFxXEcR9HAWF9gTzDxbCUXxAQWEsdn3ONsQuOkwLPedl22MIzFg3YP1gl9PG+bYvGsSxlUcRJozSFIgA */
|
||||||
id: 'Auth',
|
id: ACTOR_IDS.AUTH,
|
||||||
initial: 'checkIfLoggedIn',
|
initial: 'checkIfLoggedIn',
|
||||||
context: {
|
context: {
|
||||||
token: persistedToken,
|
token: persistedToken,
|
||||||
@ -112,19 +113,30 @@ export const authMachine = setup({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
loggedIn: {
|
loggedIn: {
|
||||||
entry: ['goToIndexPage'],
|
|
||||||
on: {
|
on: {
|
||||||
'Log out': {
|
'Log out': {
|
||||||
target: 'loggedOut',
|
target: 'loggingOut',
|
||||||
actions: () => {
|
},
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
},
|
||||||
if (isDesktop()) writeTokenFile('')
|
},
|
||||||
},
|
loggingOut: {
|
||||||
|
invoke: {
|
||||||
|
src: 'logout',
|
||||||
|
onDone: 'loggedOut',
|
||||||
|
onError: {
|
||||||
|
target: 'loggedIn',
|
||||||
|
actions: [
|
||||||
|
({ event }) => {
|
||||||
|
console.error(
|
||||||
|
'Error while logging out',
|
||||||
|
'error' in event ? `: ${event.error}` : ''
|
||||||
|
)
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
loggedOut: {
|
loggedOut: {
|
||||||
entry: ['goToSignInPage'],
|
|
||||||
on: {
|
on: {
|
||||||
'Log in': {
|
'Log in': {
|
||||||
target: 'checkIfLoggedIn',
|
target: 'checkIfLoggedIn',
|
||||||
@ -235,3 +247,12 @@ async function getAndSyncStoredToken(input: {
|
|||||||
localStorage.setItem(TOKEN_PERSIST_KEY, fileToken)
|
localStorage.setItem(TOKEN_PERSIST_KEY, fileToken)
|
||||||
return fileToken
|
return fileToken
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function logout() {
|
||||||
|
localStorage.removeItem(TOKEN_PERSIST_KEY)
|
||||||
|
if (isDesktop()) return Promise.resolve(null)
|
||||||
|
return fetch(withBaseUrl('/logout'), {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@ import { getCommandArgumentKclValuesOnly } from 'lib/commandUtils'
|
|||||||
import { MachineManager } from 'components/MachineManagerProvider'
|
import { MachineManager } from 'components/MachineManagerProvider'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { useSelector } from '@xstate/react'
|
import { useSelector } from '@xstate/react'
|
||||||
|
import { authCommands } from 'lib/commandBarConfigs/authCommandConfig'
|
||||||
|
|
||||||
export type CommandBarContext = {
|
export type CommandBarContext = {
|
||||||
commands: Command[]
|
commands: Command[]
|
||||||
@ -80,6 +81,7 @@ export type CommandBarMachineEvent =
|
|||||||
export const commandBarMachine = setup({
|
export const commandBarMachine = setup({
|
||||||
types: {
|
types: {
|
||||||
context: {} as CommandBarContext,
|
context: {} as CommandBarContext,
|
||||||
|
input: {} as { commands: Command[] },
|
||||||
events: {} as CommandBarMachineEvent,
|
events: {} as CommandBarMachineEvent,
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
@ -409,8 +411,8 @@ export const commandBarMachine = setup({
|
|||||||
},
|
},
|
||||||
}).createMachine({
|
}).createMachine({
|
||||||
/** @xstate-layout N4IgpgJg5mDOIC5QGED2BbdBDAdhABAEJYBOAxMgDaqxgDaADALqKgAONAlgC6eo6sQAD0QBaAIwB2AHTiAHAE45AZjkAmdcoaSArCoA0IAJ6JxDOdJ2SF4gCySHaqZIa2Avm8NpMuAsXJUYKSMLEggHLA8fAJhIgiiOgBssokKTpJqiXK2OsqJaoYm8eJqytKJ9hqq+eJW2h5eGNh4RKRkAGKcLb74tJRgAMbc+ANNviGCEVH8gnEKubK5ympVrrlyhabm0rZ5CtYM4hVq83ININ7NfqTSVDSQZADybGA4E2FTvDOxiGoMDNJMjo9H9lLYGDpKpsEModOJpA5XOIwakwcidOdLj1-LdqLQIGQAIIQAijHx4WDvdhcL4xUBxURqSTw3LzfYKZSSOQZWzQ5S1crMuTAiElRKuTFjFo4u74sgAJTA6FQADcwCMpRBKcxJjTorMxEzkhouftuXCGGpIdCpADmZJxfzJLY5Cp5pLydcSLj7gSqeE9d96Yh+TppKoXfkGKdlHloUyFIDUvMXBzbFl3J4LprWt6AMpgfpDLpQDWesgFovDMlXf2ffU-BBZZKuczWWylRRwvlM6Q2LIMZQ2QdHZQeq65245vqDbgPOuBunCEP88MqPQchwVNLKaHptTSYUuRI6f4npyJcfYm5Ylozobz8ShamRWkGhBmeTSQeJX+JXQ6H88jQnCMiugoELCpypQKJeWa3l6U6er0hazvOajPgGr4NsGH6WhYsHOkycglLCEJ7iclgaIosKSLGGgKFe0o3AA4lg3AABZgCQJb4KQUAAK7oK83CwBQHG4DAIwCSQJAiXxJCCcJODcAu2FBsuH55GGJ7irYHYqHpGzGKYWiWB2nK2Kcv6wWO8E5jibGcdxvH8UJIliQAInAqFDGWtY6h8i7vlkZREbYZg6JuwEmQgfxhnkHbiHYJ7yHYTGIU5XE8TgpZucponSISADuWBRLl+BdGwAncBWAkAEboDwClKSJanTEucS1Bk36DicDCpOYLoKHu-x9tGuhgoozKpBlk5ZS5FX5R50gAGpYJQnAQOxJZkBA-BgNIXQqqgADWh0qhtW3sWAeYlv0hKKe5KntW+YRFGi5RyOKDrZEaUW8pppTguGuwDRFEO-nNjnsdlrlPQVsBrVd228LlZDcSQqDemwlDsQAZtj6DSJdm2o7d91gI9rUvYFL4dYIH0RV9P0Zv9CiA3EwMAmCWgVHYKVwY0yE4oqKqcGAxV1Y1zU1uMdNYQzjbMpYcgQlFxFq66u6xXRALbkyg7-Bz0bQzcYsS1LxIEMttOYfWGldYcCWwQmNiRrU0Jq2GRxJCbHZqC6ahm96FuSwqSqquqtuqQrDudVs4iJhUyZtn9qjQokYKHjk6hSLsZhciH0hh1LACiEDNTHr04ZpJT6bIEXxcKp50YDRT-mGrrJbUgFDp3xfIFxAynbx1PPaJe0HUdOAnedJMozd4+IzXjuIJCLIQqUWjJS4MVFBByS7BzyJq38WTB-ZIs3sPo8VcvHlTzgh3HWdF2L3OD8qZST66upCdxUTLBJOUUHB6GFPpSQXt1CWEGpaOiv4EyD1vmPBGj9MbY2kLjAmRMF5kyXmg7+q8AFJwbjkXQEVsj5FyO3RAiQjjfi5CReYPck7iA8FmHAqAIBwEEAhXMf8la4VEMsWwgJuTTXNGYK0tC4rwkDvsAaSQ-qlAhIPPEkBBFvWEVycoFQhywUHGaHWH04Q7FUNBdc+x2FXwnDiSss5eJyzwFo2ucQIplBWHrcEVhuoFFirCeEuxsj8mWEOFYmZhZ2JvNOXyc4ICuLXggaxCJwG70AiUHQfIzHBKHGYDMJFhTFwWjlPKhDRKJJIRFGQcIFBsiOIHJw8ZIQ7CUGrY+9g9AnmKbDRaZSaaFRKmVNGpYqo1Uqe+FKYZan1PyElbJYjrB6C5CUJQ9DL5ROvN6Ep8MBlI3WvgkZEzGzyAbnkZK-wjan38UzeEzZtBswdADYupdjm4XoTIOwuwHB5HyGkYyRRdBBLosCIE6ZvpnFsVs24KD77lPgEFf+kzxRH32EyFYlpOzQjAZYV2ehTkfI4W4IAA */
|
/** @xstate-layout N4IgpgJg5mDOIC5QGED2BbdBDAdhABAEJYBOAxMgDaqxgDaADALqKgAONAlgC6eo6sQAD0QBaAIwB2AHTiAHAE45AZjkAmdcoaSArCoA0IAJ6JxDOdJ2SF4gCySHaqZIa2Avm8NpMuAsXJUYKSMLEggHLA8fAJhIgiiOgBssokKTpJqiXK2OsqJaoYm8eJqytKJ9hqq+eJW2h5eGNh4RKRkAGKcLb74tJRgAMbc+ANNviGCEVH8gnEKubK5ympVrrlyhabm0rZ5CtYM4hVq83ININ7NfqTSVDSQZADybGA4E2FTvDOxiGoMDNJMjo9H9lLYGDpKpsEModOJpA5XOIwakwcidOdLj1-LdqLQIGQAIIQAijHx4WDvdhcL4xUBxURqSTw3LzfYKZSSOQZWzQ5S1crMuTAiElRKuTFjFo4u74sgAJTA6FQADcwCMpRBKcxJjTorMxEzkhouftuXCGGpIdCpADmZJxfzJLY5Cp5pLydcSLj7gSqeE9d96Yh+TppKoXfkGKdlHloUyFIDUvMXBzbFl3J4LprWt6AMpgfpDLpQDWesgFovDMlXf2ffU-BBZZKuczWWylRRwvlM6Q2LIMZQ2QdHZQeq65245vqDbgPOuBunCEP88MqPQchwVNLKaHptTSYUuRI6f4npyJcfYm5Ylozobz8ShamRWkGhBmeTSQeJX+JXQ6H88jQnCMiugoELCpypQKJeWa3l6U6er0hazvOajPgGr4NsGH6WhYsHOkycglLCEJ7iclgaIosKSLGGgKFe0o3AA4lg3AABZgCQJb4KQUAAK7oK83CwBQHG4DAIwCSQJAiXxJCCcJODcAu2FBsuH55GGJ7irYHYqHpGzGKYWiWB2nK2Kcv6wWO8E5jibGcdxvH8UJIliQAInAqFDGWtY6h8i7vlkZREbYZg6JuwEmQgfxhnkHbiHYJ7yHYTGIU5XE8TgpZucponSISADuWBRLl+BdGwAncBWAkAEboDwClKSJanTEucS1Bk36DicDCpOYLoKHu-x9tGuhgoozKpBlk5ZS5FX5R50gAGpYJQnAQOxJZkBA-BgNIXQqqgADWh0qhtW3sWAeYlv0hKKe5KntW+YRFGi5RyOKDrZEaUW8pppTguGuwDRFEO-nNjnsdlrlPQVsBrVd228LlZDcSQqDemwlDsQAZtj6DSJdm2o7d91gI9rUvYFL4dYIH0RV9P0Zv9CiA3EwMAmCWgVHYKVwY0yE4oqKqcGAxV1Y1zU1uMdNYQzjbMpYcgQlFxFq66u6xXRALbkyg7-Bz0bQzcYsS1LxIEMttOYfWGldYcCWwQmNiRrU0Jq2GRxJCbHZqC6ahm96FuSwqSqquqtuqQrDudVs4iJhUyZtn9qjQokYKHjk6hSLsZhciH0hh1LACiEDNTHr04ZpJT6bIEXxcKp50YDRT-mGrrJbUgFDp3xfIFxAynbx1PPaJe0HUdOAnedJMozd4+IzXjuIJCLIQqUWjJS4MVFBByS7BzyJq38WTB-ZIs3sPo8VcvHlTzgh3HWdF2L3OD8qZST66upCdxUTLBJOUUHB6GFPpSQXt1CWEGpaOiv4EyD1vmPBGj9MbY2kLjAmRMF5kyXmg7+q8AFJwbjkXQEVsj5FyO3RAiQjjfi5CReYPck7iA8FmHAqAIBwEEAhXMf8la4VEMsWwgJuTTXNGYK0tC4rwkDvsAaSQ-qlAhIPPEkBBFvWEVycoFQhywUHGaHWH04Q7FUNBdc+x2FXwnDiSss5eJyzwFo2ucQIplBWHrcEVhuoFFirCeEuxsj8mWEOFYmZhZ2JvNOXyc4ICuLXggaxCJwG70AiUHQfIzHBKHGYDMJFhTFwWjlPKhDRKJJIRFGQcIFBsiOIHJw8ZIQ7CUGrY+9g9AnmKbDRaZSaaFRKmVNGpYqo1Uqe+FKYZan1PyElbJYjrB6C5CUJQ9DL5ROvN6Ep8MBlI3WvgkZEzGzyAbnkZK-wjan38UzeEzZtBswdADYupdjm4XoTIOwuwHB5HyGkYyRRdBBLosCIE6ZvpnFsVs24KD77lPgEFf+kzxRH32EyFYlpOzQjAZYV2ehTkfI4W4IAA */
|
||||||
context: {
|
context: ({ input }) => ({
|
||||||
commands: [],
|
commands: input.commands || [],
|
||||||
selectedCommand: undefined,
|
selectedCommand: undefined,
|
||||||
currentArgument: undefined,
|
currentArgument: undefined,
|
||||||
selectionRanges: {
|
selectionRanges: {
|
||||||
@ -425,7 +427,7 @@ export const commandBarMachine = setup({
|
|||||||
setCurrentMachine: () => {},
|
setCurrentMachine: () => {},
|
||||||
noMachinesReason: () => undefined,
|
noMachinesReason: () => undefined,
|
||||||
},
|
},
|
||||||
},
|
}),
|
||||||
id: 'Command Bar',
|
id: 'Command Bar',
|
||||||
initial: 'Closed',
|
initial: 'Closed',
|
||||||
states: {
|
states: {
|
||||||
@ -631,7 +633,11 @@ function sortCommands(a: Command, b: Command) {
|
|||||||
return a.name.localeCompare(b.name)
|
return a.name.localeCompare(b.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const commandBarActor = createActor(commandBarMachine).start()
|
export const commandBarActor = createActor(commandBarMachine, {
|
||||||
|
input: {
|
||||||
|
commands: [...authCommands],
|
||||||
|
},
|
||||||
|
}).start()
|
||||||
|
|
||||||
/** Basic state snapshot selector */
|
/** Basic state snapshot selector */
|
||||||
const cmdBarStateSelector = (state: SnapshotFrom<typeof commandBarActor>) =>
|
const cmdBarStateSelector = (state: SnapshotFrom<typeof commandBarActor>) =>
|
||||||
|
3
src/machines/machineConstants.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const ACTOR_IDS = {
|
||||||
|
AUTH: 'auth',
|
||||||
|
}
|
@ -1452,13 +1452,14 @@ export const modelingMachine = setup({
|
|||||||
unknown,
|
unknown,
|
||||||
ModelingCommandSchema['Extrude'] | undefined
|
ModelingCommandSchema['Extrude'] | undefined
|
||||||
>(async ({ input }) => {
|
>(async ({ input }) => {
|
||||||
if (!input) return new Error('No input provided')
|
if (!input) return Promise.reject('No input provided')
|
||||||
const { selection, distance } = input
|
const { selection, distance } = input
|
||||||
let ast = structuredClone(kclManager.ast)
|
let ast = structuredClone(kclManager.ast)
|
||||||
let extrudeName: string | undefined = undefined
|
|
||||||
|
|
||||||
const sourceRange = selection.graphSelections[0]?.codeRef.range
|
const pathToNode = getNodePathFromSourceRange(
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
ast,
|
||||||
|
selection.graphSelections[0]?.codeRef.range
|
||||||
|
)
|
||||||
// Add an extrude statement to the AST
|
// Add an extrude statement to the AST
|
||||||
const extrudeSketchRes = extrudeSketch(
|
const extrudeSketchRes = extrudeSketch(
|
||||||
ast,
|
ast,
|
||||||
@ -1468,7 +1469,7 @@ export const modelingMachine = setup({
|
|||||||
? distance.variableIdentifierAst
|
? distance.variableIdentifierAst
|
||||||
: distance.valueAst
|
: distance.valueAst
|
||||||
)
|
)
|
||||||
if (err(extrudeSketchRes)) return extrudeSketchRes
|
if (err(extrudeSketchRes)) return Promise.reject(extrudeSketchRes)
|
||||||
const { modifiedAst, pathToExtrudeArg } = extrudeSketchRes
|
const { modifiedAst, pathToExtrudeArg } = extrudeSketchRes
|
||||||
|
|
||||||
// Insert the distance variable if the user has provided a variable name
|
// Insert the distance variable if the user has provided a variable name
|
||||||
@ -2559,7 +2560,7 @@ export const modelingMachine = setup({
|
|||||||
|
|
||||||
'Delete segment': {
|
'Delete segment': {
|
||||||
reenter: false,
|
reenter: false,
|
||||||
actions: ['Delete segment', 'Set sketchDetails'],
|
actions: ['Delete segment', 'Set sketchDetails', 'reset selections'],
|
||||||
},
|
},
|
||||||
'code edit during sketch': '.clean slate',
|
'code edit during sketch': '.clean slate',
|
||||||
},
|
},
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
|
import { OnboardingButtons, useDismiss, useNextClick } from '.'
|
||||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useUser } from 'machines/appMachine'
|
||||||
|
|
||||||
export default function UserMenu() {
|
export default function UserMenu() {
|
||||||
const { auth } = useSettingsAuthContext()
|
const user = useUser()
|
||||||
const dismiss = useDismiss()
|
const dismiss = useDismiss()
|
||||||
const next = useNextClick(onboardingPaths.PROJECT_MENU)
|
const next = useNextClick(onboardingPaths.PROJECT_MENU)
|
||||||
const [avatarErrored, setAvatarErrored] = useState(false)
|
const [avatarErrored, setAvatarErrored] = useState(false)
|
||||||
|
|
||||||
const user = auth?.context?.user
|
|
||||||
const errorOrNoImage = !user?.image || avatarErrored
|
const errorOrNoImage = !user?.image || avatarErrored
|
||||||
const buttonDescription = errorOrNoImage ? 'the menu button' : 'your avatar'
|
const buttonDescription = errorOrNoImage ? 'the menu button' : 'your avatar'
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
|||||||
import { toSync } from 'lib/utils'
|
import { toSync } from 'lib/utils'
|
||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
|
import { authActor } from 'machines/appMachine'
|
||||||
|
|
||||||
const subtleBorder =
|
const subtleBorder =
|
||||||
'border border-solid border-chalkboard-30 dark:border-chalkboard-80'
|
'border border-solid border-chalkboard-30 dark:border-chalkboard-80'
|
||||||
@ -22,7 +23,6 @@ const cardArea = `${subtleBorder} rounded-lg px-6 py-3 text-chalkboard-70 dark:t
|
|||||||
const SignIn = () => {
|
const SignIn = () => {
|
||||||
const [userCode, setUserCode] = useState('')
|
const [userCode, setUserCode] = useState('')
|
||||||
const {
|
const {
|
||||||
auth: { send },
|
|
||||||
settings: {
|
settings: {
|
||||||
state: {
|
state: {
|
||||||
context: {
|
context: {
|
||||||
@ -70,7 +70,7 @@ const SignIn = () => {
|
|||||||
toast.error('Error while trying to log in')
|
toast.error('Error while trying to log in')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
send({ type: 'Log in', token })
|
authActor.send({ type: 'Log in', token })
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
2
src/wasm-lib/Cargo.lock
generated
@ -1710,7 +1710,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
version = "0.2.32"
|
version = "0.2.33"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"approx 0.5.1",
|
"approx 0.5.1",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
description = "KittyCAD Language implementation and tools"
|
description = "KittyCAD Language implementation and tools"
|
||||||
version = "0.2.32"
|
version = "0.2.33"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
@ -13,9 +13,7 @@ use tower_lsp::lsp_types::{
|
|||||||
MarkupKind, ParameterInformation, ParameterLabel, SignatureHelp, SignatureInformation,
|
MarkupKind, ParameterInformation, ParameterLabel, SignatureHelp, SignatureInformation,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::execution::Sketch;
|
use crate::{execution::Sketch, std::Primitive};
|
||||||
|
|
||||||
use crate::std::Primitive;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, JsonSchema, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, JsonSchema, ts_rs::TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
|
@ -7,9 +7,9 @@ use crate::{
|
|||||||
KclError, SourceRange,
|
KclError, SourceRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(super) const SETTINGS: &str = "settings";
|
pub(crate) const SETTINGS: &str = "settings";
|
||||||
pub(super) const SETTINGS_UNIT_LENGTH: &str = "defaultLengthUnit";
|
pub(crate) const SETTINGS_UNIT_LENGTH: &str = "defaultLengthUnit";
|
||||||
pub(super) const SETTINGS_UNIT_ANGLE: &str = "defaultAngleUnit";
|
pub(crate) const SETTINGS_UNIT_ANGLE: &str = "defaultAngleUnit";
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
pub(super) enum AnnotationScope {
|
pub(super) enum AnnotationScope {
|
||||||
|
@ -2,6 +2,7 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use async_recursion::async_recursion;
|
use async_recursion::async_recursion;
|
||||||
|
|
||||||
|
use super::cad_op::{OpArg, Operation};
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{
|
execution::{
|
||||||
@ -19,8 +20,6 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::cad_op::{OpArg, Operation};
|
|
||||||
|
|
||||||
impl BinaryPart {
|
impl BinaryPart {
|
||||||
#[async_recursion]
|
#[async_recursion]
|
||||||
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||||
|
@ -15,6 +15,7 @@ use kittycad_modeling_cmds as kcmc;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use super::ExecutorContext;
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{ExecState, ImportedGeometry},
|
execution::{ExecState, ImportedGeometry},
|
||||||
@ -22,8 +23,6 @@ use crate::{
|
|||||||
source_range::SourceRange,
|
source_range::SourceRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::ExecutorContext;
|
|
||||||
|
|
||||||
// Zoo co-ordinate system.
|
// Zoo co-ordinate system.
|
||||||
//
|
//
|
||||||
// * Forward: -Y
|
// * Forward: -Y
|
||||||
|
@ -581,10 +581,11 @@ impl KclValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO called UnitLen so as not to clash with UnitLength in settings)
|
// TODO called UnitLen so as not to clash with UnitLength in settings)
|
||||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
|
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum UnitLen {
|
pub enum UnitLen {
|
||||||
|
#[default]
|
||||||
Mm,
|
Mm,
|
||||||
Cm,
|
Cm,
|
||||||
M,
|
M,
|
||||||
@ -593,6 +594,19 @@ pub enum UnitLen {
|
|||||||
Yards,
|
Yards,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for UnitLen {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
UnitLen::Mm => write!(f, "mm"),
|
||||||
|
UnitLen::Cm => write!(f, "cm"),
|
||||||
|
UnitLen::M => write!(f, "m"),
|
||||||
|
UnitLen::Inches => write!(f, "in"),
|
||||||
|
UnitLen::Feet => write!(f, "ft"),
|
||||||
|
UnitLen::Yards => write!(f, "yd"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<NumericSuffix> for UnitLen {
|
impl TryFrom<NumericSuffix> for UnitLen {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
@ -644,6 +658,15 @@ pub enum UnitAngle {
|
|||||||
Radians,
|
Radians,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for UnitAngle {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
UnitAngle::Degrees => write!(f, "deg"),
|
||||||
|
UnitAngle::Radians => write!(f, "rad"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<NumericSuffix> for UnitAngle {
|
impl TryFrom<NumericSuffix> for UnitAngle {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
|
@ -13,8 +13,7 @@ use kcmc::{
|
|||||||
websocket::{ModelingSessionData, OkWebSocketResponseData},
|
websocket::{ModelingSessionData, OkWebSocketResponseData},
|
||||||
ImageFormat, ModelingCmd,
|
ImageFormat, ModelingCmd,
|
||||||
};
|
};
|
||||||
use kittycad_modeling_cmds::length_unit::LengthUnit;
|
use kittycad_modeling_cmds::{self as kcmc, length_unit::LengthUnit, websocket::WebSocketResponse};
|
||||||
use kittycad_modeling_cmds::{self as kcmc, websocket::WebSocketResponse};
|
|
||||||
use parse_display::{Display, FromStr};
|
use parse_display::{Display, FromStr};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -27,14 +26,18 @@ pub(crate) use import::{import_foreign, send_to_engine as send_import_to_engine,
|
|||||||
pub use kcl_value::{KclObjectFields, KclValue, UnitAngle, UnitLen};
|
pub use kcl_value::{KclObjectFields, KclValue, UnitAngle, UnitLen};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
mod annotations;
|
pub(crate) mod annotations;
|
||||||
mod artifact;
|
mod artifact;
|
||||||
pub(crate) mod cache;
|
pub(crate) mod cache;
|
||||||
mod cad_op;
|
mod cad_op;
|
||||||
mod exec_ast;
|
mod exec_ast;
|
||||||
mod function_param;
|
mod function_param;
|
||||||
mod import;
|
mod import;
|
||||||
mod kcl_value;
|
pub(crate) mod kcl_value;
|
||||||
|
|
||||||
|
// Re-exports.
|
||||||
|
pub use artifact::{Artifact, ArtifactCommand, ArtifactGraph, ArtifactId};
|
||||||
|
pub use cad_op::Operation;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
engine::{EngineManager, ExecutionKind},
|
engine::{EngineManager, ExecutionKind},
|
||||||
@ -52,10 +55,6 @@ use crate::{
|
|||||||
ExecError, KclErrorWithOutputs, Program,
|
ExecError, KclErrorWithOutputs, Program,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Re-exports.
|
|
||||||
pub use artifact::{Artifact, ArtifactCommand, ArtifactGraph, ArtifactId};
|
|
||||||
pub use cad_op::Operation;
|
|
||||||
|
|
||||||
/// State for executing a program.
|
/// State for executing a program.
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
@ -247,7 +246,7 @@ impl ModuleState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct MetaSettings {
|
pub struct MetaSettings {
|
||||||
@ -256,7 +255,11 @@ pub struct MetaSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MetaSettings {
|
impl MetaSettings {
|
||||||
fn update_from_annotation(&mut self, annotation: &NonCodeValue, source_range: SourceRange) -> Result<(), KclError> {
|
pub fn update_from_annotation(
|
||||||
|
&mut self,
|
||||||
|
annotation: &NonCodeValue,
|
||||||
|
source_range: SourceRange,
|
||||||
|
) -> Result<(), KclError> {
|
||||||
let properties = annotations::expect_properties(annotations::SETTINGS, annotation, source_range)?;
|
let properties = annotations::expect_properties(annotations::SETTINGS, annotation, source_range)?;
|
||||||
|
|
||||||
for p in properties {
|
for p in properties {
|
||||||
|
@ -84,7 +84,7 @@ pub use engine::{EngineManager, ExecutionKind};
|
|||||||
pub use errors::{CompilationError, ConnectionError, ExecError, KclError, KclErrorWithOutputs};
|
pub use errors::{CompilationError, ConnectionError, ExecError, KclError, KclErrorWithOutputs};
|
||||||
pub use execution::{
|
pub use execution::{
|
||||||
cache::{CacheInformation, OldAstState},
|
cache::{CacheInformation, OldAstState},
|
||||||
ExecState, ExecutorContext, ExecutorSettings, Point2d,
|
ExecState, ExecutorContext, ExecutorSettings, MetaSettings, Point2d,
|
||||||
};
|
};
|
||||||
pub use lsp::{
|
pub use lsp::{
|
||||||
copilot::Backend as CopilotLspBackend,
|
copilot::Backend as CopilotLspBackend,
|
||||||
@ -121,8 +121,7 @@ pub mod std_utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod pretty {
|
pub mod pretty {
|
||||||
pub use crate::parsing::token::NumericSuffix;
|
pub use crate::{parsing::token::NumericSuffix, unparser::format_number};
|
||||||
pub use crate::unparser::format_number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -162,6 +161,18 @@ impl Program {
|
|||||||
self.ast.compute_digest()
|
self.ast.compute_digest()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the meta settings for the kcl file from the annotations.
|
||||||
|
pub fn get_meta_settings(&self) -> Result<Option<crate::MetaSettings>, KclError> {
|
||||||
|
self.ast.get_meta_settings()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Change the meta settings for the kcl file.
|
||||||
|
pub fn change_meta_settings(&mut self, settings: crate::MetaSettings) -> Result<Self, KclError> {
|
||||||
|
Ok(Self {
|
||||||
|
ast: self.ast.change_meta_settings(settings)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn lint_all(&self) -> Result<Vec<lint::Discovered>, anyhow::Error> {
|
pub fn lint_all(&self) -> Result<Vec<lint::Discovered>, anyhow::Error> {
|
||||||
self.ast.lint_all()
|
self.ast.lint_all()
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ use tower_lsp::lsp_types::{
|
|||||||
CompletionItem, CompletionItemKind, DocumentSymbol, FoldingRange, FoldingRangeKind, Range as LspRange, SymbolKind,
|
CompletionItem, CompletionItemKind, DocumentSymbol, FoldingRange, FoldingRangeKind, Range as LspRange, SymbolKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::digest::Digest;
|
|
||||||
pub use crate::parsing::ast::types::{
|
pub use crate::parsing::ast::types::{
|
||||||
condition::{ElseIf, IfExpression},
|
condition::{ElseIf, IfExpression},
|
||||||
literal_value::LiteralValue,
|
literal_value::LiteralValue,
|
||||||
@ -26,7 +25,8 @@ pub use crate::parsing::ast::types::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
docs::StdLibFn,
|
docs::StdLibFn,
|
||||||
errors::KclError,
|
errors::KclError,
|
||||||
execution::{KclValue, Metadata, TagIdentifier},
|
execution::{annotations, KclValue, Metadata, TagIdentifier},
|
||||||
|
parsing::ast::digest::Digest,
|
||||||
parsing::PIPE_OPERATOR,
|
parsing::PIPE_OPERATOR,
|
||||||
source_range::{ModuleId, SourceRange},
|
source_range::{ModuleId, SourceRange},
|
||||||
};
|
};
|
||||||
@ -254,6 +254,52 @@ impl Node<Program> {
|
|||||||
}
|
}
|
||||||
Ok(findings)
|
Ok(findings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the annotations for the meta settings from the kcl file.
|
||||||
|
pub fn get_meta_settings(&self) -> Result<Option<crate::execution::MetaSettings>, KclError> {
|
||||||
|
let annotations = self
|
||||||
|
.non_code_meta
|
||||||
|
.start_nodes
|
||||||
|
.iter()
|
||||||
|
.filter_map(|n| n.annotation().map(|result| (result, n.as_source_range())));
|
||||||
|
for (annotation, source_range) in annotations {
|
||||||
|
if annotation.annotation_name() == Some(annotations::SETTINGS) {
|
||||||
|
let mut meta_settings = crate::execution::MetaSettings::default();
|
||||||
|
meta_settings.update_from_annotation(annotation, source_range)?;
|
||||||
|
return Ok(Some(meta_settings));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn change_meta_settings(&mut self, settings: crate::execution::MetaSettings) -> Result<Self, KclError> {
|
||||||
|
let mut new_program = self.clone();
|
||||||
|
let mut found = false;
|
||||||
|
for node in &mut new_program.non_code_meta.start_nodes {
|
||||||
|
if let Some(annotation) = node.annotation() {
|
||||||
|
if annotation.annotation_name() == Some(annotations::SETTINGS) {
|
||||||
|
let annotation = NonCodeValue::new_from_meta_settings(&settings);
|
||||||
|
*node = Node::no_src(NonCodeNode {
|
||||||
|
value: annotation,
|
||||||
|
digest: None,
|
||||||
|
});
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
let annotation = NonCodeValue::new_from_meta_settings(&settings);
|
||||||
|
new_program.non_code_meta.start_nodes.push(Node::no_src(NonCodeNode {
|
||||||
|
value: annotation,
|
||||||
|
digest: None,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(new_program)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Program {
|
impl Program {
|
||||||
@ -1106,6 +1152,24 @@ impl NonCodeValue {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_from_meta_settings(settings: &crate::execution::MetaSettings) -> NonCodeValue {
|
||||||
|
let mut properties: Vec<Node<ObjectProperty>> = vec![ObjectProperty::new(
|
||||||
|
Identifier::new(annotations::SETTINGS_UNIT_LENGTH),
|
||||||
|
Expr::Identifier(Box::new(Identifier::new(&settings.default_length_units.to_string()))),
|
||||||
|
)];
|
||||||
|
|
||||||
|
if settings.default_angle_units != Default::default() {
|
||||||
|
properties.push(ObjectProperty::new(
|
||||||
|
Identifier::new(annotations::SETTINGS_UNIT_ANGLE),
|
||||||
|
Expr::Identifier(Box::new(Identifier::new(&settings.default_angle_units.to_string()))),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
NonCodeValue::Annotation {
|
||||||
|
name: Identifier::new(annotations::SETTINGS),
|
||||||
|
properties: Some(properties),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Default, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
@ -2365,6 +2429,14 @@ impl Node<ObjectProperty> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectProperty {
|
impl ObjectProperty {
|
||||||
|
pub fn new(key: Node<Identifier>, value: Expr) -> Node<Self> {
|
||||||
|
Node::no_src(Self {
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
digest: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a hover value that includes the given character position.
|
/// Returns a hover value that includes the given character position.
|
||||||
pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> {
|
pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> {
|
||||||
let value_source_range: SourceRange = self.value.clone().into();
|
let value_source_range: SourceRange = self.value.clone().into();
|
||||||
@ -3784,4 +3856,98 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
|
|
||||||
assert_eq!(l.raw, "false");
|
assert_eq!(l.raw, "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_parse_get_meta_settings_inch() {
|
||||||
|
let some_program_string = r#"@settings(defaultLengthUnit = inch)
|
||||||
|
|
||||||
|
startSketchOn('XY')"#;
|
||||||
|
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||||
|
let result = program.get_meta_settings().unwrap();
|
||||||
|
assert!(result.is_some());
|
||||||
|
let meta_settings = result.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
meta_settings.default_length_units,
|
||||||
|
crate::execution::kcl_value::UnitLen::Inches
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_parse_get_meta_settings_inch_to_mm() {
|
||||||
|
let some_program_string = r#"@settings(defaultLengthUnit = inch)
|
||||||
|
|
||||||
|
startSketchOn('XY')"#;
|
||||||
|
let mut program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||||
|
let result = program.get_meta_settings().unwrap();
|
||||||
|
assert!(result.is_some());
|
||||||
|
let meta_settings = result.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
meta_settings.default_length_units,
|
||||||
|
crate::execution::kcl_value::UnitLen::Inches
|
||||||
|
);
|
||||||
|
|
||||||
|
// Edit the ast.
|
||||||
|
let new_program = program
|
||||||
|
.change_meta_settings(crate::execution::MetaSettings {
|
||||||
|
default_length_units: crate::execution::kcl_value::UnitLen::Mm,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let result = new_program.get_meta_settings().unwrap();
|
||||||
|
assert!(result.is_some());
|
||||||
|
let meta_settings = result.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
meta_settings.default_length_units,
|
||||||
|
crate::execution::kcl_value::UnitLen::Mm
|
||||||
|
);
|
||||||
|
|
||||||
|
let formatted = new_program.recast(&Default::default(), 0);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
formatted,
|
||||||
|
r#"@settings(defaultLengthUnit = mm)
|
||||||
|
|
||||||
|
|
||||||
|
startSketchOn('XY')
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_parse_get_meta_settings_nothing_to_mm() {
|
||||||
|
let some_program_string = r#"startSketchOn('XY')"#;
|
||||||
|
let mut program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||||
|
let result = program.get_meta_settings().unwrap();
|
||||||
|
assert!(result.is_none());
|
||||||
|
|
||||||
|
// Edit the ast.
|
||||||
|
let new_program = program
|
||||||
|
.change_meta_settings(crate::execution::MetaSettings {
|
||||||
|
default_length_units: crate::execution::kcl_value::UnitLen::Mm,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let result = new_program.get_meta_settings().unwrap();
|
||||||
|
assert!(result.is_some());
|
||||||
|
let meta_settings = result.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
meta_settings.default_length_units,
|
||||||
|
crate::execution::kcl_value::UnitLen::Mm
|
||||||
|
);
|
||||||
|
|
||||||
|
let formatted = new_program.recast(&Default::default(), 0);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
formatted,
|
||||||
|
r#"@settings(defaultLengthUnit = mm)
|
||||||
|
startSketchOn('XY')
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ use insta::rounded_redaction;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::KclError,
|
errors::KclError,
|
||||||
|
exec::ArtifactCommand,
|
||||||
|
execution::{ArtifactGraph, Operation},
|
||||||
parsing::ast::types::{Node, Program},
|
parsing::ast::types::{Node, Program},
|
||||||
source_range::ModuleId,
|
source_range::ModuleId,
|
||||||
};
|
};
|
||||||
@ -109,36 +111,12 @@ async fn execute(test_name: &str, render_to_png: bool) {
|
|||||||
".environments[].**[].z[]" => rounded_redaction(4),
|
".environments[].**[].z[]" => rounded_redaction(4),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
assert_snapshot(test_name, "Operations executed", || {
|
assert_common_snapshots(
|
||||||
insta::assert_json_snapshot!("ops", exec_state.mod_local.operations);
|
test_name,
|
||||||
});
|
exec_state.mod_local.operations,
|
||||||
assert_snapshot(test_name, "Artifact commands", || {
|
exec_state.global.artifact_commands,
|
||||||
insta::assert_json_snapshot!("artifact_commands", exec_state.global.artifact_commands, {
|
exec_state.global.artifact_graph,
|
||||||
"[].command.segment.*.x" => rounded_redaction(4),
|
);
|
||||||
"[].command.segment.*.y" => rounded_redaction(4),
|
|
||||||
"[].command.segment.*.z" => rounded_redaction(4),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
assert_snapshot(test_name, "Artifact graph flowchart", || {
|
|
||||||
let flowchart = exec_state
|
|
||||||
.global
|
|
||||||
.artifact_graph
|
|
||||||
.to_mermaid_flowchart()
|
|
||||||
.unwrap_or_else(|e| format!("Failed to convert artifact graph to flowchart: {e}"));
|
|
||||||
// Change the snapshot suffix so that it is rendered as a
|
|
||||||
// Markdown file in GitHub.
|
|
||||||
insta::assert_binary_snapshot!("artifact_graph_flowchart.md", flowchart.as_bytes().to_owned());
|
|
||||||
});
|
|
||||||
assert_snapshot(test_name, "Artifact graph mind map", || {
|
|
||||||
let mind_map = exec_state
|
|
||||||
.global
|
|
||||||
.artifact_graph
|
|
||||||
.to_mermaid_mind_map()
|
|
||||||
.unwrap_or_else(|e| format!("Failed to convert artifact graph to mind map: {e}"));
|
|
||||||
// Change the snapshot suffix so that it is rendered as a
|
|
||||||
// Markdown file in GitHub.
|
|
||||||
insta::assert_binary_snapshot!("artifact_graph_mind_map.md", mind_map.as_bytes().to_owned());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let ok_path_str = format!("tests/{test_name}/program_memory.snap");
|
let ok_path_str = format!("tests/{test_name}/program_memory.snap");
|
||||||
@ -165,17 +143,12 @@ async fn execute(test_name: &str, render_to_png: bool) {
|
|||||||
insta::assert_snapshot!("execution_error", report);
|
insta::assert_snapshot!("execution_error", report);
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_snapshot(test_name, "Operations executed", || {
|
assert_common_snapshots(
|
||||||
insta::assert_json_snapshot!("ops", error.operations);
|
test_name,
|
||||||
});
|
error.operations,
|
||||||
|
error.artifact_commands,
|
||||||
assert_snapshot(test_name, "Artifact commands", || {
|
error.artifact_graph,
|
||||||
insta::assert_json_snapshot!("artifact_commands", error.artifact_commands, {
|
);
|
||||||
"[].command.segment.*.x" => rounded_redaction(4),
|
|
||||||
"[].command.segment.*.y" => rounded_redaction(4),
|
|
||||||
"[].command.segment.*.z" => rounded_redaction(4),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
e => {
|
e => {
|
||||||
// These kinds of errors aren't expected to occur. We don't
|
// These kinds of errors aren't expected to occur. We don't
|
||||||
@ -188,6 +161,42 @@ async fn execute(test_name: &str, render_to_png: bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Assert snapshots that should happen both when KCL execution succeeds and
|
||||||
|
/// when it results in an error.
|
||||||
|
fn assert_common_snapshots(
|
||||||
|
test_name: &str,
|
||||||
|
operations: Vec<Operation>,
|
||||||
|
artifact_commands: Vec<ArtifactCommand>,
|
||||||
|
artifact_graph: ArtifactGraph,
|
||||||
|
) {
|
||||||
|
assert_snapshot(test_name, "Operations executed", || {
|
||||||
|
insta::assert_json_snapshot!("ops", operations);
|
||||||
|
});
|
||||||
|
assert_snapshot(test_name, "Artifact commands", || {
|
||||||
|
insta::assert_json_snapshot!("artifact_commands", artifact_commands, {
|
||||||
|
"[].command.segment.*.x" => rounded_redaction(4),
|
||||||
|
"[].command.segment.*.y" => rounded_redaction(4),
|
||||||
|
"[].command.segment.*.z" => rounded_redaction(4),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
assert_snapshot(test_name, "Artifact graph flowchart", || {
|
||||||
|
let flowchart = artifact_graph
|
||||||
|
.to_mermaid_flowchart()
|
||||||
|
.unwrap_or_else(|e| format!("Failed to convert artifact graph to flowchart: {e}"));
|
||||||
|
// Change the snapshot suffix so that it is rendered as a Markdown file
|
||||||
|
// in GitHub.
|
||||||
|
insta::assert_binary_snapshot!("artifact_graph_flowchart.md", flowchart.as_bytes().to_owned());
|
||||||
|
});
|
||||||
|
assert_snapshot(test_name, "Artifact graph mind map", || {
|
||||||
|
let mind_map = artifact_graph
|
||||||
|
.to_mermaid_mind_map()
|
||||||
|
.unwrap_or_else(|e| format!("Failed to convert artifact graph to mind map: {e}"));
|
||||||
|
// Change the snapshot suffix so that it is rendered as a Markdown file
|
||||||
|
// in GitHub.
|
||||||
|
insta::assert_binary_snapshot!("artifact_graph_mind_map.md", mind_map.as_bytes().to_owned());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
mod cube {
|
mod cube {
|
||||||
const TEST_NAME: &str = "cube";
|
const TEST_NAME: &str = "cube";
|
||||||
|
|
||||||
@ -209,6 +218,27 @@ mod cube {
|
|||||||
super::execute(TEST_NAME, true).await
|
super::execute(TEST_NAME, true).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mod cube_with_error {
|
||||||
|
const TEST_NAME: &str = "cube_with_error";
|
||||||
|
|
||||||
|
/// Test parsing KCL.
|
||||||
|
#[test]
|
||||||
|
fn parse() {
|
||||||
|
super::parse(TEST_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||||
|
#[test]
|
||||||
|
fn unparse() {
|
||||||
|
super::unparse(TEST_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test that KCL is executed correctly.
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn kcl_test_execute() {
|
||||||
|
super::execute(TEST_NAME, true).await
|
||||||
|
}
|
||||||
|
}
|
||||||
mod artifact_graph_example_code1 {
|
mod artifact_graph_example_code1 {
|
||||||
const TEST_NAME: &str = "artifact_graph_example_code1";
|
const TEST_NAME: &str = "artifact_graph_example_code1";
|
||||||
|
|
||||||
|
@ -3,14 +3,13 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use derive_docs::stdlib;
|
use derive_docs::stdlib;
|
||||||
|
|
||||||
|
use super::args::FromArgs;
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{ExecState, KclValue},
|
execution::{ExecState, KclValue},
|
||||||
std::Args,
|
std::Args,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::args::FromArgs;
|
|
||||||
|
|
||||||
/// Compute the remainder after dividing `num` by `div`.
|
/// Compute the remainder after dividing `num` by `div`.
|
||||||
/// If `num` is negative, the result will be too.
|
/// If `num` is negative, the result will be too.
|
||||||
pub async fn rem(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn rem(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
|
@ -11,12 +11,11 @@ use parse_display::{Display, FromStr};
|
|||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::execution::{Artifact, ArtifactId};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{
|
execution::{
|
||||||
BasePath, ExecState, Face, GeoMeta, KclValue, Path, Plane, Point2d, Point3d, Sketch, SketchSet, SketchSurface,
|
Artifact, ArtifactId, BasePath, ExecState, Face, GeoMeta, KclValue, Path, Plane, Point2d, Point3d, Sketch,
|
||||||
Solid, TagEngineInfo, TagIdentifier,
|
SketchSet, SketchSurface, Solid, TagEngineInfo, TagIdentifier,
|
||||||
},
|
},
|
||||||
parsing::ast::types::TagNode,
|
parsing::ast::types::TagNode,
|
||||||
std::{
|
std::{
|
||||||
@ -2302,7 +2301,10 @@ mod tests {
|
|||||||
|
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use crate::{execution::TagIdentifier, std::sketch::PlaneData, std::utils::calculate_circle_center};
|
use crate::{
|
||||||
|
execution::TagIdentifier,
|
||||||
|
std::{sketch::PlaneData, utils::calculate_circle_center},
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_deserialize_plane_data() {
|
fn test_deserialize_plane_data() {
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart argument_error.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map argument_error.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,4 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
```
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart array_elem_pop_empty_fail.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map array_elem_pop_empty_fail.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,4 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
```
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart array_elem_pop_fail.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map array_elem_pop_fail.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,4 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
```
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart array_elem_push_fail.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map array_elem_push_fail.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,4 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
```
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart array_index_oob.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map array_index_oob.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,4 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
```
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart comparisons_multiple.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map comparisons_multiple.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,4 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
```
|
538
src/wasm-lib/kcl/tests/cube_with_error/artifact_commands.snap
Normal file
@ -0,0 +1,538 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact commands cube_with_error.kcl
|
||||||
|
---
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "make_plane",
|
||||||
|
"origin": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"x_axis": {
|
||||||
|
"x": 1.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"y_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 1.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"size": 100.0,
|
||||||
|
"clobber": false,
|
||||||
|
"hide": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "plane_set_color",
|
||||||
|
"plane_id": "[uuid]",
|
||||||
|
"color": {
|
||||||
|
"r": 0.7,
|
||||||
|
"g": 0.28,
|
||||||
|
"b": 0.28,
|
||||||
|
"a": 0.4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "make_plane",
|
||||||
|
"origin": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"x_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 1.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"y_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 1.0
|
||||||
|
},
|
||||||
|
"size": 100.0,
|
||||||
|
"clobber": false,
|
||||||
|
"hide": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "plane_set_color",
|
||||||
|
"plane_id": "[uuid]",
|
||||||
|
"color": {
|
||||||
|
"r": 0.28,
|
||||||
|
"g": 0.7,
|
||||||
|
"b": 0.28,
|
||||||
|
"a": 0.4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "make_plane",
|
||||||
|
"origin": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"x_axis": {
|
||||||
|
"x": 1.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"y_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 1.0
|
||||||
|
},
|
||||||
|
"size": 100.0,
|
||||||
|
"clobber": false,
|
||||||
|
"hide": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "plane_set_color",
|
||||||
|
"plane_id": "[uuid]",
|
||||||
|
"color": {
|
||||||
|
"r": 0.28,
|
||||||
|
"g": 0.28,
|
||||||
|
"b": 0.7,
|
||||||
|
"a": 0.4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "make_plane",
|
||||||
|
"origin": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"x_axis": {
|
||||||
|
"x": -1.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"y_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 1.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"size": 100.0,
|
||||||
|
"clobber": false,
|
||||||
|
"hide": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "make_plane",
|
||||||
|
"origin": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"x_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": -1.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"y_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 1.0
|
||||||
|
},
|
||||||
|
"size": 100.0,
|
||||||
|
"clobber": false,
|
||||||
|
"hide": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "make_plane",
|
||||||
|
"origin": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"x_axis": {
|
||||||
|
"x": -1.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"y_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 1.0
|
||||||
|
},
|
||||||
|
"size": 100.0,
|
||||||
|
"clobber": false,
|
||||||
|
"hide": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "edge_lines_visible",
|
||||||
|
"hidden": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "set_scene_units",
|
||||||
|
"unit": "mm"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "object_visible",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"hidden": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "object_visible",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"hidden": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
177,
|
||||||
|
194,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "make_plane",
|
||||||
|
"origin": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"x_axis": {
|
||||||
|
"x": 1.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"y_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 1.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"size": 60.0,
|
||||||
|
"clobber": false,
|
||||||
|
"hide": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
177,
|
||||||
|
194,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "enable_sketch_mode",
|
||||||
|
"entity_id": "[uuid]",
|
||||||
|
"ortho": false,
|
||||||
|
"animated": false,
|
||||||
|
"adjust_camera": false,
|
||||||
|
"planar_normal": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
177,
|
||||||
|
194,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "start_path"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
177,
|
||||||
|
194,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "move_path_pen",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"to": {
|
||||||
|
"x": -20.0,
|
||||||
|
"y": -20.0,
|
||||||
|
"z": 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
202,
|
||||||
|
215,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extend_path",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"segment": {
|
||||||
|
"type": "line",
|
||||||
|
"end": {
|
||||||
|
"x": -20.0,
|
||||||
|
"y": 20.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"relative": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
223,
|
||||||
|
236,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extend_path",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"segment": {
|
||||||
|
"type": "line",
|
||||||
|
"end": {
|
||||||
|
"x": 20.0,
|
||||||
|
"y": 20.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"relative": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
244,
|
||||||
|
257,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extend_path",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"segment": {
|
||||||
|
"type": "line",
|
||||||
|
"end": {
|
||||||
|
"x": 20.0,
|
||||||
|
"y": -20.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"relative": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
265,
|
||||||
|
278,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extend_path",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"segment": {
|
||||||
|
"type": "line",
|
||||||
|
"end": {
|
||||||
|
"x": -20.0,
|
||||||
|
"y": -20.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"relative": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
286,
|
||||||
|
294,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "close_path",
|
||||||
|
"path_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
286,
|
||||||
|
294,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "sketch_mode_disable"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
302,
|
||||||
|
320,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "enable_sketch_mode",
|
||||||
|
"entity_id": "[uuid]",
|
||||||
|
"ortho": false,
|
||||||
|
"animated": false,
|
||||||
|
"adjust_camera": false,
|
||||||
|
"planar_normal": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
302,
|
||||||
|
320,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extrude",
|
||||||
|
"target": "[uuid]",
|
||||||
|
"distance": 40.0,
|
||||||
|
"faces": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
302,
|
||||||
|
320,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "sketch_mode_disable"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
302,
|
||||||
|
320,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "object_bring_to_front",
|
||||||
|
"object_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
302,
|
||||||
|
320,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_extrusion_face_info",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart cube_with_error.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,38 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
subgraph path2 [Path]
|
||||||
|
2["Path<br>[177, 194, 0]"]
|
||||||
|
3["Segment<br>[202, 215, 0]"]
|
||||||
|
4["Segment<br>[223, 236, 0]"]
|
||||||
|
5["Segment<br>[244, 257, 0]"]
|
||||||
|
6["Segment<br>[265, 278, 0]"]
|
||||||
|
7["Segment<br>[286, 294, 0]"]
|
||||||
|
8[Solid2d]
|
||||||
|
end
|
||||||
|
1["Plane<br>[177, 194, 0]"]
|
||||||
|
9["Sweep Extrusion<br>[302, 320, 0]"]
|
||||||
|
10[Wall]
|
||||||
|
11[Wall]
|
||||||
|
12[Wall]
|
||||||
|
13[Wall]
|
||||||
|
14["Cap Start"]
|
||||||
|
15["Cap End"]
|
||||||
|
1 --- 2
|
||||||
|
2 --- 3
|
||||||
|
2 --- 4
|
||||||
|
2 --- 5
|
||||||
|
2 --- 6
|
||||||
|
2 --- 7
|
||||||
|
2 ---- 9
|
||||||
|
2 --- 8
|
||||||
|
3 --- 13
|
||||||
|
4 --- 12
|
||||||
|
5 --- 11
|
||||||
|
6 --- 10
|
||||||
|
9 --- 10
|
||||||
|
9 --- 11
|
||||||
|
9 --- 12
|
||||||
|
9 --- 13
|
||||||
|
9 --- 14
|
||||||
|
9 --- 15
|
||||||
|
```
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map cube_with_error.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,23 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
Plane
|
||||||
|
Path
|
||||||
|
Segment
|
||||||
|
Wall
|
||||||
|
Segment
|
||||||
|
Wall
|
||||||
|
Segment
|
||||||
|
Wall
|
||||||
|
Segment
|
||||||
|
Wall
|
||||||
|
Segment
|
||||||
|
Sweep Extrusion
|
||||||
|
Wall
|
||||||
|
Wall
|
||||||
|
Wall
|
||||||
|
Wall
|
||||||
|
Cap Start
|
||||||
|
Cap End
|
||||||
|
Solid2d
|
||||||
|
```
|
809
src/wasm-lib/kcl/tests/cube_with_error/ast.snap
Normal file
@ -0,0 +1,809 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Result of parsing cube_with_error.kcl
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"Ok": {
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 322,
|
||||||
|
"id": {
|
||||||
|
"end": 7,
|
||||||
|
"name": "cube",
|
||||||
|
"start": 3,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"body": {
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 42,
|
||||||
|
"id": {
|
||||||
|
"end": 29,
|
||||||
|
"name": "l",
|
||||||
|
"start": 28,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"end": 42,
|
||||||
|
"left": {
|
||||||
|
"end": 38,
|
||||||
|
"name": "length",
|
||||||
|
"start": 32,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"operator": "/",
|
||||||
|
"right": {
|
||||||
|
"end": 42,
|
||||||
|
"raw": "2",
|
||||||
|
"start": 41,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": {
|
||||||
|
"value": 2.0,
|
||||||
|
"suffix": "None"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"start": 32,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
},
|
||||||
|
"start": 28,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 42,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 28,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 58,
|
||||||
|
"id": {
|
||||||
|
"end": 46,
|
||||||
|
"name": "x",
|
||||||
|
"start": 45,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"computed": false,
|
||||||
|
"end": 58,
|
||||||
|
"object": {
|
||||||
|
"end": 55,
|
||||||
|
"name": "center",
|
||||||
|
"start": 49,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"property": {
|
||||||
|
"end": 57,
|
||||||
|
"raw": "0",
|
||||||
|
"start": 56,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": {
|
||||||
|
"value": 0.0,
|
||||||
|
"suffix": "None"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"start": 49,
|
||||||
|
"type": "MemberExpression",
|
||||||
|
"type": "MemberExpression"
|
||||||
|
},
|
||||||
|
"start": 45,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 58,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 45,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 74,
|
||||||
|
"id": {
|
||||||
|
"end": 62,
|
||||||
|
"name": "y",
|
||||||
|
"start": 61,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"computed": false,
|
||||||
|
"end": 74,
|
||||||
|
"object": {
|
||||||
|
"end": 71,
|
||||||
|
"name": "center",
|
||||||
|
"start": 65,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"property": {
|
||||||
|
"end": 73,
|
||||||
|
"raw": "1",
|
||||||
|
"start": 72,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": {
|
||||||
|
"value": 1.0,
|
||||||
|
"suffix": "None"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"start": 65,
|
||||||
|
"type": "MemberExpression",
|
||||||
|
"type": "MemberExpression"
|
||||||
|
},
|
||||||
|
"start": 61,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 74,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 61,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 98,
|
||||||
|
"id": {
|
||||||
|
"end": 79,
|
||||||
|
"name": "p0",
|
||||||
|
"start": 77,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"end": 89,
|
||||||
|
"left": {
|
||||||
|
"argument": {
|
||||||
|
"end": 85,
|
||||||
|
"name": "l",
|
||||||
|
"start": 84,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 85,
|
||||||
|
"operator": "-",
|
||||||
|
"start": 83,
|
||||||
|
"type": "UnaryExpression",
|
||||||
|
"type": "UnaryExpression"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 89,
|
||||||
|
"name": "x",
|
||||||
|
"start": 88,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 83,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 97,
|
||||||
|
"left": {
|
||||||
|
"argument": {
|
||||||
|
"end": 93,
|
||||||
|
"name": "l",
|
||||||
|
"start": 92,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 93,
|
||||||
|
"operator": "-",
|
||||||
|
"start": 91,
|
||||||
|
"type": "UnaryExpression",
|
||||||
|
"type": "UnaryExpression"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 97,
|
||||||
|
"name": "y",
|
||||||
|
"start": 96,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 91,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 98,
|
||||||
|
"start": 82,
|
||||||
|
"type": "ArrayExpression",
|
||||||
|
"type": "ArrayExpression"
|
||||||
|
},
|
||||||
|
"start": 77,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 98,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 77,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 121,
|
||||||
|
"id": {
|
||||||
|
"end": 103,
|
||||||
|
"name": "p1",
|
||||||
|
"start": 101,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"end": 113,
|
||||||
|
"left": {
|
||||||
|
"argument": {
|
||||||
|
"end": 109,
|
||||||
|
"name": "l",
|
||||||
|
"start": 108,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 109,
|
||||||
|
"operator": "-",
|
||||||
|
"start": 107,
|
||||||
|
"type": "UnaryExpression",
|
||||||
|
"type": "UnaryExpression"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 113,
|
||||||
|
"name": "x",
|
||||||
|
"start": 112,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 107,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 120,
|
||||||
|
"left": {
|
||||||
|
"end": 116,
|
||||||
|
"name": "l",
|
||||||
|
"start": 115,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 120,
|
||||||
|
"name": "y",
|
||||||
|
"start": 119,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 115,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 121,
|
||||||
|
"start": 106,
|
||||||
|
"type": "ArrayExpression",
|
||||||
|
"type": "ArrayExpression"
|
||||||
|
},
|
||||||
|
"start": 101,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 121,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 101,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 143,
|
||||||
|
"id": {
|
||||||
|
"end": 126,
|
||||||
|
"name": "p2",
|
||||||
|
"start": 124,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"end": 135,
|
||||||
|
"left": {
|
||||||
|
"end": 131,
|
||||||
|
"name": "l",
|
||||||
|
"start": 130,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 135,
|
||||||
|
"name": "x",
|
||||||
|
"start": 134,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 130,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 142,
|
||||||
|
"left": {
|
||||||
|
"end": 138,
|
||||||
|
"name": "l",
|
||||||
|
"start": 137,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 142,
|
||||||
|
"name": "y",
|
||||||
|
"start": 141,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 137,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 143,
|
||||||
|
"start": 129,
|
||||||
|
"type": "ArrayExpression",
|
||||||
|
"type": "ArrayExpression"
|
||||||
|
},
|
||||||
|
"start": 124,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 143,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 124,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 166,
|
||||||
|
"id": {
|
||||||
|
"end": 148,
|
||||||
|
"name": "p3",
|
||||||
|
"start": 146,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"end": 157,
|
||||||
|
"left": {
|
||||||
|
"end": 153,
|
||||||
|
"name": "l",
|
||||||
|
"start": 152,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 157,
|
||||||
|
"name": "x",
|
||||||
|
"start": 156,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 152,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 165,
|
||||||
|
"left": {
|
||||||
|
"argument": {
|
||||||
|
"end": 161,
|
||||||
|
"name": "l",
|
||||||
|
"start": 160,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 161,
|
||||||
|
"operator": "-",
|
||||||
|
"start": 159,
|
||||||
|
"type": "UnaryExpression",
|
||||||
|
"type": "UnaryExpression"
|
||||||
|
},
|
||||||
|
"operator": "+",
|
||||||
|
"right": {
|
||||||
|
"end": 165,
|
||||||
|
"name": "y",
|
||||||
|
"start": 164,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 159,
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"type": "BinaryExpression"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 166,
|
||||||
|
"start": 151,
|
||||||
|
"type": "ArrayExpression",
|
||||||
|
"type": "ArrayExpression"
|
||||||
|
},
|
||||||
|
"start": 146,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 166,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 146,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"argument": {
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"end": 193,
|
||||||
|
"name": "p0",
|
||||||
|
"start": 191,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 190,
|
||||||
|
"name": "startSketchAt",
|
||||||
|
"start": 177,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 194,
|
||||||
|
"start": 177,
|
||||||
|
"type": "CallExpression",
|
||||||
|
"type": "CallExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"end": 211,
|
||||||
|
"name": "p1",
|
||||||
|
"start": 209,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 214,
|
||||||
|
"start": 213,
|
||||||
|
"type": "PipeSubstitution",
|
||||||
|
"type": "PipeSubstitution"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 208,
|
||||||
|
"name": "lineTo",
|
||||||
|
"start": 202,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 215,
|
||||||
|
"start": 202,
|
||||||
|
"type": "CallExpression",
|
||||||
|
"type": "CallExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"end": 232,
|
||||||
|
"name": "p2",
|
||||||
|
"start": 230,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 235,
|
||||||
|
"start": 234,
|
||||||
|
"type": "PipeSubstitution",
|
||||||
|
"type": "PipeSubstitution"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 229,
|
||||||
|
"name": "lineTo",
|
||||||
|
"start": 223,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 236,
|
||||||
|
"start": 223,
|
||||||
|
"type": "CallExpression",
|
||||||
|
"type": "CallExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"end": 253,
|
||||||
|
"name": "p3",
|
||||||
|
"start": 251,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 256,
|
||||||
|
"start": 255,
|
||||||
|
"type": "PipeSubstitution",
|
||||||
|
"type": "PipeSubstitution"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 250,
|
||||||
|
"name": "lineTo",
|
||||||
|
"start": 244,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 257,
|
||||||
|
"start": 244,
|
||||||
|
"type": "CallExpression",
|
||||||
|
"type": "CallExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"end": 274,
|
||||||
|
"name": "p0",
|
||||||
|
"start": 272,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 277,
|
||||||
|
"start": 276,
|
||||||
|
"type": "PipeSubstitution",
|
||||||
|
"type": "PipeSubstitution"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 271,
|
||||||
|
"name": "lineTo",
|
||||||
|
"start": 265,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 278,
|
||||||
|
"start": 265,
|
||||||
|
"type": "CallExpression",
|
||||||
|
"type": "CallExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"end": 293,
|
||||||
|
"start": 292,
|
||||||
|
"type": "PipeSubstitution",
|
||||||
|
"type": "PipeSubstitution"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 291,
|
||||||
|
"name": "close",
|
||||||
|
"start": 286,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 294,
|
||||||
|
"start": 286,
|
||||||
|
"type": "CallExpression",
|
||||||
|
"type": "CallExpression"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"end": 316,
|
||||||
|
"name": "length",
|
||||||
|
"start": 310,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 319,
|
||||||
|
"start": 318,
|
||||||
|
"type": "PipeSubstitution",
|
||||||
|
"type": "PipeSubstitution"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 309,
|
||||||
|
"name": "extrude",
|
||||||
|
"start": 302,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 320,
|
||||||
|
"start": 302,
|
||||||
|
"type": "CallExpression",
|
||||||
|
"type": "CallExpression"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 320,
|
||||||
|
"start": 177,
|
||||||
|
"type": "PipeExpression",
|
||||||
|
"type": "PipeExpression"
|
||||||
|
},
|
||||||
|
"end": 320,
|
||||||
|
"start": 170,
|
||||||
|
"type": "ReturnStatement",
|
||||||
|
"type": "ReturnStatement"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 322,
|
||||||
|
"nonCodeMeta": {
|
||||||
|
"nonCodeNodes": {
|
||||||
|
"6": [
|
||||||
|
{
|
||||||
|
"end": 170,
|
||||||
|
"start": 166,
|
||||||
|
"type": "NonCodeNode",
|
||||||
|
"value": {
|
||||||
|
"type": "newLine"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"startNodes": []
|
||||||
|
},
|
||||||
|
"start": 24
|
||||||
|
},
|
||||||
|
"end": 322,
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"type": "Parameter",
|
||||||
|
"identifier": {
|
||||||
|
"end": 14,
|
||||||
|
"name": "length",
|
||||||
|
"start": 8,
|
||||||
|
"type": "Identifier"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Parameter",
|
||||||
|
"identifier": {
|
||||||
|
"end": 22,
|
||||||
|
"name": "center",
|
||||||
|
"start": 16,
|
||||||
|
"type": "Identifier"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start": 7,
|
||||||
|
"type": "FunctionExpression",
|
||||||
|
"type": "FunctionExpression"
|
||||||
|
},
|
||||||
|
"start": 3,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 322,
|
||||||
|
"kind": "fn",
|
||||||
|
"start": 0,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declaration": {
|
||||||
|
"end": 349,
|
||||||
|
"id": {
|
||||||
|
"end": 330,
|
||||||
|
"name": "myCube",
|
||||||
|
"start": 324,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"end": 340,
|
||||||
|
"raw": "40",
|
||||||
|
"start": 338,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": {
|
||||||
|
"value": 40.0,
|
||||||
|
"suffix": "None"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"end": 344,
|
||||||
|
"raw": "0",
|
||||||
|
"start": 343,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": {
|
||||||
|
"value": 0.0,
|
||||||
|
"suffix": "None"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 347,
|
||||||
|
"raw": "0",
|
||||||
|
"start": 346,
|
||||||
|
"type": "Literal",
|
||||||
|
"type": "Literal",
|
||||||
|
"value": {
|
||||||
|
"value": 0.0,
|
||||||
|
"suffix": "None"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 348,
|
||||||
|
"start": 342,
|
||||||
|
"type": "ArrayExpression",
|
||||||
|
"type": "ArrayExpression"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"end": 337,
|
||||||
|
"name": "cube",
|
||||||
|
"start": 333,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"end": 349,
|
||||||
|
"start": 333,
|
||||||
|
"type": "CallExpression",
|
||||||
|
"type": "CallExpression"
|
||||||
|
},
|
||||||
|
"start": 324,
|
||||||
|
"type": "VariableDeclarator"
|
||||||
|
},
|
||||||
|
"end": 349,
|
||||||
|
"kind": "const",
|
||||||
|
"start": 324,
|
||||||
|
"type": "VariableDeclaration",
|
||||||
|
"type": "VariableDeclaration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 398,
|
||||||
|
"expression": {
|
||||||
|
"end": 398,
|
||||||
|
"name": "foo",
|
||||||
|
"start": 395,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 395,
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"type": "ExpressionStatement"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 399,
|
||||||
|
"nonCodeMeta": {
|
||||||
|
"nonCodeNodes": {
|
||||||
|
"0": [
|
||||||
|
{
|
||||||
|
"end": 324,
|
||||||
|
"start": 322,
|
||||||
|
"type": "NonCodeNode",
|
||||||
|
"value": {
|
||||||
|
"type": "newLine"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"1": [
|
||||||
|
{
|
||||||
|
"end": 394,
|
||||||
|
"start": 349,
|
||||||
|
"type": "NonCodeNode",
|
||||||
|
"value": {
|
||||||
|
"type": "newLineBlockComment",
|
||||||
|
"value": "Error, after creating meaningful output.",
|
||||||
|
"style": "line"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"startNodes": []
|
||||||
|
},
|
||||||
|
"start": 0
|
||||||
|
}
|
||||||
|
}
|
12
src/wasm-lib/kcl/tests/cube_with_error/execution_error.snap
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Error from executing cube_with_error.kcl
|
||||||
|
---
|
||||||
|
KCL UndefinedValue error
|
||||||
|
|
||||||
|
× undefined value: memory item key `foo` is not defined
|
||||||
|
╭─[22:1]
|
||||||
|
21 │ // Error, after creating meaningful output.
|
||||||
|
22 │ foo
|
||||||
|
· ───
|
||||||
|
╰────
|
22
src/wasm-lib/kcl/tests/cube_with_error/input.kcl
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
fn cube(length, center) {
|
||||||
|
l = length / 2
|
||||||
|
x = center[0]
|
||||||
|
y = center[1]
|
||||||
|
p0 = [-l + x, -l + y]
|
||||||
|
p1 = [-l + x, l + y]
|
||||||
|
p2 = [l + x, l + y]
|
||||||
|
p3 = [l + x, -l + y]
|
||||||
|
|
||||||
|
return startSketchAt(p0)
|
||||||
|
|> lineTo(p1, %)
|
||||||
|
|> lineTo(p2, %)
|
||||||
|
|> lineTo(p3, %)
|
||||||
|
|> lineTo(p0, %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(length, %)
|
||||||
|
}
|
||||||
|
|
||||||
|
myCube = cube(40, [0, 0])
|
||||||
|
|
||||||
|
// Error, after creating meaningful output.
|
||||||
|
foo
|
51
src/wasm-lib/kcl/tests/cube_with_error/ops.snap
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Operations executed cube_with_error.kcl
|
||||||
|
---
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "UserDefinedFunctionCall",
|
||||||
|
"name": "cube",
|
||||||
|
"functionSourceRange": [
|
||||||
|
7,
|
||||||
|
322,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"unlabeledArg": null,
|
||||||
|
"labeledArgs": {},
|
||||||
|
"sourceRange": [
|
||||||
|
333,
|
||||||
|
349,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"labeledArgs": {
|
||||||
|
"length": {
|
||||||
|
"sourceRange": [
|
||||||
|
310,
|
||||||
|
316,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sketch_set": {
|
||||||
|
"sourceRange": [
|
||||||
|
318,
|
||||||
|
319,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "extrude",
|
||||||
|
"sourceRange": [
|
||||||
|
302,
|
||||||
|
320,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"type": "StdLibCall",
|
||||||
|
"unlabeledArg": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "UserDefinedFunctionReturn"
|
||||||
|
}
|
||||||
|
]
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart import_cycle1.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph mind map import_cycle1.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,4 @@
|
|||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
```
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart invalid_index_fractional.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
@ -0,0 +1,3 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
```
|