Compare commits
40 Commits
Author | SHA1 | Date | |
---|---|---|---|
239ab6850e | |||
4a7dd6e650 | |||
af2609e678 | |||
30909dedda | |||
39d76ed54f | |||
4925251c29 | |||
9772869545 | |||
a7e830cd02 | |||
ca102116b6 | |||
c2fba89e77 | |||
7e31678ba2 | |||
1140ced121 | |||
32b7ddaa7c | |||
2525f99515 | |||
4b8ce34b31 | |||
6617f72373 | |||
e9033e1754 | |||
9b697e30cf | |||
a70facdab4 | |||
4083f9f3dd | |||
7ead2bb875 | |||
19d01c563e | |||
dfe7cfc91c | |||
01443e445d | |||
e16eb49f51 | |||
5d5138e8e6 | |||
e1d6e29523 | |||
49657ad2e5 | |||
b40d353994 | |||
62ffa53add | |||
64dce4d8b1 | |||
02588b2672 | |||
3d1ac2ac0b | |||
ff5ce29fd7 | |||
4bd7e02271 | |||
26042790b6 | |||
af74f3bb05 | |||
0bdedf5854 | |||
d2c6b5cf3a | |||
c42967d0e7 |
@ -15,6 +15,7 @@ on:
|
|||||||
env:
|
env:
|
||||||
CUT_RELEASE_PR: ${{ github.event_name == 'pull_request' && (contains(github.event.pull_request.title, 'Cut release v')) }}
|
CUT_RELEASE_PR: ${{ github.event_name == 'pull_request' && (contains(github.event.pull_request.title, 'Cut release v')) }}
|
||||||
BUILD_RELEASE: ${{ github.event_name == 'release' || github.event_name == 'schedule' || github.event_name == 'pull_request' && (contains(github.event.pull_request.title, 'Cut release v')) }}
|
BUILD_RELEASE: ${{ github.event_name == 'release' || github.event_name == 'schedule' || github.event_name == 'pull_request' && (contains(github.event.pull_request.title, 'Cut release v')) }}
|
||||||
|
NOTES: ${{ github.event_name == 'release' && github.event.release.body || format('Non-release build, commit {0}', github.sha) }}
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
@ -25,7 +26,6 @@ jobs:
|
|||||||
runs-on: ubuntu-22.04 # seperate job on Ubuntu for easy string manipulations (compared to Windows)
|
runs-on: ubuntu-22.04 # seperate job on Ubuntu for easy string manipulations (compared to Windows)
|
||||||
outputs:
|
outputs:
|
||||||
version: ${{ steps.export_version.outputs.version }}
|
version: ${{ steps.export_version.outputs.version }}
|
||||||
notes: ${{ steps.export_version.outputs.notes }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@ -55,8 +55,6 @@ jobs:
|
|||||||
# TODO: see if we need to inject updater nightly URL here https://dl.zoo.dev/releases/modeling-app/nightly/last_update.json
|
# TODO: see if we need to inject updater nightly URL here https://dl.zoo.dev/releases/modeling-app/nightly/last_update.json
|
||||||
|
|
||||||
- name: Generate release notes
|
- name: Generate release notes
|
||||||
env:
|
|
||||||
NOTES: ${{ github.event_name == 'release' && github.event.release.body || format('Non-release build, commit {0}', github.sha) }}
|
|
||||||
run: |
|
run: |
|
||||||
echo "$NOTES" > release-notes.md
|
echo "$NOTES" > release-notes.md
|
||||||
cat release-notes.md
|
cat release-notes.md
|
||||||
@ -84,9 +82,6 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
electron-builder.yml
|
electron-builder.yml
|
||||||
|
|
||||||
- id: export_notes
|
|
||||||
run: echo "notes=`cat release-notes.md'`" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Prepare electron-builder.yml file for updater test
|
- name: Prepare electron-builder.yml file for updater test
|
||||||
if: ${{ env.CUT_RELEASE_PR == 'true' }}
|
if: ${{ env.CUT_RELEASE_PR == 'true' }}
|
||||||
run: |
|
run: |
|
||||||
@ -262,7 +257,6 @@ jobs:
|
|||||||
VERSION_NO_V: ${{ needs.prepare-files.outputs.version }}
|
VERSION_NO_V: ${{ needs.prepare-files.outputs.version }}
|
||||||
VERSION: ${{ github.event_name == 'schedule' && needs.prepare-files.outputs.version || format('v{0}', needs.prepare-files.outputs.version) }}
|
VERSION: ${{ github.event_name == 'schedule' && needs.prepare-files.outputs.version || format('v{0}', needs.prepare-files.outputs.version) }}
|
||||||
PUB_DATE: ${{ github.event_name == 'release' && github.event.release.created_at || github.event.repository.updated_at }}
|
PUB_DATE: ${{ github.event_name == 'release' && github.event.release.created_at || github.event.repository.updated_at }}
|
||||||
NOTES: ${{ needs.prepare-files.outputs.notes }}
|
|
||||||
BUCKET_DIR: ${{ github.event_name == 'schedule' && 'dl.kittycad.io/releases/modeling-app/nightly' || 'dl.kittycad.io/releases/modeling-app' }}
|
BUCKET_DIR: ${{ github.event_name == 'schedule' && 'dl.kittycad.io/releases/modeling-app/nightly' || 'dl.kittycad.io/releases/modeling-app' }}
|
||||||
WEBSITE_DIR: ${{ github.event_name == 'schedule' && 'dl.zoo.dev/releases/modeling-app/nightly' || 'dl.zoo.dev/releases/modeling-app' }}
|
WEBSITE_DIR: ${{ github.event_name == 'schedule' && 'dl.zoo.dev/releases/modeling-app/nightly' || 'dl.zoo.dev/releases/modeling-app' }}
|
||||||
URL_CODED_NAME: ${{ github.event_name == 'schedule' && 'Zoo%20Modeling%20App%20%28Nightly%29' || 'Zoo%20Modeling%20App' }}
|
URL_CODED_NAME: ${{ github.event_name == 'schedule' && 'Zoo%20Modeling%20App%20%28Nightly%29' || 'Zoo%20Modeling%20App' }}
|
||||||
|
2
.github/workflows/cargo-test.yml
vendored
@ -62,7 +62,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |-
|
run: |-
|
||||||
cd "${{ matrix.dir }}"
|
cd "${{ matrix.dir }}"
|
||||||
cargo llvm-cov nextest --all --lcov --output-path lcov.info --test-threads=1 --no-fail-fast -P ci 2>&1 | tee /tmp/github-actions.log
|
cargo llvm-cov nextest --workspace --lcov --output-path lcov.info --test-threads=1 --no-fail-fast -P ci 2>&1 | tee /tmp/github-actions.log
|
||||||
env:
|
env:
|
||||||
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
||||||
RUST_MIN_STACK: 10485760000
|
RUST_MIN_STACK: 10485760000
|
||||||
|
38010
docs/kcl/std.json
@ -162,6 +162,28 @@ A base path.
|
|||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
A circular arc, not necessarily tangential to the current point.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `Arc`| | No |
|
||||||
|
| `center` |`[number, number]`| Center of the circle that this arc is drawn on. | No |
|
||||||
|
| `radius` |`number`| Radius of the circle that this arc is drawn on. | No |
|
||||||
|
| `from` |`[number, number]`| The from point. | No |
|
||||||
|
| `to` |`[number, number]`| The to point. | No |
|
||||||
|
| `tag` |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag of the path. | No |
|
||||||
|
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,8 +16,8 @@ A sketch is a collection of paths.
|
|||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `id` |`string`| The id of the sketch (this will change when the engine's reference to it changes. | No |
|
| `id` |`string`| The id of the sketch (this will change when the engine's reference to it changes). | No |
|
||||||
| `value` |`[` [`Path`](/docs/kcl/types/Path) `]`| The paths in the sketch. | No |
|
| `paths` |`[` [`Path`](/docs/kcl/types/Path) `]`| The paths in the sketch. | No |
|
||||||
| `on` |[`SketchSurface`](/docs/kcl/types/SketchSurface)| What the sketch is on (can be a plane or a face). | No |
|
| `on` |[`SketchSurface`](/docs/kcl/types/SketchSurface)| What the sketch is on (can be a plane or a face). | No |
|
||||||
| `start` |[`BasePath`](/docs/kcl/types/BasePath)| The starting path. | No |
|
| `start` |[`BasePath`](/docs/kcl/types/BasePath)| The starting path. | No |
|
||||||
| `tags` |`object`| Tag identifiers that have been declared in this sketch. | No |
|
| `tags` |`object`| Tag identifiers that have been declared in this sketch. | No |
|
||||||
|
@ -25,8 +25,8 @@ A sketch is a collection of paths.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `sketch`| | No |
|
| `type` |enum: `sketch`| | No |
|
||||||
| `id` |`string`| The id of the sketch (this will change when the engine's reference to it changes. | No |
|
| `id` |`string`| The id of the sketch (this will change when the engine's reference to it changes). | No |
|
||||||
| `value` |`[` [`Path`](/docs/kcl/types/Path) `]`| The paths in the sketch. | No |
|
| `paths` |`[` [`Path`](/docs/kcl/types/Path) `]`| The paths in the sketch. | No |
|
||||||
| `on` |[`SketchSurface`](/docs/kcl/types/SketchSurface)| What the sketch is on (can be a plane or a face). | No |
|
| `on` |[`SketchSurface`](/docs/kcl/types/SketchSurface)| What the sketch is on (can be a plane or a face). | No |
|
||||||
| `start` |[`BasePath`](/docs/kcl/types/BasePath)| The starting path. | No |
|
| `start` |[`BasePath`](/docs/kcl/types/BasePath)| The starting path. | No |
|
||||||
| `tags` |`object`| Tag identifiers that have been declared in this sketch. | No |
|
| `tags` |`object`| Tag identifiers that have been declared in this sketch. | No |
|
||||||
|
@ -18,7 +18,7 @@ Engine information for a tag.
|
|||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `id` |`string`| The id of the tagged object. | No |
|
| `id` |`string`| The id of the tagged object. | No |
|
||||||
| `sketch` |`string`| The sketch the tag is on. | No |
|
| `sketch` |`string`| The sketch the tag is on. | No |
|
||||||
| `path` |[`BasePath`](/docs/kcl/types/BasePath)| The path the tag is on. | No |
|
| `path` |[`Path`](/docs/kcl/types/Path)| The path the tag is on. | No |
|
||||||
| `surface` |[`ExtrudeSurface`](/docs/kcl/types/ExtrudeSurface)| The surface information for the tag. | No |
|
| `surface` |[`ExtrudeSurface`](/docs/kcl/types/ExtrudeSurface)| The surface information for the tag. | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,6 +136,9 @@ test.describe('when using the file tree to', () => {
|
|||||||
)
|
)
|
||||||
await pasteCodeInEditor(kclCube)
|
await pasteCodeInEditor(kclCube)
|
||||||
|
|
||||||
|
// TODO: We have a timeout of 1s between edits to write to disk. If you reload the page too quickly it won't write to disk.
|
||||||
|
await tronApp.page.waitForTimeout(2000)
|
||||||
|
|
||||||
await renameFile(fromFile, toFile)
|
await renameFile(fromFile, toFile)
|
||||||
await tronApp.page.reload()
|
await tronApp.page.reload()
|
||||||
|
|
||||||
@ -222,9 +225,11 @@ test.describe('when using the file tree to', () => {
|
|||||||
)
|
)
|
||||||
await pasteCodeInEditor(kclCube)
|
await pasteCodeInEditor(kclCube)
|
||||||
|
|
||||||
|
// TODO: We have a timeout of 1s between edits to write to disk. If you reload the page too quickly it won't write to disk.
|
||||||
|
await tronApp.page.waitForTimeout(2000)
|
||||||
|
|
||||||
const kcl1 = 'main.kcl'
|
const kcl1 = 'main.kcl'
|
||||||
const kcl2 = '2.kcl'
|
const kcl2 = '2.kcl'
|
||||||
|
|
||||||
await createNewFileAndSelect(kcl2)
|
await createNewFileAndSelect(kcl2)
|
||||||
const kclCylinder = await fsp.readFile(
|
const kclCylinder = await fsp.readFile(
|
||||||
'src/wasm-lib/tests/executor/inputs/cylinder.kcl',
|
'src/wasm-lib/tests/executor/inputs/cylinder.kcl',
|
||||||
@ -232,6 +237,9 @@ test.describe('when using the file tree to', () => {
|
|||||||
)
|
)
|
||||||
await pasteCodeInEditor(kclCylinder)
|
await pasteCodeInEditor(kclCylinder)
|
||||||
|
|
||||||
|
// TODO: We have a timeout of 1s between edits to write to disk. If you reload the page too quickly it won't write to disk.
|
||||||
|
await tronApp.page.waitForTimeout(2000)
|
||||||
|
|
||||||
await renameFile(kcl2, kcl1)
|
await renameFile(kcl2, kcl1)
|
||||||
|
|
||||||
await test.step(`Postcondition: ${kcl1} still has the original content`, async () => {
|
await test.step(`Postcondition: ${kcl1} still has the original content`, async () => {
|
||||||
|
@ -55,6 +55,53 @@ test.describe('Onboarding tests', () => {
|
|||||||
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
|
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Desktop: fresh onboarding executes and loads',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName: _ }, testInfo) => {
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
appSettings: {
|
||||||
|
app: {
|
||||||
|
onboardingStatus: 'incomplete',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cleanProjectDir: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
const viewportSize = { width: 1200, height: 500 }
|
||||||
|
await page.setViewportSize(viewportSize)
|
||||||
|
|
||||||
|
// Locators and constants
|
||||||
|
const newProjectButton = page.getByRole('button', { name: 'New project' })
|
||||||
|
const projectLink = page.getByTestId('project-link')
|
||||||
|
|
||||||
|
await test.step(`Create a project and open to the onboarding`, async () => {
|
||||||
|
await newProjectButton.click()
|
||||||
|
await projectLink.click()
|
||||||
|
await test.step(`Ensure the engine connection works by testing the sketch button`, async () => {
|
||||||
|
await u.waitForPageLoad()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Ensure we see the onboarding stuff`, async () => {
|
||||||
|
// Test that the onboarding pane loaded
|
||||||
|
await expect(
|
||||||
|
page.getByText('Welcome to Modeling App! This')
|
||||||
|
).toBeVisible()
|
||||||
|
|
||||||
|
// *and* that the code is shown in the editor
|
||||||
|
await expect(page.locator('.cm-content')).toContainText(
|
||||||
|
'// Shelf Bracket'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test('Code resets after confirmation', async ({ page }) => {
|
test('Code resets after confirmation', async ({ page }) => {
|
||||||
const initialCode = `sketch001 = startSketchOn('XZ')`
|
const initialCode = `sketch001 = startSketchOn('XZ')`
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
@ -888,7 +888,17 @@ export async function setupElectron({
|
|||||||
const tempSettingsFilePath = join(projectDirName, SETTINGS_FILE_NAME)
|
const tempSettingsFilePath = join(projectDirName, SETTINGS_FILE_NAME)
|
||||||
const settingsOverrides = TOML.stringify(
|
const settingsOverrides = TOML.stringify(
|
||||||
appSettings
|
appSettings
|
||||||
? { settings: appSettings }
|
? {
|
||||||
|
settings: {
|
||||||
|
...TEST_SETTINGS,
|
||||||
|
...appSettings,
|
||||||
|
app: {
|
||||||
|
...TEST_SETTINGS.app,
|
||||||
|
projectDirectory: projectDirName,
|
||||||
|
...appSettings.app,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
: {
|
: {
|
||||||
settings: {
|
settings: {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
|
@ -292,7 +292,7 @@ test.describe(`Testing gizmo, fixture-based`, () => {
|
|||||||
await test.step(`Verify the camera moved`, async () => {
|
await test.step(`Verify the camera moved`, async () => {
|
||||||
await scene.expectState({
|
await scene.expectState({
|
||||||
camera: {
|
camera: {
|
||||||
position: [0, -23865.37, 11073.54],
|
position: [0, -23865.37, 11073.53],
|
||||||
target: [0, 0, 0],
|
target: [0, 0, 0],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -430,7 +430,6 @@ test.describe('Testing settings', () => {
|
|||||||
await test.step('Check color of logo changed when in modeling view', async () => {
|
await test.step('Check color of logo changed when in modeling view', async () => {
|
||||||
await page.getByRole('button', { name: 'New project' }).click()
|
await page.getByRole('button', { name: 'New project' }).click()
|
||||||
await page.getByTestId('project-link').first().click()
|
await page.getByTestId('project-link').first().click()
|
||||||
await page.getByRole('button', { name: 'Dismiss' }).click()
|
|
||||||
await changeColor('58')
|
await changeColor('58')
|
||||||
await expect(logoLink).toHaveCSS('--primary-hue', '58')
|
await expect(logoLink).toHaveCSS('--primary-hue', '58')
|
||||||
})
|
})
|
||||||
|
2
interface.d.ts
vendored
@ -2,7 +2,7 @@ import fs from 'node:fs/promises'
|
|||||||
import fsSync from 'node:fs'
|
import fsSync from 'node:fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { dialog, shell } from 'electron'
|
import { dialog, shell } from 'electron'
|
||||||
import { MachinesListing } from 'lib/machineManager'
|
import { MachinesListing } from 'components/MachineManagerProvider'
|
||||||
|
|
||||||
type EnvFn = (value?: string) => string
|
type EnvFn = (value?: string) => string
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ echo ""
|
|||||||
echo "Suggested changelog:"
|
echo "Suggested changelog:"
|
||||||
echo "\`\`\`"
|
echo "\`\`\`"
|
||||||
echo "## What's Changed"
|
echo "## What's Changed"
|
||||||
git log $(git describe --tags --abbrev=0)..HEAD --oneline --pretty=format:%s | grep -v Bump | grep -v 'Cut release v' | awk '{print "* "toupper(substr($0,0,1))substr($0,2)}'
|
git log $(git describe --tags --match="v[0-9]*" --abbrev=0)..HEAD --oneline --pretty=format:%s | grep -v Bump | grep -v 'Cut release v' | awk '{print "* "toupper(substr($0,0,1))substr($0,2)}'
|
||||||
echo ""
|
echo ""
|
||||||
echo "**Full Changelog**: https://github.com/KittyCAD/modeling-app/compare/${latest_tag}...${new_version}"
|
echo "**Full Changelog**: https://github.com/KittyCAD/modeling-app/compare/${latest_tag}...${new_version}"
|
||||||
echo "\`\`\`"
|
echo "\`\`\`"
|
||||||
|
@ -107,6 +107,13 @@
|
|||||||
},
|
},
|
||||||
"type": "array"
|
"type": "array"
|
||||||
},
|
},
|
||||||
|
"loaded_filament_idx": {
|
||||||
|
"description": "The currently loaded filament index.",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0,
|
||||||
|
"nullable": true,
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"nozzle_diameter": {
|
"nozzle_diameter": {
|
||||||
"description": "Diameter of the extrusion nozzle, in mm.",
|
"description": "Diameter of the extrusion nozzle, in mm.",
|
||||||
"format": "double",
|
"format": "double",
|
||||||
@ -285,6 +292,21 @@
|
|||||||
"type"
|
"type"
|
||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Unknown material",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"enum": [
|
||||||
|
"unknown"
|
||||||
|
],
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -974,7 +996,7 @@
|
|||||||
},
|
},
|
||||||
"description": "",
|
"description": "",
|
||||||
"title": "machine-api",
|
"title": "machine-api",
|
||||||
"version": "0.1.0"
|
"version": "0.1.1"
|
||||||
},
|
},
|
||||||
"openapi": "3.0.3",
|
"openapi": "3.0.3",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "zoo-modeling-app",
|
"name": "zoo-modeling-app",
|
||||||
"version": "0.26.0",
|
"version": "0.26.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"productName": "Zoo Modeling App",
|
"productName": "Zoo Modeling App",
|
||||||
"author": {
|
"author": {
|
||||||
@ -161,7 +161,7 @@
|
|||||||
"@types/isomorphic-fetch": "^0.0.39",
|
"@types/isomorphic-fetch": "^0.0.39",
|
||||||
"@types/minimist": "^1.2.5",
|
"@types/minimist": "^1.2.5",
|
||||||
"@types/mocha": "^10.0.6",
|
"@types/mocha": "^10.0.6",
|
||||||
"@types/node": "^22.5.0",
|
"@types/node": "^22.7.8",
|
||||||
"@types/pixelmatch": "^5.2.6",
|
"@types/pixelmatch": "^5.2.6",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
"@types/react": "^18.3.4",
|
"@types/react": "^18.3.4",
|
||||||
|
@ -21,6 +21,7 @@ import { WasmErrBanner } from 'components/WasmErrBanner'
|
|||||||
import { CommandBar } from 'components/CommandBar/CommandBar'
|
import { CommandBar } from 'components/CommandBar/CommandBar'
|
||||||
import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
||||||
import FileMachineProvider from 'components/FileMachineProvider'
|
import FileMachineProvider from 'components/FileMachineProvider'
|
||||||
|
import { MachineManagerProvider } from 'components/MachineManagerProvider'
|
||||||
import { PATHS } from 'lib/paths'
|
import { PATHS } from 'lib/paths'
|
||||||
import {
|
import {
|
||||||
fileLoader,
|
fileLoader,
|
||||||
@ -49,6 +50,7 @@ const router = createRouter([
|
|||||||
{
|
{
|
||||||
loader: settingsLoader,
|
loader: settingsLoader,
|
||||||
id: PATHS.INDEX,
|
id: PATHS.INDEX,
|
||||||
|
// TODO: Re-evaluate if this is true
|
||||||
/* Make sure auth is the outermost provider or else we will have
|
/* Make sure auth is the outermost provider or else we will have
|
||||||
* inefficient re-renders, use the react profiler to see. */
|
* inefficient re-renders, use the react profiler to see. */
|
||||||
element: (
|
element: (
|
||||||
@ -57,7 +59,9 @@ const router = createRouter([
|
|||||||
<LspProvider>
|
<LspProvider>
|
||||||
<KclContextProvider>
|
<KclContextProvider>
|
||||||
<AppStateProvider>
|
<AppStateProvider>
|
||||||
<Outlet />
|
<MachineManagerProvider>
|
||||||
|
<Outlet />
|
||||||
|
</MachineManagerProvider>
|
||||||
</AppStateProvider>
|
</AppStateProvider>
|
||||||
</KclContextProvider>
|
</KclContextProvider>
|
||||||
</LspProvider>
|
</LspProvider>
|
||||||
|
@ -64,6 +64,27 @@ export type ReactCameraProperties =
|
|||||||
|
|
||||||
const lastCmdDelay = 50
|
const lastCmdDelay = 50
|
||||||
|
|
||||||
|
class CameraRateLimiter {
|
||||||
|
lastSend?: Date = undefined
|
||||||
|
rateLimitMs: number = 16 //60 FPS
|
||||||
|
|
||||||
|
send = (f: () => void) => {
|
||||||
|
let now = new Date()
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.lastSend === undefined ||
|
||||||
|
now.getTime() - this.lastSend.getTime() > this.rateLimitMs
|
||||||
|
) {
|
||||||
|
f()
|
||||||
|
this.lastSend = now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reset = () => {
|
||||||
|
this.lastSend = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class CameraControls {
|
export class CameraControls {
|
||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
syncDirection: 'clientToEngine' | 'engineToClient' = 'engineToClient'
|
syncDirection: 'clientToEngine' | 'engineToClient' = 'engineToClient'
|
||||||
@ -71,15 +92,15 @@ export class CameraControls {
|
|||||||
target: Vector3
|
target: Vector3
|
||||||
domElement: HTMLCanvasElement
|
domElement: HTMLCanvasElement
|
||||||
isDragging: boolean
|
isDragging: boolean
|
||||||
|
wasDragging: boolean
|
||||||
mouseDownPosition: Vector2
|
mouseDownPosition: Vector2
|
||||||
mouseNewPosition: Vector2
|
mouseNewPosition: Vector2
|
||||||
rotationSpeed = 0.3
|
rotationSpeed = 0.3
|
||||||
enableRotate = true
|
enableRotate = true
|
||||||
enablePan = true
|
enablePan = true
|
||||||
enableZoom = true
|
enableZoom = true
|
||||||
zoomDataFromLastFrame?: number = undefined
|
moveSender: CameraRateLimiter = new CameraRateLimiter()
|
||||||
// holds coordinates, and interaction
|
zoomSender: CameraRateLimiter = new CameraRateLimiter()
|
||||||
moveDataFromLastFrame?: [number, number, string] = undefined
|
|
||||||
lastPerspectiveFov: number = 45
|
lastPerspectiveFov: number = 45
|
||||||
pendingZoom: number | null = null
|
pendingZoom: number | null = null
|
||||||
pendingRotation: Vector2 | null = null
|
pendingRotation: Vector2 | null = null
|
||||||
@ -171,6 +192,36 @@ export class CameraControls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
doMove = (interaction: any, coordinates: any) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
|
this.engineCommandManager.sendSceneCommand({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd: {
|
||||||
|
type: 'camera_drag_move',
|
||||||
|
interaction: interaction,
|
||||||
|
window: {
|
||||||
|
x: coordinates[0],
|
||||||
|
y: coordinates[1],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
doZoom = (zoom: number) => {
|
||||||
|
this.handleStart()
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
|
this.engineCommandManager.sendSceneCommand({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_zoom',
|
||||||
|
magnitude: (-1 * zoom) / window.devicePixelRatio,
|
||||||
|
},
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
})
|
||||||
|
this.handleEnd()
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
isOrtho = false,
|
isOrtho = false,
|
||||||
domElement: HTMLCanvasElement,
|
domElement: HTMLCanvasElement,
|
||||||
@ -183,6 +234,7 @@ export class CameraControls {
|
|||||||
this.target = new Vector3()
|
this.target = new Vector3()
|
||||||
this.domElement = domElement
|
this.domElement = domElement
|
||||||
this.isDragging = false
|
this.isDragging = false
|
||||||
|
this.wasDragging = false
|
||||||
this.mouseDownPosition = new Vector2()
|
this.mouseDownPosition = new Vector2()
|
||||||
this.mouseNewPosition = new Vector2()
|
this.mouseNewPosition = new Vector2()
|
||||||
|
|
||||||
@ -258,49 +310,6 @@ export class CameraControls {
|
|||||||
this.onCameraChange()
|
this.onCameraChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Our stream is never more than 60fps.
|
|
||||||
// We can get away with capping our "virtual fps" to 60 then.
|
|
||||||
const FPS_VIRTUAL = 60
|
|
||||||
|
|
||||||
const doZoom = () => {
|
|
||||||
if (this.zoomDataFromLastFrame !== undefined) {
|
|
||||||
this.handleStart()
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_zoom',
|
|
||||||
magnitude:
|
|
||||||
(-1 * this.zoomDataFromLastFrame) / window.devicePixelRatio,
|
|
||||||
},
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
})
|
|
||||||
this.handleEnd()
|
|
||||||
}
|
|
||||||
this.zoomDataFromLastFrame = undefined
|
|
||||||
}
|
|
||||||
setInterval(doZoom, 1000 / FPS_VIRTUAL)
|
|
||||||
|
|
||||||
const doMove = () => {
|
|
||||||
if (this.moveDataFromLastFrame !== undefined) {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd: {
|
|
||||||
type: 'camera_drag_move',
|
|
||||||
interaction: this.moveDataFromLastFrame[2] as any,
|
|
||||||
window: {
|
|
||||||
x: this.moveDataFromLastFrame[0],
|
|
||||||
y: this.moveDataFromLastFrame[1],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.moveDataFromLastFrame = undefined
|
|
||||||
}
|
|
||||||
setInterval(doMove, 1000 / FPS_VIRTUAL)
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.engineCommandManager.subscribeTo({
|
this.engineCommandManager.subscribeTo({
|
||||||
event: 'camera_drag_end',
|
event: 'camera_drag_end',
|
||||||
@ -356,6 +365,8 @@ export class CameraControls {
|
|||||||
onMouseDown = (event: PointerEvent) => {
|
onMouseDown = (event: PointerEvent) => {
|
||||||
this.domElement.setPointerCapture(event.pointerId)
|
this.domElement.setPointerCapture(event.pointerId)
|
||||||
this.isDragging = true
|
this.isDragging = true
|
||||||
|
// Reset the wasDragging flag to false when starting a new drag
|
||||||
|
this.wasDragging = false
|
||||||
this.mouseDownPosition.set(event.clientX, event.clientY)
|
this.mouseDownPosition.set(event.clientX, event.clientY)
|
||||||
let interaction = this.getInteractionType(event)
|
let interaction = this.getInteractionType(event)
|
||||||
if (interaction === 'none') return
|
if (interaction === 'none') return
|
||||||
@ -385,11 +396,18 @@ export class CameraControls {
|
|||||||
const interaction = this.getInteractionType(event)
|
const interaction = this.getInteractionType(event)
|
||||||
if (interaction === 'none') return
|
if (interaction === 'none') return
|
||||||
|
|
||||||
|
// If there's a valid interaction and the mouse is moving,
|
||||||
|
// our past (and current) interaction was a drag.
|
||||||
|
this.wasDragging = true
|
||||||
|
|
||||||
if (this.syncDirection === 'engineToClient') {
|
if (this.syncDirection === 'engineToClient') {
|
||||||
this.moveDataFromLastFrame = [event.clientX, event.clientY, interaction]
|
this.moveSender.send(() => {
|
||||||
|
this.doMove(interaction, [event.clientX, event.clientY])
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// else "clientToEngine" (Sketch Mode) or forceUpdate
|
||||||
// Implement camera movement logic here based on deltaMove
|
// Implement camera movement logic here based on deltaMove
|
||||||
// For example, for rotating the camera around the target:
|
// For example, for rotating the camera around the target:
|
||||||
if (interaction === 'rotate') {
|
if (interaction === 'rotate') {
|
||||||
@ -418,6 +436,9 @@ export class CameraControls {
|
|||||||
* under the cursor. This recently moved from being handled in App.tsx.
|
* under the cursor. This recently moved from being handled in App.tsx.
|
||||||
* This might not be the right spot, but it is more consolidated.
|
* This might not be the right spot, but it is more consolidated.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Clear any previous drag state
|
||||||
|
this.wasDragging = false
|
||||||
if (this.syncDirection === 'engineToClient') {
|
if (this.syncDirection === 'engineToClient') {
|
||||||
const newCmdId = uuidv4()
|
const newCmdId = uuidv4()
|
||||||
|
|
||||||
@ -459,7 +480,9 @@ export class CameraControls {
|
|||||||
|
|
||||||
if (this.syncDirection === 'engineToClient') {
|
if (this.syncDirection === 'engineToClient') {
|
||||||
if (interaction === 'zoom') {
|
if (interaction === 'zoom') {
|
||||||
this.zoomDataFromLastFrame = event.deltaY
|
this.zoomSender.send(() => {
|
||||||
|
this.doZoom(event.deltaY)
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
// This case will get handled when we add pan and rotate using Apple trackpad.
|
// This case will get handled when we add pan and rotate using Apple trackpad.
|
||||||
console.error(
|
console.error(
|
||||||
|
@ -338,6 +338,11 @@ export class SceneEntities {
|
|||||||
sceneInfra.setCallbacks({
|
sceneInfra.setCallbacks({
|
||||||
onClick: async (args) => {
|
onClick: async (args) => {
|
||||||
if (!args) return
|
if (!args) return
|
||||||
|
// If there is a valid camera interaction that matches, do that instead
|
||||||
|
const interaction = sceneInfra.camControls.getInteractionType(
|
||||||
|
args.mouseEvent
|
||||||
|
)
|
||||||
|
if (interaction !== 'none') return
|
||||||
if (args.mouseEvent.which !== 1) return
|
if (args.mouseEvent.which !== 1) return
|
||||||
const { intersectionPoint } = args
|
const { intersectionPoint } = args
|
||||||
if (!intersectionPoint?.twoD || !sketchDetails?.sketchPathToNode) return
|
if (!intersectionPoint?.twoD || !sketchDetails?.sketchPathToNode) return
|
||||||
@ -407,7 +412,7 @@ export class SceneEntities {
|
|||||||
if (err(sketch)) return Promise.reject(sketch)
|
if (err(sketch)) return Promise.reject(sketch)
|
||||||
if (!sketch) return Promise.reject('sketch not found')
|
if (!sketch) return Promise.reject('sketch not found')
|
||||||
|
|
||||||
if (!isArray(sketch?.value))
|
if (!isArray(sketch?.paths))
|
||||||
return {
|
return {
|
||||||
truncatedAst,
|
truncatedAst,
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
@ -435,7 +440,7 @@ export class SceneEntities {
|
|||||||
maybeModdedAst,
|
maybeModdedAst,
|
||||||
sketch.start.__geoMeta.sourceRange
|
sketch.start.__geoMeta.sourceRange
|
||||||
)
|
)
|
||||||
if (sketch?.value?.[0]?.type !== 'Circle') {
|
if (sketch?.paths?.[0]?.type !== 'Circle') {
|
||||||
const _profileStart = createProfileStartHandle({
|
const _profileStart = createProfileStartHandle({
|
||||||
from: sketch.start.from,
|
from: sketch.start.from,
|
||||||
id: sketch.start.__geoMeta.id,
|
id: sketch.start.__geoMeta.id,
|
||||||
@ -451,16 +456,16 @@ export class SceneEntities {
|
|||||||
this.activeSegments[JSON.stringify(segPathToNode)] = _profileStart
|
this.activeSegments[JSON.stringify(segPathToNode)] = _profileStart
|
||||||
}
|
}
|
||||||
const callbacks: (() => SegmentOverlayPayload | null)[] = []
|
const callbacks: (() => SegmentOverlayPayload | null)[] = []
|
||||||
sketch.value.forEach((segment, index) => {
|
sketch.paths.forEach((segment, index) => {
|
||||||
let segPathToNode = getNodePathFromSourceRange(
|
let segPathToNode = getNodePathFromSourceRange(
|
||||||
maybeModdedAst,
|
maybeModdedAst,
|
||||||
segment.__geoMeta.sourceRange
|
segment.__geoMeta.sourceRange
|
||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
draftExpressionsIndices &&
|
draftExpressionsIndices &&
|
||||||
(sketch.value[index - 1] || sketch.start)
|
(sketch.paths[index - 1] || sketch.start)
|
||||||
) {
|
) {
|
||||||
const previousSegment = sketch.value[index - 1] || sketch.start
|
const previousSegment = sketch.paths[index - 1] || sketch.start
|
||||||
const previousSegmentPathToNode = getNodePathFromSourceRange(
|
const previousSegmentPathToNode = getNodePathFromSourceRange(
|
||||||
maybeModdedAst,
|
maybeModdedAst,
|
||||||
previousSegment.__geoMeta.sourceRange
|
previousSegment.__geoMeta.sourceRange
|
||||||
@ -511,7 +516,7 @@ export class SceneEntities {
|
|||||||
to: segment.to,
|
to: segment.to,
|
||||||
}
|
}
|
||||||
const result = initSegment({
|
const result = initSegment({
|
||||||
prevSegment: sketch.value[index - 1],
|
prevSegment: sketch.paths[index - 1],
|
||||||
callExpName,
|
callExpName,
|
||||||
input,
|
input,
|
||||||
id: segment.__geoMeta.id,
|
id: segment.__geoMeta.id,
|
||||||
@ -610,9 +615,9 @@ export class SceneEntities {
|
|||||||
variableDeclarationName
|
variableDeclarationName
|
||||||
)
|
)
|
||||||
if (err(sg)) return Promise.reject(sg)
|
if (err(sg)) return Promise.reject(sg)
|
||||||
const lastSeg = sg?.value?.slice(-1)[0] || sg.start
|
const lastSeg = sg?.paths?.slice(-1)[0] || sg.start
|
||||||
|
|
||||||
const index = sg.value.length // because we've added a new segment that's not in the memory yet, no need for `-1`
|
const index = sg.paths.length // because we've added a new segment that's not in the memory yet, no need for `-1`
|
||||||
const mod = addNewSketchLn({
|
const mod = addNewSketchLn({
|
||||||
node: _ast,
|
node: _ast,
|
||||||
programMemory: kclManager.programMemory,
|
programMemory: kclManager.programMemory,
|
||||||
@ -645,7 +650,13 @@ export class SceneEntities {
|
|||||||
sceneInfra.setCallbacks({
|
sceneInfra.setCallbacks({
|
||||||
onClick: async (args) => {
|
onClick: async (args) => {
|
||||||
if (!args) return
|
if (!args) return
|
||||||
|
// If there is a valid camera interaction that matches, do that instead
|
||||||
|
const interaction = sceneInfra.camControls.getInteractionType(
|
||||||
|
args.mouseEvent
|
||||||
|
)
|
||||||
|
if (interaction !== 'none') return
|
||||||
if (args.mouseEvent.which !== 1) return
|
if (args.mouseEvent.which !== 1) return
|
||||||
|
|
||||||
const { intersectionPoint } = args
|
const { intersectionPoint } = args
|
||||||
let intersection2d = intersectionPoint?.twoD
|
let intersection2d = intersectionPoint?.twoD
|
||||||
const profileStart = args.intersects
|
const profileStart = args.intersects
|
||||||
@ -654,7 +665,7 @@ export class SceneEntities {
|
|||||||
|
|
||||||
let modifiedAst
|
let modifiedAst
|
||||||
if (profileStart) {
|
if (profileStart) {
|
||||||
const lastSegment = sketch.value.slice(-1)[0]
|
const lastSegment = sketch.paths.slice(-1)[0]
|
||||||
modifiedAst = addCallExpressionsToPipe({
|
modifiedAst = addCallExpressionsToPipe({
|
||||||
node: kclManager.ast,
|
node: kclManager.ast,
|
||||||
programMemory: kclManager.programMemory,
|
programMemory: kclManager.programMemory,
|
||||||
@ -686,7 +697,7 @@ export class SceneEntities {
|
|||||||
})
|
})
|
||||||
if (trap(modifiedAst)) return Promise.reject(modifiedAst)
|
if (trap(modifiedAst)) return Promise.reject(modifiedAst)
|
||||||
} else if (intersection2d) {
|
} else if (intersection2d) {
|
||||||
const lastSegment = sketch.value.slice(-1)[0]
|
const lastSegment = sketch.paths.slice(-1)[0]
|
||||||
const tmp = addNewSketchLn({
|
const tmp = addNewSketchLn({
|
||||||
node: kclManager.ast,
|
node: kclManager.ast,
|
||||||
programMemory: kclManager.programMemory,
|
programMemory: kclManager.programMemory,
|
||||||
@ -735,7 +746,6 @@ export class SceneEntities {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
...this.mouseEnterLeaveCallbacks(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
setupDraftRectangle = async (
|
setupDraftRectangle = async (
|
||||||
@ -817,7 +827,7 @@ export class SceneEntities {
|
|||||||
variableDeclarationName
|
variableDeclarationName
|
||||||
)
|
)
|
||||||
if (err(sketch)) return Promise.reject(sketch)
|
if (err(sketch)) return Promise.reject(sketch)
|
||||||
const sgPaths = sketch.value
|
const sgPaths = sketch.paths
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
|
|
||||||
this.updateSegment(sketch.start, 0, 0, _ast, orthoFactor, sketch)
|
this.updateSegment(sketch.start, 0, 0, _ast, orthoFactor, sketch)
|
||||||
@ -826,6 +836,11 @@ export class SceneEntities {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
onClick: async (args) => {
|
onClick: async (args) => {
|
||||||
|
// If there is a valid camera interaction that matches, do that instead
|
||||||
|
const interaction = sceneInfra.camControls.getInteractionType(
|
||||||
|
args.mouseEvent
|
||||||
|
)
|
||||||
|
if (interaction !== 'none') return
|
||||||
// Commit the rectangle to the full AST/code and return to sketch.idle
|
// Commit the rectangle to the full AST/code and return to sketch.idle
|
||||||
const cornerPoint = args.intersectionPoint?.twoD
|
const cornerPoint = args.intersectionPoint?.twoD
|
||||||
if (!cornerPoint || args.mouseEvent.button !== 0) return
|
if (!cornerPoint || args.mouseEvent.button !== 0) return
|
||||||
@ -868,7 +883,7 @@ export class SceneEntities {
|
|||||||
variableDeclarationName
|
variableDeclarationName
|
||||||
)
|
)
|
||||||
if (err(sketch)) return
|
if (err(sketch)) return
|
||||||
const sgPaths = sketch.value
|
const sgPaths = sketch.paths
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
|
|
||||||
// Update the starting segment of the THREEjs scene
|
// Update the starting segment of the THREEjs scene
|
||||||
@ -985,7 +1000,7 @@ export class SceneEntities {
|
|||||||
variableDeclarationName
|
variableDeclarationName
|
||||||
)
|
)
|
||||||
if (err(sketch)) return
|
if (err(sketch)) return
|
||||||
const sgPaths = sketch.value
|
const sgPaths = sketch.paths
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
|
|
||||||
this.updateSegment(sketch.start, 0, 0, _ast, orthoFactor, sketch)
|
this.updateSegment(sketch.start, 0, 0, _ast, orthoFactor, sketch)
|
||||||
@ -994,6 +1009,11 @@ export class SceneEntities {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
onClick: async (args) => {
|
onClick: async (args) => {
|
||||||
|
// If there is a valid camera interaction that matches, do that instead
|
||||||
|
const interaction = sceneInfra.camControls.getInteractionType(
|
||||||
|
args.mouseEvent
|
||||||
|
)
|
||||||
|
if (interaction !== 'none') return
|
||||||
// Commit the rectangle to the full AST/code and return to sketch.idle
|
// Commit the rectangle to the full AST/code and return to sketch.idle
|
||||||
const cornerPoint = args.intersectionPoint?.twoD
|
const cornerPoint = args.intersectionPoint?.twoD
|
||||||
if (!cornerPoint || args.mouseEvent.button !== 0) return
|
if (!cornerPoint || args.mouseEvent.button !== 0) return
|
||||||
@ -1105,7 +1125,7 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const pipeIndex = pathToNode[pathToNodeIndex + 1][0] as number
|
const pipeIndex = pathToNode[pathToNodeIndex + 1][0] as number
|
||||||
if (addingNewSegmentStatus === 'nothing') {
|
if (addingNewSegmentStatus === 'nothing') {
|
||||||
const prevSegment = sketch.value[pipeIndex - 2]
|
const prevSegment = sketch.paths[pipeIndex - 2]
|
||||||
const mod = addNewSketchLn({
|
const mod = addNewSketchLn({
|
||||||
node: kclManager.ast,
|
node: kclManager.ast,
|
||||||
programMemory: kclManager.programMemory,
|
programMemory: kclManager.programMemory,
|
||||||
@ -1157,6 +1177,11 @@ export class SceneEntities {
|
|||||||
},
|
},
|
||||||
onMove: () => {},
|
onMove: () => {},
|
||||||
onClick: (args) => {
|
onClick: (args) => {
|
||||||
|
// If there is a valid camera interaction that matches, do that instead
|
||||||
|
const interaction = sceneInfra.camControls.getInteractionType(
|
||||||
|
args.mouseEvent
|
||||||
|
)
|
||||||
|
if (interaction !== 'none') return
|
||||||
if (args?.mouseEvent.which !== 1) return
|
if (args?.mouseEvent.which !== 1) return
|
||||||
if (!args || !args.selected) {
|
if (!args || !args.selected) {
|
||||||
sceneInfra.modelingSend({
|
sceneInfra.modelingSend({
|
||||||
@ -1345,7 +1370,7 @@ export class SceneEntities {
|
|||||||
}
|
}
|
||||||
if (!sketch) return
|
if (!sketch) return
|
||||||
|
|
||||||
const sgPaths = sketch.value
|
const sgPaths = sketch.paths
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
|
|
||||||
this.updateSegment(
|
this.updateSegment(
|
||||||
@ -1393,7 +1418,7 @@ export class SceneEntities {
|
|||||||
modifiedAst,
|
modifiedAst,
|
||||||
segment.__geoMeta.sourceRange
|
segment.__geoMeta.sourceRange
|
||||||
)
|
)
|
||||||
const sgPaths = sketch.value
|
const sgPaths = sketch.paths
|
||||||
const originalPathToNodeStr = JSON.stringify(segPathToNode)
|
const originalPathToNodeStr = JSON.stringify(segPathToNode)
|
||||||
segPathToNode[1][0] = varDecIndex
|
segPathToNode[1][0] = varDecIndex
|
||||||
const pathToNodeStr = JSON.stringify(segPathToNode)
|
const pathToNodeStr = JSON.stringify(segPathToNode)
|
||||||
@ -1701,7 +1726,7 @@ function prepareTruncatedMemoryAndAst(
|
|||||||
variableDeclarationName
|
variableDeclarationName
|
||||||
)
|
)
|
||||||
if (err(sg)) return sg
|
if (err(sg)) return sg
|
||||||
const lastSeg = sg?.value.slice(-1)[0]
|
const lastSeg = sg?.paths.slice(-1)[0]
|
||||||
if (draftSegment) {
|
if (draftSegment) {
|
||||||
// truncatedAst needs to setup with another segment at the end
|
// truncatedAst needs to setup with another segment at the end
|
||||||
let newSegment
|
let newSegment
|
||||||
|
@ -213,7 +213,7 @@ export class SceneInfra {
|
|||||||
to: Coords2d
|
to: Coords2d
|
||||||
angle?: number
|
angle?: number
|
||||||
}): SegmentOverlayPayload | null {
|
}): SegmentOverlayPayload | null {
|
||||||
if (group.userData.pathToNode && arrowGroup) {
|
if (!group.userData.draft && group.userData.pathToNode && arrowGroup) {
|
||||||
const vector = new Vector3(0, 0, 0)
|
const vector = new Vector3(0, 0, 0)
|
||||||
|
|
||||||
// Get the position of the object3D in world space
|
// Get the position of the object3D in world space
|
||||||
|
@ -58,7 +58,7 @@ import { err } from 'lib/trap'
|
|||||||
|
|
||||||
interface CreateSegmentArgs {
|
interface CreateSegmentArgs {
|
||||||
input: SegmentInputs
|
input: SegmentInputs
|
||||||
prevSegment: Sketch['value'][number]
|
prevSegment: Sketch['paths'][number]
|
||||||
id: string
|
id: string
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
isDraftSegment?: boolean
|
isDraftSegment?: boolean
|
||||||
@ -72,7 +72,7 @@ interface CreateSegmentArgs {
|
|||||||
|
|
||||||
interface UpdateSegmentArgs {
|
interface UpdateSegmentArgs {
|
||||||
input: SegmentInputs
|
input: SegmentInputs
|
||||||
prevSegment: Sketch['value'][number]
|
prevSegment: Sketch['paths'][number]
|
||||||
group: Group
|
group: Group
|
||||||
sceneInfra: SceneInfra
|
sceneInfra: SceneInfra
|
||||||
scale?: number
|
scale?: number
|
||||||
@ -147,6 +147,7 @@ class StraightSegment implements SegmentUtils {
|
|||||||
segmentGroup.name = STRAIGHT_SEGMENT
|
segmentGroup.name = STRAIGHT_SEGMENT
|
||||||
segmentGroup.userData = {
|
segmentGroup.userData = {
|
||||||
type: STRAIGHT_SEGMENT,
|
type: STRAIGHT_SEGMENT,
|
||||||
|
draft: isDraftSegment,
|
||||||
id,
|
id,
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
@ -347,6 +348,7 @@ class TangentialArcToSegment implements SegmentUtils {
|
|||||||
mesh.name = meshName
|
mesh.name = meshName
|
||||||
group.userData = {
|
group.userData = {
|
||||||
type: TANGENTIAL_ARC_TO_SEGMENT,
|
type: TANGENTIAL_ARC_TO_SEGMENT,
|
||||||
|
draft: isDraftSegment,
|
||||||
id,
|
id,
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
@ -515,11 +517,18 @@ class CircleSegment implements SegmentUtils {
|
|||||||
const meshType = isDraftSegment ? CIRCLE_SEGMENT_DASH : CIRCLE_SEGMENT_BODY
|
const meshType = isDraftSegment ? CIRCLE_SEGMENT_DASH : CIRCLE_SEGMENT_BODY
|
||||||
const arrowGroup = createArrowhead(scale, theme, color)
|
const arrowGroup = createArrowhead(scale, theme, color)
|
||||||
const circleCenterGroup = createCircleCenterHandle(scale, theme, color)
|
const circleCenterGroup = createCircleCenterHandle(scale, theme, color)
|
||||||
|
// A radius indicator that appears from the center to the perimeter
|
||||||
|
const radiusIndicatorGroup = createLengthIndicator({
|
||||||
|
from: center,
|
||||||
|
to: [center[0] + radius, center[1]],
|
||||||
|
scale,
|
||||||
|
})
|
||||||
|
|
||||||
arcMesh.userData.type = meshType
|
arcMesh.userData.type = meshType
|
||||||
arcMesh.name = meshType
|
arcMesh.name = meshType
|
||||||
group.userData = {
|
group.userData = {
|
||||||
type: CIRCLE_SEGMENT,
|
type: CIRCLE_SEGMENT,
|
||||||
|
draft: isDraftSegment,
|
||||||
id,
|
id,
|
||||||
from,
|
from,
|
||||||
radius,
|
radius,
|
||||||
@ -532,7 +541,7 @@ class CircleSegment implements SegmentUtils {
|
|||||||
}
|
}
|
||||||
group.name = CIRCLE_SEGMENT
|
group.name = CIRCLE_SEGMENT
|
||||||
|
|
||||||
group.add(arcMesh, arrowGroup, circleCenterGroup)
|
group.add(arcMesh, arrowGroup, circleCenterGroup, radiusIndicatorGroup)
|
||||||
const updateOverlaysCallback = this.update({
|
const updateOverlaysCallback = this.update({
|
||||||
prevSegment,
|
prevSegment,
|
||||||
input,
|
input,
|
||||||
@ -564,6 +573,9 @@ class CircleSegment implements SegmentUtils {
|
|||||||
group.userData.radius = radius
|
group.userData.radius = radius
|
||||||
group.userData.prevSegment = prevSegment
|
group.userData.prevSegment = prevSegment
|
||||||
const arrowGroup = group.getObjectByName(ARROWHEAD) as Group
|
const arrowGroup = group.getObjectByName(ARROWHEAD) as Group
|
||||||
|
const radiusLengthIndicator = group.getObjectByName(
|
||||||
|
SEGMENT_LENGTH_LABEL
|
||||||
|
) as Group
|
||||||
const circleCenterHandle = group.getObjectByName(
|
const circleCenterHandle = group.getObjectByName(
|
||||||
CIRCLE_CENTER_HANDLE
|
CIRCLE_CENTER_HANDLE
|
||||||
) as Group
|
) as Group
|
||||||
@ -581,11 +593,14 @@ class CircleSegment implements SegmentUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (arrowGroup) {
|
if (arrowGroup) {
|
||||||
arrowGroup.position.set(
|
// The arrowhead is placed at the perimeter of the circle,
|
||||||
center[0] + Math.cos(Math.PI / 4) * radius,
|
// pointing up and to the right
|
||||||
center[1] + Math.sin(Math.PI / 4) * radius,
|
const arrowPoint = {
|
||||||
0
|
x: center[0] + Math.cos(Math.PI / 4) * radius,
|
||||||
)
|
y: center[1] + Math.sin(Math.PI / 4) * radius,
|
||||||
|
}
|
||||||
|
|
||||||
|
arrowGroup.position.set(arrowPoint.x, arrowPoint.y, 0)
|
||||||
|
|
||||||
const arrowheadAngle = Math.PI / 4
|
const arrowheadAngle = Math.PI / 4
|
||||||
arrowGroup.quaternion.setFromUnitVectors(
|
arrowGroup.quaternion.setFromUnitVectors(
|
||||||
@ -596,6 +611,31 @@ class CircleSegment implements SegmentUtils {
|
|||||||
arrowGroup.visible = isHandlesVisible
|
arrowGroup.visible = isHandlesVisible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (radiusLengthIndicator) {
|
||||||
|
// The radius indicator is placed at the midpoint of the radius,
|
||||||
|
// at a 45 degree CCW angle from the positive X-axis
|
||||||
|
const indicatorPoint = {
|
||||||
|
x: center[0] + (Math.cos(Math.PI / 4) * radius) / 2,
|
||||||
|
y: center[1] + (Math.sin(Math.PI / 4) * radius) / 2,
|
||||||
|
}
|
||||||
|
const labelWrapper = radiusLengthIndicator.getObjectByName(
|
||||||
|
SEGMENT_LENGTH_LABEL_TEXT
|
||||||
|
) as CSS2DObject
|
||||||
|
const labelWrapperElem = labelWrapper.element as HTMLDivElement
|
||||||
|
const label = labelWrapperElem.children[0] as HTMLParagraphElement
|
||||||
|
label.innerText = `${roundOff(radius)}`
|
||||||
|
label.classList.add(SEGMENT_LENGTH_LABEL_TEXT)
|
||||||
|
const isPlaneBackFace = center[0] > indicatorPoint.x
|
||||||
|
label.style.setProperty(
|
||||||
|
'--degree',
|
||||||
|
`${isPlaneBackFace ? '45' : '-45'}deg`
|
||||||
|
)
|
||||||
|
label.style.setProperty('--x', `0px`)
|
||||||
|
label.style.setProperty('--y', `0px`)
|
||||||
|
labelWrapper.position.set(indicatorPoint.x, indicatorPoint.y, 0)
|
||||||
|
radiusLengthIndicator.visible = isHandlesVisible
|
||||||
|
}
|
||||||
|
|
||||||
if (circleCenterHandle) {
|
if (circleCenterHandle) {
|
||||||
circleCenterHandle.position.set(center[0], center[1], 0)
|
circleCenterHandle.position.set(center[0], center[1], 0)
|
||||||
circleCenterHandle.scale.set(scale, scale, scale)
|
circleCenterHandle.scale.set(scale, scale, scale)
|
||||||
|
@ -140,6 +140,13 @@ const FileTreeItem = ({
|
|||||||
async (eventType, path) => {
|
async (eventType, path) => {
|
||||||
// Don't try to read a file that was removed.
|
// Don't try to read a file that was removed.
|
||||||
if (isCurrentFile && eventType !== 'unlink') {
|
if (isCurrentFile && eventType !== 'unlink') {
|
||||||
|
// Prevents a cyclic read / write causing editor problems such as
|
||||||
|
// misplaced cursor positions.
|
||||||
|
if (codeManager.writeCausedByAppCheckedInFileTreeFileSystemWatcher) {
|
||||||
|
codeManager.writeCausedByAppCheckedInFileTreeFileSystemWatcher = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let code = await window.electron.readFile(path, { encoding: 'utf-8' })
|
let code = await window.electron.readFile(path, { encoding: 'utf-8' })
|
||||||
code = normalizeLineEndings(code)
|
code = normalizeLineEndings(code)
|
||||||
codeManager.updateCodeStateEditor(code)
|
codeManager.updateCodeStateEditor(code)
|
||||||
@ -488,6 +495,12 @@ export const FileTreeInner = ({
|
|||||||
// Refresh the file tree when there are changes.
|
// Refresh the file tree when there are changes.
|
||||||
useFileSystemWatcher(
|
useFileSystemWatcher(
|
||||||
async (eventType, path) => {
|
async (eventType, path) => {
|
||||||
|
// Our other watcher races with this watcher on the current file changes,
|
||||||
|
// so we need to stop this one from reacting at all, otherwise Bad Things
|
||||||
|
// Happen™.
|
||||||
|
const isCurrentFile = loaderData.file?.path === path
|
||||||
|
const hasChanged = eventType === 'change'
|
||||||
|
if (isCurrentFile && hasChanged) return
|
||||||
fileSend({ type: 'Refresh' })
|
fileSend({ type: 'Refresh' })
|
||||||
},
|
},
|
||||||
[loaderData?.project?.path, fileContext.selectedDirectory.path].filter(
|
[loaderData?.project?.path, fileContext.selectedDirectory.path].filter(
|
||||||
|
@ -23,6 +23,7 @@ export function LowerRightControls({
|
|||||||
}) {
|
}) {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const filePath = useAbsoluteFilePath()
|
const filePath = useAbsoluteFilePath()
|
||||||
|
|
||||||
const linkOverrideClassName =
|
const linkOverrideClassName =
|
||||||
'!text-chalkboard-70 hover:!text-chalkboard-80 dark:!text-chalkboard-40 dark:hover:!text-chalkboard-30'
|
'!text-chalkboard-70 hover:!text-chalkboard-80 dark:!text-chalkboard-40 dark:hover:!text-chalkboard-30'
|
||||||
|
|
||||||
|
123
src/components/MachineManagerProvider.tsx
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import { createContext, useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
import { engineCommandManager } from 'lib/singletons'
|
||||||
|
import { CommandsContext } from 'components/CommandBar/CommandBarProvider'
|
||||||
|
import { isDesktop } from 'lib/isDesktop'
|
||||||
|
import { components } from 'lib/machine-api'
|
||||||
|
import { reportRejection } from 'lib/trap'
|
||||||
|
import { toSync } from 'lib/utils'
|
||||||
|
|
||||||
|
export type MachinesListing = Array<
|
||||||
|
components['schemas']['MachineInfoResponse']
|
||||||
|
>
|
||||||
|
|
||||||
|
export interface MachineManager {
|
||||||
|
machines: MachinesListing
|
||||||
|
machineApiIp: string | null
|
||||||
|
currentMachine: components['schemas']['MachineInfoResponse'] | null
|
||||||
|
noMachinesReason: () => string | undefined
|
||||||
|
setCurrentMachine: (
|
||||||
|
m: components['schemas']['MachineInfoResponse'] | null
|
||||||
|
) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MachineManagerContext = createContext<MachineManager>({
|
||||||
|
machines: [],
|
||||||
|
machineApiIp: null,
|
||||||
|
currentMachine: null,
|
||||||
|
setCurrentMachine: (
|
||||||
|
_: components['schemas']['MachineInfoResponse'] | null
|
||||||
|
) => {},
|
||||||
|
noMachinesReason: () => undefined,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const MachineManagerProvider = ({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode
|
||||||
|
}) => {
|
||||||
|
const [machines, setMachines] = useState<MachinesListing>([])
|
||||||
|
const [machineApiIp, setMachineApiIp] = useState<string | null>(null)
|
||||||
|
const [currentMachine, setCurrentMachine] = useState<
|
||||||
|
components['schemas']['MachineInfoResponse'] | null
|
||||||
|
>(null)
|
||||||
|
|
||||||
|
const commandBarActor = CommandsContext.useActorRef()
|
||||||
|
|
||||||
|
// Get the reason message for why there are no machines.
|
||||||
|
const noMachinesReason = (): string | undefined => {
|
||||||
|
if (machines.length > 0) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
if (machineApiIp === null) {
|
||||||
|
return 'Machine API server was not discovered'
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Machine API server was discovered, but no machines are available'
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isDesktop()) return
|
||||||
|
|
||||||
|
const update = async () => {
|
||||||
|
const _machineApiIp = await window.electron.getMachineApiIp()
|
||||||
|
if (_machineApiIp === null) return
|
||||||
|
|
||||||
|
setMachineApiIp(_machineApiIp)
|
||||||
|
|
||||||
|
const _machines = await window.electron.listMachines(_machineApiIp)
|
||||||
|
setMachines(_machines)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a background job to update the machines every ten seconds.
|
||||||
|
// If MDNS is already watching, this timeout will wait until it's done to trigger the
|
||||||
|
// finding again.
|
||||||
|
let timeoutId: ReturnType<typeof setTimeout> | undefined = undefined
|
||||||
|
const timeoutLoop = () => {
|
||||||
|
clearTimeout(timeoutId)
|
||||||
|
timeoutId = setTimeout(
|
||||||
|
toSync(async () => {
|
||||||
|
await update()
|
||||||
|
timeoutLoop()
|
||||||
|
}, reportRejection),
|
||||||
|
1000
|
||||||
|
)
|
||||||
|
}
|
||||||
|
timeoutLoop()
|
||||||
|
update().catch(reportRejection)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// Update engineCommandManager's copy of this data.
|
||||||
|
useEffect(() => {
|
||||||
|
const machineManagerNext = {
|
||||||
|
machines,
|
||||||
|
machineApiIp,
|
||||||
|
currentMachine,
|
||||||
|
noMachinesReason,
|
||||||
|
setCurrentMachine,
|
||||||
|
}
|
||||||
|
|
||||||
|
engineCommandManager.machineManager = machineManagerNext
|
||||||
|
|
||||||
|
commandBarActor.send({
|
||||||
|
type: 'Set machine manager',
|
||||||
|
data: machineManagerNext,
|
||||||
|
})
|
||||||
|
}, [machines, machineApiIp, currentMachine])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MachineManagerContext.Provider
|
||||||
|
value={{
|
||||||
|
machines,
|
||||||
|
machineApiIp,
|
||||||
|
currentMachine,
|
||||||
|
setCurrentMachine,
|
||||||
|
noMachinesReason,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{' '}
|
||||||
|
{children}{' '}
|
||||||
|
</MachineManagerContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
@ -1,5 +1,11 @@
|
|||||||
import { useMachine } from '@xstate/react'
|
import { useMachine } from '@xstate/react'
|
||||||
import React, { createContext, useEffect, useMemo, useRef } from 'react'
|
import React, {
|
||||||
|
createContext,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useContext,
|
||||||
|
} from 'react'
|
||||||
import {
|
import {
|
||||||
Actor,
|
Actor,
|
||||||
AnyStateMachine,
|
AnyStateMachine,
|
||||||
@ -28,7 +34,7 @@ import {
|
|||||||
editorManager,
|
editorManager,
|
||||||
sceneEntitiesManager,
|
sceneEntitiesManager,
|
||||||
} from 'lib/singletons'
|
} from 'lib/singletons'
|
||||||
import { machineManager } from 'lib/machineManager'
|
import { MachineManagerContext } from 'components/MachineManagerProvider'
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance'
|
import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance'
|
||||||
import {
|
import {
|
||||||
@ -69,7 +75,7 @@ import { exportFromEngine } from 'lib/exportFromEngine'
|
|||||||
import { Models } from '@kittycad/lib/dist/types/src'
|
import { Models } from '@kittycad/lib/dist/types/src'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { EditorSelection, Transaction } from '@codemirror/state'
|
import { EditorSelection, Transaction } from '@codemirror/state'
|
||||||
import { useNavigate, useSearchParams } from 'react-router-dom'
|
import { useLoaderData, useNavigate, useSearchParams } from 'react-router-dom'
|
||||||
import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls'
|
import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls'
|
||||||
import { getVarNameModal } from 'hooks/useToolbarGuards'
|
import { getVarNameModal } from 'hooks/useToolbarGuards'
|
||||||
import { err, reportRejection, trap } from 'lib/trap'
|
import { err, reportRejection, trap } from 'lib/trap'
|
||||||
@ -84,6 +90,7 @@ import {
|
|||||||
import { submitAndAwaitTextToKcl } from 'lib/textToCad'
|
import { submitAndAwaitTextToKcl } from 'lib/textToCad'
|
||||||
import { useFileContext } from 'hooks/useFileContext'
|
import { useFileContext } from 'hooks/useFileContext'
|
||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
|
import { IndexLoaderData } from 'lib/types'
|
||||||
|
|
||||||
type MachineContext<T extends AnyStateMachine> = {
|
type MachineContext<T extends AnyStateMachine> = {
|
||||||
state: StateFrom<T>
|
state: StateFrom<T>
|
||||||
@ -116,6 +123,7 @@ export const ModelingMachineProvider = ({
|
|||||||
} = useSettingsAuthContext()
|
} = useSettingsAuthContext()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { context, send: fileMachineSend } = useFileContext()
|
const { context, send: fileMachineSend } = useFileContext()
|
||||||
|
const { file } = useLoaderData() as IndexLoaderData
|
||||||
const token = auth?.context?.token
|
const token = auth?.context?.token
|
||||||
const streamRef = useRef<HTMLDivElement>(null)
|
const streamRef = useRef<HTMLDivElement>(null)
|
||||||
const persistedContext = useMemo(() => getPersistedContext(), [])
|
const persistedContext = useMemo(() => getPersistedContext(), [])
|
||||||
@ -138,6 +146,8 @@ export const ModelingMachineProvider = ({
|
|||||||
// >
|
// >
|
||||||
// )
|
// )
|
||||||
|
|
||||||
|
const machineManager = useContext(MachineManagerContext)
|
||||||
|
|
||||||
const [modelingState, modelingSend, modelingActor] = useMachine(
|
const [modelingState, modelingSend, modelingActor] = useMachine(
|
||||||
modelingMachine.provide({
|
modelingMachine.provide({
|
||||||
actions: {
|
actions: {
|
||||||
@ -406,18 +416,35 @@ export const ModelingMachineProvider = ({
|
|||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
Make: ({ event }) => {
|
Make: ({ context, event }) => {
|
||||||
if (event.type !== 'Make') return
|
if (event.type !== 'Make') return
|
||||||
// Check if we already have an export intent.
|
// Check if we already have an export intent.
|
||||||
if (engineCommandManager.exportIntent) {
|
if (engineCommandManager.exportInfo) {
|
||||||
toast.error('Already exporting')
|
toast.error('Already exporting')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Set the export intent.
|
// Set the export intent.
|
||||||
engineCommandManager.exportIntent = ExportIntent.Make
|
engineCommandManager.exportInfo = {
|
||||||
|
intent: ExportIntent.Make,
|
||||||
|
name: file?.name || '',
|
||||||
|
}
|
||||||
|
|
||||||
// Set the current machine.
|
// Set the current machine.
|
||||||
machineManager.currentMachine = event.data.machine
|
// Due to our use of singeton pattern, we need to do this to reliably
|
||||||
|
// update this object across React and non-React boundary.
|
||||||
|
// We need to do this eagerly because of the exportToEngine call below.
|
||||||
|
if (engineCommandManager.machineManager === null) {
|
||||||
|
console.warn(
|
||||||
|
"engineCommandManager.machineManager is null. It shouldn't be at this point. Aborting operation."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
engineCommandManager.machineManager.currentMachine =
|
||||||
|
event.data.machine
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the rest of the UI that needs to know the current machine
|
||||||
|
context.machineManager.setCurrentMachine(event.data.machine)
|
||||||
|
|
||||||
const format: Models['OutputFormat_type'] = {
|
const format: Models['OutputFormat_type'] = {
|
||||||
type: 'stl',
|
type: 'stl',
|
||||||
@ -443,12 +470,16 @@ export const ModelingMachineProvider = ({
|
|||||||
},
|
},
|
||||||
'Engine export': ({ event }) => {
|
'Engine export': ({ event }) => {
|
||||||
if (event.type !== 'Export') return
|
if (event.type !== 'Export') return
|
||||||
if (engineCommandManager.exportIntent) {
|
if (engineCommandManager.exportInfo) {
|
||||||
toast.error('Already exporting')
|
toast.error('Already exporting')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Set the export intent.
|
// Set the export intent.
|
||||||
engineCommandManager.exportIntent = ExportIntent.Save
|
engineCommandManager.exportInfo = {
|
||||||
|
intent: ExportIntent.Save,
|
||||||
|
// This never gets used its only for make.
|
||||||
|
name: '',
|
||||||
|
}
|
||||||
|
|
||||||
const format = {
|
const format = {
|
||||||
...event.data,
|
...event.data,
|
||||||
@ -635,6 +666,7 @@ export const ModelingMachineProvider = ({
|
|||||||
input.plane
|
input.plane
|
||||||
)
|
)
|
||||||
await kclManager.updateAst(modifiedAst, false)
|
await kclManager.updateAst(modifiedAst, false)
|
||||||
|
sceneInfra.camControls.enableRotate = false
|
||||||
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
||||||
|
|
||||||
await letEngineAnimateAndSyncCamAfter(
|
await letEngineAnimateAndSyncCamAfter(
|
||||||
@ -985,6 +1017,7 @@ export const ModelingMachineProvider = ({
|
|||||||
...modelingMachineDefaultContext.store,
|
...modelingMachineDefaultContext.store,
|
||||||
...persistedContext,
|
...persistedContext,
|
||||||
},
|
},
|
||||||
|
machineManager,
|
||||||
},
|
},
|
||||||
// devTools: true,
|
// devTools: true,
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ export const processMemory = (programMemory: ProgramMemory) => {
|
|||||||
return rest
|
return rest
|
||||||
})
|
})
|
||||||
} else if (!err(sg)) {
|
} else if (!err(sg)) {
|
||||||
processedMemory[key] = sg.value.map(({ __geoMeta, ...rest }: Path) => {
|
processedMemory[key] = sg.paths.map(({ __geoMeta, ...rest }: Path) => {
|
||||||
return rest
|
return rest
|
||||||
})
|
})
|
||||||
} else if ((val.type as any) === 'Function') {
|
} else if ((val.type as any) === 'Function') {
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import { Resizable } from 're-resizable'
|
import { Resizable } from 're-resizable'
|
||||||
import { MouseEventHandler, useCallback, useEffect, useMemo } from 'react'
|
import {
|
||||||
|
MouseEventHandler,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useContext,
|
||||||
|
} from 'react'
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
import { SidebarAction, SidebarType, sidebarPanes } from './ModelingPanes'
|
import { SidebarAction, SidebarType, sidebarPanes } from './ModelingPanes'
|
||||||
import Tooltip from 'components/Tooltip'
|
import Tooltip from 'components/Tooltip'
|
||||||
@ -13,7 +19,7 @@ import { CustomIconName } from 'components/CustomIcon'
|
|||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { IconDefinition } from '@fortawesome/free-solid-svg-icons'
|
import { IconDefinition } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { useKclContext } from 'lang/KclProvider'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
import { machineManager } from 'lib/machineManager'
|
import { MachineManagerContext } from 'components/MachineManagerProvider'
|
||||||
|
|
||||||
interface ModelingSidebarProps {
|
interface ModelingSidebarProps {
|
||||||
paneOpacity: '' | 'opacity-20' | 'opacity-40'
|
paneOpacity: '' | 'opacity-20' | 'opacity-40'
|
||||||
@ -29,6 +35,7 @@ function getPlatformString(): 'web' | 'desktop' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||||
|
const machineManager = useContext(MachineManagerContext)
|
||||||
const { commandBarSend } = useCommandsContext()
|
const { commandBarSend } = useCommandsContext()
|
||||||
const kclContext = useKclContext()
|
const kclContext = useKclContext()
|
||||||
const { settings } = useSettingsAuthContext()
|
const { settings } = useSettingsAuthContext()
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import { Popover } from '@headlessui/react'
|
import { Popover } from '@headlessui/react'
|
||||||
|
import { useContext } from 'react'
|
||||||
import Tooltip from './Tooltip'
|
import Tooltip from './Tooltip'
|
||||||
import { machineManager } from 'lib/machineManager'
|
|
||||||
import { isDesktop } from 'lib/isDesktop'
|
import { isDesktop } from 'lib/isDesktop'
|
||||||
|
import { components } from 'lib/machine-api'
|
||||||
|
import { MachineManagerContext } from 'components/MachineManagerProvider'
|
||||||
import { CustomIcon } from './CustomIcon'
|
import { CustomIcon } from './CustomIcon'
|
||||||
|
|
||||||
export const NetworkMachineIndicator = ({
|
export const NetworkMachineIndicator = ({
|
||||||
@ -9,9 +11,12 @@ export const NetworkMachineIndicator = ({
|
|||||||
}: {
|
}: {
|
||||||
className?: string
|
className?: string
|
||||||
}) => {
|
}) => {
|
||||||
const machineCount = machineManager.machineCount()
|
const {
|
||||||
const reason = machineManager.noMachinesReason()
|
noMachinesReason,
|
||||||
const machines = machineManager.machines
|
machines,
|
||||||
|
machines: { length: machineCount },
|
||||||
|
} = useContext(MachineManagerContext)
|
||||||
|
const reason = noMachinesReason()
|
||||||
|
|
||||||
return isDesktop() ? (
|
return isDesktop() ? (
|
||||||
<Popover className="relative">
|
<Popover className="relative">
|
||||||
@ -47,34 +52,36 @@ export const NetworkMachineIndicator = ({
|
|||||||
</div>
|
</div>
|
||||||
{machineCount > 0 && (
|
{machineCount > 0 && (
|
||||||
<ul className="divide-y divide-chalkboard-20 dark:divide-chalkboard-80">
|
<ul className="divide-y divide-chalkboard-20 dark:divide-chalkboard-80">
|
||||||
{machines.map((machine) => {
|
{machines.map(
|
||||||
return (
|
(machine: components['schemas']['MachineInfoResponse']) => {
|
||||||
<li key={machine.id} className={'px-2 py-4 gap-1 last:mb-0 '}>
|
return (
|
||||||
<p className="">{machine.id.toUpperCase()}</p>
|
<li key={machine.id} className={'px-2 py-4 gap-1 last:mb-0 '}>
|
||||||
<p className="text-chalkboard-60 dark:text-chalkboard-50 text-xs">
|
<p className="">{machine.id.toUpperCase()}</p>
|
||||||
{machine.make_model.model}
|
<p className="text-chalkboard-60 dark:text-chalkboard-50 text-xs">
|
||||||
</p>
|
{machine.make_model.model}
|
||||||
{machine.extra &&
|
</p>
|
||||||
machine.extra.type === 'bambu' &&
|
{machine.extra &&
|
||||||
machine.extra.nozzle_diameter && (
|
machine.extra.type === 'bambu' &&
|
||||||
<p className="text-chalkboard-60 dark:text-chalkboard-50 text-xs">
|
machine.extra.nozzle_diameter && (
|
||||||
Nozzle Diameter: {machine.extra.nozzle_diameter}
|
<p className="text-chalkboard-60 dark:text-chalkboard-50 text-xs">
|
||||||
</p>
|
Nozzle Diameter: {machine.extra.nozzle_diameter}
|
||||||
)}
|
</p>
|
||||||
<p className="text-chalkboard-60 dark:text-chalkboard-50 text-xs">
|
)}
|
||||||
{`Status: ${machine.state.state
|
<p className="text-chalkboard-60 dark:text-chalkboard-50 text-xs">
|
||||||
.charAt(0)
|
{`Status: ${machine.state.state
|
||||||
.toUpperCase()}${machine.state.state.slice(1)}`}
|
.charAt(0)
|
||||||
{machine.state.state === 'failed' && machine.state.message
|
.toUpperCase()}${machine.state.state.slice(1)}`}
|
||||||
? ` (${machine.state.message})`
|
{machine.state.state === 'failed' && machine.state.message
|
||||||
: ''}
|
? ` (${machine.state.message})`
|
||||||
{machine.state.state === 'running' && machine.progress
|
: ''}
|
||||||
? ` (${Math.round(machine.progress)}%)`
|
{machine.state.state === 'running' && machine.progress
|
||||||
: ''}
|
? ` (${Math.round(machine.progress)}%)`
|
||||||
</p>
|
: ''}
|
||||||
</li>
|
</p>
|
||||||
)
|
</li>
|
||||||
})}
|
)
|
||||||
|
}
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
</Popover.Panel>
|
</Popover.Panel>
|
||||||
|
@ -4,14 +4,14 @@ import { type IndexLoaderData } from 'lib/types'
|
|||||||
import { PATHS } from 'lib/paths'
|
import { PATHS } from 'lib/paths'
|
||||||
import { isDesktop } from '../lib/isDesktop'
|
import { isDesktop } from '../lib/isDesktop'
|
||||||
import { Link, useLocation, useNavigate } from 'react-router-dom'
|
import { Link, useLocation, useNavigate } from 'react-router-dom'
|
||||||
import { Fragment, useMemo } from 'react'
|
import { Fragment, useMemo, useContext } from 'react'
|
||||||
import { Logo } from './Logo'
|
import { Logo } from './Logo'
|
||||||
import { APP_NAME } from 'lib/constants'
|
import { APP_NAME } from 'lib/constants'
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { CustomIcon } from './CustomIcon'
|
import { CustomIcon } from './CustomIcon'
|
||||||
import { useLspContext } from './LspProvider'
|
import { useLspContext } from './LspProvider'
|
||||||
import { engineCommandManager } from 'lib/singletons'
|
import { engineCommandManager } from 'lib/singletons'
|
||||||
import { machineManager } from 'lib/machineManager'
|
import { MachineManagerContext } from 'components/MachineManagerProvider'
|
||||||
import usePlatform from 'hooks/usePlatform'
|
import usePlatform from 'hooks/usePlatform'
|
||||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||||
import Tooltip from './Tooltip'
|
import Tooltip from './Tooltip'
|
||||||
@ -96,6 +96,8 @@ function ProjectMenuPopover({
|
|||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const filePath = useAbsoluteFilePath()
|
const filePath = useAbsoluteFilePath()
|
||||||
|
const machineManager = useContext(MachineManagerContext)
|
||||||
|
|
||||||
const { commandBarState, commandBarSend } = useCommandsContext()
|
const { commandBarState, commandBarSend } = useCommandsContext()
|
||||||
const { onProjectClose } = useLspContext()
|
const { onProjectClose } = useLspContext()
|
||||||
const exportCommandInfo = { name: 'Export', groupId: 'modeling' }
|
const exportCommandInfo = { name: 'Export', groupId: 'modeling' }
|
||||||
@ -106,7 +108,7 @@ function ProjectMenuPopover({
|
|||||||
(c) => c.name === obj.name && c.groupId === obj.groupId
|
(c) => c.name === obj.name && c.groupId === obj.groupId
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
const machineCount = machineManager.machineCount()
|
const machineCount = machineManager.machines.length
|
||||||
|
|
||||||
// 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 projectMenuItems = useMemo<(ActionButtonProps | 'break')[]>(
|
const projectMenuItems = useMemo<(ActionButtonProps | 'break')[]>(
|
||||||
|
@ -255,10 +255,14 @@ export const Stream = () => {
|
|||||||
}, [mediaStream])
|
}, [mediaStream])
|
||||||
|
|
||||||
const handleMouseUp: MouseEventHandler<HTMLDivElement> = (e) => {
|
const handleMouseUp: MouseEventHandler<HTMLDivElement> = (e) => {
|
||||||
|
// If we've got no stream or connection, don't do anything
|
||||||
if (!isNetworkOkay) return
|
if (!isNetworkOkay) return
|
||||||
if (!videoRef.current) return
|
if (!videoRef.current) return
|
||||||
|
// If we're in sketch mode, don't send a engine-side select event
|
||||||
if (state.matches('Sketch')) return
|
if (state.matches('Sketch')) return
|
||||||
if (state.matches({ idle: 'showPlanes' })) return
|
if (state.matches({ idle: 'showPlanes' })) return
|
||||||
|
// If we're mousing up from a camera drag, don't send a select event
|
||||||
|
if (sceneInfra.camControls.wasDragging === true) return
|
||||||
|
|
||||||
if (btnName(e.nativeEvent).left) {
|
if (btnName(e.nativeEvent).left) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
|
@ -32,7 +32,7 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
sourceRange: [46, 71],
|
sourceRange: [46, 71],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
value: [
|
paths: [
|
||||||
{
|
{
|
||||||
type: 'ToPoint',
|
type: 'ToPoint',
|
||||||
tag: null,
|
tag: null,
|
||||||
@ -96,7 +96,7 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
on: expect.any(Object),
|
on: expect.any(Object),
|
||||||
start: expect.any(Object),
|
start: expect.any(Object),
|
||||||
type: 'Sketch',
|
type: 'Sketch',
|
||||||
value: [
|
paths: [
|
||||||
{
|
{
|
||||||
type: 'ToPoint',
|
type: 'ToPoint',
|
||||||
from: [0, 0],
|
from: [0, 0],
|
||||||
@ -202,7 +202,7 @@ const sk2 = startSketchOn('XY')
|
|||||||
info: expect.any(Object),
|
info: expect.any(Object),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
value: [
|
paths: [
|
||||||
{
|
{
|
||||||
type: 'ToPoint',
|
type: 'ToPoint',
|
||||||
from: [0, 0],
|
from: [0, 0],
|
||||||
@ -294,7 +294,7 @@ const sk2 = startSketchOn('XY')
|
|||||||
info: expect.any(Object),
|
info: expect.any(Object),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
value: [
|
paths: [
|
||||||
{
|
{
|
||||||
type: 'ToPoint',
|
type: 'ToPoint',
|
||||||
from: [0, 0],
|
from: [0, 0],
|
||||||
|
@ -18,6 +18,9 @@ export default class CodeManager {
|
|||||||
#updateState: (arg: string) => void = () => {}
|
#updateState: (arg: string) => void = () => {}
|
||||||
private _currentFilePath: string | null = null
|
private _currentFilePath: string | null = null
|
||||||
private _hotkeys: { [key: string]: () => void } = {}
|
private _hotkeys: { [key: string]: () => void } = {}
|
||||||
|
private timeoutWriter: ReturnType<typeof setTimeout> | undefined = undefined
|
||||||
|
|
||||||
|
public writeCausedByAppCheckedInFileTreeFileSystemWatcher = false
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
if (isDesktop()) {
|
if (isDesktop()) {
|
||||||
@ -115,7 +118,12 @@ export default class CodeManager {
|
|||||||
|
|
||||||
async writeToFile() {
|
async writeToFile() {
|
||||||
if (isDesktop()) {
|
if (isDesktop()) {
|
||||||
setTimeout(() => {
|
// Only write our buffer contents to file once per second. Any faster
|
||||||
|
// and file-system watchers which read, will receive empty data during
|
||||||
|
// writes.
|
||||||
|
clearTimeout(this.timeoutWriter)
|
||||||
|
this.writeCausedByAppCheckedInFileTreeFileSystemWatcher = true
|
||||||
|
this.timeoutWriter = setTimeout(() => {
|
||||||
// Wait one event loop to give a chance for params to be set
|
// Wait one event loop to give a chance for params to be set
|
||||||
// Save the file to disk
|
// Save the file to disk
|
||||||
this._currentFilePath &&
|
this._currentFilePath &&
|
||||||
@ -126,7 +134,7 @@ export default class CodeManager {
|
|||||||
console.error('error saving file', err)
|
console.error('error saving file', err)
|
||||||
toast.error('Error saving file, please check file permissions')
|
toast.error('Error saving file, please check file permissions')
|
||||||
})
|
})
|
||||||
})
|
}, 1000)
|
||||||
} else {
|
} else {
|
||||||
safeLSSetItem(PERSIST_CODE_KEY, this.code)
|
safeLSSetItem(PERSIST_CODE_KEY, this.code)
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ const newVar = myVar + 1`
|
|||||||
`
|
`
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
// geo is three js buffer geometry and is very bloated to have in tests
|
// geo is three js buffer geometry and is very bloated to have in tests
|
||||||
const minusGeo = mem.get('mySketch')?.value?.value
|
const minusGeo = mem.get('mySketch')?.value?.paths
|
||||||
expect(minusGeo).toEqual([
|
expect(minusGeo).toEqual([
|
||||||
{
|
{
|
||||||
type: 'ToPoint',
|
type: 'ToPoint',
|
||||||
@ -175,7 +175,7 @@ const newVar = myVar + 1`
|
|||||||
info: expect.any(Object),
|
info: expect.any(Object),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
value: [
|
paths: [
|
||||||
{
|
{
|
||||||
type: 'ToPoint',
|
type: 'ToPoint',
|
||||||
to: [1, 1],
|
to: [1, 1],
|
||||||
@ -367,7 +367,7 @@ describe('testing math operators', () => {
|
|||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
const sketch = sketchFromKclValue(mem.get('part001'), 'part001')
|
const sketch = sketchFromKclValue(mem.get('part001'), 'part001')
|
||||||
// result of `-legLen(5, min(3, 999))` should be -4
|
// result of `-legLen(5, min(3, 999))` should be -4
|
||||||
const yVal = (sketch as Sketch).value?.[0]?.to?.[1]
|
const yVal = (sketch as Sketch).paths?.[0]?.to?.[1]
|
||||||
expect(yVal).toBe(-4)
|
expect(yVal).toBe(-4)
|
||||||
})
|
})
|
||||||
it('test that % substitution feeds down CallExp->ArrExp->UnaryExp->CallExp', async () => {
|
it('test that % substitution feeds down CallExp->ArrExp->UnaryExp->CallExp', async () => {
|
||||||
@ -385,8 +385,8 @@ describe('testing math operators', () => {
|
|||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
const sketch = sketchFromKclValue(mem.get('part001'), 'part001')
|
const sketch = sketchFromKclValue(mem.get('part001'), 'part001')
|
||||||
// expect -legLen(segLen('seg01'), myVar) to equal -4 setting the y value back to 0
|
// expect -legLen(segLen('seg01'), myVar) to equal -4 setting the y value back to 0
|
||||||
expect((sketch as Sketch).value?.[1]?.from).toEqual([3, 4])
|
expect((sketch as Sketch).paths?.[1]?.from).toEqual([3, 4])
|
||||||
expect((sketch as Sketch).value?.[1]?.to).toEqual([6, 0])
|
expect((sketch as Sketch).paths?.[1]?.to).toEqual([6, 0])
|
||||||
const removedUnaryExp = code.replace(
|
const removedUnaryExp = code.replace(
|
||||||
`-legLen(segLen(seg01), myVar)`,
|
`-legLen(segLen(seg01), myVar)`,
|
||||||
`legLen(segLen(seg01), myVar)`
|
`legLen(segLen(seg01), myVar)`
|
||||||
@ -398,7 +398,7 @@ describe('testing math operators', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// without the minus sign, the y value should be 8
|
// without the minus sign, the y value should be 8
|
||||||
expect((removedUnaryExpMemSketch as Sketch).value?.[1]?.to).toEqual([6, 8])
|
expect((removedUnaryExpMemSketch as Sketch).paths?.[1]?.to).toEqual([6, 8])
|
||||||
})
|
})
|
||||||
it('with nested callExpression and binaryExpression', async () => {
|
it('with nested callExpression and binaryExpression', async () => {
|
||||||
const code = 'const myVar = 2 + min(100, -1 + legLen(5, 3))'
|
const code = 'const myVar = 2 + min(100, -1 + legLen(5, 3))'
|
||||||
|
@ -717,7 +717,7 @@ export function isLinesParallelAndConstrained(
|
|||||||
constraintType === 'angle' || constraintLevel === 'full'
|
constraintType === 'angle' || constraintLevel === 'full'
|
||||||
|
|
||||||
// get the previous segment
|
// get the previous segment
|
||||||
const prevSegment = sg.value[secondaryIndex - 1]
|
const prevSegment = sg.paths[secondaryIndex - 1]
|
||||||
const prevSourceRange = prevSegment.__geoMeta.sourceRange
|
const prevSourceRange = prevSegment.__geoMeta.sourceRange
|
||||||
|
|
||||||
const isParallelAndConstrained =
|
const isParallelAndConstrained =
|
||||||
|
@ -28,6 +28,7 @@ import {
|
|||||||
} from 'lib/constants'
|
} from 'lib/constants'
|
||||||
import { KclManager } from 'lang/KclSingleton'
|
import { KclManager } from 'lang/KclSingleton'
|
||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
|
import { MachineManager } from 'components/MachineManagerProvider'
|
||||||
|
|
||||||
// TODO(paultag): This ought to be tweakable.
|
// TODO(paultag): This ought to be tweakable.
|
||||||
const pingIntervalMs = 5_000
|
const pingIntervalMs = 5_000
|
||||||
@ -50,6 +51,11 @@ export enum ExportIntent {
|
|||||||
Make = 'make',
|
Make = 'make',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ExportInfo {
|
||||||
|
intent: ExportIntent
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
type ClientMetrics = Models['ClientMetrics_type']
|
type ClientMetrics = Models['ClientMetrics_type']
|
||||||
|
|
||||||
interface WebRTCClientMetrics extends ClientMetrics {
|
interface WebRTCClientMetrics extends ClientMetrics {
|
||||||
@ -1354,7 +1360,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
* export in progress. Otherwise it is an enum value of the intent.
|
* export in progress. Otherwise it is an enum value of the intent.
|
||||||
* Another export cannot be started if one is already in progress.
|
* Another export cannot be started if one is already in progress.
|
||||||
*/
|
*/
|
||||||
private _exportIntent: ExportIntent | null = null
|
private _exportInfo: ExportInfo | null = null
|
||||||
_commandLogCallBack: (command: CommandLog[]) => void = () => {}
|
_commandLogCallBack: (command: CommandLog[]) => void = () => {}
|
||||||
|
|
||||||
subscriptions: {
|
subscriptions: {
|
||||||
@ -1410,12 +1416,15 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
(() => {}) as any
|
(() => {}) as any
|
||||||
kclManager: null | KclManager = null
|
kclManager: null | KclManager = null
|
||||||
|
|
||||||
set exportIntent(intent: ExportIntent | null) {
|
// The current "manufacturing machine" aka 3D printer, CNC, etc.
|
||||||
this._exportIntent = intent
|
public machineManager: MachineManager | null = null
|
||||||
|
|
||||||
|
set exportInfo(info: ExportInfo | null) {
|
||||||
|
this._exportInfo = info
|
||||||
}
|
}
|
||||||
|
|
||||||
get exportIntent() {
|
get exportInfo() {
|
||||||
return this._exportIntent
|
return this._exportInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
start({
|
start({
|
||||||
@ -1607,7 +1616,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
// because in all other cases we send JSON strings. But in the case of
|
// because in all other cases we send JSON strings. But in the case of
|
||||||
// export we send a binary blob.
|
// export we send a binary blob.
|
||||||
// Pass this to our export function.
|
// Pass this to our export function.
|
||||||
if (this.exportIntent === null || this.pendingExport === undefined) {
|
if (this.exportInfo === null || this.pendingExport === undefined) {
|
||||||
toast.error(
|
toast.error(
|
||||||
'Export intent was not set, but export data was received'
|
'Export intent was not set, but export data was received'
|
||||||
)
|
)
|
||||||
@ -1617,7 +1626,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (this.exportIntent) {
|
switch (this.exportInfo.intent) {
|
||||||
case ExportIntent.Save: {
|
case ExportIntent.Save: {
|
||||||
exportSave(event.data, this.pendingExport.toastId).then(() => {
|
exportSave(event.data, this.pendingExport.toastId).then(() => {
|
||||||
this.pendingExport?.resolve(null)
|
this.pendingExport?.resolve(null)
|
||||||
@ -1625,21 +1634,28 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
case ExportIntent.Make: {
|
case ExportIntent.Make: {
|
||||||
exportMake(event.data, this.pendingExport.toastId).then(
|
if (!this.machineManager) {
|
||||||
(result) => {
|
console.warn('Some how, no manufacturing machine is selected.')
|
||||||
if (result) {
|
break
|
||||||
this.pendingExport?.resolve(null)
|
}
|
||||||
} else {
|
|
||||||
this.pendingExport?.reject('Failed to make export')
|
exportMake(
|
||||||
}
|
event.data,
|
||||||
},
|
this.exportInfo.name,
|
||||||
this.pendingExport?.reject
|
this.pendingExport.toastId,
|
||||||
)
|
this.machineManager
|
||||||
|
).then((result) => {
|
||||||
|
if (result) {
|
||||||
|
this.pendingExport?.resolve(null)
|
||||||
|
} else {
|
||||||
|
this.pendingExport?.reject('Failed to make export')
|
||||||
|
}
|
||||||
|
}, this.pendingExport?.reject)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Set the export intent back to null.
|
// Set the export intent back to null.
|
||||||
this.exportIntent = null
|
this.exportInfo = null
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1953,15 +1969,15 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
return Promise.resolve(null)
|
return Promise.resolve(null)
|
||||||
} else if (cmd.type === 'export') {
|
} else if (cmd.type === 'export') {
|
||||||
const promise = new Promise<null>((resolve, reject) => {
|
const promise = new Promise<null>((resolve, reject) => {
|
||||||
if (this.exportIntent === null) {
|
if (this.exportInfo === null) {
|
||||||
if (this.exportIntent === null) {
|
if (this.exportInfo === null) {
|
||||||
toast.error('Export intent was not set, but export is being sent')
|
toast.error('Export intent was not set, but export is being sent')
|
||||||
console.error('Export intent was not set, but export is being sent')
|
console.error('Export intent was not set, but export is being sent')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const toastId = toast.loading(
|
const toastId = toast.loading(
|
||||||
this.exportIntent === ExportIntent.Save
|
this.exportInfo.intent === ExportIntent.Save
|
||||||
? EXPORT_TOAST_MESSAGES.START
|
? EXPORT_TOAST_MESSAGES.START
|
||||||
: MAKE_TOAST_MESSAGES.START
|
: MAKE_TOAST_MESSAGES.START
|
||||||
)
|
)
|
||||||
@ -1975,7 +1991,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
resolve(passThrough)
|
resolve(passThrough)
|
||||||
},
|
},
|
||||||
reject: (reason: string) => {
|
reject: (reason: string) => {
|
||||||
this.exportIntent = null
|
this.exportInfo = null
|
||||||
reject(reason)
|
reject(reason)
|
||||||
},
|
},
|
||||||
commandId: command.cmd_id,
|
commandId: command.cmd_id,
|
||||||
|
@ -64,7 +64,7 @@ const ARC_SEGMENT_ERR = new Error('Invalid input, expected "arc-segment"')
|
|||||||
export type Coords2d = [number, number]
|
export type Coords2d = [number, number]
|
||||||
|
|
||||||
export function getCoordsFromPaths(skGroup: Sketch, index = 0): Coords2d {
|
export function getCoordsFromPaths(skGroup: Sketch, index = 0): Coords2d {
|
||||||
const currentPath = skGroup?.value?.[index]
|
const currentPath = skGroup?.paths?.[index]
|
||||||
if (!currentPath && skGroup?.start) {
|
if (!currentPath && skGroup?.start) {
|
||||||
return skGroup.start.to
|
return skGroup.start.to
|
||||||
} else if (!currentPath) {
|
} else if (!currentPath) {
|
||||||
@ -1704,7 +1704,7 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
|||||||
varName
|
varName
|
||||||
)
|
)
|
||||||
if (err(sketch)) return sketch
|
if (err(sketch)) return sketch
|
||||||
const intersectPath = sketch.value.find(
|
const intersectPath = sketch.paths.find(
|
||||||
({ tag }: Path) => tag && tag.value === intersectTagName
|
({ tag }: Path) => tag && tag.value === intersectTagName
|
||||||
)
|
)
|
||||||
let offset = 0
|
let offset = 0
|
||||||
|
@ -18,7 +18,7 @@ export function getSketchSegmentFromPathToNode(
|
|||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
segment: Sketch['value'][number]
|
segment: Sketch['paths'][number]
|
||||||
index: number
|
index: number
|
||||||
}
|
}
|
||||||
| Error {
|
| Error {
|
||||||
@ -39,15 +39,15 @@ export function getSketchSegmentFromSourceRange(
|
|||||||
[rangeStart, rangeEnd]: SourceRange
|
[rangeStart, rangeEnd]: SourceRange
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
segment: Sketch['value'][number]
|
segment: Sketch['paths'][number]
|
||||||
index: number
|
index: number
|
||||||
}
|
}
|
||||||
| Error {
|
| Error {
|
||||||
const lineIndex = sketch.value.findIndex(
|
const lineIndex = sketch.paths.findIndex(
|
||||||
({ __geoMeta: { sourceRange } }: Path) =>
|
({ __geoMeta: { sourceRange } }: Path) =>
|
||||||
sourceRange[0] <= rangeStart && sourceRange[1] >= rangeEnd
|
sourceRange[0] <= rangeStart && sourceRange[1] >= rangeEnd
|
||||||
)
|
)
|
||||||
const line = sketch.value[lineIndex]
|
const line = sketch.paths[lineIndex]
|
||||||
if (line) {
|
if (line) {
|
||||||
return {
|
return {
|
||||||
segment: line,
|
segment: line,
|
||||||
|
@ -1732,7 +1732,7 @@ export function transformAstSketchLines({
|
|||||||
if (err(_segment)) return _segment
|
if (err(_segment)) return _segment
|
||||||
referencedSegment = _segment.segment
|
referencedSegment = _segment.segment
|
||||||
} else {
|
} else {
|
||||||
referencedSegment = sketch.value.find(
|
referencedSegment = sketch.paths.find(
|
||||||
(path) => path.tag?.value === _referencedSegmentName
|
(path) => path.tag?.value === _referencedSegmentName
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import { StateMachineCommandSetConfig, KclCommandValue } from 'lib/commandTypes'
|
|||||||
import { KCL_DEFAULT_LENGTH, KCL_DEFAULT_DEGREE } from 'lib/constants'
|
import { KCL_DEFAULT_LENGTH, KCL_DEFAULT_DEGREE } from 'lib/constants'
|
||||||
import { components } from 'lib/machine-api'
|
import { components } from 'lib/machine-api'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { machineManager } from 'lib/machineManager'
|
|
||||||
import { modelingMachine, SketchTool } from 'machines/modelingMachine'
|
import { modelingMachine, SketchTool } from 'machines/modelingMachine'
|
||||||
|
|
||||||
type OutputFormat = Models['OutputFormat_type']
|
type OutputFormat = Models['OutputFormat_type']
|
||||||
@ -187,41 +186,41 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
machine.make_model.model ||
|
machine.make_model.model ||
|
||||||
machine.make_model.manufacturer ||
|
machine.make_model.manufacturer ||
|
||||||
'Unknown Machine',
|
'Unknown Machine',
|
||||||
options: () => {
|
options: (commandBarContext) => {
|
||||||
return Object.entries(machineManager.machines).map(
|
|
||||||
([_, machine]) => ({
|
|
||||||
name:
|
|
||||||
`${machine.id} (${
|
|
||||||
machine.make_model.model || machine.make_model.manufacturer
|
|
||||||
}) (${machine.state.state})` +
|
|
||||||
(machine.hardware_configuration &&
|
|
||||||
machine.hardware_configuration.type !== 'none' &&
|
|
||||||
machine.hardware_configuration.config.nozzle_diameter
|
|
||||||
? ` - Nozzle Diameter: ${machine.hardware_configuration.config.nozzle_diameter}`
|
|
||||||
: '') +
|
|
||||||
(machine.hardware_configuration &&
|
|
||||||
machine.hardware_configuration.type !== 'none' &&
|
|
||||||
machine.hardware_configuration.config.filaments &&
|
|
||||||
machine.hardware_configuration.config.filaments[0]
|
|
||||||
? ` - ${
|
|
||||||
machine.hardware_configuration.config.filaments[0].name
|
|
||||||
} #${
|
|
||||||
machine.hardware_configuration.config &&
|
|
||||||
machine.hardware_configuration.config.filaments[0].color?.slice(
|
|
||||||
0,
|
|
||||||
6
|
|
||||||
)
|
|
||||||
}`
|
|
||||||
: ''),
|
|
||||||
isCurrent: false,
|
|
||||||
disabled: machine.state.state !== 'idle',
|
|
||||||
value: machine as components['schemas']['MachineInfoResponse'],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
},
|
|
||||||
defaultValue: () => {
|
|
||||||
return Object.values(
|
return Object.values(
|
||||||
machineManager.machines
|
commandBarContext.machineManager?.machines || []
|
||||||
|
).map((machine: components['schemas']['MachineInfoResponse']) => ({
|
||||||
|
name:
|
||||||
|
`${machine.id} (${
|
||||||
|
machine.make_model.model || machine.make_model.manufacturer
|
||||||
|
}) (${machine.state.state})` +
|
||||||
|
(machine.hardware_configuration &&
|
||||||
|
machine.hardware_configuration.type !== 'none' &&
|
||||||
|
machine.hardware_configuration.config.nozzle_diameter
|
||||||
|
? ` - Nozzle Diameter: ${machine.hardware_configuration.config.nozzle_diameter}`
|
||||||
|
: '') +
|
||||||
|
(machine.hardware_configuration &&
|
||||||
|
machine.hardware_configuration.type !== 'none' &&
|
||||||
|
machine.hardware_configuration.config.filaments &&
|
||||||
|
machine.hardware_configuration.config.filaments[0]
|
||||||
|
? ` - ${
|
||||||
|
machine.hardware_configuration.config.filaments[0].name
|
||||||
|
} #${
|
||||||
|
machine.hardware_configuration.config &&
|
||||||
|
machine.hardware_configuration.config.filaments[0].color?.slice(
|
||||||
|
0,
|
||||||
|
6
|
||||||
|
)
|
||||||
|
}`
|
||||||
|
: ''),
|
||||||
|
isCurrent: false,
|
||||||
|
disabled: machine.state.state !== 'idle',
|
||||||
|
value: machine,
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
defaultValue: (commandBarContext) => {
|
||||||
|
return Object.values(
|
||||||
|
commandBarContext.machineManager.machines || []
|
||||||
)[0] as components['schemas']['MachineInfoResponse']
|
)[0] as components['schemas']['MachineInfoResponse']
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -5,6 +5,7 @@ import { Selection } from './selections'
|
|||||||
import { Identifier, Expr, VariableDeclaration } from 'lang/wasm'
|
import { Identifier, Expr, VariableDeclaration } from 'lang/wasm'
|
||||||
import { commandBarMachine } from 'machines/commandBarMachine'
|
import { commandBarMachine } from 'machines/commandBarMachine'
|
||||||
import { ReactNode } from 'react'
|
import { ReactNode } from 'react'
|
||||||
|
import { MachineManager } from 'components/MachineManagerProvider'
|
||||||
|
|
||||||
type Icon = CustomIconName
|
type Icon = CustomIconName
|
||||||
const PLATFORMS = ['both', 'web', 'desktop'] as const
|
const PLATFORMS = ['both', 'web', 'desktop'] as const
|
||||||
@ -127,6 +128,7 @@ export type CommandArgumentConfig<
|
|||||||
| ((
|
| ((
|
||||||
commandBarContext: {
|
commandBarContext: {
|
||||||
argumentsToSubmit: Record<string, unknown>
|
argumentsToSubmit: Record<string, unknown>
|
||||||
|
machineManager?: MachineManager
|
||||||
}, // Should be the commandbarMachine's context, but it creates a circular dependency
|
}, // Should be the commandbarMachine's context, but it creates a circular dependency
|
||||||
machineContext?: C
|
machineContext?: C
|
||||||
) => CommandArgumentOption<OutputType>[])
|
) => CommandArgumentOption<OutputType>[])
|
||||||
|
@ -92,6 +92,7 @@ export const MAKE_TOAST_MESSAGES = {
|
|||||||
NO_MACHINE_API_IP: 'No machine api ip available',
|
NO_MACHINE_API_IP: 'No machine api ip available',
|
||||||
NO_CURRENT_MACHINE: 'No current machine available',
|
NO_CURRENT_MACHINE: 'No current machine available',
|
||||||
NO_MACHINE_ID: 'No machine id available',
|
NO_MACHINE_ID: 'No machine id available',
|
||||||
|
NO_NAME: 'No name provided',
|
||||||
ERROR_STARTING_PRINT: 'Error while starting print',
|
ERROR_STARTING_PRINT: 'Error while starting print',
|
||||||
SUCCESS: 'Started print successfully',
|
SUCCESS: 'Started print successfully',
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { deserialize_files } from 'wasm-lib/pkg/wasm_lib'
|
import { deserialize_files } from 'wasm-lib/pkg/wasm_lib'
|
||||||
import { machineManager } from './machineManager'
|
import { MachineManager } from 'components/MachineManagerProvider'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { components } from './machine-api'
|
import { components } from './machine-api'
|
||||||
import ModelingAppFile from './modelingAppFile'
|
import ModelingAppFile from './modelingAppFile'
|
||||||
@ -8,9 +8,17 @@ import { MAKE_TOAST_MESSAGES } from './constants'
|
|||||||
// Make files locally from an export call.
|
// Make files locally from an export call.
|
||||||
export async function exportMake(
|
export async function exportMake(
|
||||||
data: ArrayBuffer,
|
data: ArrayBuffer,
|
||||||
toastId: string
|
name: string,
|
||||||
|
toastId: string,
|
||||||
|
machineManager: MachineManager
|
||||||
): Promise<Response | null> {
|
): Promise<Response | null> {
|
||||||
if (machineManager.machineCount() === 0) {
|
if (name === '') {
|
||||||
|
console.error(MAKE_TOAST_MESSAGES.NO_NAME)
|
||||||
|
toast.error(MAKE_TOAST_MESSAGES.NO_NAME, { id: toastId })
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (machineManager.machines.length === 0) {
|
||||||
console.error(MAKE_TOAST_MESSAGES.NO_MACHINES)
|
console.error(MAKE_TOAST_MESSAGES.NO_MACHINES)
|
||||||
toast.error(MAKE_TOAST_MESSAGES.NO_MACHINES, { id: toastId })
|
toast.error(MAKE_TOAST_MESSAGES.NO_MACHINES, { id: toastId })
|
||||||
return null
|
return null
|
||||||
@ -39,7 +47,7 @@ export async function exportMake(
|
|||||||
|
|
||||||
const params: components['schemas']['PrintParameters'] = {
|
const params: components['schemas']['PrintParameters'] = {
|
||||||
machine_id: machineId,
|
machine_id: machineId,
|
||||||
job_name: 'Exported Job', // TODO: make this the project name.
|
job_name: name,
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
console.log('params', params)
|
console.log('params', params)
|
||||||
|
9
src/lib/machine-api.d.ts
vendored
@ -138,6 +138,11 @@ export interface components {
|
|||||||
FdmHardwareConfiguration: {
|
FdmHardwareConfiguration: {
|
||||||
/** @description The filaments the printer has access to. */
|
/** @description The filaments the printer has access to. */
|
||||||
filaments: components['schemas']['Filament'][]
|
filaments: components['schemas']['Filament'][]
|
||||||
|
/**
|
||||||
|
* Format: uint
|
||||||
|
* @description The currently loaded filament index.
|
||||||
|
*/
|
||||||
|
loaded_filament_idx?: number | null
|
||||||
/**
|
/**
|
||||||
* Format: double
|
* Format: double
|
||||||
* @description Diameter of the extrusion nozzle, in mm.
|
* @description Diameter of the extrusion nozzle, in mm.
|
||||||
@ -191,6 +196,10 @@ export interface components {
|
|||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
type: 'composite'
|
type: 'composite'
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
/** @enum {string} */
|
||||||
|
type: 'unknown'
|
||||||
|
}
|
||||||
/** @description The hardware configuration of a machine. */
|
/** @description The hardware configuration of a machine. */
|
||||||
HardwareConfiguration:
|
HardwareConfiguration:
|
||||||
| {
|
| {
|
||||||
|
@ -1,105 +0,0 @@
|
|||||||
import { isDesktop } from './isDesktop'
|
|
||||||
import { components } from './machine-api'
|
|
||||||
import { reportRejection } from './trap'
|
|
||||||
import { toSync } from './utils'
|
|
||||||
|
|
||||||
export type MachinesListing = Array<
|
|
||||||
components['schemas']['MachineInfoResponse']
|
|
||||||
>
|
|
||||||
|
|
||||||
export class MachineManager {
|
|
||||||
private _isDesktop: boolean = isDesktop()
|
|
||||||
private _machines: MachinesListing = []
|
|
||||||
private _machineApiIp: string | null = null
|
|
||||||
private _currentMachine: components['schemas']['MachineInfoResponse'] | null =
|
|
||||||
null
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
if (!this._isDesktop) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updateMachines().catch(reportRejection)
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
if (!this._isDesktop) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start a background job to update the machines every ten seconds.
|
|
||||||
// If MDNS is already watching, this timeout will wait until it's done to trigger the
|
|
||||||
// finding again.
|
|
||||||
let timeoutId: ReturnType<typeof setTimeout> | undefined = undefined
|
|
||||||
const timeoutLoop = () => {
|
|
||||||
clearTimeout(timeoutId)
|
|
||||||
timeoutId = setTimeout(
|
|
||||||
toSync(async () => {
|
|
||||||
await this.updateMachineApiIp()
|
|
||||||
await this.updateMachines()
|
|
||||||
timeoutLoop()
|
|
||||||
}, reportRejection),
|
|
||||||
10000
|
|
||||||
)
|
|
||||||
}
|
|
||||||
timeoutLoop()
|
|
||||||
}
|
|
||||||
|
|
||||||
get machines(): MachinesListing {
|
|
||||||
return this._machines
|
|
||||||
}
|
|
||||||
|
|
||||||
machineCount(): number {
|
|
||||||
return this._machines.length
|
|
||||||
}
|
|
||||||
|
|
||||||
get machineApiIp(): string | null {
|
|
||||||
return this._machineApiIp
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the reason message for why there are no machines.
|
|
||||||
noMachinesReason(): string | undefined {
|
|
||||||
if (this.machineCount() > 0) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.machineApiIp === null) {
|
|
||||||
return 'Machine API server was not discovered'
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'Machine API server was discovered, but no machines are available'
|
|
||||||
}
|
|
||||||
|
|
||||||
get currentMachine(): components['schemas']['MachineInfoResponse'] | null {
|
|
||||||
return this._currentMachine
|
|
||||||
}
|
|
||||||
|
|
||||||
set currentMachine(
|
|
||||||
machine: components['schemas']['MachineInfoResponse'] | null
|
|
||||||
) {
|
|
||||||
this._currentMachine = machine
|
|
||||||
}
|
|
||||||
|
|
||||||
private async updateMachines(): Promise<void> {
|
|
||||||
if (!this._isDesktop) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._machineApiIp === null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this._machines = await window.electron.listMachines(this._machineApiIp)
|
|
||||||
}
|
|
||||||
|
|
||||||
private async updateMachineApiIp(): Promise<void> {
|
|
||||||
if (!this._isDesktop) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this._machineApiIp = await window.electron.getMachineApiIp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const machineManager = new MachineManager()
|
|
||||||
machineManager.start()
|
|
@ -7,6 +7,7 @@ import {
|
|||||||
} from 'lib/commandTypes'
|
} from 'lib/commandTypes'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { getCommandArgumentKclValuesOnly } from 'lib/commandUtils'
|
import { getCommandArgumentKclValuesOnly } from 'lib/commandUtils'
|
||||||
|
import { MachineManager } from 'components/MachineManagerProvider'
|
||||||
|
|
||||||
export type CommandBarContext = {
|
export type CommandBarContext = {
|
||||||
commands: Command[]
|
commands: Command[]
|
||||||
@ -14,6 +15,7 @@ export type CommandBarContext = {
|
|||||||
currentArgument?: CommandArgument<unknown> & { name: string }
|
currentArgument?: CommandArgument<unknown> & { name: string }
|
||||||
selectionRanges: Selections
|
selectionRanges: Selections
|
||||||
argumentsToSubmit: { [x: string]: unknown }
|
argumentsToSubmit: { [x: string]: unknown }
|
||||||
|
machineManager: MachineManager
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CommandBarMachineEvent =
|
export type CommandBarMachineEvent =
|
||||||
@ -71,6 +73,7 @@ export type CommandBarMachineEvent =
|
|||||||
type: 'Change current argument'
|
type: 'Change current argument'
|
||||||
data: { [x: string]: CommandArgumentWithName<unknown> }
|
data: { [x: string]: CommandArgumentWithName<unknown> }
|
||||||
}
|
}
|
||||||
|
| { type: 'Set machine manager'; data: MachineManager }
|
||||||
|
|
||||||
export const commandBarMachine = setup({
|
export const commandBarMachine = setup({
|
||||||
types: {
|
types: {
|
||||||
@ -90,6 +93,12 @@ export const commandBarMachine = setup({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
'Set machine manager': assign({
|
||||||
|
machineManager: ({ event, context }) => {
|
||||||
|
if (event.type !== 'Set machine manager') return context.machineManager
|
||||||
|
return event.data
|
||||||
|
},
|
||||||
|
}),
|
||||||
'Execute command': ({ context, event }) => {
|
'Execute command': ({ context, event }) => {
|
||||||
const { selectedCommand } = context
|
const { selectedCommand } = context
|
||||||
if (!selectedCommand) return
|
if (!selectedCommand) return
|
||||||
@ -339,6 +348,13 @@ export const commandBarMachine = setup({
|
|||||||
codeBasedSelections: [],
|
codeBasedSelections: [],
|
||||||
},
|
},
|
||||||
argumentsToSubmit: {},
|
argumentsToSubmit: {},
|
||||||
|
machineManager: {
|
||||||
|
machines: [],
|
||||||
|
machineApiIp: null,
|
||||||
|
currentMachine: null,
|
||||||
|
setCurrentMachine: () => {},
|
||||||
|
noMachinesReason: () => undefined,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
id: 'Command Bar',
|
id: 'Command Bar',
|
||||||
initial: 'Closed',
|
initial: 'Closed',
|
||||||
@ -520,6 +536,11 @@ export const commandBarMachine = setup({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
on: {
|
on: {
|
||||||
|
'Set machine manager': {
|
||||||
|
reenter: false,
|
||||||
|
actions: 'Set machine manager',
|
||||||
|
},
|
||||||
|
|
||||||
Close: {
|
Close: {
|
||||||
target: '.Closed',
|
target: '.Closed',
|
||||||
},
|
},
|
||||||
|
@ -64,6 +64,7 @@ import toast from 'react-hot-toast'
|
|||||||
import { ToolbarModeName } from 'lib/toolbar'
|
import { ToolbarModeName } from 'lib/toolbar'
|
||||||
import { quaternionFromUpNForward } from 'clientSideScene/helpers'
|
import { quaternionFromUpNForward } from 'clientSideScene/helpers'
|
||||||
import { Vector3 } from 'three'
|
import { Vector3 } from 'three'
|
||||||
|
import { MachineManager } from 'components/MachineManagerProvider'
|
||||||
|
|
||||||
export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY'
|
export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY'
|
||||||
|
|
||||||
@ -301,6 +302,7 @@ export const getPersistedContext = (): Partial<PersistedModelingContext> => {
|
|||||||
export interface ModelingMachineContext {
|
export interface ModelingMachineContext {
|
||||||
currentMode: ToolbarModeName
|
currentMode: ToolbarModeName
|
||||||
currentTool: SketchTool
|
currentTool: SketchTool
|
||||||
|
machineManager: MachineManager
|
||||||
selection: string[]
|
selection: string[]
|
||||||
selectionRanges: Selections
|
selectionRanges: Selections
|
||||||
sketchDetails: SketchDetails | null
|
sketchDetails: SketchDetails | null
|
||||||
@ -315,6 +317,13 @@ export interface ModelingMachineContext {
|
|||||||
export const modelingMachineDefaultContext: ModelingMachineContext = {
|
export const modelingMachineDefaultContext: ModelingMachineContext = {
|
||||||
currentMode: 'modeling',
|
currentMode: 'modeling',
|
||||||
currentTool: 'none',
|
currentTool: 'none',
|
||||||
|
machineManager: {
|
||||||
|
machines: [],
|
||||||
|
machineApiIp: null,
|
||||||
|
currentMachine: null,
|
||||||
|
setCurrentMachine: () => {},
|
||||||
|
noMachinesReason: () => undefined,
|
||||||
|
},
|
||||||
selection: [],
|
selection: [],
|
||||||
selectionRanges: {
|
selectionRanges: {
|
||||||
otherSelections: [],
|
otherSelections: [],
|
||||||
|
@ -4,7 +4,7 @@ import fs from 'node:fs/promises'
|
|||||||
import os from 'node:os'
|
import os from 'node:os'
|
||||||
import fsSync from 'node:fs'
|
import fsSync from 'node:fs'
|
||||||
import packageJson from '../package.json'
|
import packageJson from '../package.json'
|
||||||
import { MachinesListing } from 'lib/machineManager'
|
import { MachinesListing } from 'components/MachineManagerProvider'
|
||||||
import chokidar from 'chokidar'
|
import chokidar from 'chokidar'
|
||||||
|
|
||||||
const open = (args: any) => ipcRenderer.invoke('dialog.showOpenDialog', args)
|
const open = (args: any) => ipcRenderer.invoke('dialog.showOpenDialog', args)
|
||||||
|
@ -23,6 +23,9 @@ import { codeManager, editorManager, kclManager } from 'lib/singletons'
|
|||||||
import { bracket } from 'lib/exampleKcl'
|
import { bracket } from 'lib/exampleKcl'
|
||||||
import { toSync } from 'lib/utils'
|
import { toSync } from 'lib/utils'
|
||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
|
import { useNetworkContext } from 'hooks/useNetworkContext'
|
||||||
|
import { NetworkHealthState } from 'hooks/useNetworkStatus'
|
||||||
|
import { EngineConnectionStateType } from 'lang/std/engineConnection'
|
||||||
|
|
||||||
export const kbdClasses =
|
export const kbdClasses =
|
||||||
'py-0.5 px-1 text-sm rounded bg-chalkboard-10 dark:bg-chalkboard-100 border border-chalkboard-50 border-b-2'
|
'py-0.5 px-1 text-sm rounded bg-chalkboard-10 dark:bg-chalkboard-100 border border-chalkboard-50 border-b-2'
|
||||||
@ -80,8 +83,20 @@ export const onboardingRoutes = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export function useDemoCode() {
|
export function useDemoCode() {
|
||||||
|
const { overallState, immediateState } = useNetworkContext()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!editorManager.editorView || codeManager.code === bracket) return
|
// Don't run if the editor isn't loaded or the code is already the bracket
|
||||||
|
if (!editorManager.editorView || codeManager.code === bracket) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Don't run if the network isn't healthy or the connection isn't established
|
||||||
|
if (
|
||||||
|
overallState !== NetworkHealthState.Ok ||
|
||||||
|
immediateState.type !== EngineConnectionStateType.ConnectionEstablished
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
setTimeout(
|
setTimeout(
|
||||||
toSync(async () => {
|
toSync(async () => {
|
||||||
codeManager.updateCodeStateEditor(bracket)
|
codeManager.updateCodeStateEditor(bracket)
|
||||||
@ -89,7 +104,7 @@ export function useDemoCode() {
|
|||||||
await codeManager.writeToFile()
|
await codeManager.writeToFile()
|
||||||
}, reportRejection)
|
}, reportRejection)
|
||||||
)
|
)
|
||||||
}, [editorManager.editorView])
|
}, [editorManager.editorView, immediateState, overallState])
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useNextClick(newStatus: string) {
|
export function useNextClick(newStatus: string) {
|
||||||
|
128
src/wasm-lib/Cargo.lock
generated
@ -121,9 +121,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.89"
|
version = "1.0.91"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
|
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
]
|
]
|
||||||
@ -176,7 +176,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -187,7 +187,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -204,7 +204,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -465,7 +465,7 @@ dependencies = [
|
|||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -656,7 +656,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"strsim",
|
"strsim",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -667,7 +667,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -722,7 +722,7 @@ checksum = "4078275de501a61ceb9e759d37bdd3d7210e654dbc167ac1a3678ef4435ed57b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -751,7 +751,7 @@ dependencies = [
|
|||||||
"rustfmt-wrapper",
|
"rustfmt-wrapper",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_tokenstream",
|
"serde_tokenstream",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -762,7 +762,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -789,7 +789,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -827,7 +827,7 @@ checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -988,7 +988,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1084,7 +1084,7 @@ dependencies = [
|
|||||||
"inflections",
|
"inflections",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1542,7 +1542,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
version = "0.2.22"
|
version = "0.2.23"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"approx 0.5.1",
|
"approx 0.5.1",
|
||||||
@ -1612,12 +1612,12 @@ dependencies = [
|
|||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-test-server"
|
name = "kcl-test-server"
|
||||||
version = "0.1.14"
|
version = "0.1.15"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"hyper 0.14.30",
|
"hyper 0.14.30",
|
||||||
@ -1644,9 +1644,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad"
|
name = "kittycad"
|
||||||
version = "0.3.23"
|
version = "0.3.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "71b6f0c34165939697548dd0c94221200dbb8b5d1c84b5d8e803e70f9f720ea7"
|
checksum = "f6359cc0a1bbccbcf78775eea17a033cf2aa89d3fe6a9784f8ce94e5f882c185"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1684,9 +1684,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad-modeling-cmds"
|
name = "kittycad-modeling-cmds"
|
||||||
version = "0.2.68"
|
version = "0.2.71"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7e3aedfcc1d8ea9995ec3eb78a6743c585c9380475c48701797f107489b696aa"
|
checksum = "c6d2160dcb0e5373b1242a760dbf17fb9c12de523c410c11b145381c852b377b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -1716,7 +1716,7 @@ dependencies = [
|
|||||||
"kittycad-modeling-cmds-macros-impl",
|
"kittycad-modeling-cmds-macros-impl",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1727,7 +1727,7 @@ checksum = "6607507a8a0e4273b943179f0a3ef8e90712308d1d3095246040c29cfdbf985b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2112,7 +2112,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"regex-syntax 0.8.5",
|
"regex-syntax 0.8.5",
|
||||||
"structmeta",
|
"structmeta",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2126,7 +2126,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"regex-syntax 0.8.5",
|
"regex-syntax 0.8.5",
|
||||||
"structmeta",
|
"structmeta",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2166,7 +2166,7 @@ dependencies = [
|
|||||||
"pest_meta",
|
"pest_meta",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2224,7 +2224,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2337,9 +2337,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.88"
|
version = "1.0.89"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9"
|
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@ -2391,7 +2391,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"pyo3-macros-backend",
|
"pyo3-macros-backend",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2404,7 +2404,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"pyo3-build-config",
|
"pyo3-build-config",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2586,9 +2586,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.11.0"
|
version = "1.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
|
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -2925,7 +2925,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde_derive_internals",
|
"serde_derive_internals",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2965,9 +2965,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.210"
|
version = "1.0.213"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
@ -2983,13 +2983,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.210"
|
version = "1.0.213"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3000,14 +3000,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.128"
|
version = "1.0.132"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
|
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.6.0",
|
"indexmap 2.6.0",
|
||||||
"itoa",
|
"itoa",
|
||||||
@ -3024,7 +3024,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3045,7 +3045,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde",
|
"serde",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3182,7 +3182,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"structmeta-derive",
|
"structmeta-derive",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3193,7 +3193,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3215,7 +3215,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3237,9 +3237,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.79"
|
version = "2.0.85"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
|
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -3263,7 +3263,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3326,22 +3326,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.64"
|
version = "1.0.65"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.64"
|
version = "1.0.65"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3437,7 +3437,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3579,7 +3579,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3607,7 +3607,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3689,7 +3689,7 @@ checksum = "0ea0b99e8ec44abd6f94a18f28f7934437809dd062820797c52401298116f70e"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3865,7 +3865,7 @@ dependencies = [
|
|||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3927,7 +3927,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3962,7 +3962,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
@ -4328,7 +4328,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.79",
|
"syn 2.0.85",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -71,8 +71,8 @@ members = [
|
|||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
http = "1"
|
http = "1"
|
||||||
kittycad = { version = "0.3.23", default-features = false, features = ["js", "requests"] }
|
kittycad = { version = "0.3.25", default-features = false, features = ["js", "requests"] }
|
||||||
kittycad-modeling-cmds = { version = "0.2.68", features = ["websocket"] }
|
kittycad-modeling-cmds = { version = "0.2.71", features = ["websocket"] }
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "executor"
|
name = "executor"
|
||||||
@ -83,6 +83,6 @@ name = "modify"
|
|||||||
path = "tests/modify/main.rs"
|
path = "tests/modify/main.rs"
|
||||||
|
|
||||||
# Example: how to point modeling-api at a different repo (e.g. a branch or a local clone)
|
# Example: how to point modeling-api at a different repo (e.g. a branch or a local clone)
|
||||||
#[patch."https://github.com/KittyCAD/modeling-api"]
|
#[patch.crates-io]
|
||||||
#kittycad-modeling-cmds = { path = "../../../modeling-api/modeling-cmds" }
|
#kittycad-modeling-cmds = { path = "../../../modeling-api/modeling-cmds" }
|
||||||
#kittycad-modeling-session = { path = "../../../modeling-api/modeling-session" }
|
#kittycad-modeling-session = { path = "../../../modeling-api/modeling-session" }
|
||||||
|
@ -17,13 +17,13 @@ convert_case = "0.6.0"
|
|||||||
once_cell = "1.20.2"
|
once_cell = "1.20.2"
|
||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
quote = "1"
|
quote = "1"
|
||||||
regex = "1.10"
|
regex = "1.11"
|
||||||
serde = { version = "1.0.210", features = ["derive"] }
|
serde = { version = "1.0.213", features = ["derive"] }
|
||||||
serde_tokenstream = "0.2"
|
serde_tokenstream = "0.2"
|
||||||
syn = { version = "2.0.79", features = ["full"] }
|
syn = { version = "2.0.85", features = ["full"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = "1.0.89"
|
anyhow = "1.0.91"
|
||||||
expectorate = "1.1.0"
|
expectorate = "1.1.0"
|
||||||
pretty_assertions = "1.4.1"
|
pretty_assertions = "1.4.1"
|
||||||
rustfmt-wrapper = "0.2.1"
|
rustfmt-wrapper = "0.2.1"
|
||||||
|
@ -15,7 +15,7 @@ databake = "0.1.8"
|
|||||||
kcl-lib = { path = "../kcl" }
|
kcl-lib = { path = "../kcl" }
|
||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
quote = "1"
|
quote = "1"
|
||||||
syn = { version = "2.0.79", features = ["full"] }
|
syn = { version = "2.0.85", features = ["full"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.4.1"
|
pretty_assertions = "1.4.1"
|
||||||
|
@ -12,7 +12,7 @@ fn basic() {
|
|||||||
let expected = Program {
|
let expected = Program {
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 11,
|
end: 11,
|
||||||
body: vec![BodyItem::VariableDeclaration(VariableDeclaration {
|
body: vec![BodyItem::VariableDeclaration(Box::new(VariableDeclaration {
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 11,
|
end: 11,
|
||||||
declarations: vec![VariableDeclarator {
|
declarations: vec![VariableDeclarator {
|
||||||
@ -36,7 +36,7 @@ fn basic() {
|
|||||||
visibility: ItemVisibility::Default,
|
visibility: ItemVisibility::Default,
|
||||||
kind: VariableKind::Const,
|
kind: VariableKind::Const,
|
||||||
digest: None,
|
digest: None,
|
||||||
})],
|
}))],
|
||||||
non_code_meta: NonCodeMeta::default(),
|
non_code_meta: NonCodeMeta::default(),
|
||||||
digest: None,
|
digest: None,
|
||||||
};
|
};
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-test-server"
|
name = "kcl-test-server"
|
||||||
description = "A test server for KCL"
|
description = "A test server for KCL"
|
||||||
version = "0.1.14"
|
version = "0.1.15"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.89"
|
anyhow = "1.0.91"
|
||||||
hyper = { version = "0.14.29", features = ["http1", "server", "tcp"] }
|
hyper = { version = "0.14.29", features = ["http1", "server", "tcp"] }
|
||||||
kcl-lib = { version = "0.2", path = "../kcl" }
|
kcl-lib = { version = "0.2", path = "../kcl" }
|
||||||
pico-args = "0.5.0"
|
pico-args = "0.5.0"
|
||||||
serde = { version = "1.0.210", features = ["derive"] }
|
serde = { version = "1.0.213", features = ["derive"] }
|
||||||
serde_json = "1.0.128"
|
serde_json = "1.0.128"
|
||||||
tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] }
|
||||||
|
@ -31,7 +31,7 @@ pub struct ServerArgs {
|
|||||||
/// Where to find the engine.
|
/// Where to find the engine.
|
||||||
/// If none, uses the prod engine.
|
/// If none, uses the prod engine.
|
||||||
/// This is useful for testing a local engine instance.
|
/// This is useful for testing a local engine instance.
|
||||||
/// Overridden by the $LOCAL_ENGINE_ADDR environment variable.
|
/// Overridden by the $ZOO_HOST environment variable.
|
||||||
pub engine_address: Option<String>,
|
pub engine_address: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,8 +44,8 @@ impl ServerArgs {
|
|||||||
num_engine_conns: pargs.opt_value_from_str("--num-engine-conns")?.unwrap_or(1),
|
num_engine_conns: pargs.opt_value_from_str("--num-engine-conns")?.unwrap_or(1),
|
||||||
engine_address: pargs.opt_value_from_str("--engine-address")?,
|
engine_address: pargs.opt_value_from_str("--engine-address")?,
|
||||||
};
|
};
|
||||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
if let Ok(addr) = std::env::var("ZOO_HOST") {
|
||||||
println!("Overriding engine address via $LOCAL_ENGINE_ADDR");
|
println!("Overriding engine address via $ZOO_HOST");
|
||||||
args.engine_address = Some(addr);
|
args.engine_address = Some(addr);
|
||||||
}
|
}
|
||||||
println!("Config is {args:?}");
|
println!("Config is {args:?}");
|
||||||
|
@ -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.22"
|
version = "0.2.23"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
@ -11,7 +11,7 @@ keywords = ["kcl", "KittyCAD", "CAD"]
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = { version = "1.0.89", features = ["backtrace"] }
|
anyhow = { version = "1.0.91", features = ["backtrace"] }
|
||||||
async-recursion = "1.1.1"
|
async-recursion = "1.1.1"
|
||||||
async-trait = "0.1.83"
|
async-trait = "0.1.83"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
@ -38,11 +38,11 @@ pyo3 = { version = "0.22.5", optional = true }
|
|||||||
reqwest = { version = "0.12", default-features = false, features = ["stream", "rustls-tls"] }
|
reqwest = { version = "0.12", default-features = false, features = ["stream", "rustls-tls"] }
|
||||||
ropey = "1.6.1"
|
ropey = "1.6.1"
|
||||||
schemars = { version = "0.8.17", features = ["impl_json_schema", "url", "uuid1", "preserve_order"] }
|
schemars = { version = "0.8.17", features = ["impl_json_schema", "url", "uuid1", "preserve_order"] }
|
||||||
serde = { version = "1.0.210", features = ["derive"] }
|
serde = { version = "1.0.213", features = ["derive"] }
|
||||||
serde_json = "1.0.128"
|
serde_json = "1.0.128"
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
tabled = { version = "0.15.0", optional = true }
|
tabled = { version = "0.15.0", optional = true }
|
||||||
thiserror = "1.0.64"
|
thiserror = "1.0.65"
|
||||||
toml = "0.8.19"
|
toml = "0.8.19"
|
||||||
ts-rs = { version = "10.0.0", features = ["uuid-impl", "url-impl", "chrono-impl", "no-serde-warnings", "serde-json-impl"] }
|
ts-rs = { version = "10.0.0", features = ["uuid-impl", "url-impl", "chrono-impl", "no-serde-warnings", "serde-json-impl"] }
|
||||||
url = { version = "2.5.2", features = ["serde"] }
|
url = { version = "2.5.2", features = ["serde"] }
|
||||||
|
@ -454,7 +454,7 @@ pub(crate) use impl_value_meta;
|
|||||||
pub enum BodyItem {
|
pub enum BodyItem {
|
||||||
ImportStatement(Box<ImportStatement>),
|
ImportStatement(Box<ImportStatement>),
|
||||||
ExpressionStatement(ExpressionStatement),
|
ExpressionStatement(ExpressionStatement),
|
||||||
VariableDeclaration(VariableDeclaration),
|
VariableDeclaration(Box<VariableDeclaration>),
|
||||||
ReturnStatement(ReturnStatement),
|
ReturnStatement(ReturnStatement),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2719,7 +2719,7 @@ pub struct FunctionExpression {
|
|||||||
impl_value_meta!(FunctionExpression);
|
impl_value_meta!(FunctionExpression);
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct RequiredParamAfterOptionalParam(pub Parameter);
|
pub struct RequiredParamAfterOptionalParam(pub Box<Parameter>);
|
||||||
|
|
||||||
impl std::fmt::Display for RequiredParamAfterOptionalParam {
|
impl std::fmt::Display for RequiredParamAfterOptionalParam {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
@ -2751,7 +2751,7 @@ impl FunctionExpression {
|
|||||||
if param.optional {
|
if param.optional {
|
||||||
found_optional = true;
|
found_optional = true;
|
||||||
} else if found_optional {
|
} else if found_optional {
|
||||||
return Err(RequiredParamAfterOptionalParam(param.clone()));
|
return Err(RequiredParamAfterOptionalParam(Box::new(param.clone())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let boundary = self.params.partition_point(|param| !param.optional);
|
let boundary = self.params.partition_point(|param| !param.optional);
|
||||||
|
@ -551,13 +551,13 @@ impl ArrayRangeExpression {
|
|||||||
.execute_expr(&self.start_element, exec_state, &metadata, StatementKind::Expression)
|
.execute_expr(&self.start_element, exec_state, &metadata, StatementKind::Expression)
|
||||||
.await?
|
.await?
|
||||||
.get_json_value()?;
|
.get_json_value()?;
|
||||||
let start = parse_json_number_as_u64(&start, (&*self.start_element).into())?;
|
let start = parse_json_number_as_i64(&start, (&*self.start_element).into())?;
|
||||||
let metadata = Metadata::from(&*self.end_element);
|
let metadata = Metadata::from(&*self.end_element);
|
||||||
let end = ctx
|
let end = ctx
|
||||||
.execute_expr(&self.end_element, exec_state, &metadata, StatementKind::Expression)
|
.execute_expr(&self.end_element, exec_state, &metadata, StatementKind::Expression)
|
||||||
.await?
|
.await?
|
||||||
.get_json_value()?;
|
.get_json_value()?;
|
||||||
let end = parse_json_number_as_u64(&end, (&*self.end_element).into())?;
|
let end = parse_json_number_as_i64(&end, (&*self.end_element).into())?;
|
||||||
|
|
||||||
if end < start {
|
if end < start {
|
||||||
return Err(KclError::Semantic(KclErrorDetails {
|
return Err(KclError::Semantic(KclErrorDetails {
|
||||||
@ -603,9 +603,9 @@ impl ObjectExpression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_json_number_as_u64(j: &serde_json::Value, source_range: SourceRange) -> Result<u64, KclError> {
|
fn parse_json_number_as_i64(j: &serde_json::Value, source_range: SourceRange) -> Result<i64, KclError> {
|
||||||
if let serde_json::Value::Number(n) = &j {
|
if let serde_json::Value::Number(n) = &j {
|
||||||
n.as_u64().ok_or_else(|| {
|
n.as_i64().ok_or_else(|| {
|
||||||
KclError::Syntax(KclErrorDetails {
|
KclError::Syntax(KclErrorDetails {
|
||||||
source_ranges: vec![source_range],
|
source_ranges: vec![source_range],
|
||||||
message: format!("Invalid integer: {}", j),
|
message: format!("Invalid integer: {}", j),
|
||||||
|
@ -1127,7 +1127,7 @@ pub struct TagEngineInfo {
|
|||||||
/// The sketch the tag is on.
|
/// The sketch the tag is on.
|
||||||
pub sketch: uuid::Uuid,
|
pub sketch: uuid::Uuid,
|
||||||
/// The path the tag is on.
|
/// The path the tag is on.
|
||||||
pub path: Option<BasePath>,
|
pub path: Option<Path>,
|
||||||
/// The surface information for the tag.
|
/// The surface information for the tag.
|
||||||
pub surface: Option<ExtrudeSurface>,
|
pub surface: Option<ExtrudeSurface>,
|
||||||
}
|
}
|
||||||
@ -1137,10 +1137,10 @@ pub struct TagEngineInfo {
|
|||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type", rename_all = "camelCase")]
|
#[serde(tag = "type", rename_all = "camelCase")]
|
||||||
pub struct Sketch {
|
pub struct Sketch {
|
||||||
/// The id of the sketch (this will change when the engine's reference to it changes.
|
/// The id of the sketch (this will change when the engine's reference to it changes).
|
||||||
pub id: uuid::Uuid,
|
pub id: uuid::Uuid,
|
||||||
/// The paths in the sketch.
|
/// The paths in the sketch.
|
||||||
pub value: Vec<Path>,
|
pub paths: Vec<Path>,
|
||||||
/// What the sketch is on (can be a plane or a face).
|
/// What the sketch is on (can be a plane or a face).
|
||||||
pub on: SketchSurface,
|
pub on: SketchSurface,
|
||||||
/// The starting path.
|
/// The starting path.
|
||||||
@ -1206,7 +1206,7 @@ impl Sketch {
|
|||||||
tag_identifier.info = Some(TagEngineInfo {
|
tag_identifier.info = Some(TagEngineInfo {
|
||||||
id: base.geo_meta.id,
|
id: base.geo_meta.id,
|
||||||
sketch: self.id,
|
sketch: self.id,
|
||||||
path: Some(base.clone()),
|
path: Some(current_path.clone()),
|
||||||
surface: None,
|
surface: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1215,7 +1215,7 @@ impl Sketch {
|
|||||||
|
|
||||||
/// Get the path most recently sketched.
|
/// Get the path most recently sketched.
|
||||||
pub(crate) fn latest_path(&self) -> Option<&Path> {
|
pub(crate) fn latest_path(&self) -> Option<&Path> {
|
||||||
self.value.last()
|
self.paths.last()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The "pen" is an imaginary pen drawing the path.
|
/// The "pen" is an imaginary pen drawing the path.
|
||||||
@ -1656,6 +1656,43 @@ pub enum Path {
|
|||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
base: BasePath,
|
base: BasePath,
|
||||||
},
|
},
|
||||||
|
/// A circular arc, not necessarily tangential to the current point.
|
||||||
|
Arc {
|
||||||
|
#[serde(flatten)]
|
||||||
|
base: BasePath,
|
||||||
|
/// Center of the circle that this arc is drawn on.
|
||||||
|
center: [f64; 2],
|
||||||
|
/// Radius of the circle that this arc is drawn on.
|
||||||
|
radius: f64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// What kind of path is this?
|
||||||
|
#[derive(Display)]
|
||||||
|
enum PathType {
|
||||||
|
ToPoint,
|
||||||
|
Base,
|
||||||
|
TangentialArc,
|
||||||
|
TangentialArcTo,
|
||||||
|
Circle,
|
||||||
|
Horizontal,
|
||||||
|
AngledLineTo,
|
||||||
|
Arc,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Path> for PathType {
|
||||||
|
fn from(value: &Path) -> Self {
|
||||||
|
match value {
|
||||||
|
Path::ToPoint { .. } => Self::ToPoint,
|
||||||
|
Path::TangentialArcTo { .. } => Self::TangentialArcTo,
|
||||||
|
Path::TangentialArc { .. } => Self::TangentialArc,
|
||||||
|
Path::Circle { .. } => Self::Circle,
|
||||||
|
Path::Horizontal { .. } => Self::Horizontal,
|
||||||
|
Path::AngledLineTo { .. } => Self::AngledLineTo,
|
||||||
|
Path::Base { .. } => Self::Base,
|
||||||
|
Path::Arc { .. } => Self::Arc,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Path {
|
impl Path {
|
||||||
@ -1668,6 +1705,7 @@ impl Path {
|
|||||||
Path::TangentialArcTo { base, .. } => base.geo_meta.id,
|
Path::TangentialArcTo { base, .. } => base.geo_meta.id,
|
||||||
Path::TangentialArc { base, .. } => base.geo_meta.id,
|
Path::TangentialArc { base, .. } => base.geo_meta.id,
|
||||||
Path::Circle { base, .. } => base.geo_meta.id,
|
Path::Circle { base, .. } => base.geo_meta.id,
|
||||||
|
Path::Arc { base, .. } => base.geo_meta.id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1680,6 +1718,7 @@ impl Path {
|
|||||||
Path::TangentialArcTo { base, .. } => base.tag.clone(),
|
Path::TangentialArcTo { base, .. } => base.tag.clone(),
|
||||||
Path::TangentialArc { base, .. } => base.tag.clone(),
|
Path::TangentialArc { base, .. } => base.tag.clone(),
|
||||||
Path::Circle { base, .. } => base.tag.clone(),
|
Path::Circle { base, .. } => base.tag.clone(),
|
||||||
|
Path::Arc { base, .. } => base.tag.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1692,6 +1731,47 @@ impl Path {
|
|||||||
Path::TangentialArcTo { base, .. } => base,
|
Path::TangentialArcTo { base, .. } => base,
|
||||||
Path::TangentialArc { base, .. } => base,
|
Path::TangentialArc { base, .. } => base,
|
||||||
Path::Circle { base, .. } => base,
|
Path::Circle { base, .. } => base,
|
||||||
|
Path::Arc { base, .. } => base,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Where does this path segment start?
|
||||||
|
pub fn get_from(&self) -> &[f64; 2] {
|
||||||
|
&self.get_base().from
|
||||||
|
}
|
||||||
|
/// Where does this path segment end?
|
||||||
|
pub fn get_to(&self) -> &[f64; 2] {
|
||||||
|
&self.get_base().to
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Length of this path segment, in cartesian plane.
|
||||||
|
pub fn length(&self) -> f64 {
|
||||||
|
match self {
|
||||||
|
Self::ToPoint { .. } | Self::Base { .. } | Self::Horizontal { .. } | Self::AngledLineTo { .. } => {
|
||||||
|
linear_distance(self.get_from(), self.get_to())
|
||||||
|
}
|
||||||
|
Self::TangentialArc {
|
||||||
|
base: _,
|
||||||
|
center,
|
||||||
|
ccw: _,
|
||||||
|
}
|
||||||
|
| Self::TangentialArcTo {
|
||||||
|
base: _,
|
||||||
|
center,
|
||||||
|
ccw: _,
|
||||||
|
} => {
|
||||||
|
// The radius can be calculated as the linear distance between `to` and `center`,
|
||||||
|
// or between `from` and `center`. They should be the same.
|
||||||
|
let radius = linear_distance(self.get_from(), center);
|
||||||
|
debug_assert_eq!(radius, linear_distance(self.get_to(), center));
|
||||||
|
// TODO: Call engine utils to figure this out.
|
||||||
|
linear_distance(self.get_from(), self.get_to())
|
||||||
|
}
|
||||||
|
Self::Circle { radius, .. } => 2.0 * std::f64::consts::PI * radius,
|
||||||
|
Self::Arc { .. } => {
|
||||||
|
// TODO: Call engine utils to figure this out.
|
||||||
|
linear_distance(self.get_from(), self.get_to())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1704,10 +1784,22 @@ impl Path {
|
|||||||
Path::TangentialArcTo { base, .. } => Some(base),
|
Path::TangentialArcTo { base, .. } => Some(base),
|
||||||
Path::TangentialArc { base, .. } => Some(base),
|
Path::TangentialArc { base, .. } => Some(base),
|
||||||
Path::Circle { base, .. } => Some(base),
|
Path::Circle { base, .. } => Some(base),
|
||||||
|
Path::Arc { base, .. } => Some(base),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compute the straight-line distance between a pair of (2D) points.
|
||||||
|
#[rustfmt::skip]
|
||||||
|
fn linear_distance(
|
||||||
|
[x0, y0]: &[f64; 2],
|
||||||
|
[x1, y1]: &[f64; 2]
|
||||||
|
) -> f64 {
|
||||||
|
let y_sq = (y1 - y0).powi(2);
|
||||||
|
let x_sq = (x1 - x0).powi(2);
|
||||||
|
(y_sq + x_sq).sqrt()
|
||||||
|
}
|
||||||
|
|
||||||
/// An extrude surface.
|
/// An extrude surface.
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
@ -1888,9 +1980,73 @@ impl From<crate::settings::types::ModelingSettings> for ExecutorSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new zoo api client.
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
pub fn new_zoo_client(token: Option<String>, engine_addr: Option<String>) -> Result<kittycad::Client> {
|
||||||
|
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||||
|
let http_client = reqwest::Client::builder()
|
||||||
|
.user_agent(user_agent)
|
||||||
|
// For file conversions we need this to be long.
|
||||||
|
.timeout(std::time::Duration::from_secs(600))
|
||||||
|
.connect_timeout(std::time::Duration::from_secs(60));
|
||||||
|
let ws_client = reqwest::Client::builder()
|
||||||
|
.user_agent(user_agent)
|
||||||
|
// For file conversions we need this to be long.
|
||||||
|
.timeout(std::time::Duration::from_secs(600))
|
||||||
|
.connect_timeout(std::time::Duration::from_secs(60))
|
||||||
|
.connection_verbose(true)
|
||||||
|
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||||
|
.http1_only();
|
||||||
|
|
||||||
|
let zoo_token_env = std::env::var("ZOO_API_TOKEN");
|
||||||
|
|
||||||
|
let token = if let Some(token) = token {
|
||||||
|
token
|
||||||
|
} else if let Ok(token) = std::env::var("KITTYCAD_API_TOKEN") {
|
||||||
|
if let Ok(zoo_token) = zoo_token_env {
|
||||||
|
if zoo_token != token {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Both environment variables KITTYCAD_API_TOKEN=`{}` and ZOO_API_TOKEN=`{}` are set. Use only one.",
|
||||||
|
token,
|
||||||
|
zoo_token
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
token
|
||||||
|
} else if let Ok(token) = zoo_token_env {
|
||||||
|
token
|
||||||
|
} else {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"No API token found in environment variables. Use KITTYCAD_API_TOKEN or ZOO_API_TOKEN"
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the client.
|
||||||
|
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||||
|
// Set an engine address if it's set.
|
||||||
|
let kittycad_host_env = std::env::var("KITTYCAD_HOST");
|
||||||
|
if let Some(addr) = engine_addr {
|
||||||
|
client.set_base_url(addr);
|
||||||
|
} else if let Ok(addr) = std::env::var("ZOO_HOST") {
|
||||||
|
if let Ok(kittycad_host) = kittycad_host_env {
|
||||||
|
if kittycad_host != addr {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Both environment variables KITTYCAD_HOST=`{}` and ZOO_HOST=`{}` are set. Use only one.",
|
||||||
|
kittycad_host,
|
||||||
|
addr
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client.set_base_url(addr);
|
||||||
|
} else if let Ok(addr) = kittycad_host_env {
|
||||||
|
client.set_base_url(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(client)
|
||||||
|
}
|
||||||
|
|
||||||
impl ExecutorContext {
|
impl ExecutorContext {
|
||||||
/// Create a new default executor context.
|
/// Create a new default executor context.
|
||||||
/// Also returns the response HTTP headers from the server.
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub async fn new(client: &kittycad::Client, settings: ExecutorSettings) -> Result<Self> {
|
pub async fn new(client: &kittycad::Client, settings: ExecutorSettings) -> Result<Self> {
|
||||||
let (ws, _headers) = client
|
let (ws, _headers) = client
|
||||||
@ -1935,6 +2091,35 @@ impl ExecutorContext {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new default executor context.
|
||||||
|
/// With a kittycad client.
|
||||||
|
/// This allows for passing in `ZOO_API_TOKEN` and `ZOO_HOST` as environment
|
||||||
|
/// variables.
|
||||||
|
/// But also allows for passing in a token and engine address directly.
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
pub async fn new_with_client(
|
||||||
|
settings: ExecutorSettings,
|
||||||
|
token: Option<String>,
|
||||||
|
engine_addr: Option<String>,
|
||||||
|
) -> Result<Self> {
|
||||||
|
// Create the client.
|
||||||
|
let client = new_zoo_client(token, engine_addr)?;
|
||||||
|
|
||||||
|
let ctx = Self::new(&client, settings).await?;
|
||||||
|
Ok(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new default executor context.
|
||||||
|
/// With the default kittycad client.
|
||||||
|
/// This allows for passing in `ZOO_API_TOKEN` and `ZOO_HOST` as environment
|
||||||
|
/// variables.
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
pub async fn new_with_default_client(settings: ExecutorSettings) -> Result<Self> {
|
||||||
|
// Create the client.
|
||||||
|
let ctx = Self::new_with_client(settings, None, None).await?;
|
||||||
|
Ok(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_mock(&self) -> bool {
|
pub fn is_mock(&self) -> bool {
|
||||||
self.context_type == ContextType::Mock || self.context_type == ContextType::MockCustomForwarded
|
self.context_type == ContextType::Mock || self.context_type == ContextType::MockCustomForwarded
|
||||||
}
|
}
|
||||||
@ -1942,35 +2127,7 @@ impl ExecutorContext {
|
|||||||
/// For executing unit tests.
|
/// For executing unit tests.
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub async fn new_for_unit_test(units: UnitLength, engine_addr: Option<String>) -> Result<Self> {
|
pub async fn new_for_unit_test(units: UnitLength, engine_addr: Option<String>) -> Result<Self> {
|
||||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"));
|
let ctx = ExecutorContext::new_with_client(
|
||||||
let http_client = reqwest::Client::builder()
|
|
||||||
.user_agent(user_agent)
|
|
||||||
// For file conversions we need this to be long.
|
|
||||||
.timeout(std::time::Duration::from_secs(600))
|
|
||||||
.connect_timeout(std::time::Duration::from_secs(60));
|
|
||||||
let ws_client = reqwest::Client::builder()
|
|
||||||
.user_agent(user_agent)
|
|
||||||
// For file conversions we need this to be long.
|
|
||||||
.timeout(std::time::Duration::from_secs(600))
|
|
||||||
.connect_timeout(std::time::Duration::from_secs(60))
|
|
||||||
.connection_verbose(true)
|
|
||||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
|
||||||
.http1_only();
|
|
||||||
|
|
||||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
|
||||||
|
|
||||||
// Create the client.
|
|
||||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
|
||||||
// Set a local engine address if it's set.
|
|
||||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
|
||||||
client.set_base_url(addr);
|
|
||||||
}
|
|
||||||
if let Some(addr) = engine_addr {
|
|
||||||
client.set_base_url(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
let ctx = ExecutorContext::new(
|
|
||||||
&client,
|
|
||||||
ExecutorSettings {
|
ExecutorSettings {
|
||||||
units,
|
units,
|
||||||
highlight_edges: true,
|
highlight_edges: true,
|
||||||
@ -1978,6 +2135,8 @@ impl ExecutorContext {
|
|||||||
show_grid: false,
|
show_grid: false,
|
||||||
replay: None,
|
replay: None,
|
||||||
},
|
},
|
||||||
|
None,
|
||||||
|
engine_addr,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(ctx)
|
Ok(ctx)
|
||||||
|
@ -3,41 +3,13 @@ use std::sync::{Arc, RwLock};
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use tower_lsp::LanguageServer;
|
use tower_lsp::LanguageServer;
|
||||||
|
|
||||||
fn new_zoo_client() -> kittycad::Client {
|
|
||||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
|
||||||
let http_client = reqwest::Client::builder()
|
|
||||||
.user_agent(user_agent)
|
|
||||||
// For file conversions we need this to be long.
|
|
||||||
.timeout(std::time::Duration::from_secs(600))
|
|
||||||
.connect_timeout(std::time::Duration::from_secs(60));
|
|
||||||
let ws_client = reqwest::Client::builder()
|
|
||||||
.user_agent(user_agent)
|
|
||||||
// For file conversions we need this to be long.
|
|
||||||
.timeout(std::time::Duration::from_secs(600))
|
|
||||||
.connect_timeout(std::time::Duration::from_secs(60))
|
|
||||||
.connection_verbose(true)
|
|
||||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
|
||||||
.http1_only();
|
|
||||||
|
|
||||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
|
||||||
|
|
||||||
// Create the client.
|
|
||||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
|
||||||
// Set a local engine address if it's set.
|
|
||||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
|
||||||
client.set_base_url(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
client
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a fake kcl lsp server for testing.
|
// Create a fake kcl lsp server for testing.
|
||||||
pub async fn kcl_lsp_server(execute: bool) -> Result<crate::lsp::kcl::Backend> {
|
pub async fn kcl_lsp_server(execute: bool) -> Result<crate::lsp::kcl::Backend> {
|
||||||
let stdlib = crate::std::StdLib::new();
|
let stdlib = crate::std::StdLib::new();
|
||||||
let stdlib_completions = crate::lsp::kcl::get_completions_from_stdlib(&stdlib)?;
|
let stdlib_completions = crate::lsp::kcl::get_completions_from_stdlib(&stdlib)?;
|
||||||
let stdlib_signatures = crate::lsp::kcl::get_signatures_from_stdlib(&stdlib)?;
|
let stdlib_signatures = crate::lsp::kcl::get_signatures_from_stdlib(&stdlib)?;
|
||||||
|
|
||||||
let zoo_client = new_zoo_client();
|
let zoo_client = crate::executor::new_zoo_client(None, None)?;
|
||||||
|
|
||||||
let executor_ctx = if execute {
|
let executor_ctx = if execute {
|
||||||
Some(crate::executor::ExecutorContext::new(&zoo_client, Default::default()).await?)
|
Some(crate::executor::ExecutorContext::new(&zoo_client, Default::default()).await?)
|
||||||
|
@ -1342,7 +1342,7 @@ fn declaration_keyword(i: TokenSlice) -> PResult<(VariableKind, Token)> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a variable/constant declaration.
|
/// Parse a variable/constant declaration.
|
||||||
fn declaration(i: TokenSlice) -> PResult<VariableDeclaration> {
|
fn declaration(i: TokenSlice) -> PResult<Box<VariableDeclaration>> {
|
||||||
let (visibility, visibility_token) = opt(terminated(item_visibility, whitespace))
|
let (visibility, visibility_token) = opt(terminated(item_visibility, whitespace))
|
||||||
.parse_next(i)?
|
.parse_next(i)?
|
||||||
.map_or((ItemVisibility::Default, None), |pair| (pair.0, Some(pair.1)));
|
.map_or((ItemVisibility::Default, None), |pair| (pair.0, Some(pair.1)));
|
||||||
@ -1404,7 +1404,7 @@ fn declaration(i: TokenSlice) -> PResult<VariableDeclaration> {
|
|||||||
.map_err(|e| e.cut())?;
|
.map_err(|e| e.cut())?;
|
||||||
|
|
||||||
let end = val.end();
|
let end = val.end();
|
||||||
Ok(VariableDeclaration {
|
Ok(Box::new(VariableDeclaration {
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
declarations: vec![VariableDeclarator {
|
declarations: vec![VariableDeclarator {
|
||||||
@ -1417,7 +1417,7 @@ fn declaration(i: TokenSlice) -> PResult<VariableDeclaration> {
|
|||||||
visibility,
|
visibility,
|
||||||
kind,
|
kind,
|
||||||
digest: None,
|
digest: None,
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Token> for Identifier {
|
impl TryFrom<Token> for Identifier {
|
||||||
|
@ -141,15 +141,15 @@ pub(crate) async fn do_post_extrude(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if sketch.value.is_empty() {
|
if sketch.paths.is_empty() {
|
||||||
return Err(KclError::Type(KclErrorDetails {
|
return Err(KclError::Type(KclErrorDetails {
|
||||||
message: "Expected a non-empty sketch".to_string(),
|
message: "Expected a non-empty sketch".to_string(),
|
||||||
source_ranges: vec![args.source_range],
|
source_ranges: vec![args.source_range],
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let edge_id = sketch.value.iter().find_map(|segment| match segment {
|
let edge_id = sketch.paths.iter().find_map(|segment| match segment {
|
||||||
Path::ToPoint { base } | Path::Circle { base, .. } => Some(base.geo_meta.id),
|
Path::ToPoint { base } | Path::Circle { base, .. } | Path::Arc { base, .. } => Some(base.geo_meta.id),
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -229,12 +229,15 @@ pub(crate) async fn do_post_extrude(
|
|||||||
} = analyze_faces(exec_state, &args, face_infos);
|
} = analyze_faces(exec_state, &args, face_infos);
|
||||||
// Iterate over the sketch.value array and add face_id to GeoMeta
|
// Iterate over the sketch.value array and add face_id to GeoMeta
|
||||||
let new_value = sketch
|
let new_value = sketch
|
||||||
.value
|
.paths
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|path| {
|
.flat_map(|path| {
|
||||||
if let Some(Some(actual_face_id)) = face_id_map.get(&path.get_base().geo_meta.id) {
|
if let Some(Some(actual_face_id)) = face_id_map.get(&path.get_base().geo_meta.id) {
|
||||||
match path {
|
match path {
|
||||||
Path::TangentialArc { .. } | Path::TangentialArcTo { .. } | Path::Circle { .. } => {
|
Path::Arc { .. }
|
||||||
|
| Path::TangentialArc { .. }
|
||||||
|
| Path::TangentialArcTo { .. }
|
||||||
|
| Path::Circle { .. } => {
|
||||||
let extrude_surface = ExtrudeSurface::ExtrudeArc(crate::executor::ExtrudeArc {
|
let extrude_surface = ExtrudeSurface::ExtrudeArc(crate::executor::ExtrudeArc {
|
||||||
face_id: *actual_face_id,
|
face_id: *actual_face_id,
|
||||||
tag: path.get_base().tag.clone(),
|
tag: path.get_base().tag.clone(),
|
||||||
|
@ -42,7 +42,7 @@ fn inner_segment_end_x(tag: &TagIdentifier, exec_state: &mut ExecState, args: Ar
|
|||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(path.to[0])
|
Ok(path.get_base().to[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the segment end of y.
|
/// Returns the segment end of y.
|
||||||
@ -79,7 +79,7 @@ fn inner_segment_end_y(tag: &TagIdentifier, exec_state: &mut ExecState, args: Ar
|
|||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(path.to[1])
|
Ok(path.get_to()[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the last segment of x.
|
/// Returns the last segment of x.
|
||||||
@ -109,7 +109,7 @@ pub async fn last_segment_x(_exec_state: &mut ExecState, args: Args) -> Result<K
|
|||||||
}]
|
}]
|
||||||
fn inner_last_segment_x(sketch: Sketch, args: Args) -> Result<f64, KclError> {
|
fn inner_last_segment_x(sketch: Sketch, args: Args) -> Result<f64, KclError> {
|
||||||
let last_line = sketch
|
let last_line = sketch
|
||||||
.value
|
.paths
|
||||||
.last()
|
.last()
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
KclError::Type(KclErrorDetails {
|
KclError::Type(KclErrorDetails {
|
||||||
@ -149,7 +149,7 @@ pub async fn last_segment_y(_exec_state: &mut ExecState, args: Args) -> Result<K
|
|||||||
}]
|
}]
|
||||||
fn inner_last_segment_y(sketch: Sketch, args: Args) -> Result<f64, KclError> {
|
fn inner_last_segment_y(sketch: Sketch, args: Args) -> Result<f64, KclError> {
|
||||||
let last_line = sketch
|
let last_line = sketch
|
||||||
.value
|
.paths
|
||||||
.last()
|
.last()
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
KclError::Type(KclErrorDetails {
|
KclError::Type(KclErrorDetails {
|
||||||
@ -202,7 +202,7 @@ fn inner_segment_length(tag: &TagIdentifier, exec_state: &mut ExecState, args: A
|
|||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let result = ((path.from[1] - path.to[1]).powi(2) + (path.from[0] - path.to[0]).powi(2)).sqrt();
|
let result = path.length();
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
@ -242,7 +242,7 @@ fn inner_segment_angle(tag: &TagIdentifier, exec_state: &mut ExecState, args: Ar
|
|||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let result = between(path.from.into(), path.to.into());
|
let result = between(path.get_from().into(), path.get_to().into());
|
||||||
|
|
||||||
Ok(result.to_degrees())
|
Ok(result.to_degrees())
|
||||||
}
|
}
|
||||||
@ -286,10 +286,10 @@ fn inner_angle_to_match_length_x(
|
|||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let length = ((path.from[1] - path.to[1]).powi(2) + (path.from[0] - path.to[0]).powi(2)).sqrt();
|
let length = path.length();
|
||||||
|
|
||||||
let last_line = sketch
|
let last_line = sketch
|
||||||
.value
|
.paths
|
||||||
.last()
|
.last()
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
KclError::Type(KclErrorDetails {
|
KclError::Type(KclErrorDetails {
|
||||||
@ -350,10 +350,10 @@ fn inner_angle_to_match_length_y(
|
|||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let length = ((path.from[1] - path.to[1]).powi(2) + (path.from[0] - path.to[0]).powi(2)).sqrt();
|
let length = path.length();
|
||||||
|
|
||||||
let last_line = sketch
|
let last_line = sketch
|
||||||
.value
|
.paths
|
||||||
.last()
|
.last()
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
KclError::Type(KclErrorDetails {
|
KclError::Type(KclErrorDetails {
|
||||||
|
@ -134,7 +134,7 @@ async fn inner_circle(
|
|||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.value.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
|
|
||||||
args.batch_modeling_cmd(id, ModelingCmd::from(mcmd::ClosePath { path_id: new_sketch.id }))
|
args.batch_modeling_cmd(id, ModelingCmd::from(mcmd::ClosePath { path_id: new_sketch.id }))
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -154,7 +154,7 @@ async fn inner_line_to(
|
|||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.value.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
|
|
||||||
Ok(new_sketch)
|
Ok(new_sketch)
|
||||||
}
|
}
|
||||||
@ -323,7 +323,7 @@ async fn inner_line(
|
|||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.value.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
|
|
||||||
Ok(new_sketch)
|
Ok(new_sketch)
|
||||||
}
|
}
|
||||||
@ -506,7 +506,7 @@ async fn inner_angled_line(
|
|||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.value.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
Ok(new_sketch)
|
Ok(new_sketch)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -813,7 +813,7 @@ async fn inner_angled_line_that_intersects(
|
|||||||
|
|
||||||
let from = sketch.current_pen_position()?;
|
let from = sketch.current_pen_position()?;
|
||||||
let to = intersection_with_parallel_line(
|
let to = intersection_with_parallel_line(
|
||||||
&[path.from.into(), path.to.into()],
|
&[path.get_from().into(), path.get_to().into()],
|
||||||
data.offset.unwrap_or_default(),
|
data.offset.unwrap_or_default(),
|
||||||
data.angle,
|
data.angle,
|
||||||
from,
|
from,
|
||||||
@ -1237,14 +1237,16 @@ pub(crate) async fn inner_start_profile_at(
|
|||||||
id: path_id,
|
id: path_id,
|
||||||
original_id: path_id,
|
original_id: path_id,
|
||||||
on: sketch_surface.clone(),
|
on: sketch_surface.clone(),
|
||||||
value: vec![],
|
paths: vec![],
|
||||||
meta: vec![args.source_range.into()],
|
meta: vec![args.source_range.into()],
|
||||||
tags: if let Some(tag) = &tag {
|
tags: if let Some(tag) = &tag {
|
||||||
let mut tag_identifier: TagIdentifier = tag.into();
|
let mut tag_identifier: TagIdentifier = tag.into();
|
||||||
tag_identifier.info = Some(TagEngineInfo {
|
tag_identifier.info = Some(TagEngineInfo {
|
||||||
id: current_path.geo_meta.id,
|
id: current_path.geo_meta.id,
|
||||||
sketch: path_id,
|
sketch: path_id,
|
||||||
path: Some(current_path.clone()),
|
path: Some(Path::Base {
|
||||||
|
base: current_path.clone(),
|
||||||
|
}),
|
||||||
surface: None,
|
surface: None,
|
||||||
});
|
});
|
||||||
HashMap::from([(tag.name.to_string(), tag_identifier)])
|
HashMap::from([(tag.name.to_string(), tag_identifier)])
|
||||||
@ -1411,7 +1413,7 @@ pub(crate) async fn inner_close(
|
|||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.value.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
|
|
||||||
Ok(new_sketch)
|
Ok(new_sketch)
|
||||||
}
|
}
|
||||||
@ -1528,7 +1530,7 @@ pub(crate) async fn inner_arc(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let current_path = Path::ToPoint {
|
let current_path = Path::Arc {
|
||||||
base: BasePath {
|
base: BasePath {
|
||||||
from: from.into(),
|
from: from.into(),
|
||||||
to: end.into(),
|
to: end.into(),
|
||||||
@ -1538,6 +1540,8 @@ pub(crate) async fn inner_arc(
|
|||||||
metadata: args.source_range.into(),
|
metadata: args.source_range.into(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
center: center.into(),
|
||||||
|
radius,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut new_sketch = sketch.clone();
|
let mut new_sketch = sketch.clone();
|
||||||
@ -1545,7 +1549,7 @@ pub(crate) async fn inner_arc(
|
|||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.value.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
|
|
||||||
Ok(new_sketch)
|
Ok(new_sketch)
|
||||||
}
|
}
|
||||||
@ -1677,7 +1681,7 @@ async fn inner_tangential_arc(
|
|||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.value.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
|
|
||||||
Ok(new_sketch)
|
Ok(new_sketch)
|
||||||
}
|
}
|
||||||
@ -1773,7 +1777,7 @@ async fn inner_tangential_arc_to(
|
|||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.value.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
|
|
||||||
Ok(new_sketch)
|
Ok(new_sketch)
|
||||||
}
|
}
|
||||||
@ -1858,7 +1862,7 @@ async fn inner_tangential_arc_to_relative(
|
|||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.value.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
|
|
||||||
Ok(new_sketch)
|
Ok(new_sketch)
|
||||||
}
|
}
|
||||||
@ -1951,7 +1955,7 @@ async fn inner_bezier_curve(
|
|||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.value.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
|
|
||||||
Ok(new_sketch)
|
Ok(new_sketch)
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ pub fn normalize(angle: Angle) -> Angle {
|
|||||||
Angle::from_degrees(if result > 180.0 { result - 360.0 } else { result })
|
Angle::from_degrees(if result > 180.0 { result - 360.0 } else { result })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gives the ▲-angle between from and to angles (shortest path), use radians.
|
/// Gives the ▲-angle between from and to angles (shortest path)
|
||||||
///
|
///
|
||||||
/// Sign of the returned angle denotes direction, positive means counterClockwise 🔄
|
/// Sign of the returned angle denotes direction, positive means counterClockwise 🔄
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -43,38 +43,7 @@ async fn do_execute_and_snapshot(ctx: &ExecutorContext, code: &str) -> anyhow::R
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn new_context(units: UnitLength, with_auth: bool) -> anyhow::Result<ExecutorContext> {
|
async fn new_context(units: UnitLength, with_auth: bool) -> anyhow::Result<ExecutorContext> {
|
||||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
let ctx = ExecutorContext::new_with_client(
|
||||||
let http_client = reqwest::Client::builder()
|
|
||||||
.user_agent(user_agent)
|
|
||||||
// For file conversions we need this to be long.
|
|
||||||
.timeout(std::time::Duration::from_secs(600))
|
|
||||||
.connect_timeout(std::time::Duration::from_secs(60));
|
|
||||||
let ws_client = reqwest::Client::builder()
|
|
||||||
.user_agent(user_agent)
|
|
||||||
// For file conversions we need this to be long.
|
|
||||||
.timeout(std::time::Duration::from_secs(600))
|
|
||||||
.connect_timeout(std::time::Duration::from_secs(60))
|
|
||||||
.connection_verbose(true)
|
|
||||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
|
||||||
.http1_only();
|
|
||||||
|
|
||||||
let token = if with_auth {
|
|
||||||
std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set")
|
|
||||||
} else {
|
|
||||||
"bad_token".to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create the client.
|
|
||||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
|
||||||
// Set a local engine address if it's set.
|
|
||||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
|
||||||
if with_auth {
|
|
||||||
client.set_base_url(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let ctx = ExecutorContext::new(
|
|
||||||
&client,
|
|
||||||
ExecutorSettings {
|
ExecutorSettings {
|
||||||
units,
|
units,
|
||||||
highlight_edges: true,
|
highlight_edges: true,
|
||||||
@ -82,6 +51,8 @@ async fn new_context(units: UnitLength, with_auth: bool) -> anyhow::Result<Execu
|
|||||||
show_grid: false,
|
show_grid: false,
|
||||||
replay: None,
|
replay: None,
|
||||||
},
|
},
|
||||||
|
if with_auth { None } else { Some("bad_token".to_string()) },
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(ctx)
|
Ok(ctx)
|
||||||
|
@ -290,7 +290,7 @@ where
|
|||||||
walk_value(&xs.expression, f)
|
walk_value(&xs.expression, f)
|
||||||
}
|
}
|
||||||
BodyItem::VariableDeclaration(vd) => {
|
BodyItem::VariableDeclaration(vd) => {
|
||||||
if !f.walk(vd.into())? {
|
if !f.walk(vd.as_ref().into())? {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
for dec in &vd.declarations {
|
for dec in &vd.declarations {
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "1.81.0"
|
channel = "1.82.0"
|
||||||
components = ["clippy", "rustfmt"]
|
components = ["clippy", "rustfmt"]
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
exampleSketch = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> line([0, 2], %)
|
||||||
|
|> line([3, 1], %)
|
||||||
|
|> line([0, -4], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(1, %)
|
||||||
|
|
||||||
|
pattn1 = patternLinear3d({
|
||||||
|
axis: [1, 0, 0],
|
||||||
|
instances: 7,
|
||||||
|
distance: 6
|
||||||
|
}, exampleSketch)
|
||||||
|
|
||||||
|
pattn2 = patternCircular3d({axis: [0,0, 1], center: [-20, -20, -20], instances: 41, arcDegrees: 360, rotateDuplicates: false}, pattn1)
|
@ -0,0 +1,19 @@
|
|||||||
|
exampleSketch = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> line([0, 2], %)
|
||||||
|
|> line([3, 1], %)
|
||||||
|
|> line([0, -4], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(1, %)
|
||||||
|
|
||||||
|
pattn1 = patternLinear3d({
|
||||||
|
axis: [1, 0, 0],
|
||||||
|
instances: 7,
|
||||||
|
distance: 6
|
||||||
|
}, exampleSketch)
|
||||||
|
|
||||||
|
pattn2 = patternLinear3d({
|
||||||
|
axis: [0, 0, 1],
|
||||||
|
distance: 1,
|
||||||
|
instances: 7
|
||||||
|
}, pattn1)
|
@ -1,5 +1,5 @@
|
|||||||
fn cube = (pos, scale) => {
|
fn square = (pos, scale) => {
|
||||||
const sg = startSketchOn('XY')
|
sg = startSketchOn('XY')
|
||||||
|> startProfileAt(pos, %)
|
|> startProfileAt(pos, %)
|
||||||
|> line([0, scale], %)
|
|> line([0, scale], %)
|
||||||
|> line([scale, 0], %)
|
|> line([scale, 0], %)
|
||||||
@ -9,9 +9,9 @@ fn cube = (pos, scale) => {
|
|||||||
return sg
|
return sg
|
||||||
}
|
}
|
||||||
|
|
||||||
const b1 = cube([0,0], 10)
|
sq = square([0,0], 10)
|
||||||
const b2 = cube([3,3], 4)
|
cb = square([3,3], 4)
|
||||||
|> extrude(10, %)
|
|> extrude(10, %)
|
||||||
|
|
||||||
const pt1 = b1.value[0]
|
pt1 = sq.paths[0]
|
||||||
const pt2 = b2.value[0]
|
pt2 = cb.value[0]
|
||||||
|
6
src/wasm-lib/tests/executor/inputs/neg_xz_plane.kcl
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> lineTo([100, 100], %)
|
||||||
|
|> lineTo([100, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5 + 7, %)
|
@ -1,3 +1,2 @@
|
|||||||
xs = [-5..5]
|
xs = [int(-5)..5]
|
||||||
assertEqual(xs[0], -5, 0.001, "first element is -5")
|
assertEqual(xs[0], -5, 0.001, "first element is -5")
|
||||||
assert(false)
|
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
// Shelf Bracket
|
||||||
|
// This is a shelf bracket made out of 6061-T6 aluminum sheet metal. The required thickness is calculated based on a point load of 300 lbs applied to the end of the shelf. There are two brackets holding up the shelf, so the moment experienced is divided by 2. The shelf is 1 foot long from the wall.
|
||||||
|
|
||||||
|
|
||||||
|
// Define our bracket feet lengths
|
||||||
|
shelfMountL = 8 // The length of the bracket holding up the shelf is 6 inches
|
||||||
|
wallMountL = 6 // the length of the bracket
|
||||||
|
|
||||||
|
|
||||||
|
// Define constants required to calculate the thickness needed to support 300 lbs
|
||||||
|
sigmaAllow = 35000 // psi
|
||||||
|
width = 6 // inch
|
||||||
|
p = 300 // Force on shelf - lbs
|
||||||
|
L = 12 // inches
|
||||||
|
M = L * p / 2 // Moment experienced at fixed end of bracket
|
||||||
|
FOS = 2 // Factor of safety of 2 to be conservative
|
||||||
|
|
||||||
|
|
||||||
|
// Calculate the thickness off the bending stress and factor of safety
|
||||||
|
thickness = sqrt(6 * M * FOS / (width * sigmaAllow))
|
||||||
|
|
||||||
|
// 0.25 inch fillet radius
|
||||||
|
filletR = 0.25
|
||||||
|
|
||||||
|
// Sketch the bracket and extrude with fillets
|
||||||
|
bracket = startSketchOn('XY')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> line([0, wallMountL], %, $outerEdge)
|
||||||
|
|> line([-shelfMountL, 0], %, $seg01)
|
||||||
|
|> line([0, -thickness], %)
|
||||||
|
|> line([shelfMountL - thickness, 0], %, $innerEdge)
|
||||||
|
|> line([0, -wallMountL + thickness], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(width, %)
|
||||||
|
|> fillet({
|
||||||
|
radius: filletR,
|
||||||
|
tags: [
|
||||||
|
getNextAdjacentEdge(innerEdge)
|
||||||
|
]
|
||||||
|
}, %)
|
||||||
|
|> fillet({
|
||||||
|
radius: filletR + thickness,
|
||||||
|
tags: [
|
||||||
|
getNextAdjacentEdge(outerEdge)
|
||||||
|
]
|
||||||
|
}, %)
|
||||||
|
|
||||||
|
sketch001 = startSketchOn(bracket, seg01)
|
||||||
|
|> startProfileAt([4.28, 3.83], %)
|
||||||
|
|> line([2.17, -0.03], %)
|
||||||
|
|> line([-0.07, -1.8], %)
|
||||||
|
|> line([-2.07, 0.05], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(10, %)
|
6
src/wasm-lib/tests/executor/inputs/xz_plane.kcl
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
part001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> lineTo([100, 100], %)
|
||||||
|
|> lineTo([100, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5 + 7, %)
|
@ -1389,84 +1389,6 @@ extrusion = startSketchOn('XY')
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn kcl_test_xz_plane() {
|
|
||||||
let code = r#"part001 = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([0, 0], %)
|
|
||||||
|> lineTo([100, 100], %)
|
|
||||||
|> lineTo([100, 0], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(5 + 7, %)
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
|
||||||
assert_out("xz_plane", &result);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn kcl_test_neg_xz_plane() {
|
|
||||||
let code = r#"part001 = startSketchOn('-XZ')
|
|
||||||
|> startProfileAt([0, 0], %)
|
|
||||||
|> lineTo([100, 100], %)
|
|
||||||
|> lineTo([100, 0], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(5 + 7, %)
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
|
||||||
assert_out("neg_xz_plane", &result);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn kcl_test_linear_pattern3d_a_pattern() {
|
|
||||||
let code = r#"exampleSketch = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([0, 0], %)
|
|
||||||
|> line([0, 2], %)
|
|
||||||
|> line([3, 1], %)
|
|
||||||
|> line([0, -4], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(1, %)
|
|
||||||
|
|
||||||
pattn1 = patternLinear3d({
|
|
||||||
axis: [1, 0, 0],
|
|
||||||
instances: 7,
|
|
||||||
distance: 6
|
|
||||||
}, exampleSketch)
|
|
||||||
|
|
||||||
pattn2 = patternLinear3d({
|
|
||||||
axis: [0, 0, 1],
|
|
||||||
distance: 1,
|
|
||||||
instances: 7
|
|
||||||
}, pattn1)
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
|
||||||
assert_out("linear_pattern3d_a_pattern", &result);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn kcl_test_circular_pattern3d_a_pattern() {
|
|
||||||
let code = r#"exampleSketch = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([0, 0], %)
|
|
||||||
|> line([0, 2], %)
|
|
||||||
|> line([3, 1], %)
|
|
||||||
|> line([0, -4], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(1, %)
|
|
||||||
|
|
||||||
pattn1 = patternLinear3d({
|
|
||||||
axis: [1, 0, 0],
|
|
||||||
instances: 7,
|
|
||||||
distance: 6
|
|
||||||
}, exampleSketch)
|
|
||||||
|
|
||||||
pattn2 = patternCircular3d({axis: [0,0, 1], center: [-20, -20, -20], instances: 41, arcDegrees: 360, rotateDuplicates: false}, pattn1)
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
|
||||||
assert_out("circular_pattern3d_a_pattern", &result);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn kcl_test_array_of_sketches() {
|
async fn kcl_test_array_of_sketches() {
|
||||||
let code = r#"plane001 = startSketchOn('XZ')
|
let code = r#"plane001 = startSketchOn('XZ')
|
||||||
@ -1496,69 +1418,6 @@ extrude(10, sketch001)
|
|||||||
assert_out("array_of_sketches", &result);
|
assert_out("array_of_sketches", &result);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn kcl_test_sketch_on_face_after_fillets_referencing_face() {
|
|
||||||
let code = r#"// Shelf Bracket
|
|
||||||
// This is a shelf bracket made out of 6061-T6 aluminum sheet metal. The required thickness is calculated based on a point load of 300 lbs applied to the end of the shelf. There are two brackets holding up the shelf, so the moment experienced is divided by 2. The shelf is 1 foot long from the wall.
|
|
||||||
|
|
||||||
|
|
||||||
// Define our bracket feet lengths
|
|
||||||
shelfMountL = 8 // The length of the bracket holding up the shelf is 6 inches
|
|
||||||
wallMountL = 6 // the length of the bracket
|
|
||||||
|
|
||||||
|
|
||||||
// Define constants required to calculate the thickness needed to support 300 lbs
|
|
||||||
sigmaAllow = 35000 // psi
|
|
||||||
width = 6 // inch
|
|
||||||
p = 300 // Force on shelf - lbs
|
|
||||||
L = 12 // inches
|
|
||||||
M = L * p / 2 // Moment experienced at fixed end of bracket
|
|
||||||
FOS = 2 // Factor of safety of 2 to be conservative
|
|
||||||
|
|
||||||
|
|
||||||
// Calculate the thickness off the bending stress and factor of safety
|
|
||||||
thickness = sqrt(6 * M * FOS / (width * sigmaAllow))
|
|
||||||
|
|
||||||
// 0.25 inch fillet radius
|
|
||||||
filletR = 0.25
|
|
||||||
|
|
||||||
// Sketch the bracket and extrude with fillets
|
|
||||||
bracket = startSketchOn('XY')
|
|
||||||
|> startProfileAt([0, 0], %)
|
|
||||||
|> line([0, wallMountL], %, $outerEdge)
|
|
||||||
|> line([-shelfMountL, 0], %, $seg01)
|
|
||||||
|> line([0, -thickness], %)
|
|
||||||
|> line([shelfMountL - thickness, 0], %, $innerEdge)
|
|
||||||
|> line([0, -wallMountL + thickness], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(width, %)
|
|
||||||
|> fillet({
|
|
||||||
radius: filletR,
|
|
||||||
tags: [
|
|
||||||
getNextAdjacentEdge(innerEdge)
|
|
||||||
]
|
|
||||||
}, %)
|
|
||||||
|> fillet({
|
|
||||||
radius: filletR + thickness,
|
|
||||||
tags: [
|
|
||||||
getNextAdjacentEdge(outerEdge)
|
|
||||||
]
|
|
||||||
}, %)
|
|
||||||
|
|
||||||
sketch001 = startSketchOn(bracket, seg01)
|
|
||||||
|> startProfileAt([4.28, 3.83], %)
|
|
||||||
|> line([2.17, -0.03], %)
|
|
||||||
|> line([-0.07, -1.8], %)
|
|
||||||
|> line([-2.07, 0.05], %)
|
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|
||||||
|> close(%)
|
|
||||||
|> extrude(10, %)
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
|
||||||
assert_out("sketch_on_face_after_fillets_referencing_face", &result);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn kcl_test_circular_pattern3d_array_of_extrudes() {
|
async fn kcl_test_circular_pattern3d_array_of_extrudes() {
|
||||||
let code = r#"plane001 = startSketchOn('XZ')
|
let code = r#"plane001 = startSketchOn('XZ')
|
||||||
|
@ -101,7 +101,7 @@ gen_test!(property_of_object);
|
|||||||
gen_test!(index_of_array);
|
gen_test!(index_of_array);
|
||||||
gen_test!(comparisons);
|
gen_test!(comparisons);
|
||||||
gen_test!(array_range_expr);
|
gen_test!(array_range_expr);
|
||||||
gen_test_fail!(array_range_negative_expr, "syntax: Invalid integer: -5.0");
|
gen_test!(array_range_negative_expr);
|
||||||
gen_test_fail!(
|
gen_test_fail!(
|
||||||
invalid_index_str,
|
invalid_index_str,
|
||||||
"semantic: Only integers >= 0 can be used as the index of an array, but you're using a string"
|
"semantic: Only integers >= 0 can be used as the index of an array, but you're using a string"
|
||||||
|
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 127 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 33 KiB |
@ -19,6 +19,15 @@ macro_rules! kcl_test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kcl_test!("sketch_on_face", kcl_test_sketch_on_face);
|
kcl_test!("sketch_on_face", kcl_test_sketch_on_face);
|
||||||
|
kcl_test!("neg_xz_plane", kcl_test_neg_xz_plane);
|
||||||
|
kcl_test!("xz_plane", kcl_test_xz_plane);
|
||||||
|
kcl_test!(
|
||||||
|
"sketch_on_face_after_fillets_referencing_face",
|
||||||
|
kcl_test_sketch_on_face_after_fillets_referencing_face
|
||||||
|
);
|
||||||
|
kcl_test!("circular_pattern3d_a_pattern", kcl_test_circular_pattern3d_a_pattern);
|
||||||
|
kcl_test!("linear_pattern3d_a_pattern", kcl_test_linear_pattern3d_a_pattern);
|
||||||
|
|
||||||
kcl_test!("tangential_arc", kcl_test_tangential_arc);
|
kcl_test!("tangential_arc", kcl_test_tangential_arc);
|
||||||
kcl_test!(
|
kcl_test!(
|
||||||
"big_number_angle_to_match_length_x",
|
"big_number_angle_to_match_length_x",
|
||||||
|
@ -8,33 +8,10 @@ use pretty_assertions::assert_eq;
|
|||||||
|
|
||||||
/// Setup the engine and parse code for an ast.
|
/// Setup the engine and parse code for an ast.
|
||||||
async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid::Uuid)> {
|
async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid::Uuid)> {
|
||||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
|
||||||
let http_client = reqwest::Client::builder()
|
|
||||||
.user_agent(user_agent)
|
|
||||||
// For file conversions we need this to be long.
|
|
||||||
.timeout(std::time::Duration::from_secs(600))
|
|
||||||
.connect_timeout(std::time::Duration::from_secs(60));
|
|
||||||
let ws_client = reqwest::Client::builder()
|
|
||||||
.user_agent(user_agent)
|
|
||||||
// For file conversions we need this to be long.
|
|
||||||
.timeout(std::time::Duration::from_secs(600))
|
|
||||||
.connect_timeout(std::time::Duration::from_secs(60))
|
|
||||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
|
||||||
.http1_only();
|
|
||||||
|
|
||||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
|
||||||
|
|
||||||
// Create the client.
|
|
||||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
|
||||||
// Set a local engine address if it's set.
|
|
||||||
if let Ok(addr) = std::env::var("LOCAL_ENGINE_ADDR") {
|
|
||||||
client.set_base_url(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
let tokens = kcl_lib::token::lexer(code)?;
|
let tokens = kcl_lib::token::lexer(code)?;
|
||||||
let parser = kcl_lib::parser::Parser::new(tokens);
|
let parser = kcl_lib::parser::Parser::new(tokens);
|
||||||
let program = parser.ast()?;
|
let program = parser.ast()?;
|
||||||
let ctx = kcl_lib::executor::ExecutorContext::new(&client, Default::default()).await?;
|
let ctx = kcl_lib::executor::ExecutorContext::new_with_default_client(Default::default()).await?;
|
||||||
let exec_state = ctx.run(&program, None, IdGenerator::default(), None).await?;
|
let exec_state = ctx.run(&program, None, IdGenerator::default(), None).await?;
|
||||||
|
|
||||||
// We need to get the sketch ID.
|
// We need to get the sketch ID.
|
||||||
|
39
yarn.lock
@ -2597,10 +2597,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433"
|
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433"
|
||||||
integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==
|
integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==
|
||||||
|
|
||||||
"@types/node@*", "@types/node@^22.5.0":
|
"@types/node@*", "@types/node@^22.7.8":
|
||||||
version "22.5.0"
|
version "22.7.8"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.0.tgz#10f01fe9465166b4cab72e75f60d8b99d019f958"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.8.tgz#a1dbf0dc5f71bdd2642fc89caef65d58747ce825"
|
||||||
integrity sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==
|
integrity sha512-a922jJy31vqR5sk+kAdIENJjHblqcZ4RmERviFsER4WJcEONqxKcjNOlk0q7OUfrF5sddT+vng070cdfMlrPLg==
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types "~6.19.2"
|
undici-types "~6.19.2"
|
||||||
|
|
||||||
@ -8785,7 +8785,16 @@ string-natural-compare@^3.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
|
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
|
||||||
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
|
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
|
||||||
|
|
||||||
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
"string-width-cjs@npm:string-width@^4.2.0":
|
||||||
|
version "4.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||||
|
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||||
|
dependencies:
|
||||||
|
emoji-regex "^8.0.0"
|
||||||
|
is-fullwidth-code-point "^3.0.0"
|
||||||
|
strip-ansi "^6.0.1"
|
||||||
|
|
||||||
|
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||||
@ -8879,7 +8888,14 @@ string_decoder@~1.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer "~5.1.0"
|
safe-buffer "~5.1.0"
|
||||||
|
|
||||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||||
|
version "6.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||||
|
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||||
|
dependencies:
|
||||||
|
ansi-regex "^5.0.1"
|
||||||
|
|
||||||
|
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||||
@ -9753,7 +9769,16 @@ word-wrap@^1.2.3, word-wrap@^1.2.5:
|
|||||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
|
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
|
||||||
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
|
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
|
||||||
|
|
||||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
|
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||||
|
version "7.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||||
|
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||||
|
dependencies:
|
||||||
|
ansi-styles "^4.0.0"
|
||||||
|
string-width "^4.1.0"
|
||||||
|
strip-ansi "^6.0.0"
|
||||||
|
|
||||||
|
wrap-ansi@^7.0.0:
|
||||||
version "7.0.0"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||||
|