Compare commits

...

15 Commits

Author SHA1 Message Date
2f51763df9 Cut release v0.24.0 (#2972)
* Cut release v0.24.0

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Trigger CI

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Trigger CI

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Trigger CI

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Trigger CI

---------

Co-authored-by: Frank Noirot <frank@zoo.dev>
Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-07-09 10:29:08 -04:00
7c4bf8d793 fix when you comment out it should re-execute (#2975)
* fix when you comment out it should re-execute

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* copilot being a little shit

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fmt

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* fix

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* turn copilot off

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* fix for realisesi

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* remove footguns

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2024-07-08 21:07:15 -07:00
4747cdcab6 Bump syn from 2.0.69 to 2.0.70 in /src/wasm-lib (#2981)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.69 to 2.0.70.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.69...2.0.70)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-08 20:36:30 -07:00
de73f335fe Bump uuid from 1.9.1 to 1.10.0 in /src/wasm-lib (#2980)
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.9.1 to 1.10.0.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/1.9.1...1.10.0)

---
updated-dependencies:
- dependency-name: uuid
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-08 20:36:19 -07:00
a62004da82 Bump clap from 4.5.7 to 4.5.9 in /src/wasm-lib (#2979)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.7 to 4.5.9.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.7...v4.5.9)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-08 20:35:54 -07:00
92e0da1f8d playwright fixmes 😭 (#2977)
* add fixmes 😭

* fmt
2024-07-09 11:11:32 +10:00
a111473658 Uses the grammar marijn made :) (#2967)
* Add a Lezer KCL grammar

* fmt

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* make tsc happy

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* turn off semantic tokens in favor of grammar

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* fixups

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* empty

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: Marijn Haverbeke <marijn@haverbeke.berlin>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-07-08 16:47:30 -07:00
7cfed9bff4 Add a timeout instead of insta teardown on window hide (#2970)
* Add a timeout instead of insta teardown on window hide

* Close sketch mode on teardown
2024-07-09 07:58:09 +10:00
a30bd185d8 Allow modify sketch when extrude on end of pipe expr (#2960) 2024-07-09 07:57:37 +10:00
e8cae630a1 remove all z_near far params in engine calls, enable ssao again (#2956)
* remove all z_near far params in engine calls, enable ssao again

* fmt
2024-07-09 06:53:33 +10:00
74ec749560 Add segment length indicators to straight segments in sketch mode (#2935)
* Rough impl of line lengths, still duplicating

* Make sure the labels get cleared along with the rest of the sketch

* Show current units in segment length indicators

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Re-run CI after snapshots

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Re-run CI

* Make sure `close` segments get insert segment handles

* Skip engine connection tests on Safari with a todo

* Fmt

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-07-08 16:41:00 -04:00
ebdaf59d1c Bump pyo3 from 0.22.0 to 0.22.1 in /src/wasm-lib (#2949) 2024-07-08 12:44:55 -07:00
cd68414d54 Bump async-trait from 0.1.80 to 0.1.81 in /src/wasm-lib (#2948) 2024-07-08 12:44:43 -07:00
c8238ff04a Bump syn from 2.0.68 to 2.0.69 in /src/wasm-lib (#2947)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.68 to 2.0.69.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.68...2.0.69)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-08 10:00:23 -07:00
8089369108 Bump serde from 1.0.203 to 1.0.204 in /src/wasm-lib (#2946)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.203 to 1.0.204.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.203...v1.0.204)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-08 10:00:13 -07:00
44 changed files with 660 additions and 342 deletions

View File

@ -560,6 +560,50 @@ test.describe('Testing Camera Movement', () => {
}) })
test.describe('Editor tests', () => { test.describe('Editor tests', () => {
test('can comment out code with ctrl+/', async ({ page }) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1000, height: 500 })
await u.waitForAuthSkipAppStart()
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
// check no error to begin with
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
await u.codeLocator.click()
await page.keyboard.type(`const sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)`)
await page.keyboard.down(CtrlKey)
await page.keyboard.press('/')
await page.keyboard.up(CtrlKey)
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
// |> close(%)`)
// uncomment the code
await page.keyboard.down(CtrlKey)
await page.keyboard.press('/')
await page.keyboard.up(CtrlKey)
await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)`)
})
test('if you click the format button it formats your code', async ({ test('if you click the format button it formats your code', async ({
page, page,
}) => { }) => {
@ -1221,7 +1265,9 @@ test.describe('Editor tests', () => {
|> close(%)`) |> close(%)`)
}) })
test('Can undo a sketch modification with ctrl+z', async ({ page }) => { // failing for the same reason as "Can edit a sketch that has been extruded in the same pipe"
// please fix together
test.fixme('Can undo a sketch modification with ctrl+z', async ({ page }) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
@ -4070,9 +4116,11 @@ test.describe('Sketch tests', () => {
}) })
}) })
test('Can edit a sketch that has been extruded in the same pipe', async ({ // failing for the same reason as "Can undo a sketch modification with ctrl+z"
page, // please fix together
}) => { test.fixme(
'Can edit a sketch that has been extruded in the same pipe',
async ({ page }) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
@ -4171,7 +4219,8 @@ test.describe('Sketch tests', () => {
|> line([2.65, -2.69], %) |> line([2.65, -2.69], %)
|> close(%) |> close(%)
|> extrude(5, %)`) |> extrude(5, %)`)
}) }
)
test('Can edit a sketch that has been revolved in the same pipe', async ({ test('Can edit a sketch that has been revolved in the same pipe', async ({
page, page,
@ -6721,7 +6770,15 @@ ${extraLine ? 'const myVar = segLen(seg01, part001)' : ''}`
}) })
test.describe('Test network and connection issues', () => { test.describe('Test network and connection issues', () => {
test('simulate network down and network little widget', async ({ page }) => { test('simulate network down and network little widget', async ({
page,
browserName,
}) => {
// TODO: Don't skip Mac for these. After `window.tearDown` is working in Safari, these should work on webkit
test.skip(
browserName === 'webkit',
'Skip on Safari until `window.tearDown` is working there'
)
const u = await getUtils(page) const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
@ -6791,7 +6848,15 @@ test.describe('Test network and connection issues', () => {
await expect(page.getByText('Network Health (Connected)')).toBeVisible() await expect(page.getByText('Network Health (Connected)')).toBeVisible()
}) })
test('Engine disconnect & reconnect in sketch mode', async ({ page }) => { test('Engine disconnect & reconnect in sketch mode', async ({
page,
browserName,
}) => {
// TODO: Don't skip Mac for these. After `window.tearDown` is working in Safari, these should work on webkit
test.skip(
browserName === 'webkit',
'Skip on Safari until `window.tearDown` is working there'
)
const u = await getUtils(page) const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio const PUR = 400 / 37.5 //pixeltoUnitRatio

View File

@ -435,6 +435,8 @@ test('Draft segments should look right', async ({ page, context }) => {
await page.mouse.move(startXPx + PUR * 30, 500 - PUR * 20, { steps: 10 }) await page.mouse.move(startXPx + PUR * 30, 500 - PUR * 20, { steps: 10 })
await page.waitForTimeout(300)
await expect(page).toHaveScreenshot({ await expect(page).toHaveScreenshot({
maxDiffPixels: 100, maxDiffPixels: 100,
}) })

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -1,6 +1,6 @@
{ {
"name": "untitled-app", "name": "untitled-app",
"version": "0.23.1", "version": "0.24.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@codemirror/autocomplete": "^6.17.0", "@codemirror/autocomplete": "^6.17.0",
@ -17,7 +17,9 @@
"@fortawesome/react-fontawesome": "^0.2.0", "@fortawesome/react-fontawesome": "^0.2.0",
"@headlessui/react": "^1.7.19", "@headlessui/react": "^1.7.19",
"@headlessui/tailwindcss": "^0.2.0", "@headlessui/tailwindcss": "^0.2.0",
"@kittycad/lib": "^0.0.69", "@kittycad/lib": "^0.0.70",
"@lezer/highlight": "^1.2.0",
"@lezer/lr": "^1.4.1",
"@react-hook/resize-observer": "^2.0.1", "@react-hook/resize-observer": "^2.0.1",
"@replit/codemirror-interact": "^6.3.1", "@replit/codemirror-interact": "^6.3.1",
"@tauri-apps/api": "^2.0.0-beta.14", "@tauri-apps/api": "^2.0.0-beta.14",
@ -109,6 +111,7 @@
"@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/preset-env": "^7.24.3", "@babel/preset-env": "^7.24.3",
"@iarna/toml": "^2.2.5", "@iarna/toml": "^2.2.5",
"@lezer/generator": "^1.7.1",
"@playwright/test": "^1.45.1", "@playwright/test": "^1.45.1",
"@tauri-apps/cli": "==2.0.0-beta.13", "@tauri-apps/cli": "==2.0.0-beta.13",
"@testing-library/jest-dom": "^5.14.1", "@testing-library/jest-dom": "^5.14.1",

View File

@ -71,6 +71,9 @@ export interface LanguageServerOptions {
) => void ) => void
changesDelay?: number changesDelay?: number
doSemanticTokens?: boolean
doFoldingRanges?: boolean
} }
export class LanguageServerPlugin implements PluginValue { export class LanguageServerPlugin implements PluginValue {
@ -87,6 +90,9 @@ export class LanguageServerPlugin implements PluginValue {
notification: LSP.NotificationMessage notification: LSP.NotificationMessage
) => void ) => void
private doSemanticTokens: boolean = false
private doFoldingRanges: boolean = false
private _defferer = deferExecution((code: string) => { private _defferer = deferExecution((code: string) => {
try { try {
// Update the state (not the editor) with the new code. // Update the state (not the editor) with the new code.
@ -109,6 +115,9 @@ export class LanguageServerPlugin implements PluginValue {
this.client = options.client this.client = options.client
this.documentVersion = 0 this.documentVersion = 0
this.doSemanticTokens = options.doSemanticTokens ?? false
this.doFoldingRanges = options.doFoldingRanges ?? false
if (options.changesDelay) { if (options.changesDelay) {
this.changesDelay = options.changesDelay this.changesDelay = options.changesDelay
} }
@ -220,6 +229,7 @@ export class LanguageServerPlugin implements PluginValue {
async getFoldingRanges(): Promise<LSP.FoldingRange[] | null> { async getFoldingRanges(): Promise<LSP.FoldingRange[] | null> {
if ( if (
!this.doFoldingRanges ||
!this.client.ready || !this.client.ready ||
!this.client.getServerCapabilities().foldingRangeProvider !this.client.getServerCapabilities().foldingRangeProvider
) )
@ -445,6 +455,7 @@ export class LanguageServerPlugin implements PluginValue {
async requestSemanticTokens() { async requestSemanticTokens() {
if ( if (
!this.doSemanticTokens ||
!this.client.ready || !this.client.ready ||
!this.client.getServerCapabilities().semanticTokensProvider !this.client.getServerCapabilities().semanticTokensProvider
) { ) {

View File

@ -80,5 +80,5 @@
} }
}, },
"productName": "Zoo Modeling App", "productName": "Zoo Modeling App",
"version": "0.23.1" "version": "0.24.0"
} }

View File

@ -529,7 +529,6 @@ export class CameraControls {
parameters: { parameters: {
fov_y: fov_y:
this.camera instanceof PerspectiveCamera ? this.camera.fov : 45, this.camera instanceof PerspectiveCamera ? this.camera.fov : 45,
...calculateNearFarFromFOV(this.lastPerspectiveFov),
}, },
}, },
}) })
@ -612,8 +611,6 @@ export class CameraControls {
type: 'default_camera_set_perspective', type: 'default_camera_set_perspective',
parameters: { parameters: {
fov_y: newFov, fov_y: newFov,
z_near: 0.01,
z_far: 1000,
}, },
}, },
}) })
@ -631,8 +628,6 @@ export class CameraControls {
target: this.target, target: this.target,
}), }),
fov_y: newFov, fov_y: newFov,
z_near: 0.01,
z_far: 1000,
}, },
}) })
} }

View File

@ -96,6 +96,7 @@ export const ClientSideScene = ({
if (!canvasRef.current) return if (!canvasRef.current) return
const canvas = canvasRef.current const canvas = canvasRef.current
canvas.appendChild(sceneInfra.renderer.domElement) canvas.appendChild(sceneInfra.renderer.domElement)
canvas.appendChild(sceneInfra.labelRenderer.domElement)
sceneInfra.animate() sceneInfra.animate()
canvas.addEventListener('mousemove', sceneInfra.onMouseMove, false) canvas.addEventListener('mousemove', sceneInfra.onMouseMove, false)
canvas.addEventListener('mousedown', sceneInfra.onMouseDown, false) canvas.addEventListener('mousedown', sceneInfra.onMouseDown, false)

View File

@ -29,6 +29,9 @@ import {
INTERSECTION_PLANE_LAYER, INTERSECTION_PLANE_LAYER,
OnMouseEnterLeaveArgs, OnMouseEnterLeaveArgs,
RAYCASTABLE_PLANE, RAYCASTABLE_PLANE,
SEGMENT_LENGTH_LABEL,
SEGMENT_LENGTH_LABEL_OFFSET_PX,
SEGMENT_LENGTH_LABEL_TEXT,
SKETCH_GROUP_SEGMENTS, SKETCH_GROUP_SEGMENTS,
SKETCH_LAYER, SKETCH_LAYER,
X_AXIS, X_AXIS,
@ -47,6 +50,7 @@ import {
programMemoryInit, programMemoryInit,
recast, recast,
SketchGroup, SketchGroup,
ExtrudeGroup,
VariableDeclaration, VariableDeclaration,
VariableDeclarator, VariableDeclarator,
} from 'lang/wasm' } from 'lang/wasm'
@ -102,6 +106,7 @@ import {
} from 'lib/rectangleTool' } from 'lib/rectangleTool'
import { getThemeColorForThreeJs } from 'lib/theme' import { getThemeColorForThreeJs } from 'lib/theme'
import { err, trap } from 'lib/trap' import { err, trap } from 'lib/trap'
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
type DraftSegment = 'line' | 'tangentialArcTo' type DraftSegment = 'line' | 'tangentialArcTo'
@ -414,7 +419,7 @@ export class SceneEntities {
} }
) )
let seg let seg: Group
const _node1 = getNodeFromPath<CallExpression>( const _node1 = getNodeFromPath<CallExpression>(
maybeModdedAst, maybeModdedAst,
segPathToNode, segPathToNode,
@ -1075,9 +1080,16 @@ export class SceneEntities {
programMemoryOverride, programMemoryOverride,
}) })
this.sceneProgramMemory = programMemory this.sceneProgramMemory = programMemory
const sketchGroup = programMemory.root[
variableDeclarationName const maybeSketchGroup = programMemory.root[variableDeclarationName]
] as SketchGroup let sketchGroup = undefined
if (maybeSketchGroup.type === 'SketchGroup') {
sketchGroup = maybeSketchGroup
} else if ((maybeSketchGroup as ExtrudeGroup).sketchGroup) {
sketchGroup = (maybeSketchGroup as ExtrudeGroup).sketchGroup
}
if (!sketchGroup) return
const sgPaths = sketchGroup.value const sgPaths = sketchGroup.value
const orthoFactor = orthoScale(sceneInfra.camControls.camera) const orthoFactor = orthoScale(sceneInfra.camControls.camera)
@ -1302,6 +1314,7 @@ export class SceneEntities {
shape.moveTo(0, (-SEGMENT_WIDTH_PX / 2) * scale) // The width of the line in px (2.4px in this case) shape.moveTo(0, (-SEGMENT_WIDTH_PX / 2) * scale) // The width of the line in px (2.4px in this case)
shape.lineTo(0, (SEGMENT_WIDTH_PX / 2) * scale) shape.lineTo(0, (SEGMENT_WIDTH_PX / 2) * scale)
const arrowGroup = group.getObjectByName(ARROWHEAD) as Group const arrowGroup = group.getObjectByName(ARROWHEAD) as Group
const labelGroup = group.getObjectByName(SEGMENT_LENGTH_LABEL) as Group
const length = Math.sqrt( const length = Math.sqrt(
Math.pow(to[0] - from[0], 2) + Math.pow(to[1] - from[1], 2) Math.pow(to[0] - from[0], 2) + Math.pow(to[1] - from[1], 2)
@ -1347,6 +1360,29 @@ export class SceneEntities {
extraSegmentGroup.visible = isHandlesVisible extraSegmentGroup.visible = isHandlesVisible
} }
if (labelGroup) {
const labelWrapper = labelGroup.getObjectByName(
SEGMENT_LENGTH_LABEL_TEXT
) as CSS2DObject
const labelWrapperElem = labelWrapper.element as HTMLDivElement
const label = labelWrapperElem.children[0] as HTMLParagraphElement
label.innerText = `${roundOff(length)}${sceneInfra._baseUnit}`
label.classList.add(SEGMENT_LENGTH_LABEL_TEXT)
const offsetFromMidpoint = new Vector2(to[0] - from[0], to[1] - from[1])
.normalize()
.rotateAround(new Vector2(0, 0), Math.PI / 2)
.multiplyScalar(SEGMENT_LENGTH_LABEL_OFFSET_PX * scale)
label.style.setProperty('--x', `${offsetFromMidpoint.x}px`)
label.style.setProperty('--y', `${offsetFromMidpoint.y}px`)
labelWrapper.position.set(
(from[0] + to[0]) / 2 + offsetFromMidpoint.x,
(from[1] + to[1]) / 2 + offsetFromMidpoint.y,
0
)
labelGroup.visible = isHandlesVisible
}
const straightSegmentBody = group.children.find( const straightSegmentBody = group.children.find(
(child) => child.userData.type === STRAIGHT_SEGMENT_BODY (child) => child.userData.type === STRAIGHT_SEGMENT_BODY
) as Mesh ) as Mesh
@ -1397,6 +1433,14 @@ export class SceneEntities {
) )
let shouldResolve = false let shouldResolve = false
if (sketchSegments) { if (sketchSegments) {
// We have to manually remove the CSS2DObjects
// as they don't get removed when the group is removed
sketchSegments.traverse((object) => {
if (object instanceof CSS2DObject) {
object.element.remove()
object.remove()
}
})
this.scene.remove(sketchSegments) this.scene.remove(sketchSegments)
shouldResolve = true shouldResolve = true
} else { } else {

View File

@ -31,6 +31,7 @@ import { EngineCommandManager } from 'lang/std/engineConnection'
import { MouseState, SegmentOverlayPayload } from 'machines/modelingMachine' import { MouseState, SegmentOverlayPayload } from 'machines/modelingMachine'
import { getAngle, throttle } from 'lib/utils' import { getAngle, throttle } from 'lib/utils'
import { Themes } from 'lib/theme' import { Themes } from 'lib/theme'
import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer'
type SendType = ReturnType<typeof useModelingContext>['send'] type SendType = ReturnType<typeof useModelingContext>['send']
@ -54,6 +55,9 @@ export const Y_AXIS = 'yAxis'
export const AXIS_GROUP = 'axisGroup' export const AXIS_GROUP = 'axisGroup'
export const SKETCH_GROUP_SEGMENTS = 'sketch-group-segments' export const SKETCH_GROUP_SEGMENTS = 'sketch-group-segments'
export const ARROWHEAD = 'arrowhead' export const ARROWHEAD = 'arrowhead'
export const SEGMENT_LENGTH_LABEL = 'segment-length-label'
export const SEGMENT_LENGTH_LABEL_TEXT = 'segment-length-label-text'
export const SEGMENT_LENGTH_LABEL_OFFSET_PX = 30
export interface OnMouseEnterLeaveArgs { export interface OnMouseEnterLeaveArgs {
selected: Object3D<Object3DEventMap> selected: Object3D<Object3DEventMap>
@ -95,6 +99,7 @@ export class SceneInfra {
static instance: SceneInfra static instance: SceneInfra
scene: Scene scene: Scene
renderer: WebGLRenderer renderer: WebGLRenderer
labelRenderer: CSS2DRenderer
camControls: CameraControls camControls: CameraControls
isPerspective = true isPerspective = true
fov = 45 fov = 45
@ -264,6 +269,13 @@ export class SceneInfra {
this.renderer = new WebGLRenderer({ antialias: true, alpha: true }) // Enable transparency this.renderer = new WebGLRenderer({ antialias: true, alpha: true }) // Enable transparency
this.renderer.setSize(window.innerWidth, window.innerHeight) this.renderer.setSize(window.innerWidth, window.innerHeight)
this.renderer.setClearColor(0x000000, 0) // Set clear color to black with 0 alpha (fully transparent) this.renderer.setClearColor(0x000000, 0) // Set clear color to black with 0 alpha (fully transparent)
// LABEL RENDERER
this.labelRenderer = new CSS2DRenderer()
this.labelRenderer.setSize(window.innerWidth, window.innerHeight)
this.labelRenderer.domElement.style.position = 'absolute'
this.labelRenderer.domElement.style.top = '0px'
this.labelRenderer.domElement.style.pointerEvents = 'none'
window.addEventListener('resize', this.onWindowResize) window.addEventListener('resize', this.onWindowResize)
this.camControls = new CameraControls( this.camControls = new CameraControls(
@ -328,6 +340,7 @@ export class SceneInfra {
onWindowResize = () => { onWindowResize = () => {
this.renderer.setSize(window.innerWidth, window.innerHeight) this.renderer.setSize(window.innerWidth, window.innerHeight)
this.labelRenderer.setSize(window.innerWidth, window.innerHeight)
} }
animate = () => { animate = () => {
@ -337,6 +350,7 @@ export class SceneInfra {
// console.log('animation frame', this.cameraControls.camera) // console.log('animation frame', this.cameraControls.camera)
this.camControls.update() this.camControls.update()
this.renderer.render(this.scene, this.camControls.camera) this.renderer.render(this.scene, this.camControls.camera)
this.labelRenderer.render(this.scene, this.camControls.camera)
} }
} }

View File

@ -21,6 +21,7 @@ import {
Vector3, Vector3,
} from 'three' } from 'three'
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js' import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
import { PathToNode, SketchGroup, getTangentialArcToInfo } from 'lang/wasm' import { PathToNode, SketchGroup, getTangentialArcToInfo } from 'lang/wasm'
import { import {
EXTRA_SEGMENT_HANDLE, EXTRA_SEGMENT_HANDLE,
@ -36,8 +37,14 @@ import {
TANGENTIAL_ARC_TO__SEGMENT_DASH, TANGENTIAL_ARC_TO__SEGMENT_DASH,
} from './sceneEntities' } from './sceneEntities'
import { getTangentPointFromPreviousArc } from 'lib/utils2d' import { getTangentPointFromPreviousArc } from 'lib/utils2d'
import { ARROWHEAD } from './sceneInfra' import {
ARROWHEAD,
SEGMENT_LENGTH_LABEL,
SEGMENT_LENGTH_LABEL_OFFSET_PX,
SEGMENT_LENGTH_LABEL_TEXT,
} from './sceneInfra'
import { Themes, getThemeColorForThreeJs } from 'lib/theme' import { Themes, getThemeColorForThreeJs } from 'lib/theme'
import { roundOff } from 'lib/utils'
export function profileStart({ export function profileStart({
from, from,
@ -101,7 +108,7 @@ export function straightSegment({
theme: Themes theme: Themes
isSelected?: boolean isSelected?: boolean
}): Group { }): Group {
const group = new Group() const segmentGroup = new Group()
const shape = new Shape() const shape = new Shape()
shape.moveTo(0, (-SEGMENT_WIDTH_PX / 2) * scale) shape.moveTo(0, (-SEGMENT_WIDTH_PX / 2) * scale)
@ -133,7 +140,7 @@ export function straightSegment({
: STRAIGHT_SEGMENT_BODY : STRAIGHT_SEGMENT_BODY
mesh.name = STRAIGHT_SEGMENT_BODY mesh.name = STRAIGHT_SEGMENT_BODY
group.userData = { segmentGroup.userData = {
type: STRAIGHT_SEGMENT, type: STRAIGHT_SEGMENT,
id, id,
from, from,
@ -143,37 +150,60 @@ export function straightSegment({
callExpName, callExpName,
baseColor, baseColor,
} }
group.name = STRAIGHT_SEGMENT segmentGroup.name = STRAIGHT_SEGMENT
segmentGroup.add(mesh)
const length = Math.sqrt( const length = Math.sqrt(
Math.pow(to[0] - from[0], 2) + Math.pow(to[1] - from[1], 2) Math.pow(to[0] - from[0], 2) + Math.pow(to[1] - from[1], 2)
) )
const arrowGroup = createArrowhead(scale, theme, color)
arrowGroup.position.set(to[0], to[1], 0)
const dir = new Vector3()
.subVectors(new Vector3(to[0], to[1], 0), new Vector3(from[0], from[1], 0))
.normalize()
arrowGroup.quaternion.setFromUnitVectors(new Vector3(0, 1, 0), dir)
const pxLength = length / scale const pxLength = length / scale
const shouldHide = pxLength < HIDE_SEGMENT_LENGTH const shouldHide = pxLength < HIDE_SEGMENT_LENGTH
arrowGroup.visible = !shouldHide
group.add(mesh)
if (callExpName !== 'close') group.add(arrowGroup)
// All segment types get an extra segment handle,
// Which is a little plus sign that appears at the origin of the segment
// and can be dragged to insert a new segment
const extraSegmentGroup = createExtraSegmentHandle(scale, texture, theme) const extraSegmentGroup = createExtraSegmentHandle(scale, texture, theme)
const offsetFromBase = new Vector2(to[0] - from[0], to[1] - from[1]) const directionVector = new Vector2(
.normalize() to[0] - from[0],
.multiplyScalar(EXTRA_SEGMENT_OFFSET_PX * scale) to[1] - from[1]
).normalize()
const offsetFromBase = directionVector.multiplyScalar(
EXTRA_SEGMENT_OFFSET_PX * scale
)
extraSegmentGroup.position.set( extraSegmentGroup.position.set(
from[0] + offsetFromBase.x, from[0] + offsetFromBase.x,
from[1] + offsetFromBase.y, from[1] + offsetFromBase.y,
0 0
) )
extraSegmentGroup.visible = !shouldHide extraSegmentGroup.visible = !shouldHide
group.add(extraSegmentGroup) segmentGroup.add(extraSegmentGroup)
return group // Segment decorators that only apply to non-close segments
if (callExpName !== 'close') {
// an arrowhead that appears at the end of the segment
const arrowGroup = createArrowhead(scale, theme, color)
arrowGroup.position.set(to[0], to[1], 0)
const dir = new Vector3()
.subVectors(
new Vector3(to[0], to[1], 0),
new Vector3(from[0], from[1], 0)
)
.normalize()
arrowGroup.quaternion.setFromUnitVectors(new Vector3(0, 1, 0), dir)
arrowGroup.visible = !shouldHide
segmentGroup.add(arrowGroup)
// A length indicator that appears at the midpoint of the segment
const lengthIndicatorGroup = createLengthIndicator({
from,
to,
scale,
length,
})
segmentGroup.add(lengthIndicatorGroup)
}
return segmentGroup
} }
function createArrowhead(scale = 1, theme: Themes, color?: number): Group { function createArrowhead(scale = 1, theme: Themes, color?: number): Group {
@ -230,6 +260,46 @@ function createExtraSegmentHandle(
return extraSegmentGroup return extraSegmentGroup
} }
/**
* Creates a group containing a CSS2DObject with the length of the segment
*/
function createLengthIndicator({
from,
to,
scale,
length,
}: {
from: Coords2d
to: Coords2d
scale: number
length: number
}) {
const lengthIndicatorGroup = new Group()
lengthIndicatorGroup.name = SEGMENT_LENGTH_LABEL
// Make the elements
const lengthIndicatorText = document.createElement('p')
lengthIndicatorText.classList.add(SEGMENT_LENGTH_LABEL_TEXT)
lengthIndicatorText.innerText = roundOff(length).toString()
const lengthIndicatorWrapper = document.createElement('div')
// Style the elements
lengthIndicatorWrapper.style.position = 'absolute'
lengthIndicatorWrapper.appendChild(lengthIndicatorText)
const cssObject = new CSS2DObject(lengthIndicatorWrapper)
cssObject.name = SEGMENT_LENGTH_LABEL_TEXT
// Position the elements based on the line's heading
const offsetFromMidpoint = new Vector2(to[0] - from[0], to[1] - from[1])
.normalize()
.rotateAround(new Vector2(0, 0), -Math.PI / 2)
.multiplyScalar(SEGMENT_LENGTH_LABEL_OFFSET_PX * scale)
lengthIndicatorText.style.setProperty('--x', `${offsetFromMidpoint.x}px`)
lengthIndicatorText.style.setProperty('--y', `${offsetFromMidpoint.y}px`)
lengthIndicatorGroup.add(cssObject)
return lengthIndicatorGroup
}
export function tangentialArcToSegment({ export function tangentialArcToSegment({
prevSegment, prevSegment,
from, from,

View File

@ -8,7 +8,7 @@ import {
LanguageServerPlugin, LanguageServerPlugin,
} from '@kittycad/codemirror-lsp-client' } from '@kittycad/codemirror-lsp-client'
import { TEST, VITE_KC_API_BASE_URL } from 'env' import { TEST, VITE_KC_API_BASE_URL } from 'env'
import KclLanguageSupport from 'editor/plugins/lsp/kcl/language' import { kcl } from 'editor/plugins/lsp/kcl/language'
import { copilotPlugin } from 'editor/plugins/lsp/copilot' import { copilotPlugin } from 'editor/plugins/lsp/copilot'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { Extension } from '@codemirror/state' import { Extension } from '@codemirror/state'
@ -146,7 +146,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
let plugin = null let plugin = null
if (isKclLspReady && !TEST && kclLspClient) { if (isKclLspReady && !TEST && kclLspClient) {
// Set up the lsp plugin. // Set up the lsp plugin.
const lsp = new KclLanguageSupport({ const lsp = kcl({
documentUri: `file:///${PROJECT_ENTRYPOINT}`, documentUri: `file:///${PROJECT_ENTRYPOINT}`,
workspaceFolders: getWorkspaceFolders(), workspaceFolders: getWorkspaceFolders(),
client: kclLspClient, client: kclLspClient,

View File

@ -8,7 +8,7 @@ import { NetworkHealthState } from 'hooks/useNetworkStatus'
import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp' import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp'
import { butName } from 'lib/cameraControls' import { butName } from 'lib/cameraControls'
import { sendSelectEventToEngine } from 'lib/selections' import { sendSelectEventToEngine } from 'lib/selections'
import { kclManager, engineCommandManager } from 'lib/singletons' import { kclManager, engineCommandManager, sceneInfra } from 'lib/singletons'
export const Stream = () => { export const Stream = () => {
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(true)
@ -51,40 +51,42 @@ export const Stream = () => {
capture: true, capture: true,
}) })
// Teardown everything if we go hidden or reconnect
if (globalThis?.window?.document) {
globalThis.window.document.onvisibilitychange = () => {
if (globalThis.window.document.visibilityState === 'hidden') {
videoRef.current?.pause()
setIsFreezeFrame(true)
window.requestAnimationFrame(() => {
engineCommandManager.engineConnection?.tearDown({ freeze: true })
})
} else {
engineCommandManager.engineConnection?.connect(true)
}
}
}
const IDLE_TIME_MS = 1000 * 20 const IDLE_TIME_MS = 1000 * 20
let timeoutIdIdle: ReturnType<typeof setTimeout> | undefined = undefined let timeoutIdIdleA: ReturnType<typeof setTimeout> | undefined = undefined
const onIdle = () => { const teardown = () => {
videoRef.current?.pause() videoRef.current?.pause()
setIsFreezeFrame(true) setIsFreezeFrame(true)
kclManager.isFirstRender = true sceneInfra.modelingSend({ type: 'Cancel' })
setIsFirstRender(true)
// Give video time to pause // Give video time to pause
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
engineCommandManager.engineConnection?.tearDown({ freeze: true }) engineCommandManager.engineConnection?.tearDown({ freeze: true })
}) })
} }
// Teardown everything if we go hidden or reconnect
if (globalThis?.window?.document) {
globalThis.window.document.onvisibilitychange = () => {
if (globalThis.window.document.visibilityState === 'hidden') {
clearTimeout(timeoutIdIdleA)
timeoutIdIdleA = setTimeout(teardown, IDLE_TIME_MS)
} else if (!engineCommandManager.engineConnection?.isReady()) {
clearTimeout(timeoutIdIdleA)
engineCommandManager.engineConnection?.connect(true)
}
}
}
let timeoutIdIdleB: ReturnType<typeof setTimeout> | undefined = undefined
const onAnyInput = () => { const onAnyInput = () => {
if (!engineCommandManager.engineConnection?.isReady()) { if (!engineCommandManager.engineConnection?.isReady()) {
engineCommandManager.engineConnection?.connect(true) engineCommandManager.engineConnection?.connect(true)
} }
clearTimeout(timeoutIdIdle) // Clear both timers
timeoutIdIdle = setTimeout(onIdle, IDLE_TIME_MS) clearTimeout(timeoutIdIdleA)
clearTimeout(timeoutIdIdleB)
timeoutIdIdleB = setTimeout(teardown, IDLE_TIME_MS)
} }
globalThis?.window?.document?.addEventListener('keydown', onAnyInput) globalThis?.window?.document?.addEventListener('keydown', onAnyInput)
@ -93,7 +95,7 @@ export const Stream = () => {
globalThis?.window?.document?.addEventListener('scroll', onAnyInput) globalThis?.window?.document?.addEventListener('scroll', onAnyInput)
globalThis?.window?.document?.addEventListener('touchstart', onAnyInput) globalThis?.window?.document?.addEventListener('touchstart', onAnyInput)
timeoutIdIdle = setTimeout(onIdle, IDLE_TIME_MS) timeoutIdIdleB = setTimeout(teardown, IDLE_TIME_MS)
return () => { return () => {
globalThis?.window?.document?.removeEventListener('paste', handlePaste, { globalThis?.window?.document?.removeEventListener('paste', handlePaste, {

View File

@ -12,14 +12,14 @@ import {
setDiagnosticsEffect, setDiagnosticsEffect,
} from '@codemirror/lint' } from '@codemirror/lint'
const updateOutsideEditorAnnotation = Annotation.define<null>() const updateOutsideEditorAnnotation = Annotation.define<boolean>()
export const updateOutsideEditorEvent = updateOutsideEditorAnnotation.of(null) export const updateOutsideEditorEvent = updateOutsideEditorAnnotation.of(true)
const modelingMachineAnnotation = Annotation.define<null>() const modelingMachineAnnotation = Annotation.define<boolean>()
export const modelingMachineEvent = modelingMachineAnnotation.of(null) export const modelingMachineEvent = modelingMachineAnnotation.of(true)
const setDiagnosticsAnnotation = Annotation.define<null>() const setDiagnosticsAnnotation = Annotation.define<boolean>()
export const setDiagnosticsEvent = setDiagnosticsAnnotation.of(null) export const setDiagnosticsEvent = setDiagnosticsAnnotation.of(true)
function diagnosticIsEqual(d1: Diagnostic, d2: Diagnostic): boolean { function diagnosticIsEqual(d1: Diagnostic, d2: Diagnostic): boolean {
return d1.from === d2.from && d1.to === d2.to && d1.message === d2.message return d1.from === d2.from && d1.to === d2.to && d1.message === d2.message
@ -123,7 +123,11 @@ export default class EditorManager {
this._editorView.dispatch({ this._editorView.dispatch({
effects: [setDiagnosticsEffect.of(diagnostics)], effects: [setDiagnosticsEffect.of(diagnostics)],
annotations: [setDiagnosticsEvent, Transaction.addToHistory.of(false)], annotations: [
setDiagnosticsEvent,
updateOutsideEditorEvent,
Transaction.addToHistory.of(false),
],
}) })
} }

View File

@ -37,11 +37,11 @@ import { CopilotAcceptCompletionParams } from 'wasm-lib/kcl/bindings/CopilotAcce
import { CopilotRejectCompletionParams } from 'wasm-lib/kcl/bindings/CopilotRejectCompletionParams' import { CopilotRejectCompletionParams } from 'wasm-lib/kcl/bindings/CopilotRejectCompletionParams'
import { editorManager } from 'lib/singletons' import { editorManager } from 'lib/singletons'
const copilotPluginAnnotation = Annotation.define<null>() const copilotPluginAnnotation = Annotation.define<boolean>()
export const copilotPluginEvent = copilotPluginAnnotation.of(null) export const copilotPluginEvent = copilotPluginAnnotation.of(true)
const rejectSuggestionAnnotation = Annotation.define<null>() const rejectSuggestionAnnotation = Annotation.define<boolean>()
export const rejectSuggestionCommand = rejectSuggestionAnnotation.of(null) export const rejectSuggestionCommand = rejectSuggestionAnnotation.of(true)
// Effects to tell StateEffect what to do with GhostText // Effects to tell StateEffect what to do with GhostText
const addSuggestion = StateEffect.define<Suggestion>() const addSuggestion = StateEffect.define<Suggestion>()
@ -229,7 +229,7 @@ export class CompletionRequester implements PluginValue {
isRelevant = true isRelevant = true
} else if (tr.isUserEvent('move')) { } else if (tr.isUserEvent('move')) {
isRelevant = true isRelevant = true
} else if (tr.annotation(copilotPluginEvent.type) !== undefined) { } else if (tr.annotation(copilotPluginEvent.type)) {
isRelevant = true isRelevant = true
} }
} }
@ -457,6 +457,7 @@ export class CompletionRequester implements PluginValue {
effects: clearSuggestion.of(null), effects: clearSuggestion.of(null),
annotations: [ annotations: [
rejectSuggestionCommand, rejectSuggestionCommand,
copilotPluginEvent,
Transaction.addToHistory.of(false), Transaction.addToHistory.of(false),
], ],
}) })

View File

@ -0,0 +1,26 @@
import { styleTags, tags as t } from '@lezer/highlight'
export const klcHighlight = styleTags({
'fn var let const': t.definitionKeyword,
return: t.controlKeyword,
'true false': t.bool,
nil: t.null,
'AddOp MultOp ExpOp': t.arithmeticOperator,
CompOp: t.logicOperator,
'Equals Arrow': t.definitionOperator,
PipeOperator: t.controlOperator,
String: t.string,
Number: t.number,
LineComment: t.lineComment,
BlockComment: t.blockComment,
Shebang: t.meta,
PipeSubstitution: t.atom,
VariableDefinition: t.definition(t.variableName),
VariableName: t.variableName,
PropertyName: t.propertyName,
TagDeclarator: t.tagName,
'( )': t.paren,
'{ }': t.brace,
'[ ]': t.bracket,
', . : ? ..': t.punctuation,
})

View File

@ -12,6 +12,9 @@ import { UpdateUnitsParams } from 'wasm-lib/kcl/bindings/UpdateUnitsParams'
import { UpdateCanExecuteParams } from 'wasm-lib/kcl/bindings/UpdateCanExecuteParams' import { UpdateCanExecuteParams } from 'wasm-lib/kcl/bindings/UpdateCanExecuteParams'
import { UpdateUnitsResponse } from 'wasm-lib/kcl/bindings/UpdateUnitsResponse' import { UpdateUnitsResponse } from 'wasm-lib/kcl/bindings/UpdateUnitsResponse'
import { UpdateCanExecuteResponse } from 'wasm-lib/kcl/bindings/UpdateCanExecuteResponse' import { UpdateCanExecuteResponse } from 'wasm-lib/kcl/bindings/UpdateCanExecuteResponse'
import { codeManagerUpdateEvent } from 'lang/codeManager'
import { copilotPluginEvent } from '../copilot'
import { updateOutsideEditorEvent } from 'editor/manager'
const changesDelay = 600 const changesDelay = 600
@ -45,11 +48,10 @@ export class KclPlugin implements PluginValue {
editorManager.setEditorView(viewUpdate.view) editorManager.setEditorView(viewUpdate.view)
let isUserSelect = false let isUserSelect = false
let isRelevant = false let isRelevant = viewUpdate.docChanged
for (const tr of viewUpdate.transactions) { for (const tr of viewUpdate.transactions) {
if (tr.isUserEvent('select')) { if (tr.isUserEvent('select')) {
isUserSelect = true isUserSelect = true
break
} else if (tr.isUserEvent('input')) { } else if (tr.isUserEvent('input')) {
isRelevant = true isRelevant = true
} else if (tr.isUserEvent('delete')) { } else if (tr.isUserEvent('delete')) {
@ -63,6 +65,21 @@ export class KclPlugin implements PluginValue {
} else if (tr.annotation(lspFormatCodeEvent.type)) { } else if (tr.annotation(lspFormatCodeEvent.type)) {
isRelevant = true isRelevant = true
} }
// Don't make this an else.
if (tr.annotation(codeManagerUpdateEvent.type)) {
// We want to ignore when we are forcing the editor to update.
isRelevant = false
break
} else if (tr.annotation(copilotPluginEvent.type)) {
// We want to ignore when copilot is doing stuff.
isRelevant = false
break
} else if (tr.annotation(updateOutsideEditorEvent.type)) {
// We want to ignore other events outside the editor.
isRelevant = false
break
}
} }
// If we have a user select event, we want to update what parts are // If we have a user select event, we want to update what parts are

View File

@ -0,0 +1,113 @@
@precedence {
member
call
exp @left
mult @left
add @left
comp @left
pipe @left
range
}
@top Program {
Shebang?
statement*
}
statement[@isGroup=Statement] {
FunctionDeclaration { kw<"fn"> VariableDefinition Equals ParamList Arrow Body } |
VariableDeclaration { (kw<"var"> | kw<"let"> | kw<"const">) VariableDefinition Equals expression } |
ReturnStatement { kw<"return"> expression } |
ExpressionStatement { expression }
}
ParamList { "(" commaSep<Parameter { VariableDefinition "?"? (":" type)? }> ")" }
Body { "{" statement* "}" }
expression[@isGroup=Expression] {
String |
Number |
VariableName |
TagDeclarator |
kw<"true"> | kw<"false"> | kw<"nil"> |
PipeSubstitution |
BinaryExpression {
expression !add AddOp expression |
expression !mult MultOp expression |
expression !exp ExpOp expression |
expression !comp CompOp expression
} |
UnaryExpression { AddOp expression } |
ParenthesizedExpression { "(" expression ")" } |
CallExpression { expression !call ArgumentList } |
ArrayExpression { "[" commaSep<expression | IntegerRange { expression !range ".." expression }> "]" } |
ObjectExpression { "{" commaSep<ObjectProperty> "}" } |
MemberExpression { expression !member "." PropertyName } |
SubscriptExpression { expression !member "[" expression "]" } |
PipeExpression { expression (!pipe PipeOperator expression)+ }
}
ObjectProperty { PropertyName ":" expression }
ArgumentList { "(" commaSep<expression> ")" }
type[@isGroup=Type] {
@specialize[@name=PrimitiveType]<
identifier,
"string" | "number" | "bool" | "sketch_group" | "sketch_surface" | "extrude_group"
> |
ArrayType { type !member "[" "]" } |
ObjectType { "{" commaSep<ObjectProperty { PropertyName ":" type }> "}" }
}
VariableDefinition { identifier }
VariableName { identifier }
@skip { whitespace | LineComment | BlockComment }
kw<term> { @specialize[@name={term}]<identifier, term> }
commaSep<term> { (term ("," term)*)? ","? }
@tokens {
String[isolate] { "'" ("\\" _ | !['\\])* "'" | '"' ("\\" _ | !["\\])* '"' }
Number { "." @digit+ | @digit+ ("." @digit*)? }
@precedence { Number, "." }
AddOp { "+" | "-" }
MultOp { "/" | "*" | "\\" }
ExpOp { "^" }
CompOp { $[<>] "="? | "!=" | "==" }
Equals { "=" }
Arrow { "=>" }
PipeOperator { "|>" }
PipeSubstitution { "%" }
identifier { (@asciiLetter | "_") (@asciiLetter | @digit | "_")* }
PropertyName { identifier }
TagDeclarator { "$" identifier }
whitespace { @whitespace+ }
LineComment[isolate] { "//" ![\n]* }
BlockComment[isolate] { "/*" blockCommentRest }
blockCommentRest { @eof | ![*] blockCommentRest | "*" blockCommentStar }
blockCommentStar { @eof | "/" | ![/] blockCommentRest | "*" blockCommentStar }
@precedence { LineComment, BlockComment, MultOp }
Shebang { "#!" ![\n]* }
"(" ")"
"{" "}"
"[" "]"
"," "?" ":" "." ".."
}
@external propSource klcHighlight from "./highlight"
@detectDelim

View File

@ -1,9 +1,13 @@
// Code mirror language implementation for kcl. // Code mirror language implementation for kcl.
import { import {
Language, LRLanguage,
defineLanguageFacet,
LanguageSupport, LanguageSupport,
indentNodeProp,
continuedIndent,
delimitedIndent,
foldNodeProp,
foldInside,
} from '@codemirror/language' } from '@codemirror/language'
import { import {
LanguageServerClient, LanguageServerClient,
@ -11,18 +15,8 @@ import {
} from '@kittycad/codemirror-lsp-client' } from '@kittycad/codemirror-lsp-client'
import { kclPlugin } from '.' import { kclPlugin } from '.'
import type * as LSP from 'vscode-languageserver-protocol' import type * as LSP from 'vscode-languageserver-protocol'
import KclParser from './parser' // @ts-ignore: No types available
import { parser } from './kcl.grammar'
const data = defineLanguageFacet({
// https://codemirror.net/docs/ref/#commands.CommentTokens
commentTokens: {
line: '//',
block: {
open: '/*',
close: '*/',
},
},
})
export interface LanguageOptions { export interface LanguageOptions {
workspaceFolders: LSP.WorkspaceFolder[] workspaceFolders: LSP.WorkspaceFolder[]
@ -34,26 +28,40 @@ export interface LanguageOptions {
) => void ) => void
} }
class KclLanguage extends Language { export const KclLanguage = LRLanguage.define({
constructor(options: LanguageOptions) { name: 'klc',
const plugin = kclPlugin({ parser: parser.configure({
props: [
indentNodeProp.add({
Body: delimitedIndent({ closing: '}' }),
BlockComment: () => null,
'Statement Property': continuedIndent({ except: /^{/ }),
}),
foldNodeProp.add({
'Body ArrayExpression ObjectExpression': foldInside,
BlockComment(tree) {
return { from: tree.from + 2, to: tree.to - 2 }
},
PipeExpression(tree) {
return { from: tree.firstChild!.to, to: tree.to }
},
}),
],
}),
languageData: {
commentTokens: { line: '//', block: { open: '/*', close: '*/' } },
},
})
export function kcl(options: LanguageOptions) {
return new LanguageSupport(
KclLanguage,
kclPlugin({
documentUri: options.documentUri, documentUri: options.documentUri,
workspaceFolders: options.workspaceFolders, workspaceFolders: options.workspaceFolders,
allowHTMLContent: true, allowHTMLContent: true,
client: options.client, client: options.client,
processLspNotification: options.processLspNotification, processLspNotification: options.processLspNotification,
}) })
)
const parser = new KclParser()
super(data, parser, [plugin], 'kcl')
}
}
export default class KclLanguageSupport extends LanguageSupport {
constructor(options: LanguageOptions) {
const lang = new KclLanguage(options)
super(lang)
}
} }

View File

@ -1,47 +0,0 @@
// Extends the codemirror Parser for kcl.
// This is really just a no-op parser since we use semantic tokens from the LSP
// server.
import {
Parser,
Input,
TreeFragment,
PartialParse,
Tree,
NodeType,
} from '@lezer/common'
import { DocInput } from '@codemirror/language'
export default class KclParser extends Parser {
createParse(
input: Input,
fragments: readonly TreeFragment[],
ranges: readonly { from: number; to: number }[]
): PartialParse {
let parse: PartialParse = new Context(input)
return parse
}
}
class Context implements PartialParse {
private input: DocInput
stoppedAt: number = 0
constructor(input: Input) {
this.input = input as DocInput
}
get parsedPos(): number {
return 0
}
advance(): Tree | null {
this.stoppedAt = this.input.doc.length
return new Tree(NodeType.none, [], [], this.input.doc.length)
}
stopAt(pos: number) {
this.stoppedAt = pos
}
}

View File

@ -254,6 +254,10 @@ code {
color: rgb(120, 120, 120, 0.8) !important; color: rgb(120, 120, 120, 0.8) !important;
} }
.segment-length-label-text {
transform: translate(var(--x, 0), var(--y, 0));
}
@layer components { @layer components {
kbd.hotkey { kbd.hotkey {
@apply font-mono text-xs inline-block px-1 py-0.5 rounded-sm; @apply font-mono text-xs inline-block px-1 py-0.5 rounded-sm;

View File

@ -11,8 +11,8 @@ import { KeyBinding } from '@codemirror/view'
const PERSIST_CODE_KEY = 'persistCode' const PERSIST_CODE_KEY = 'persistCode'
const codeManagerUpdateAnnotation = Annotation.define<null>() const codeManagerUpdateAnnotation = Annotation.define<boolean>()
export const codeManagerUpdateEvent = codeManagerUpdateAnnotation.of(null) export const codeManagerUpdateEvent = codeManagerUpdateAnnotation.of(true)
export default class CodeManager { export default class CodeManager {
private _code: string = bracket private _code: string = bracket

View File

@ -157,7 +157,7 @@ export function createSettings() {
), ),
}), }),
enableSSAO: new Setting<boolean>({ enableSSAO: new Setting<boolean>({
defaultValue: false, defaultValue: true,
description: description:
'Whether or not Screen Space Ambient Occlusion (SSAO) is enabled', 'Whether or not Screen Space Ambient Occlusion (SSAO) is enabled',
validate: (v) => typeof v === 'boolean', validate: (v) => typeof v === 'boolean',

120
src/wasm-lib/Cargo.lock generated
View File

@ -169,18 +169,18 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.80" version = "0.1.81"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -191,7 +191,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -400,9 +400,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.7" version = "4.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -410,9 +410,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.7" version = "4.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -424,14 +424,14 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.5" version = "4.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
dependencies = [ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -631,7 +631,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim 0.10.0", "strsim 0.10.0",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -642,7 +642,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -697,7 +697,7 @@ checksum = "4078275de501a61ceb9e759d37bdd3d7210e654dbc167ac1a3678ef4435ed57b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
"synstructure", "synstructure",
] ]
@ -726,7 +726,7 @@ dependencies = [
"rustfmt-wrapper", "rustfmt-wrapper",
"serde", "serde",
"serde_tokenstream", "serde_tokenstream",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -737,7 +737,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -764,7 +764,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -936,7 +936,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -1026,7 +1026,7 @@ dependencies = [
"inflections", "inflections",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -1448,7 +1448,7 @@ dependencies = [
"pretty_assertions", "pretty_assertions",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -1824,7 +1824,7 @@ dependencies = [
"regex", "regex",
"regex-syntax 0.8.3", "regex-syntax 0.8.3",
"structmeta", "structmeta",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -1877,7 +1877,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -1996,9 +1996,9 @@ dependencies = [
[[package]] [[package]]
name = "pyo3" name = "pyo3"
version = "0.22.0" version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1962a33ed2a201c637fc14a4e0fd4e06e6edfdeee6a5fede0dab55507ad74cf7" checksum = "4e99090d12f6182924499253aaa1e73bf15c69cea8d2774c3c781e35badc3548"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"indoc", "indoc",
@ -2014,9 +2014,9 @@ dependencies = [
[[package]] [[package]]
name = "pyo3-build-config" name = "pyo3-build-config"
version = "0.22.0" version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab7164b2202753bd33afc7f90a10355a719aa973d1f94502c50d06f3488bc420" checksum = "7879eb018ac754bba32cb0eec7526391c02c14a093121857ed09fbf1d1057d41"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"target-lexicon", "target-lexicon",
@ -2024,9 +2024,9 @@ dependencies = [
[[package]] [[package]]
name = "pyo3-ffi" name = "pyo3-ffi"
version = "0.22.0" version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6424906ca49013c0829c5c1ed405e20e2da2dc78b82d198564880a704e6a7b7" checksum = "ce2baa5559a411fc1cf519295f24c34b53d5d725818bc96b5abf94762da09041"
dependencies = [ dependencies = [
"libc", "libc",
"pyo3-build-config", "pyo3-build-config",
@ -2034,27 +2034,27 @@ dependencies = [
[[package]] [[package]]
name = "pyo3-macros" name = "pyo3-macros"
version = "0.22.0" version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b2f19e153122d64afd8ce7aaa72f06a00f52e34e1d1e74b6d71baea396460a" checksum = "049621c20a23f2def20f4fe67978d1da8d8a883d64b9c21362f3b776e254edc7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"pyo3-macros-backend", "pyo3-macros-backend",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
name = "pyo3-macros-backend" name = "pyo3-macros-backend"
version = "0.22.0" version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd698c04cac17cf0fe63d47790ab311b8b25542f5cb976b65c374035c50f1eef" checksum = "0e969ee2e025435f1819d31a275ba4bb9cbbdf3ac535227fdbd85b9322ffe144"
dependencies = [ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
"pyo3-build-config", "pyo3-build-config",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -2516,7 +2516,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde_derive_internals", "serde_derive_internals",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -2566,9 +2566,9 @@ checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.203" version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
@ -2584,13 +2584,13 @@ dependencies = [
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.203" version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -2601,7 +2601,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -2624,7 +2624,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -2645,7 +2645,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde", "serde",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -2782,7 +2782,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"structmeta-derive", "structmeta-derive",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -2793,7 +2793,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -2837,9 +2837,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.68" version = "2.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2860,7 +2860,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -2943,7 +2943,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -3039,7 +3039,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -3192,7 +3192,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -3220,7 +3220,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -3297,7 +3297,7 @@ checksum = "c88cc88fd23b5a04528f3a8436024f20010a16ec18eb23c164b1242f65860130"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
"termcolor", "termcolor",
] ]
@ -3419,9 +3419,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.9.1" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
dependencies = [ dependencies = [
"getrandom", "getrandom",
"serde", "serde",
@ -3455,7 +3455,7 @@ dependencies = [
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]
@ -3516,7 +3516,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -3551,7 +3551,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -3876,7 +3876,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.68", "syn 2.0.70",
] ]
[[package]] [[package]]

View File

@ -11,14 +11,14 @@ crate-type = ["cdylib"]
[dependencies] [dependencies]
bson = { version = "2.11.0", features = ["uuid-1", "chrono"] } bson = { version = "2.11.0", features = ["uuid-1", "chrono"] }
clap = "4.5.7" clap = "4.5.9"
gloo-utils = "0.2.0" gloo-utils = "0.2.0"
kcl-lib = { path = "kcl" } kcl-lib = { path = "kcl" }
kittycad.workspace = true kittycad.workspace = true
serde_json = "1.0.120" serde_json = "1.0.120"
tokio = { version = "1.38.0", features = ["sync"] } tokio = { version = "1.38.0", features = ["sync"] }
toml = "0.8.14" toml = "0.8.14"
uuid = { version = "1.9.1", features = ["v4", "js", "serde"] } uuid = { version = "1.10.0", features = ["v4", "js", "serde"] }
wasm-bindgen = "0.2.91" wasm-bindgen = "0.2.91"
wasm-bindgen-futures = "0.4.42" wasm-bindgen-futures = "0.4.42"
@ -31,7 +31,7 @@ pretty_assertions = "1.4.0"
reqwest = { version = "0.11.26", default-features = false } reqwest = { version = "0.11.26", default-features = false }
tokio = { version = "1.38.0", features = ["rt-multi-thread", "macros", "time"] } tokio = { version = "1.38.0", features = ["rt-multi-thread", "macros", "time"] }
twenty-twenty = "0.8" twenty-twenty = "0.8"
uuid = { version = "1.9.1", features = ["v4", "js", "serde"] } uuid = { version = "1.10.0", features = ["v4", "js", "serde"] }
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1.7" console_error_panic_hook = "0.1.7"

View File

@ -18,9 +18,9 @@ once_cell = "1.19.0"
proc-macro2 = "1" proc-macro2 = "1"
quote = "1" quote = "1"
regex = "1.10" regex = "1.10"
serde = { version = "1.0.203", features = ["derive"] } serde = { version = "1.0.204", features = ["derive"] }
serde_tokenstream = "0.2" serde_tokenstream = "0.2"
syn = { version = "2.0.68", features = ["full"] } syn = { version = "2.0.70", features = ["full"] }
[dev-dependencies] [dev-dependencies]
anyhow = "1.0.86" anyhow = "1.0.86"

View File

@ -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.68", features = ["full"] } syn = { version = "2.0.70", features = ["full"] }
[dev-dependencies] [dev-dependencies]
pretty_assertions = "1.4.0" pretty_assertions = "1.4.0"

View File

@ -10,6 +10,6 @@ anyhow = "1.0.86"
hyper = { version = "0.14.29", features = ["server"] } hyper = { version = "0.14.29", features = ["server"] }
kcl-lib = { version = "0.1.70", path = "../kcl" } kcl-lib = { version = "0.1.70", path = "../kcl" }
pico-args = "0.5.0" pico-args = "0.5.0"
serde = { version = "1.0.203", features = ["derive"] } serde = { version = "1.0.204", features = ["derive"] }
serde_json = "1.0.120" serde_json = "1.0.120"
tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.38.0", features = ["macros", "rt-multi-thread"] }

View File

@ -13,10 +13,10 @@ keywords = ["kcl", "KittyCAD", "CAD"]
[dependencies] [dependencies]
anyhow = { version = "1.0.86", features = ["backtrace"] } anyhow = { version = "1.0.86", features = ["backtrace"] }
async-recursion = "1.1.1" async-recursion = "1.1.1"
async-trait = "0.1.80" async-trait = "0.1.81"
base64 = "0.22.1" base64 = "0.22.1"
chrono = "0.4.38" chrono = "0.4.38"
clap = { version = "4.5.7", default-features = false, optional = true } clap = { version = "4.5.9", default-features = false, optional = true }
dashmap = "6.0.1" dashmap = "6.0.1"
databake = { version = "0.1.8", features = ["derive"] } databake = { version = "0.1.8", features = ["derive"] }
derive-docs = { version = "0.1.20", path = "../derive-docs" } derive-docs = { version = "0.1.20", path = "../derive-docs" }
@ -28,18 +28,18 @@ kittycad = { workspace = true, features = ["clap"] }
lazy_static = "1.5.0" lazy_static = "1.5.0"
mime_guess = "2.0.5" mime_guess = "2.0.5"
parse-display = "0.9.1" parse-display = "0.9.1"
pyo3 = { version = "0.22.0", optional = true } pyo3 = { version = "0.22.1", optional = true }
reqwest = { version = "0.11.26", default-features = false, features = ["stream", "rustls-tls"] } reqwest = { version = "0.11.26", 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"] } schemars = { version = "0.8.17", features = ["impl_json_schema", "url", "uuid1"] }
serde = { version = "1.0.203", features = ["derive"] } serde = { version = "1.0.204", features = ["derive"] }
serde_json = "1.0.120" serde_json = "1.0.120"
sha2 = "0.10.8" sha2 = "0.10.8"
thiserror = "1.0.61" thiserror = "1.0.61"
toml = "0.8.14" toml = "0.8.14"
ts-rs = { version = "9.0.1", features = ["uuid-impl", "url-impl", "chrono-impl", "no-serde-warnings", "serde-json-impl"] } ts-rs = { version = "9.0.1", 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"] }
uuid = { version = "1.9.1", features = ["v4", "js", "serde"] } uuid = { version = "1.10.0", features = ["v4", "js", "serde"] }
validator = { version = "0.18.1", features = ["derive"] } validator = { version = "0.18.1", features = ["derive"] }
winnow = "0.5.40" winnow = "0.5.40"
zip = { version = "2.0.0", default-features = false } zip = { version = "2.0.0", default-features = false }

View File

@ -231,7 +231,6 @@ impl Backend {
|> line([0, scale], %) |> line([0, scale], %)
|> line([scale, 0], %) |> line([scale, 0], %)
|> line([0, -scale], %) |> line([0, -scale], %)
return sg return sg
} }
const part001 = cube([0,0], 20) const part001 = cube([0,0], 20)

View File

@ -12,7 +12,8 @@
"@types/wicg-file-system-access", "@types/wicg-file-system-access",
"node", "node",
"@wdio/globals/types", "@wdio/globals/types",
"mocha" "mocha",
"@lezer/generator"
], ],
"target": "esnext", "target": "esnext",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
@ -32,6 +33,6 @@
"jsx": "react-jsx" "jsx": "react-jsx"
}, },
"include": ["src", "e2e", "packages", "./*.ts"], "include": ["src", "e2e", "packages", "./*.ts"],
"exclude": ["node_modules"], "exclude": ["node_modules", "./*.grammar"],
"references": [{ "path": "./tsconfig.node.json" }] "references": [{ "path": "./tsconfig.node.json" }]
} }

View File

@ -3,6 +3,8 @@ import viteTsconfigPaths from 'vite-tsconfig-paths'
import eslint from 'vite-plugin-eslint' import eslint from 'vite-plugin-eslint'
import { defineConfig, configDefaults } from 'vitest/config' import { defineConfig, configDefaults } from 'vitest/config'
import version from 'vite-plugin-package-version' import version from 'vite-plugin-package-version'
// @ts-ignore: No types available
import { lezer } from '@lezer/generator/rollup'
const config = defineConfig({ const config = defineConfig({
server: { server: {
@ -58,7 +60,7 @@ const config = defineConfig({
'@kittycad/codemirror-lsp-client': '/packages/codemirror-lsp-client/src', '@kittycad/codemirror-lsp-client': '/packages/codemirror-lsp-client/src',
}, },
}, },
plugins: [react(), viteTsconfigPaths(), eslint(), version()], plugins: [react(), viteTsconfigPaths(), eslint(), version(), lezer()],
worker: { worker: {
plugins: () => [viteTsconfigPaths()], plugins: () => [viteTsconfigPaths()],
}, },

View File

@ -1628,10 +1628,10 @@
resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60" resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60"
integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA== integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==
"@kittycad/lib@^0.0.69": "@kittycad/lib@^0.0.70":
version "0.0.69" version "0.0.70"
resolved "https://registry.yarnpkg.com/@kittycad/lib/-/lib-0.0.69.tgz#755fb5bc87ea6401d013be8047955b890e88493d" resolved "https://registry.yarnpkg.com/@kittycad/lib/-/lib-0.0.70.tgz#398ec3b2385cef055bbd7c2681040f08b3751ac6"
integrity sha512-D6VBx2kUd0ya2op+SYcnB+/JU0TU8/rYh4o5VGCxO7Z13wW7SiAawucHhid3KSzzXfwjLXeoEXXYBv0rRzLJ3A== integrity sha512-P6IyfUIiCZ5Cc7EDx/apXBsmHAUmO/yhMw5E6fviMCRt0sNJnUfed6iTmfTpq2m44k7k8Vrn0WwrkQMsReLUhA==
dependencies: dependencies:
node-fetch "3.3.2" node-fetch "3.3.2"
openapi-types "^12.0.0" openapi-types "^12.0.0"
@ -1643,14 +1643,22 @@
resolved "https://registry.yarnpkg.com/@lezer/common/-/common-1.2.1.tgz#198b278b7869668e1bebbe687586e12a42731049" resolved "https://registry.yarnpkg.com/@lezer/common/-/common-1.2.1.tgz#198b278b7869668e1bebbe687586e12a42731049"
integrity sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ== integrity sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==
"@lezer/highlight@^1.0.0": "@lezer/generator@^1.7.1":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@lezer/generator/-/generator-1.7.1.tgz#90c1a9de2fb4d5a714216fa659058c7859accaab"
integrity sha512-MgPJN9Si+ccxzXl3OAmCeZuUKw4XiPl4y664FX/hnnyG9CTqUPq65N3/VGPA2jD23D7QgMTtNqflta+cPN+5mQ==
dependencies:
"@lezer/common" "^1.1.0"
"@lezer/lr" "^1.3.0"
"@lezer/highlight@^1.0.0", "@lezer/highlight@^1.2.0":
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/@lezer/highlight/-/highlight-1.2.0.tgz#e5898c3644208b4b589084089dceeea2966f7780" resolved "https://registry.yarnpkg.com/@lezer/highlight/-/highlight-1.2.0.tgz#e5898c3644208b4b589084089dceeea2966f7780"
integrity sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA== integrity sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==
dependencies: dependencies:
"@lezer/common" "^1.0.0" "@lezer/common" "^1.0.0"
"@lezer/lr@^1.0.0": "@lezer/lr@^1.0.0", "@lezer/lr@^1.3.0", "@lezer/lr@^1.4.1":
version "1.4.1" version "1.4.1"
resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-1.4.1.tgz#fe25f051880a754e820b28148d90aa2a96b8bdd2" resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-1.4.1.tgz#fe25f051880a754e820b28148d90aa2a96b8bdd2"
integrity sha512-CHsKq8DMKBf9b3yXPDIU4DbH+ZJd/sJdYOW2llbW/HudP5u0VS6Bfq1hLYfgU7uAYGFIyGGQIsSOXGPEErZiJw== integrity sha512-CHsKq8DMKBf9b3yXPDIU4DbH+ZJd/sJdYOW2llbW/HudP5u0VS6Bfq1hLYfgU7uAYGFIyGGQIsSOXGPEErZiJw==
@ -7535,16 +7543,7 @@ 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-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
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@^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==
@ -7622,14 +7621,7 @@ 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-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, 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==
@ -8497,7 +8489,7 @@ workerpool@6.2.1:
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": "wrap-ansi-cjs@npm:wrap-ansi@^7.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==
@ -8515,15 +8507,6 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0" string-width "^4.1.0"
strip-ansi "^6.0.0" strip-ansi "^6.0.0"
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@^8.1.0: wrap-ansi@^8.1.0:
version "8.1.0" version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"