Merge branch 'main' into kurt-bring-back-multi-profile

This commit is contained in:
Jonathan Tran
2025-01-07 20:23:06 -05:00
177 changed files with 15126 additions and 1124 deletions

View File

@ -1,3 +1,3 @@
[codespell] [codespell]
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,absolutey,atleast,ue,afterall ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,atleast,ue,afterall
skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./src/lib/machine-api.d.ts skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./src/lib/machine-api.d.ts,./packages/codemirror-lang-kcl/test/all.test.ts

View File

@ -5,24 +5,28 @@
version: 2 version: 2
updates: updates:
- package-ecosystem: 'npm' # See documentation for possible values - package-ecosystem: 'npm' # See documentation for possible values
directory: '/' # Location of package manifests directory: '/' # Location of package manifests
schedule: schedule:
interval: 'weekly' interval: 'weekly'
reviewers: reviewers:
- franknoirot - franknoirot
- irev-dev - irev-dev
- package-ecosystem: 'github-actions' # See documentation for possible values - package-ecosystem: 'github-actions' # See documentation for possible values
directory: '/' # Location of package manifests directory: '/' # Location of package manifests
schedule: schedule:
interval: 'weekly' interval: 'weekly'
reviewers: reviewers:
- adamchalmers - adamchalmers
- jessfraz - jessfraz
- package-ecosystem: 'cargo' # See documentation for possible values - package-ecosystem: 'cargo' # See documentation for possible values
directory: '/src/wasm-lib/' # Location of package manifests directory: '/src/wasm-lib/' # Location of package manifests
schedule: schedule:
interval: 'weekly' interval: 'weekly'
reviewers: reviewers:
- adamchalmers - adamchalmers
- jessfraz - jessfraz
groups:
serde-dependencies:
patterns:
- "serde*"

View File

@ -173,7 +173,13 @@ jobs:
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }} CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }} WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
run: yarn electron-builder --config --publish always DEBUG: "electron-notarize*"
# TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures
uses: nick-fields/retry@v3.0.0
with:
timeout_minutes: 10
max_attempts: 3
command: yarn electron-builder --config --publish always
- name: List artifacts in out/ - name: List artifacts in out/
run: ls -R out run: ls -R out
@ -228,7 +234,13 @@ jobs:
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }} CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }} WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
run: yarn electron-builder --config --publish always DEBUG: "electron-notarize*"
# TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures
uses: nick-fields/retry@v3.0.0
with:
timeout_minutes: 10
max_attempts: 3
command: yarn electron-builder --config --publish always
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
if: ${{ env.IS_RELEASE == 'true' }} if: ${{ env.IS_RELEASE == 'true' }}

View File

@ -18,7 +18,6 @@ permissions:
jobs: jobs:
check-rust-changes: check-rust-changes:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
rust-changed: ${{ steps.filter.outputs.rust }} rust-changed: ${{ steps.filter.outputs.rust }}
@ -35,7 +34,6 @@ jobs:
- 'src/wasm-lib/**' - 'src/wasm-lib/**'
electron: electron:
if: github.event.pull_request.draft == false
timeout-minutes: 60 timeout-minutes: 60
name: playwright:electron:${{ matrix.os }} ${{ matrix.shardIndex }} ${{ matrix.shardTotal }} name: playwright:electron:${{ matrix.os }} ${{ matrix.shardIndex }} ${{ matrix.shardTotal }}
strategy: strategy:
@ -129,9 +127,12 @@ jobs:
shell: bash shell: bash
run: yarn tron:package run: yarn tron:package
- name: Run ubuntu/chrome snapshots - name: Run ubuntu/chrome snapshots
if: ${{ matrix.os == 'namespace-profile-ubuntu-8-cores' && matrix.shardIndex == 1 }}
shell: bash shell: bash
# TODO: break this in its own job, for now it's not slowing down the overall execution as ubuntu is the quickest,
# but we could do better. This forces a large 1/1 shard of all 20 snapshot tests that runs in about 3 minutes.
run: | run: |
PLATFORM=web yarn playwright test --config=playwright.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} PLATFORM=web yarn playwright test --config=playwright.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=1/1
env: env:
CI: true CI: true
NODE_ENV: development NODE_ENV: development
@ -152,6 +153,7 @@ jobs:
continue-on-error: true continue-on-error: true
run: rm -r test-results run: rm -r test-results
- name: check for changes - name: check for changes
if: ${{ matrix.os == 'namespace-profile-ubuntu-8-cores' && matrix.shardIndex == 1 }}
shell: bash shell: bash
id: git-check id: git-check
run: | run: |

View File

@ -337,13 +337,47 @@ For individual testing:
yarn test abstractSyntaxTree -t "unexpected closed curly brace" --silent=false yarn test abstractSyntaxTree -t "unexpected closed curly brace" --silent=false
``` ```
Which will run our suite of [Vitest unit](https://vitest.dev/) and [React Testing Library E2E](https://testing-library.com/docs/react-testing-library/intro/) tests, in interactive mode by default. Which will run our suite of [Vitest unit](https://vitest.dev/) and [React Testing Library E2E](https://testing-library.com/docs/react-testing-library/intro) tests, in interactive mode by default.
### Rust tests ### Rust tests
```bash **Dependencies**
- `KITTYCAD_API_TOKEN`
- `cargo-nextest`
- `just`
#### Setting KITTYCAD_API_TOKEN
Use the production zoo.dev token, set this environment variable before running the tests
#### Installing cargonextest
```
cd src/wasm-lib cd src/wasm-lib
KITTYCAD_API_TOKEN=XXX cargo test -- --test-threads=1 cargo search cargo-nextest
cargo install cargo-nextest
```
#### just
install [`just`](https://github.com/casey/just?tab=readme-ov-file#pre-built-binaries)
#### Running the tests
```bash
# With just
# Make sure KITTYCAD_API_TOKEN=<prod zoo.dev token> is set
# Make sure you installed cargo-nextest
# Make sure you installed just
cd src/wasm-lib
just test
```
```bash
# Without just
# Make sure KITTYCAD_API_TOKEN=<prod zoo.dev token> is set
# Make sure you installed cargo-nextest
cd src/wasm-lib
export RUST_BRACKTRACE="full" && cargo nextest run --workspace --test-threads=1
``` ```
Where `XXX` is an API token from the production engine (NOT the dev environment). Where `XXX` is an API token from the production engine (NOT the dev environment).

File diff suppressed because one or more lines are too long

View File

@ -35,6 +35,7 @@ layout: manual
* [`ceil`](kcl/ceil) * [`ceil`](kcl/ceil)
* [`chamfer`](kcl/chamfer) * [`chamfer`](kcl/chamfer)
* [`circle`](kcl/circle) * [`circle`](kcl/circle)
* [`circleThreePoint`](kcl/circleThreePoint)
* [`close`](kcl/close) * [`close`](kcl/close)
* [`cm`](kcl/cm) * [`cm`](kcl/cm)
* [`cos`](kcl/cos) * [`cos`](kcl/cos)
@ -80,6 +81,7 @@ layout: manual
* [`pi`](kcl/pi) * [`pi`](kcl/pi)
* [`polar`](kcl/polar) * [`polar`](kcl/polar)
* [`polygon`](kcl/polygon) * [`polygon`](kcl/polygon)
* [`pop`](kcl/pop)
* [`pow`](kcl/pow) * [`pow`](kcl/pow)
* [`profileStart`](kcl/profileStart) * [`profileStart`](kcl/profileStart)
* [`profileStartX`](kcl/profileStartX) * [`profileStartX`](kcl/profileStartX)

File diff suppressed because one or more lines are too long

39
docs/kcl/pop.md Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
---
title: "CircleThreePointData"
excerpt: "Data for drawing a 3-point circle"
layout: manual
---
Data for drawing a 3-point circle
**Type:** `object`
## Properties
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `p1` |`[number, number]`| Point one for circle derivation. | No |
| `p2` |`[number, number]`| Point two for circle derivation. | No |
| `p3` |`[number, number]`| Point three for circle derivation. | No |

View File

@ -156,7 +156,7 @@ test.describe('Basic sketch', () => {
await doBasicSketch(page, homePage, ['code']) await doBasicSketch(page, homePage, ['code'])
}) })
test.fixme('code pane closed at start', async ({ page, homePage }) => { test('code pane closed at start', async ({ page, homePage }) => {
// Load the app with the code panes // Load the app with the code panes
await page.addInitScript(async (persistModelingContext) => { await page.addInitScript(async (persistModelingContext) => {
localStorage.setItem( localStorage.setItem(

View File

@ -0,0 +1,127 @@
import { test, expect } from './zoo-test'
import * as fsp from 'fs/promises'
import { join } from 'path'
const FEATURE_TREE_EXAMPLE_CODE = `export fn timesFive(x) {
return 5 * x
}
export fn triangle() {
return startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> xLine(10, %)
|> line([-10, -5], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
}
length001 = timesFive(1) * 5
sketch001 = startSketchOn('XZ')
|> startProfileAt([20, 10], %)
|> line([10, 10], %)
|> angledLine([-45, length001], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
revolve001 = revolve({ axis = "X" }, sketch001)
triangle()
|> extrude(30, %)
plane001 = offsetPlane('XY', 10)
sketch002 = startSketchOn(plane001)
|> startProfileAt([-20, 0], %)
|> line([5, -15], %)
|> xLine(-10, %)
|> lineTo([-40, 0], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
extrude001 = extrude(10, sketch002)
`
test.describe('Feature Tree pane', () => {
test(
'User can go to definition and go to function definition',
{ tag: '@electron' },
async ({ context, homePage, scene, editor, toolbar }) => {
await context.folderSetupFn(async (dir) => {
const bracketDir = join(dir, 'test-sample')
await fsp.mkdir(bracketDir, { recursive: true })
await fsp.writeFile(
join(bracketDir, 'main.kcl'),
FEATURE_TREE_EXAMPLE_CODE,
'utf-8'
)
})
await test.step('setup test', async () => {
await homePage.expectState({
projectCards: [
{
title: 'test-sample',
fileCount: 1,
},
],
sortBy: 'last-modified-desc',
})
await homePage.openProject('test-sample')
await scene.waitForExecutionDone()
await editor.closePane()
await toolbar.openFeatureTreePane()
})
async function testViewSource({
operationName,
operationIndex,
expectedActiveLine,
}: {
operationName: string
operationIndex: number
expectedActiveLine: string
}) {
await test.step(`Go to definition of the ${operationName}`, async () => {
await toolbar.viewSourceOnOperation(operationName, operationIndex)
await editor.expectState({
highlightedCode: '',
diagnostics: [],
activeLines: [expectedActiveLine],
})
await expect(
editor.activeLine.first(),
`${operationName} code should be scrolled into view`
).toBeVisible()
})
}
await testViewSource({
operationName: 'Offset Plane',
operationIndex: 0,
expectedActiveLine: "plane001 = offsetPlane('XY', 10)",
})
await testViewSource({
operationName: 'Extrude',
operationIndex: 1,
expectedActiveLine: 'extrude001 = extrude(10, sketch002)',
})
await testViewSource({
operationName: 'Revolve',
operationIndex: 0,
expectedActiveLine: 'revolve001 = revolve({ axis = "X" }, sketch001)',
})
await testViewSource({
operationName: 'Triangle',
operationIndex: 0,
expectedActiveLine: 'triangle()',
})
await test.step('Go to definition on the triangle function', async () => {
await toolbar.goToDefinitionOnOperation('Triangle', 0)
await editor.expectState({
highlightedCode: '',
diagnostics: [],
activeLines: ['export fn triangle() {'],
})
await expect(
editor.activeLine.first(),
'Triangle function definition should be scrolled into view'
).toBeVisible()
})
}
)
})

View File

@ -20,7 +20,7 @@ export class EditorFixture {
private diagnosticsTooltip!: Locator private diagnosticsTooltip!: Locator
private diagnosticsGutterIcon!: Locator private diagnosticsGutterIcon!: Locator
private codeContent!: Locator private codeContent!: Locator
private activeLine!: Locator public activeLine!: Locator
constructor(page: Page) { constructor(page: Page) {
this.page = page this.page = page

View File

@ -1,6 +1,13 @@
import type { Page, Locator } from '@playwright/test' import type { Page, Locator } from '@playwright/test'
import { expect } from '../zoo-test' import { expect } from '../zoo-test'
import { doAndWaitForImageDiff } from '../test-utils' import {
checkIfPaneIsOpen,
closePane,
doAndWaitForImageDiff,
openPane,
} from '../test-utils'
import { SidebarType } from 'components/ModelingSidebar/ModelingPanes'
import { SIDEBAR_BUTTON_SUFFIX } from 'lib/constants'
export class ToolbarFixture { export class ToolbarFixture {
public page: Page public page: Page
@ -23,6 +30,10 @@ export class ToolbarFixture {
filePane!: Locator filePane!: Locator
exeIndicator!: Locator exeIndicator!: Locator
treeInputField!: Locator treeInputField!: Locator
/** The sidebar button for the Feature Tree pane */
featureTreeId = 'feature-tree' as const
/** The pane element for the Feature Tree */
featureTreePane!: Locator
constructor(page: Page) { constructor(page: Page) {
this.page = page this.page = page
@ -47,6 +58,7 @@ export class ToolbarFixture {
this.treeInputField = page.getByTestId('tree-input-field') this.treeInputField = page.getByTestId('tree-input-field')
this.filePane = page.locator('#files-pane') this.filePane = page.locator('#files-pane')
this.featureTreePane = page.locator('#feature-tree-pane')
this.fileCreateToast = page.getByText('Successfully created') this.fileCreateToast = page.getByText('Successfully created')
this.exeIndicator = page.getByTestId('model-state-indicator-execution-done') this.exeIndicator = page.getByTestId('model-state-indicator-execution-done')
} }
@ -106,4 +118,76 @@ export class ToolbarFixture {
).toBeVisible() ).toBeVisible()
await this.page.getByTestId('dropdown-center-rectangle').click() await this.page.getByTestId('dropdown-center-rectangle').click()
} }
async closePane(paneId: SidebarType) {
return closePane(this.page, paneId + SIDEBAR_BUTTON_SUFFIX)
}
async openPane(paneId: SidebarType) {
return openPane(this.page, paneId + SIDEBAR_BUTTON_SUFFIX)
}
async checkIfPaneIsOpen(paneId: SidebarType) {
return checkIfPaneIsOpen(this.page, paneId + SIDEBAR_BUTTON_SUFFIX)
}
async openFeatureTreePane() {
return this.openPane(this.featureTreeId)
}
async closeFeatureTreePane() {
await this.closePane(this.featureTreeId)
}
async checkIfFeatureTreePaneIsOpen() {
return this.checkIfPaneIsOpen(this.featureTreeId)
}
/**
* Get a specific operation button from the Feature Tree pane
*/
async getFeatureTreeOperation(operationName: string, operationIndex: number) {
await this.openFeatureTreePane()
await expect(this.featureTreePane).toBeVisible()
return this.featureTreePane
.getByRole('button', {
name: operationName,
})
.nth(operationIndex)
}
/**
* View source on a specific operation in the Feature Tree pane.
* @param operationName The name of the operation type
* @param operationIndex The index out of operations of this type
*/
async viewSourceOnOperation(operationName: string, operationIndex: number) {
const operationButton = await this.getFeatureTreeOperation(
operationName,
operationIndex
)
const viewSourceMenuButton = this.page.getByRole('button', {
name: 'View KCL source code',
})
await operationButton.click({ button: 'right' })
await expect(viewSourceMenuButton).toBeVisible()
await viewSourceMenuButton.click()
}
/**
* Go to definition on a specific operation in the Feature Tree pane
*/
async goToDefinitionOnOperation(
operationName: string,
operationIndex: number
) {
const operationButton = await this.getFeatureTreeOperation(
operationName,
operationIndex
)
const goToDefinitionMenuButton = this.page.getByRole('button', {
name: 'View function definition',
})
await operationButton.click({ button: 'right' })
await expect(goToDefinitionMenuButton).toBeVisible()
await goToDefinitionMenuButton.click()
}
} }

View File

@ -2263,3 +2263,84 @@ loft([profile001, profile002])
) )
}) })
}) })
// Regression test for https://github.com/KittyCAD/modeling-app/issues/4891
test.describe(`Click based selection don't brick the app when clicked out of range after format using cache`, () => {
test(`Can select a line that reformmed after entering sketch mode`, async ({
context,
page,
scene,
toolbar,
editor,
homePage,
}) => {
// We seed the scene with a single offset plane
await context.addInitScript(() => {
localStorage.setItem(
'persistCode',
`sketch001 = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line([3.14, 3.14], %)
|> arcTo({
end = [4, 2],
interior = [1, 2]
}, %)
`
)
})
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await test.step(`format the code`, async () => {
// doesn't contain condensed version
await editor.expectEditor.not.toContain(
`arcTo({ end = [4, 2], interior = [1, 2] }, %)`
)
// click the code to enter sketch mode
await page.getByText(`arcTo`).click()
// Format the code.
await page.locator('#code-pane button:first-child').click()
await page.locator('button:has-text("Format code")').click()
})
await test.step(`Ensure the code reformatted`, async () => {
await editor.expectEditor.toContain(
`arcTo({ end = [4, 2], interior = [1, 2] }, %)`
)
})
const [arcClick, arcHover] = scene.makeMouseHelpers(699, 337)
await test.step('Ensure we can hover the arc', async () => {
await arcHover()
// Check that the code is highlighted
await editor.expectState({
activeLines: ["sketch001=startSketchOn('XZ')"],
diagnostics: [],
highlightedCode: 'arcTo({end = [4, 2], interior = [1, 2]}, %)',
})
})
await test.step('reset the selection', async () => {
// Move the mouse out of the way
await page.mouse.move(655, 337)
await editor.expectState({
activeLines: ["sketch001=startSketchOn('XZ')"],
diagnostics: [],
highlightedCode: '',
})
})
await test.step('Ensure we can click the arc', async () => {
await arcClick()
// Check that the code is highlighted
await editor.expectState({
activeLines: [],
diagnostics: [],
highlightedCode: 'arcTo({end = [4, 2], interior = [1, 2]}, %)',
})
})
})
})

View File

@ -375,6 +375,7 @@ const extrudeDefaultPlane = async (context: any, page: any, plane: string) => {
await u.closeKclCodePanel() await u.closeKclCodePanel()
await expect(page).toHaveScreenshot({ await expect(page).toHaveScreenshot({
maxDiffPixels: 100, maxDiffPixels: 100,
mask: [page.getByTestId('model-state-indicator')],
}) })
await u.openKclCodePanel() await u.openKclCodePanel()
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -524,7 +524,7 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
await u.clearCommandLogs() await u.clearCommandLogs()
await page.keyboard.press('Backspace') await page.keyboard.press('Backspace')
await expect(page.getByText('Unable to delete part')).toBeVisible() await expect(page.getByText('Unable to delete selection')).toBeVisible()
}) })
test('Hovering over 3d features highlights code, clicking puts the cursor in the right place and sends selection id to engine', async ({ test('Hovering over 3d features highlights code, clicking puts the cursor in the right place and sends selection id to engine', async ({
page, page,

View File

@ -156,13 +156,13 @@ test.describe('Text-to-CAD tests', () => {
const cmdSearchBar = page.getByPlaceholder('Search commands') const cmdSearchBar = page.getByPlaceholder('Search commands')
await expect(cmdSearchBar).toBeVisible() await expect(cmdSearchBar).toBeVisible()
const textToCadCommand = page.getByText('Text-to-CAD') const textToCadCommand = page.getByRole('option', { name: 'Text-to-CAD' })
await expect(textToCadCommand.first()).toBeVisible() await expect(textToCadCommand.first()).toBeVisible()
// Click the Text-to-CAD command // Click the Text-to-CAD command
await textToCadCommand.first().click() await textToCadCommand.first().click()
// Enter the prompt. // Enter the prompt.
const prompt = page.getByText('Prompt') const prompt = page.getByRole('textbox', { name: 'Prompt' })
await expect(prompt.first()).toBeVisible() await expect(prompt.first()).toBeVisible()
// Type the prompt. // Type the prompt.
@ -224,13 +224,13 @@ test.describe('Text-to-CAD tests', () => {
const cmdSearchBar = page.getByPlaceholder('Search commands') const cmdSearchBar = page.getByPlaceholder('Search commands')
await expect(cmdSearchBar).toBeVisible() await expect(cmdSearchBar).toBeVisible()
const textToCadCommand = page.getByText('Text-to-CAD') const textToCadCommand = page.getByRole('option', { name: 'Text-to-CAD' })
await expect(textToCadCommand.first()).toBeVisible() await expect(textToCadCommand.first()).toBeVisible()
// Click the Text-to-CAD command // Click the Text-to-CAD command
await textToCadCommand.first().click() await textToCadCommand.first().click()
// Enter the prompt. // Enter the prompt.
const prompt = page.getByText('Prompt') const prompt = page.getByRole('textbox', { name: 'Prompt' })
await expect(prompt.first()).toBeVisible() await expect(prompt.first()).toBeVisible()
const badPrompt = 'akjsndladf lajbhflauweyfaaaljhr472iouafyvsssssss' const badPrompt = 'akjsndladf lajbhflauweyfaaaljhr472iouafyvsssssss'
@ -314,13 +314,13 @@ test.describe('Text-to-CAD tests', () => {
const cmdSearchBar = page.getByPlaceholder('Search commands') const cmdSearchBar = page.getByPlaceholder('Search commands')
await expect(cmdSearchBar).toBeVisible() await expect(cmdSearchBar).toBeVisible()
const textToCadCommand = page.getByText('Text-to-CAD') const textToCadCommand = page.getByRole('option', { name: 'Text-to-CAD' })
await expect(textToCadCommand.first()).toBeVisible() await expect(textToCadCommand.first()).toBeVisible()
// Click the Text-to-CAD command // Click the Text-to-CAD command
await textToCadCommand.first().click() await textToCadCommand.first().click()
// Enter the prompt. // Enter the prompt.
const prompt = page.getByText('Prompt') const prompt = page.getByRole('textbox', { name: 'Prompt' })
await expect(prompt.first()).toBeVisible() await expect(prompt.first()).toBeVisible()
const badPrompt = 'akjsndladflajbhflauweyf15;' const badPrompt = 'akjsndladflajbhflauweyf15;'
@ -392,13 +392,13 @@ test.describe('Text-to-CAD tests', () => {
const cmdSearchBar = page.getByPlaceholder('Search commands') const cmdSearchBar = page.getByPlaceholder('Search commands')
await expect(cmdSearchBar).toBeVisible() await expect(cmdSearchBar).toBeVisible()
const textToCadCommand = page.getByText('Text-to-CAD') const textToCadCommand = page.getByRole('option', { name: 'Text-to-CAD' })
await expect(textToCadCommand.first()).toBeVisible() await expect(textToCadCommand.first()).toBeVisible()
// Click the Text-to-CAD command // Click the Text-to-CAD command
await textToCadCommand.first().click() await textToCadCommand.first().click()
// Enter the prompt. // Enter the prompt.
const prompt = page.getByText('Prompt') const prompt = page.getByRole('textbox', { name: 'Prompt' })
await expect(prompt.first()).toBeVisible() await expect(prompt.first()).toBeVisible()
// Type the prompt. // Type the prompt.
@ -604,7 +604,7 @@ async function sendPromptFromCommandBar(page: Page, promptStr: string) {
await page.waitForTimeout(1000) await page.waitForTimeout(1000)
// Enter the prompt. // Enter the prompt.
const prompt = page.getByText('Prompt') const prompt = page.getByRole('textbox', { name: 'Prompt' })
await expect(prompt.first()).toBeVisible() await expect(prompt.first()).toBeVisible()
// Type the prompt. // Type the prompt.

View File

@ -38,7 +38,7 @@ win:
# - arm64 # - arm64
signingHashAlgorithms: signingHashAlgorithms:
- sha256 - sha256
sign: "./sign-win.js" sign: "./scripts/sign-win.js"
publisherName: "KittyCAD Inc" # needs to be exactly like on Digicert publisherName: "KittyCAD Inc" # needs to be exactly like on Digicert
icon: "assets/icon.ico" icon: "assets/icon.ico"
fileAssociations: fileAssociations:

7
interface.d.ts vendored
View File

@ -11,6 +11,13 @@ export interface IElectronAPI {
open: typeof dialog.showOpenDialog open: typeof dialog.showOpenDialog
save: typeof dialog.showSaveDialog save: typeof dialog.showSaveDialog
openExternal: typeof shell.openExternal openExternal: typeof shell.openExternal
takeElectronWindowScreenshot: ({
width,
height,
}: {
width: number
height: number
}) => Promise<string>
showInFolder: typeof shell.showItemInFolder showInFolder: typeof shell.showItemInFolder
/** Require to be called first before {@link loginWithDeviceFlow} */ /** Require to be called first before {@link loginWithDeviceFlow} */
startDeviceFlow: (host: string) => Promise<string> startDeviceFlow: (host: string) => Promise<string>

View File

@ -10,23 +10,23 @@
}, },
"description": "Edit CAD visually or with code", "description": "Edit CAD visually or with code",
"main": ".vite/build/main.js", "main": ".vite/build/main.js",
"license": "none", "license": "MIT",
"dependencies": { "dependencies": {
"@codemirror/autocomplete": "^6.17.0", "@codemirror/autocomplete": "^6.17.0",
"@codemirror/commands": "^6.6.0", "@codemirror/commands": "^6.6.0",
"@codemirror/language": "^6.10.3", "@codemirror/language": "^6.10.3",
"@codemirror/lint": "^6.8.1", "@codemirror/lint": "^6.8.4",
"@codemirror/search": "^6.5.6", "@codemirror/search": "^6.5.6",
"@codemirror/state": "^6.4.1", "@codemirror/state": "^6.4.1",
"@codemirror/theme-one-dark": "^6.1.2", "@codemirror/theme-one-dark": "^6.1.2",
"@csstools/postcss-oklab-function": "^4.0.2", "@csstools/postcss-oklab-function": "^4.0.7",
"@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-brands-svg-icons": "^6.5.2", "@fortawesome/free-brands-svg-icons": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.4.2", "@fortawesome/free-solid-svg-icons": "^6.4.2",
"@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": "2.0.7", "@kittycad/lib": "2.0.12",
"@lezer/highlight": "^1.2.1", "@lezer/highlight": "^1.2.1",
"@lezer/lr": "^1.4.1", "@lezer/lr": "^1.4.1",
"@react-hook/resize-observer": "^2.0.1", "@react-hook/resize-observer": "^2.0.1",
@ -52,13 +52,13 @@
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-hot-toast": "^2.4.1", "react-hot-toast": "^2.4.1",
"react-hotkeys-hook": "^4.5.1", "react-hotkeys-hook": "^4.6.1",
"react-json-view": "^1.21.3", "react-json-view": "^1.21.3",
"react-modal": "^3.16.1", "react-modal": "^3.16.3",
"react-modal-promise": "^1.0.2", "react-modal-promise": "^1.0.2",
"react-router-dom": "^6.28.0", "react-router-dom": "^6.28.0",
"sketch-helpers": "^0.0.4", "sketch-helpers": "^0.0.4",
"three": "^0.166.1", "three": "^0.172.0",
"ua-parser-js": "^1.0.37", "ua-parser-js": "^1.0.37",
"uuid": "^11.0.2", "uuid": "^11.0.2",
"vscode-jsonrpc": "^8.2.1", "vscode-jsonrpc": "^8.2.1",
@ -166,7 +166,7 @@
"@types/react": "^18.3.4", "@types/react": "^18.3.4",
"@types/react-dom": "^18.3.1", "@types/react-dom": "^18.3.1",
"@types/react-modal": "^3.16.3", "@types/react-modal": "^3.16.3",
"@types/three": "^0.163.0", "@types/three": "^0.172.0",
"@types/ua-parser-js": "^0.7.39", "@types/ua-parser-js": "^0.7.39",
"@types/uuid": "^9.0.8", "@types/uuid": "^9.0.8",
"@types/wicg-file-system-access": "^2023.10.5", "@types/wicg-file-system-access": "^2023.10.5",
@ -186,7 +186,7 @@
"eslint-plugin-css-modules": "^2.12.0", "eslint-plugin-css-modules": "^2.12.0",
"eslint-plugin-import": "^2.30.0", "eslint-plugin-import": "^2.30.0",
"eslint-plugin-suggest-no-throw": "^1.0.0", "eslint-plugin-suggest-no-throw": "^1.0.0",
"happy-dom": "^15.11.7", "happy-dom": "^16.3.0",
"http-server": "^14.1.1", "http-server": "^14.1.1",
"husky": "^9.1.5", "husky": "^9.1.5",
"kill-port": "^2.0.1", "kill-port": "^2.0.1",

View File

@ -0,0 +1,7 @@
node_modules
build
dist
tsconfig.tsbuildinfo
*.d.ts
*.js
!rollup.config.js

View File

@ -0,0 +1,36 @@
{
"name": "@kittycad/codemirror-lang-kcl",
"version": "1.0.0",
"description": "Zoo KCL language support for CodeMirror 6.",
"main": "src/index.ts",
"scripts": {
"build": "rollup -c",
"test": "vitest --config vitest.main.config.ts run"
},
"type": "module",
"repository": "https://github.com/KittyCAD/modeling-app",
"author": "Zoo Engineering Team",
"license": "MIT",
"private": false,
"exports": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"types": "dist/index.d.ts",
"dependencies": {
"@codemirror/language": "^6.10.3",
"@codemirror/state": "^6.4.1",
"@lezer/highlight": "^1.2.1",
"typescript": "^5.7.2"
},
"devDependencies": {
"@lezer/generator": "^1.7.2",
"@rollup/plugin-typescript": "^12.1.2",
"rollup": "^4.29.1",
"rollup-plugin-dts": "^6.1.1",
"vitest": "^2.1.8"
},
"files": [
"dist/"
]
}

View File

@ -0,0 +1,25 @@
import dts from 'rollup-plugin-dts'
import { lezer } from '@lezer/generator/rollup'
import typescript from '@rollup/plugin-typescript'
export default [
{
input: 'src/index.ts',
// imports are considered internal if they start with './' or '/' or 'word:'
external: (id) => id != 'tslib' && !/^(\.?\/|\w:)/.test(id),
output: [
{ file: 'dist/index.cjs', format: 'cjs' },
{ file: 'dist/index.js', format: 'es' },
],
plugins: [lezer(), typescript()],
},
{
input: 'src/index.ts',
external: (id) => id != 'tslib' && !/^(\.?\/|\w:)/.test(id),
output: [
{ file: 'dist/index.d.cts', format: 'cjs' },
{ file: 'dist/index.d.ts', format: 'es' },
],
plugins: [lezer(), typescript(), dts()],
},
]

View File

@ -0,0 +1,42 @@
// Base CodeMirror language support for kcl.
import {
LRLanguage,
LanguageSupport,
indentNodeProp,
continuedIndent,
delimitedIndent,
foldNodeProp,
foldInside,
} from '@codemirror/language'
// @ts-ignore: No types available
import { parser } from './kcl.grammar'
export const KclLanguage = LRLanguage.define({
name: 'kcl',
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() {
return new LanguageSupport(KclLanguage)
}

View File

@ -17,7 +17,7 @@
statement[@isGroup=Statement] { statement[@isGroup=Statement] {
ImportStatement { kw<"import"> ImportItems ImportFrom String } | ImportStatement { kw<"import"> ImportItems ImportFrom String } |
FunctionDeclaration { kw<"export">? kw<"fn"> VariableDefinition Equals ParamList Arrow Body } | FunctionDeclaration { kw<"export">? kw<"fn"> VariableDefinition Equals? ParamList Arrow? Body } |
VariableDeclaration { kw<"export">? (kw<"var"> | kw<"let"> | kw<"const">)? VariableDefinition Equals expression } | VariableDeclaration { kw<"export">? (kw<"var"> | kw<"let"> | kw<"const">)? VariableDefinition Equals expression } |
ReturnStatement { kw<"return"> expression } | ReturnStatement { kw<"return"> expression } |
ExpressionStatement { expression } ExpressionStatement { expression }
@ -57,7 +57,7 @@ expression[@isGroup=Expression] {
UnaryOp { AddOp | BangOp } UnaryOp { AddOp | BangOp }
ObjectProperty { PropertyName ":" expression } ObjectProperty { PropertyName (":" | Equals) expression }
ArgumentList { "(" commaSep<expression> ")" } ArgumentList { "(" commaSep<expression> ")" }
@ -85,7 +85,7 @@ commaSep1NoTrailingComma<term> { term ("," term)* }
@tokens { @tokens {
String[isolate] { "'" ("\\" _ | !['\\])* "'" | '"' ("\\" _ | !["\\])* '"' } String[isolate] { "'" ("\\" _ | !['\\])* "'" | '"' ("\\" _ | !["\\])* '"' }
Number { "." @digit+ | @digit+ ("." @digit*)? } Number { "." @digit+ | @digit+ ("." @digit+)? }
@precedence { Number, "." } @precedence { Number, "." }
AddOp { "+" | "-" } AddOp { "+" | "-" }

View File

@ -0,0 +1,22 @@
import { KclLanguage } from '../src/index'
import { fileTests } from '@lezer/generator/dist/test'
import * as fs from 'fs'
import * as path from 'path'
let caseDir = path.dirname(__filename)
for (let file of fs.readdirSync(caseDir)) {
if (!/\.txt$/.test(file)) continue
let fname = /^[^\.]*/.exec(file)?.at(0)
if (fname) {
let tests = fileTests(
fs.readFileSync(path.join(caseDir, file), 'utf8'),
file
)
describe(fname, () => {
for (let { name, run } of tests) it(name, () => run(KclLanguage.parser))
})
}
}

View File

@ -0,0 +1,60 @@
# Booleans
true
false
==>
Program(ExpressionStatement(true), ExpressionStatement(false))
# Identifiers
one
_Two_Three
Four5
==>
Program(ExpressionStatement(VariableName),
ExpressionStatement(VariableName),
ExpressionStatement(VariableName))
# Strings
"hello"
'hi'
"one\"\\two"
'3\'\\four\x'
==>
Program(ExpressionStatement(String),
ExpressionStatement(String),
ExpressionStatement(String),
ExpressionStatement(String))
# VariableDeclaration
let a = 'abc'
export const x = 0.2
==>
Program(VariableDeclaration(let, VariableDefinition, Equals, String),
VariableDeclaration(export, const, VariableDefinition, Equals, Number))
# IfExpression
if x { 1 } else { $tag }
==>
Program(ExpressionStatement(IfExpression(if, VariableName, Body(ExpressionStatement(Number)), else, Body(ExpressionStatement(TagDeclarator)))))
# Shebang
#!anything
==>
Program(Shebang)

View File

@ -0,0 +1,60 @@
# full
fn two = () => {
return 2
}
==>
Program(FunctionDeclaration(fn,
VariableDefinition,
Equals,
ParamList,
Arrow,
Body(ReturnStatement(return,
Number))))
# = is optional
fn one () => {
return 1
}
==>
Program(FunctionDeclaration(fn,
VariableDefinition,
ParamList,
Arrow,
Body(ReturnStatement(return,
Number))))
# => is optional
fn one = () {
return 1
}
==>
Program(FunctionDeclaration(fn,
VariableDefinition,
Equals,
ParamList,
Body(ReturnStatement(return,
Number))))
# terse
fn two() {
return 2
}
==>
Program(FunctionDeclaration(fn,
VariableDefinition,
ParamList,
Body(ReturnStatement(return,
Number))))

View File

@ -0,0 +1,20 @@
# colon (deprecated)
x = { k: 123 }
==>
Program(VariableDeclaration(VariableDefinition,
Equals,
ObjectExpression(ObjectProperty(PropertyName,
Number))))
# equal
x = { k = 123 }
==>
Program(VariableDeclaration(VariableDefinition,
Equals,
ObjectExpression(ObjectProperty(PropertyName,
Equals,
Number))))

View File

@ -0,0 +1,43 @@
# spaced
a = [0 .. 1]
==>
Program(VariableDeclaration(VariableDefinition,
Equals,
ArrayExpression(IntegerRange(Number,
Number))))
# compact
a = [0..1]
==>
Program(VariableDeclaration(VariableDefinition,
Equals,
ArrayExpression(IntegerRange(Number,
Number))))
# expr spaced
a = [start .. start + 10]
==>
Program(VariableDeclaration(VariableDefinition,
Equals,
ArrayExpression(IntegerRange(VariableName,
BinaryExpression(VariableName,
AddOp,
Number)))))
# expr compact
a = [start..start + 10]
==>
Program(VariableDeclaration(VariableDefinition,
Equals,
ArrayExpression(IntegerRange(VariableName,
BinaryExpression(VariableName,
AddOp,
Number)))))

View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"composite": true,
"rootDir": "src",
"outDir": "dist",
"target": "esnext",
"lib": ["dom", "dom.iterable", "esnext"],
"module": "esnext",
"esModuleInterop": true,
"moduleResolution": "node",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitAny": true,
"skipLibCheck": true,
"declaration": true
},
"include": ["src", "./*.ts"],
"exclude": ["node_modules", "vitest.main.config.ts"]
}

View File

@ -0,0 +1,29 @@
// Overrides the test options from the modeling-app config.
import viteTsconfigPaths from 'vite-tsconfig-paths'
import { defineConfig, configDefaults } from 'vitest/config'
// @ts-ignore: No types available
import { lezer } from '@lezer/generator/rollup'
const config = defineConfig({
test: {
globals: true,
pool: 'forks',
poolOptions: {
forks: {
maxForks: 2,
minForks: 1,
},
},
environment: 'node',
reporters: process.env.GITHUB_ACTIONS
? ['dot', 'github-actions']
: ['verbose', 'hanging-process'],
testTimeout: 1000,
hookTimeout: 1000,
teardownTimeout: 1000,
},
plugins: [viteTsconfigPaths(), lezer()],
})
export default config

View File

@ -0,0 +1,714 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@babel/code-frame@^7.24.2":
version "7.26.2"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==
dependencies:
"@babel/helper-validator-identifier" "^7.25.9"
js-tokens "^4.0.0"
picocolors "^1.0.0"
"@babel/helper-validator-identifier@^7.25.9":
version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
"@codemirror/language@^6.10.3":
version "6.10.8"
resolved "https://registry.yarnpkg.com/@codemirror/language/-/language-6.10.8.tgz#3e3a346a2b0a8cf63ee1cfe03349eb1965dce5f9"
integrity sha512-wcP8XPPhDH2vTqf181U8MbZnW+tDyPYy0UzVOa+oHORjyT+mhhom9vBd7dApJwoDz9Nb/a8kHjJIsuA/t8vNFw==
dependencies:
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.23.0"
"@lezer/common" "^1.1.0"
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
style-mod "^4.0.0"
"@codemirror/state@^6.0.0", "@codemirror/state@^6.4.1", "@codemirror/state@^6.5.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.5.0.tgz#e98dde85620618651543152fe1c2483300a0ccc9"
integrity sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==
dependencies:
"@marijn/find-cluster-break" "^1.0.0"
"@codemirror/view@^6.23.0":
version "6.36.1"
resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-6.36.1.tgz#3c543b8fd72c96b30c4b2b1464d1ebce7e0c5c4b"
integrity sha512-miD1nyT4m4uopZaDdO2uXU/LLHliKNYL9kB1C1wJHrunHLm/rpkb5QVSokqgw9hFqEZakrdlb/VGWX8aYZTslQ==
dependencies:
"@codemirror/state" "^6.5.0"
style-mod "^4.1.0"
w3c-keyname "^2.2.4"
"@esbuild/aix-ppc64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f"
integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==
"@esbuild/android-arm64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052"
integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==
"@esbuild/android-arm@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28"
integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==
"@esbuild/android-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e"
integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==
"@esbuild/darwin-arm64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a"
integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==
"@esbuild/darwin-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22"
integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==
"@esbuild/freebsd-arm64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e"
integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==
"@esbuild/freebsd-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261"
integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==
"@esbuild/linux-arm64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b"
integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==
"@esbuild/linux-arm@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9"
integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==
"@esbuild/linux-ia32@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2"
integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==
"@esbuild/linux-loong64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df"
integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==
"@esbuild/linux-mips64el@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe"
integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==
"@esbuild/linux-ppc64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4"
integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==
"@esbuild/linux-riscv64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc"
integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==
"@esbuild/linux-s390x@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de"
integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==
"@esbuild/linux-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0"
integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==
"@esbuild/netbsd-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047"
integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==
"@esbuild/openbsd-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70"
integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==
"@esbuild/sunos-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b"
integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==
"@esbuild/win32-arm64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d"
integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==
"@esbuild/win32-ia32@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b"
integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==
"@esbuild/win32-x64@0.21.5":
version "0.21.5"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c"
integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==
"@jridgewell/sourcemap-codec@^1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
"@lezer/common@^1.0.0", "@lezer/common@^1.1.0":
version "1.2.3"
resolved "https://registry.yarnpkg.com/@lezer/common/-/common-1.2.3.tgz#138fcddab157d83da557554851017c6c1e5667fd"
integrity sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==
"@lezer/generator@^1.7.2":
version "1.7.2"
resolved "https://registry.yarnpkg.com/@lezer/generator/-/generator-1.7.2.tgz#a491c91eb9f117ea803e748fa97574514156a2a3"
integrity sha512-CwgULPOPPmH54tv4gki18bElLCdJ1+FBC+nGVSVD08vFWDsMjS7KEjNTph9JOypDnet90ujN3LzQiW3CyVODNQ==
dependencies:
"@lezer/common" "^1.1.0"
"@lezer/lr" "^1.3.0"
"@lezer/highlight@^1.0.0", "@lezer/highlight@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@lezer/highlight/-/highlight-1.2.1.tgz#596fa8f9aeb58a608be0a563e960c373cbf23f8b"
integrity sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==
dependencies:
"@lezer/common" "^1.0.0"
"@lezer/lr@^1.0.0", "@lezer/lr@^1.3.0":
version "1.4.2"
resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-1.4.2.tgz#931ea3dea8e9de84e90781001dae30dea9ff1727"
integrity sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==
dependencies:
"@lezer/common" "^1.0.0"
"@marijn/find-cluster-break@^1.0.0":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz#775374306116d51c0c500b8c4face0f9a04752d8"
integrity sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==
"@rollup/plugin-typescript@^12.1.2":
version "12.1.2"
resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-12.1.2.tgz#ebaeec2e7376faa889030ccd7cb485a649e63118"
integrity sha512-cdtSp154H5sv637uMr1a8OTWB0L1SWDSm1rDGiyfcGcvQ6cuTs4MDk2BVEBGysUWago4OJN4EQZqOTl/QY3Jgg==
dependencies:
"@rollup/pluginutils" "^5.1.0"
resolve "^1.22.1"
"@rollup/pluginutils@^5.1.0":
version "5.1.4"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.4.tgz#bb94f1f9eaaac944da237767cdfee6c5b2262d4a"
integrity sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==
dependencies:
"@types/estree" "^1.0.0"
estree-walker "^2.0.2"
picomatch "^4.0.2"
"@rollup/rollup-android-arm-eabi@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.1.tgz#9bd38df6a29afb7f0336d988bc8112af0c8816c0"
integrity sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==
"@rollup/rollup-android-arm64@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.29.1.tgz#bd1a98390e15b76eeef907175a37c5f0f9e4d214"
integrity sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew==
"@rollup/rollup-darwin-arm64@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.29.1.tgz#bc6fa8a2cc77b5f367424e5e994e3537524e6879"
integrity sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw==
"@rollup/rollup-darwin-x64@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.29.1.tgz#76059c91f06b17406347b127df10f065283b2e61"
integrity sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng==
"@rollup/rollup-freebsd-arm64@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.29.1.tgz#83178315c0be4b4c8c1fd835e1952d2dc1eb4e6e"
integrity sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw==
"@rollup/rollup-freebsd-x64@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.29.1.tgz#1ef24fa0576bf7899a0a0a649156606dbd7a0d46"
integrity sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w==
"@rollup/rollup-linux-arm-gnueabihf@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.29.1.tgz#443a6f5681bf4611caae42988994a6d8ee676216"
integrity sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A==
"@rollup/rollup-linux-arm-musleabihf@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.29.1.tgz#9738b27184102228637a683e5f35b22ea352394f"
integrity sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ==
"@rollup/rollup-linux-arm64-gnu@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.29.1.tgz#b5e9d5e30ff36a19bedd29c715ba18a1889ff269"
integrity sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA==
"@rollup/rollup-linux-arm64-musl@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.29.1.tgz#1d8f68f0829b57f746ec03432ad046f1af014a98"
integrity sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA==
"@rollup/rollup-linux-loongarch64-gnu@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.29.1.tgz#07027feb883408e74a3002c8e50caaedd288ae38"
integrity sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw==
"@rollup/rollup-linux-powerpc64le-gnu@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.29.1.tgz#544ce1b0847a9c1240425e86f33daceac7ec4e12"
integrity sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w==
"@rollup/rollup-linux-riscv64-gnu@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.29.1.tgz#64be13d51852ec1e2dfbd25d997ed5f42f35ea6d"
integrity sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ==
"@rollup/rollup-linux-s390x-gnu@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.29.1.tgz#31f51e1e05c6264552d03875d9e2e673f0fd86e3"
integrity sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g==
"@rollup/rollup-linux-x64-gnu@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.29.1.tgz#f4c95b26f4ad69ebdb64b42f0ae4da2a0f617958"
integrity sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ==
"@rollup/rollup-linux-x64-musl@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.29.1.tgz#ab7be89192f72beb9ea6e2386186fefde4f69d82"
integrity sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA==
"@rollup/rollup-win32-arm64-msvc@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.29.1.tgz#7f12efb8240b238346951559998802722944421e"
integrity sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig==
"@rollup/rollup-win32-ia32-msvc@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.29.1.tgz#353d14d6eee943004d129796e4feddd3aa260921"
integrity sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng==
"@rollup/rollup-win32-x64-msvc@4.29.1":
version "4.29.1"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.29.1.tgz#c82f04a09ba481e13857d6f2516e072aaa51b7f4"
integrity sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg==
"@types/estree@1.0.6", "@types/estree@^1.0.0":
version "1.0.6"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
"@vitest/expect@2.1.8":
version "2.1.8"
resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.1.8.tgz#13fad0e8d5a0bf0feb675dcf1d1f1a36a1773bc1"
integrity sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==
dependencies:
"@vitest/spy" "2.1.8"
"@vitest/utils" "2.1.8"
chai "^5.1.2"
tinyrainbow "^1.2.0"
"@vitest/mocker@2.1.8":
version "2.1.8"
resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-2.1.8.tgz#51dec42ac244e949d20009249e033e274e323f73"
integrity sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==
dependencies:
"@vitest/spy" "2.1.8"
estree-walker "^3.0.3"
magic-string "^0.30.12"
"@vitest/pretty-format@2.1.8", "@vitest/pretty-format@^2.1.8":
version "2.1.8"
resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.8.tgz#88f47726e5d0cf4ba873d50c135b02e4395e2bca"
integrity sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==
dependencies:
tinyrainbow "^1.2.0"
"@vitest/runner@2.1.8":
version "2.1.8"
resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.8.tgz#b0e2dd29ca49c25e9323ea2a45a5125d8729759f"
integrity sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==
dependencies:
"@vitest/utils" "2.1.8"
pathe "^1.1.2"
"@vitest/snapshot@2.1.8":
version "2.1.8"
resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.1.8.tgz#d5dc204f4b95dc8b5e468b455dfc99000047d2de"
integrity sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==
dependencies:
"@vitest/pretty-format" "2.1.8"
magic-string "^0.30.12"
pathe "^1.1.2"
"@vitest/spy@2.1.8":
version "2.1.8"
resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.8.tgz#bc41af3e1e6a41ae3b67e51f09724136b88fa447"
integrity sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==
dependencies:
tinyspy "^3.0.2"
"@vitest/utils@2.1.8":
version "2.1.8"
resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.8.tgz#f8ef85525f3362ebd37fd25d268745108d6ae388"
integrity sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==
dependencies:
"@vitest/pretty-format" "2.1.8"
loupe "^3.1.2"
tinyrainbow "^1.2.0"
assertion-error@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7"
integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==
cac@^6.7.14:
version "6.7.14"
resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959"
integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==
chai@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/chai/-/chai-5.1.2.tgz#3afbc340b994ae3610ca519a6c70ace77ad4378d"
integrity sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==
dependencies:
assertion-error "^2.0.1"
check-error "^2.1.1"
deep-eql "^5.0.1"
loupe "^3.1.0"
pathval "^2.0.0"
check-error@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc"
integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==
debug@^4.3.7:
version "4.4.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
dependencies:
ms "^2.1.3"
deep-eql@^5.0.1:
version "5.0.2"
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341"
integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==
es-module-lexer@^1.5.4:
version "1.6.0"
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.6.0.tgz#da49f587fd9e68ee2404fe4e256c0c7d3a81be21"
integrity sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==
esbuild@^0.21.3:
version "0.21.5"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d"
integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==
optionalDependencies:
"@esbuild/aix-ppc64" "0.21.5"
"@esbuild/android-arm" "0.21.5"
"@esbuild/android-arm64" "0.21.5"
"@esbuild/android-x64" "0.21.5"
"@esbuild/darwin-arm64" "0.21.5"
"@esbuild/darwin-x64" "0.21.5"
"@esbuild/freebsd-arm64" "0.21.5"
"@esbuild/freebsd-x64" "0.21.5"
"@esbuild/linux-arm" "0.21.5"
"@esbuild/linux-arm64" "0.21.5"
"@esbuild/linux-ia32" "0.21.5"
"@esbuild/linux-loong64" "0.21.5"
"@esbuild/linux-mips64el" "0.21.5"
"@esbuild/linux-ppc64" "0.21.5"
"@esbuild/linux-riscv64" "0.21.5"
"@esbuild/linux-s390x" "0.21.5"
"@esbuild/linux-x64" "0.21.5"
"@esbuild/netbsd-x64" "0.21.5"
"@esbuild/openbsd-x64" "0.21.5"
"@esbuild/sunos-x64" "0.21.5"
"@esbuild/win32-arm64" "0.21.5"
"@esbuild/win32-ia32" "0.21.5"
"@esbuild/win32-x64" "0.21.5"
estree-walker@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
estree-walker@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d"
integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==
dependencies:
"@types/estree" "^1.0.0"
expect-type@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.1.0.tgz#a146e414250d13dfc49eafcfd1344a4060fa4c75"
integrity sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==
fsevents@~2.3.2, fsevents@~2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
hasown@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
dependencies:
function-bind "^1.1.2"
is-core-module@^2.16.0:
version "2.16.1"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4"
integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==
dependencies:
hasown "^2.0.2"
js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
loupe@^3.1.0, loupe@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240"
integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==
magic-string@^0.30.10, magic-string@^0.30.12:
version "0.30.17"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453"
integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==
dependencies:
"@jridgewell/sourcemap-codec" "^1.5.0"
ms@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
nanoid@^3.3.7:
version "3.3.8"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf"
integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==
path-parse@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
pathe@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec"
integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==
pathval@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25"
integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==
picocolors@^1.0.0, picocolors@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
picomatch@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab"
integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==
postcss@^8.4.43:
version "8.4.49"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19"
integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==
dependencies:
nanoid "^3.3.7"
picocolors "^1.1.1"
source-map-js "^1.2.1"
resolve@^1.22.1:
version "1.22.10"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39"
integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==
dependencies:
is-core-module "^2.16.0"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
rollup-plugin-dts@^6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/rollup-plugin-dts/-/rollup-plugin-dts-6.1.1.tgz#46b33f4d1d7f4e66f1171ced9b282ac11a15a254"
integrity sha512-aSHRcJ6KG2IHIioYlvAOcEq6U99sVtqDDKVhnwt70rW6tsz3tv5OSjEiWcgzfsHdLyGXZ/3b/7b/+Za3Y6r1XA==
dependencies:
magic-string "^0.30.10"
optionalDependencies:
"@babel/code-frame" "^7.24.2"
rollup@^4.20.0, rollup@^4.29.1:
version "4.29.1"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.29.1.tgz#a9aaaece817e5f778489e5bf82e379cc8a5c05bc"
integrity sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==
dependencies:
"@types/estree" "1.0.6"
optionalDependencies:
"@rollup/rollup-android-arm-eabi" "4.29.1"
"@rollup/rollup-android-arm64" "4.29.1"
"@rollup/rollup-darwin-arm64" "4.29.1"
"@rollup/rollup-darwin-x64" "4.29.1"
"@rollup/rollup-freebsd-arm64" "4.29.1"
"@rollup/rollup-freebsd-x64" "4.29.1"
"@rollup/rollup-linux-arm-gnueabihf" "4.29.1"
"@rollup/rollup-linux-arm-musleabihf" "4.29.1"
"@rollup/rollup-linux-arm64-gnu" "4.29.1"
"@rollup/rollup-linux-arm64-musl" "4.29.1"
"@rollup/rollup-linux-loongarch64-gnu" "4.29.1"
"@rollup/rollup-linux-powerpc64le-gnu" "4.29.1"
"@rollup/rollup-linux-riscv64-gnu" "4.29.1"
"@rollup/rollup-linux-s390x-gnu" "4.29.1"
"@rollup/rollup-linux-x64-gnu" "4.29.1"
"@rollup/rollup-linux-x64-musl" "4.29.1"
"@rollup/rollup-win32-arm64-msvc" "4.29.1"
"@rollup/rollup-win32-ia32-msvc" "4.29.1"
"@rollup/rollup-win32-x64-msvc" "4.29.1"
fsevents "~2.3.2"
siginfo@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30"
integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==
source-map-js@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
stackback@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b"
integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==
std-env@^3.8.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5"
integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==
style-mod@^4.0.0, style-mod@^4.1.0:
version "4.1.2"
resolved "https://registry.yarnpkg.com/style-mod/-/style-mod-4.1.2.tgz#ca238a1ad4786520f7515a8539d5a63691d7bf67"
integrity sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==
supports-preserve-symlinks-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
tinybench@^2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b"
integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==
tinyexec@^0.3.1:
version "0.3.2"
resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2"
integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==
tinypool@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.2.tgz#706193cc532f4c100f66aa00b01c42173d9051b2"
integrity sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==
tinyrainbow@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5"
integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==
tinyspy@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a"
integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==
typescript@^5.7.2:
version "5.7.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6"
integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==
vite-node@2.1.8:
version "2.1.8"
resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.8.tgz#9495ca17652f6f7f95ca7c4b568a235e0c8dbac5"
integrity sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==
dependencies:
cac "^6.7.14"
debug "^4.3.7"
es-module-lexer "^1.5.4"
pathe "^1.1.2"
vite "^5.0.0"
vite@^5.0.0:
version "5.4.11"
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.11.tgz#3b415cd4aed781a356c1de5a9ebafb837715f6e5"
integrity sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==
dependencies:
esbuild "^0.21.3"
postcss "^8.4.43"
rollup "^4.20.0"
optionalDependencies:
fsevents "~2.3.3"
vitest@^2.1.8:
version "2.1.8"
resolved "https://registry.yarnpkg.com/vitest/-/vitest-2.1.8.tgz#2e6a00bc24833574d535c96d6602fb64163092fa"
integrity sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==
dependencies:
"@vitest/expect" "2.1.8"
"@vitest/mocker" "2.1.8"
"@vitest/pretty-format" "^2.1.8"
"@vitest/runner" "2.1.8"
"@vitest/snapshot" "2.1.8"
"@vitest/spy" "2.1.8"
"@vitest/utils" "2.1.8"
chai "^5.1.2"
debug "^4.3.7"
expect-type "^1.1.0"
magic-string "^0.30.12"
pathe "^1.1.2"
std-env "^3.8.0"
tinybench "^2.9.0"
tinyexec "^0.3.1"
tinypool "^1.0.1"
tinyrainbow "^1.2.0"
vite "^5.0.0"
vite-node "2.1.8"
why-is-node-running "^2.3.0"
w3c-keyname@^2.2.4:
version "2.2.8"
resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz#7b17c8c6883d4e8b86ac8aba79d39e880f8869c5"
integrity sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==
why-is-node-running@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04"
integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==
dependencies:
siginfo "^2.0.0"
stackback "0.0.2"

View File

@ -1,6 +1,7 @@
import re import re
import os import os
import requests import requests
import textwrap
webhook_url = os.getenv('DISCORD_WEBHOOK_URL') webhook_url = os.getenv('DISCORD_WEBHOOK_URL')
release_version = os.getenv('RELEASE_VERSION') release_version = os.getenv('RELEASE_VERSION')
@ -25,12 +26,11 @@ if len(modified_release_body) > max_length:
# Message to send to Discord # Message to send to Discord
data = { data = {
"content": "content": textwrap.dedent(f'''
f'''
**{release_version}** is now available! Check out the latest features and improvements here: <https://zoo.dev/modeling-app/download> **{release_version}** is now available! Check out the latest features and improvements here: <https://zoo.dev/modeling-app/download>
{modified_release_body} {modified_release_body}
''', '''),
"username": "Modeling App Release Updates", "username": "Modeling App Release Updates",
"avatar_url": "https://raw.githubusercontent.com/KittyCAD/modeling-app/main/public/discord-avatar.png" "avatar_url": "https://raw.githubusercontent.com/KittyCAD/modeling-app/main/public/discord-avatar.png"
} }

View File

@ -84,6 +84,11 @@
"title": "Hex nut", "title": "Hex nut",
"description": "A hex nut is a type of fastener with a threaded hole and a hexagonal outer shape, used in a wide variety of applications to secure parts together. The hexagonal shape allows for a greater torque to be applied with wrenches or tools, making it one of the most common nut types in hardware." "description": "A hex nut is a type of fastener with a threaded hole and a hexagonal outer shape, used in a wide variety of applications to secure parts together. The hexagonal shape allows for a greater torque to be applied with wrenches or tools, making it one of the most common nut types in hardware."
}, },
{
"file": "i-beam.kcl",
"title": "I-beam",
"description": "A structural metal beam with an I shaped cross section. Often used in construction"
},
{ {
"file": "kitt.kcl", "file": "kitt.kcl",
"title": "Kitt", "title": "Kitt",

View File

@ -24,8 +24,7 @@ exports.default = async (configuration) => {
try { try {
execSync( execSync(
`smctl sign --fingerprint="${ `smctl sign --fingerprint="${process.env.WINDOWS_CERTIFICATE_THUMBPRINT
process.env.WINDOWS_CERTIFICATE_THUMBPRINT
}" --input "${String(configuration.path)}"`, }" --input "${String(configuration.path)}"`,
{ {
stdio: 'inherit', stdio: 'inherit',
@ -33,6 +32,6 @@ exports.default = async (configuration) => {
) )
console.log('Signing using signWin.js script: successful') console.log('Signing using signWin.js script: successful')
} catch (error) { } catch (error) {
console.error('Signing using signWin.js script: failed:', error) throw new Error('Signing using signWin.js script: failed:', error)
} }
} }

View File

@ -3,6 +3,9 @@ import {
DoubleSide, DoubleSide,
Group, Group,
Intersection, Intersection,
Line,
LineDashedMaterial,
BufferGeometry,
Mesh, Mesh,
MeshBasicMaterial, MeshBasicMaterial,
Object3D, Object3D,
@ -13,6 +16,7 @@ import {
Points, Points,
Quaternion, Quaternion,
Scene, Scene,
SphereGeometry,
Vector2, Vector2,
Vector3, Vector3,
} from 'three' } from 'three'
@ -31,6 +35,8 @@ import {
SKETCH_LAYER, SKETCH_LAYER,
X_AXIS, X_AXIS,
Y_AXIS, Y_AXIS,
CIRCLE_3_POINT_DRAFT_POINT,
CIRCLE_3_POINT_DRAFT_CIRCLE,
} from './sceneInfra' } from './sceneInfra'
import { isQuaternionVertical, quaternionFromUpNForward } from './helpers' import { isQuaternionVertical, quaternionFromUpNForward } from './helpers'
import { import {
@ -62,6 +68,7 @@ import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
import { executeAst, ToolTip } from 'lang/langHelpers' import { executeAst, ToolTip } from 'lang/langHelpers'
import { import {
createProfileStartHandle, createProfileStartHandle,
createArcGeometry,
SegmentUtils, SegmentUtils,
segmentUtils, segmentUtils,
} from './segments' } from './segments'
@ -1370,6 +1377,231 @@ export class SceneEntities {
}) })
return { updatedEntryNodePath, updatedSketchNodePaths } return { updatedEntryNodePath, updatedSketchNodePaths }
} }
// lee: Well, it appears all our code in sceneEntities each act as their own
// kind of classes. In this case, I'll keep utility functions pertaining to
// circle3Point here. Feel free to extract as needed.
entryDraftCircle3Point = (
done: () => void,
startSketchOnASTNodePath: PathToNode,
forward: Vector3,
up: Vector3,
sketchOrigin: Vector3
): (() => void) => {
// lee: Not a fan we need to re-iterate this dummy object all over the place
// just to get the scale but okie dokie.
const dummy = new Mesh()
dummy.position.set(0, 0, 0)
const scale = sceneInfra.getClientSceneScaleFactor(dummy)
const orientation = quaternionFromUpNForward(up, forward)
// Reminder: the intersection plane is the primary way to derive a XY
// position from a mouse click in ThreeJS.
// Here, we position and orient so it's facing the viewer.
this.intersectionPlane!.setRotationFromQuaternion(orientation)
this.intersectionPlane!.position.copy(sketchOrigin)
// Keep track of points in the scene with their ThreeJS ids.
const points: Map<number, Vector2> = new Map()
// Keep a reference so we can destroy and recreate as needed.
let groupCircle: Group | undefined
// Add our new group to the list of groups to render
const groupOfDrafts = new Group()
groupOfDrafts.name = 'circle-3-point-group'
groupOfDrafts.position.copy(sketchOrigin)
// lee: I'm keeping this here as a developer gotchya:
// Do not reorient your surfaces to the intersection plane. Your points are
// already in 3D space, not 2D. If you intersect say XZ, you want the points
// to continue to live at the 3D intersection point, not be rotated to end
// up elsewhere!
// groupOfDrafts.setRotationFromQuaternion(orientation)
this.scene.add(groupOfDrafts)
const DRAFT_POINT_RADIUS = 6
const createPoint = (center: Vector3): number => {
const geometry = new SphereGeometry(DRAFT_POINT_RADIUS)
const color = getThemeColorForThreeJs(sceneInfra._theme)
const material = new MeshBasicMaterial({ color })
const mesh = new Mesh(geometry, material)
mesh.userData = { type: CIRCLE_3_POINT_DRAFT_POINT }
mesh.layers.set(SKETCH_LAYER)
mesh.position.copy(center)
mesh.scale.set(scale, scale, scale)
mesh.renderOrder = 100
groupOfDrafts.add(mesh)
return mesh.id
}
const circle3Point = (
points: Vector2[]
): undefined | { center: Vector3; radius: number } => {
// A 3-point circle is undefined if it doesn't have 3 points :)
if (points.length !== 3) return undefined
// y = (i/j)(x-h) + b
// i and j variables for the slopes
const i = [points[1].x - points[0].x, points[2].x - points[1].x]
const j = [points[1].y - points[0].y, points[2].y - points[1].y]
// Our / threejs coordinate system affects this a lot. If you take this
// code into a different code base, you may have to adjust a/b to being
// -1/a/b, b/a, etc! In this case, a/-b did the trick.
const m = [i[0] / -j[0], i[1] / -j[1]]
const h = [
(points[0].x + points[1].x) / 2,
(points[1].x + points[2].x) / 2,
]
const b = [
(points[0].y + points[1].y) / 2,
(points[1].y + points[2].y) / 2,
]
// Algebraically derived
const x = (-m[0] * h[0] + b[0] - b[1] + m[1] * h[1]) / (m[1] - m[0])
const y = m[0] * (x - h[0]) + b[0]
const center = new Vector3(x, y, 0)
const radius = Math.sqrt((points[1].x - x) ** 2 + (points[1].y - y) ** 2)
return {
center,
radius,
}
}
// TO BE SHORT LIVED: unused function to draw the circle and lines.
// @ts-ignore
// eslint-disable-next-line
const createCircle3Point = (points: Vector2[]) => {
const circleParams = circle3Point(points)
// A circle cannot be created for these points.
if (!circleParams) return
const color = getThemeColorForThreeJs(sceneInfra._theme)
const geometryCircle = createArcGeometry({
center: [circleParams.center.x, circleParams.center.y],
radius: circleParams.radius,
startAngle: 0,
endAngle: Math.PI * 2,
ccw: true,
isDashed: true,
scale,
})
const materialCircle = new MeshBasicMaterial({ color })
if (groupCircle) groupOfDrafts.remove(groupCircle)
groupCircle = new Group()
groupCircle.renderOrder = 1
const meshCircle = new Mesh(geometryCircle, materialCircle)
meshCircle.userData = { type: CIRCLE_3_POINT_DRAFT_CIRCLE }
meshCircle.layers.set(SKETCH_LAYER)
meshCircle.position.set(circleParams.center.x, circleParams.center.y, 0)
meshCircle.scale.set(scale, scale, scale)
groupCircle.add(meshCircle)
const geometryPolyLine = new BufferGeometry().setFromPoints([
...points,
points[0],
])
const materialPolyLine = new LineDashedMaterial({
color,
scale,
dashSize: 6,
gapSize: 6,
})
const meshPolyLine = new Line(geometryPolyLine, materialPolyLine)
meshPolyLine.computeLineDistances()
groupCircle.add(meshPolyLine)
groupOfDrafts.add(groupCircle)
}
// The target of our dragging
let target: Object3D | undefined = undefined
const cleanupFn = () => {
this.scene.remove(groupOfDrafts)
}
sceneInfra.setCallbacks({
async onDrag(args) {
const draftPointsIntersected = args.intersects.filter(
(intersected) =>
intersected.object.userData.type === CIRCLE_3_POINT_DRAFT_POINT
)
const firstPoint = draftPointsIntersected[0]
if (firstPoint && !target) {
target = firstPoint.object
}
// The user was off their mark! Missed the object to select.
if (!target) return
target.position.copy(args.intersectionPoint.threeD)
points.set(target.id, args.intersectionPoint.twoD)
},
async onDragEnd(_args) {
target = undefined
},
async onClick(args) {
if (points.size >= 3) return
if (!args.intersectionPoint) return
const id = createPoint(args.intersectionPoint.threeD)
points.set(id, args.intersectionPoint.twoD)
if (points.size < 2) return
// We've now got 3 points, let's create our circle!
const astSnapshot = structuredClone(kclManager.ast)
let nodeQueryResult
nodeQueryResult = getNodeFromPath<VariableDeclaration>(
astSnapshot,
startSketchOnASTNodePath,
'VariableDeclaration'
)
if (err(nodeQueryResult)) return Promise.reject(nodeQueryResult)
const startSketchOnASTNode = nodeQueryResult
const circleParams = circle3Point(Array.from(points.values()))
if (!circleParams) return
const kclCircle3Point = parse(`circle({
center = [${circleParams.center.x}, ${circleParams.center.y}],
radius = ${circleParams.radius},
}, %)`)
if (err(kclCircle3Point) || kclCircle3Point.program === null) return
if (kclCircle3Point.program.body[0].type !== 'ExpressionStatement')
return
const clonedStartSketchOnASTNode = structuredClone(startSketchOnASTNode)
startSketchOnASTNode.node.declaration.init = createPipeExpression([
clonedStartSketchOnASTNode.node.declaration.init,
kclCircle3Point.program.body[0].expression,
])
await kclManager.executeAstMock(astSnapshot)
await codeManager.updateEditorWithAstAndWriteToFile(astSnapshot)
done()
},
})
return cleanupFn
}
setupDraftCircle = async ( setupDraftCircle = async (
sketchEntryNodePath: PathToNode, sketchEntryNodePath: PathToNode,
sketchNodePaths: PathToNode[], sketchNodePaths: PathToNode[],

View File

@ -62,6 +62,8 @@ export const ARROWHEAD = 'arrowhead'
export const SEGMENT_LENGTH_LABEL = 'segment-length-label' export const SEGMENT_LENGTH_LABEL = 'segment-length-label'
export const SEGMENT_LENGTH_LABEL_TEXT = 'segment-length-label-text' export const SEGMENT_LENGTH_LABEL_TEXT = 'segment-length-label-text'
export const SEGMENT_LENGTH_LABEL_OFFSET_PX = 30 export const SEGMENT_LENGTH_LABEL_OFFSET_PX = 30
export const CIRCLE_3_POINT_DRAFT_POINT = 'circle-3-point-draft-point'
export const CIRCLE_3_POINT_DRAFT_CIRCLE = 'circle-3-point-draft-circle'
export interface OnMouseEnterLeaveArgs { export interface OnMouseEnterLeaveArgs {
selected: Object3D<Object3DEventMap> selected: Object3D<Object3DEventMap>

View File

@ -21,7 +21,8 @@ export function AstExplorer() {
const node = _node const node = _node
return ( return (
<div id="ast-explorer" className="relative"> <details id="ast-explorer" className="relative">
<summary>AST Explorer</summary>
<div className=""> <div className="">
filter out keys:<div className="w-2 inline-block"></div> filter out keys:<div className="w-2 inline-block"></div>
{['start', 'end', 'type'].map((key) => { {['start', 'end', 'type'].map((key) => {
@ -58,7 +59,7 @@ export function AstExplorer() {
/> />
</pre> </pre>
</div> </div>
</div> </details>
) )
} }

View File

@ -194,7 +194,7 @@ function ReviewingButton() {
autoFocus autoFocus
type="submit" type="submit"
form="review-form" form="review-form"
className="w-fit !p-0 rounded-sm border !border-primary hover:shadow" className="w-fit !p-0 rounded-sm hover:shadow"
iconStart={{ iconStart={{
icon: 'checkmark', icon: 'checkmark',
bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110', bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110',
@ -212,7 +212,7 @@ function GatheringArgsButton() {
Element="button" Element="button"
type="submit" type="submit"
form="arg-form" form="arg-form"
className="w-fit !p-0 rounded-sm border !border-primary hover:shadow" className="w-fit !p-0 rounded-sm hover:shadow"
iconStart={{ iconStart={{
icon: 'arrowRight', icon: 'arrowRight',
bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110', bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110',

View File

@ -1,5 +1,11 @@
import { Completion } from '@codemirror/autocomplete' import {
import { EditorView, ViewUpdate } from '@codemirror/view' closeBrackets,
closeBracketsKeymap,
Completion,
completionKeymap,
completionStatus,
} from '@codemirror/autocomplete'
import { EditorView, keymap, ViewUpdate } from '@codemirror/view'
import { CustomIcon } from 'components/CustomIcon' import { CustomIcon } from 'components/CustomIcon'
import { useCommandsContext } from 'hooks/useCommandsContext' import { useCommandsContext } from 'hooks/useCommandsContext'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
@ -95,6 +101,7 @@ function CommandBarKclInput({
label: v.key, label: v.key,
detail: String(roundOff(v.value as number)), detail: String(roundOff(v.value as number)),
})) }))
const varMentionsExtension = varMentions(varMentionData)
const { setContainer } = useCodeMirror({ const { setContainer } = useCodeMirror({
container: editorRef.current, container: editorRef.current,
@ -112,23 +119,40 @@ function CommandBarKclInput({
? getSystemTheme() ? getSystemTheme()
: settings.context.app.theme.current, : settings.context.app.theme.current,
extensions: [ extensions: [
EditorView.domEventHandlers({ varMentionsExtension,
keydown: (event) => {
if (event.key === 'Backspace' && value === '') {
event.preventDefault()
stepBack()
} else if (event.key === 'Enter') {
event.preventDefault()
handleSubmit()
}
},
}),
varMentions(varMentionData),
EditorView.updateListener.of((vu: ViewUpdate) => { EditorView.updateListener.of((vu: ViewUpdate) => {
if (vu.docChanged) { if (vu.docChanged) {
setValue(vu.state.doc.toString()) setValue(vu.state.doc.toString())
} }
}), }),
closeBrackets(),
keymap.of([
...closeBracketsKeymap,
...completionKeymap,
{
key: 'Enter',
run: (editor) => {
// Only submit if there is no completion active
if (completionStatus(editor.state) === null) {
handleSubmit()
return true
} else {
return false
}
},
},
{
key: 'Backspace',
run: (editor) => {
// Only step back if the editor is empty
if (editor.state.doc.toString() === '') {
stepBack()
return true
}
return false
},
},
]),
], ],
}) })
@ -227,7 +251,7 @@ function CommandBarKclInput({
} }
}} }}
onKeyUp={(e) => { onKeyUp={(e) => {
if (e.key === 'Enter') { if (e.key === 'Enter' && canSubmit) {
handleSubmit() handleSubmit()
} }
}} }}

View File

@ -75,6 +75,7 @@ function CommandBarTextareaInput({
target.selectionStart = selectionStart + 1 target.selectionStart = selectionStart + 1
target.selectionEnd = selectionStart + 1 target.selectionEnd = selectionStart + 1
} else if (event.key === 'Enter') { } else if (event.key === 'Enter') {
event.preventDefault()
formRef.current?.dispatchEvent( formRef.current?.dispatchEvent(
new Event('submit', { bubbles: true }) new Event('submit', { bubbles: true })
) )

View File

@ -392,6 +392,26 @@ const CustomIconMap = {
/> />
</svg> </svg>
), ),
eyeOpen: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10 14.5C5.58172 14.5 3 10.5 3 10.5C3 10.5 5.58172 5.5 10 5.5C14.4183 5.5 17 10.5 17 10.5C17 10.5 14.4183 14.5 10 14.5ZM4.24209 10.4865L4.19946 10.4348C4.23234 10.3833 4.26774 10.3288 4.30565 10.2717C4.59304 9.83862 5.01753 9.26342 5.56519 8.69184C6.67884 7.52958 8.18459 6.5 10 6.5C11.8154 6.5 13.3212 7.52958 14.4348 8.69184C14.9825 9.26342 15.407 9.83862 15.6944 10.2717C15.7323 10.3288 15.7677 10.3833 15.8005 10.4348L15.7579 10.4865C15.4766 10.8257 15.0582 11.2796 14.516 11.7324C13.4249 12.6433 11.8946 13.5 10 13.5C8.10539 13.5 6.57507 12.6433 5.48405 11.7324C4.9418 11.2796 4.52342 10.8257 4.24209 10.4865ZM12 10C12 11.1046 11.1046 12 10 12C8.89543 12 8 11.1046 8 10C8 8.89543 8.89543 8 10 8C11.1046 8 12 8.89543 12 10ZM13 10C13 11.6569 11.6569 13 10 13C8.34315 13 7 11.6569 7 10C7 8.34315 8.34315 7 10 7C11.6569 7 13 8.34315 13 10Z"
fill="currentColor"
/>
</svg>
),
eyeCrossedOut: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.35352 5.39647L14.253 15.296L14.9601 14.5889L5.06062 4.68936L4.35352 5.39647ZM10.9303 13.4303L11.7785 14.2785C11.2246 14.4184 10.631 14.5 10 14.5C5.58172 14.5 3 10.5 3 10.5C3 10.5 3.76379 9.02078 5.17147 7.67148L5.8787 8.37872C5.771 8.48155 5.66647 8.58616 5.56519 8.69186C5.01753 9.26343 4.59304 9.83863 4.30565 10.2717C4.26774 10.3288 4.23234 10.3833 4.19946 10.4348L4.24209 10.4866C4.52342 10.8257 4.9418 11.2797 5.48405 11.7324C6.57507 12.6433 8.10539 13.5 10 13.5C10.3206 13.5 10.6309 13.4755 10.9303 13.4303ZM10 5.50001C9.16896 5.50001 8.4029 5.6769 7.70677 5.96414L8.48545 6.74282C8.96231 6.58848 9.46785 6.50001 10 6.50001C11.8154 6.50001 13.3212 7.52959 14.4348 8.69186C14.9825 9.26343 15.407 9.83863 15.6944 10.2717C15.7323 10.3288 15.7677 10.3833 15.8005 10.4348L15.7579 10.4866C15.4766 10.8257 15.0582 11.2797 14.516 11.7324C14.3321 11.8859 14.1357 12.0379 13.9272 12.1845L14.6438 12.9011C16.1692 11.7871 17 10.5 17 10.5C17 10.5 14.4183 5.50001 10 5.50001ZM10 7.00001C9.62554 7.00001 9.2671 7.06862 8.93658 7.19395L9.75723 8.0146C9.8368 8.00497 9.91782 8.00001 10 8.00001C11.1046 8.00001 12 8.89544 12 10C12 10.0822 11.995 10.1632 11.9854 10.2428L12.8061 11.0634C12.9314 10.7329 13 10.3745 13 10C13 8.34316 11.6569 7.00001 10 7.00001ZM7 10C7 9.8421 7.0122 9.68704 7.03571 9.53572L8.08776 10.5878C8.28175 11.2197 8.78035 11.7183 9.41224 11.9123L10.4643 12.9643C10.313 12.9878 10.1579 13 10 13C8.34315 13 7 11.6569 7 10Z"
fill="currentColor"
/>
</svg>
),
fillet: ( fillet: (
<svg <svg
viewBox="0 0 20 20" viewBox="0 0 20 20"
@ -533,6 +553,16 @@ const CustomIconMap = {
/> />
</svg> </svg>
), ),
hollow: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M5.67443 5.38863L7.24585 3.87359L6.55986 3.15721L4.51827 5.12555L4.5391 5.14731L4.51129 5.14139V6.14139V13.9073V14.9073L5.48944 15.1152L12.5048 16.6064L13.4829 16.8143V16.7896L13.5043 16.8119L15.5459 14.8436L14.8599 14.1272L13.4829 15.4548V8.04838V7.63961L13.5043 7.66193L15.5459 5.69359L14.8599 4.97721L12.851 6.91405L12.5048 6.84046L5.67443 5.38863ZM12.5048 7.84046L5.48944 6.34931V14.1152L12.5048 15.6064V7.84046ZM6.21381 8.04101V8.51384V8.54101L7.1075 8.73098V8.7038L7.19195 8.72175V7.74893L7.1075 7.73098L6.70288 7.64497L6.21381 7.54101V8.04101ZM6.21381 12.4051V12.3779L7.1075 12.5679V12.5951L7.19195 12.613V13.5859L7.1075 13.5679L6.70288 13.4819L6.21381 13.3779V12.8779V12.4051ZM10.6823 14.3277L10.5978 14.3098V13.337L10.6823 13.3549V13.3277L11.576 13.5177V13.5449V14.0177V14.5177L11.0869 14.4138L10.6823 14.3277ZM11.576 9.6536V9.68078L10.6823 9.49082V9.46364L10.5978 9.44569V8.47287L10.6823 8.49082L11.0869 8.57683L11.576 8.68078V9.18078V9.6536ZM8.0012 8.42094V7.92094L9.7886 8.30086V8.80086V9.30086L8.0012 8.92094V8.42094ZM11.0869 10.5225L11.576 10.6264V12.5721L11.0869 12.4681L10.5978 12.3642V10.4185L11.0869 10.5225ZM9.7886 13.6378V14.1378L8.0012 13.7579V13.2579V12.7579L9.7886 13.1378V13.6378ZM6.70288 11.5363L6.21381 11.4323V9.48666L6.70288 9.59061L7.19195 9.69457V11.6402L6.70288 11.5363Z"
fill="currentColor"
/>
</svg>
),
horizontal: ( horizontal: (
<svg <svg
viewBox="0 0 20 20" viewBox="0 0 20 20"
@ -563,6 +593,22 @@ const CustomIconMap = {
/> />
</svg> </svg>
), ),
import: (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M9.64645 12.3535L10 12.7071L10.3536 12.3535L13.8536 8.85352L13.1464 8.14642L10.5 10.7929L10.5 3H9.5L9.5 10.7929L6.85355 8.14642L6.14645 8.85352L9.64645 12.3535ZM15 5H12.4999V4H15H16V5V15V16H15H5H4V15V5V4H5H7.49988V5H5V15H15V5Z"
fill="currentColor"
/>
</svg>
),
'intersection-offset': ( 'intersection-offset': (
<svg <svg
viewBox="0 0 20 20" viewBox="0 0 20 20"
@ -741,6 +787,16 @@ const CustomIconMap = {
/> />
</svg> </svg>
), ),
model: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.91773 10L10 6.89417L15.0823 10L10 13.1058L4.91773 10ZM10 5.72222L16.0411 9.41403L17 10L17 12.0541H16V10.6111L10.5 13.9722V16.0541H9.5V13.9722L4 10.6111V12.0541H3V10L3.95886 9.41403L10 5.72222Z"
fill="currentColor"
/>
</svg>
),
move: ( move: (
<svg <svg
viewBox="0 0 20 20" viewBox="0 0 20 20"
@ -801,6 +857,58 @@ const CustomIconMap = {
/> />
</svg> </svg>
), ),
patternCircular2d: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11 4C11 4.55228 10.5523 5 10 5C9.44772 5 9 4.55228 9 4C9 3.44772 9.44772 3 10 3C10.5523 3 11 3.44772 11 4ZM12 4C12 5.10457 11.1046 6 10 6C8.89543 6 8 5.10457 8 4C8 2.89543 8.89543 2 10 2C11.1046 2 12 2.89543 12 4ZM16 7C16 7.55228 15.5523 8 15 8C14.4477 8 14 7.55228 14 7C14 6.44772 14.4477 6 15 6C15.5523 6 16 6.44772 16 7ZM17 7C17 8.10457 16.1046 9 15 9C13.8954 9 13 8.10457 13 7C13 5.89543 13.8954 5 15 5C16.1046 5 17 5.89543 17 7ZM15 14C15.5523 14 16 13.5523 16 13C16 12.4477 15.5523 12 15 12C14.4477 12 14 12.4477 14 13C14 13.5523 14.4477 14 15 14ZM15 15C16.1046 15 17 14.1046 17 13C17 11.8954 16.1046 11 15 11C13.8954 11 13 11.8954 13 13C13 14.1046 13.8954 15 15 15ZM11 16C11 16.5523 10.5523 17 10 17C9.44772 17 9 16.5523 9 16C9 15.4477 9.44772 15 10 15C10.5523 15 11 15.4477 11 16ZM12 16C12 17.1046 11.1046 18 10 18C8.89543 18 8 17.1046 8 16C8 14.8954 8.89543 14 10 14C11.1046 14 12 14.8954 12 16ZM5 14C5.55228 14 6 13.5523 6 13C6 12.4477 5.55228 12 5 12C4.44772 12 4 12.4477 4 13C4 13.5523 4.44772 14 5 14ZM5 15C6.10457 15 7 14.1046 7 13C7 11.8954 6.10457 11 5 11C3.89543 11 3 11.8954 3 13C3 14.1046 3.89543 15 5 15ZM6 7C6 7.55228 5.55228 8 5 8C4.44772 8 4 7.55228 4 7C4 6.44772 4.44772 6 5 6C5.55228 6 6 6.44772 6 7ZM7 7C7 8.10457 6.10457 9 5 9C3.89543 9 3 8.10457 3 7C3 5.89543 3.89543 5 5 5C6.10457 5 7 5.89543 7 7Z"
fill="currentColor"
/>
</svg>
),
patternCircular3d: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11 4C11 4.55228 10.5523 5 10 5C9.44772 5 9 4.55228 9 4C9 3.44772 9.44772 3 10 3C10.5523 3 11 3.44772 11 4ZM12 4C12 5.10457 11.1046 6 10 6C8.89543 6 8 5.10457 8 4C8 2.89543 8.89543 2 10 2C11.1046 2 12 2.89543 12 4ZM16 7C16 7.55228 15.5523 8 15 8C14.4477 8 14 7.55228 14 7C14 6.44772 14.4477 6 15 6C15.5523 6 16 6.44772 16 7ZM17 7C17 8.10457 16.1046 9 15 9C13.8954 9 13 8.10457 13 7C13 5.89543 13.8954 5 15 5C16.1046 5 17 5.89543 17 7ZM15 14C15.5523 14 16 13.5523 16 13C16 12.4477 15.5523 12 15 12C14.4477 12 14 12.4477 14 13C14 13.5523 14.4477 14 15 14ZM15 15C16.1046 15 17 14.1046 17 13C17 11.8954 16.1046 11 15 11C13.8954 11 13 11.8954 13 13C13 14.1046 13.8954 15 15 15ZM11 16C11 16.5523 10.5523 17 10 17C9.44772 17 9 16.5523 9 16C9 15.4477 9.44772 15 10 15C10.5523 15 11 15.4477 11 16ZM12 16C12 17.1046 11.1046 18 10 18C8.89543 18 8 17.1046 8 16C8 14.8954 8.89543 14 10 14C11.1046 14 12 14.8954 12 16ZM5 14C5.55228 14 6 13.5523 6 13C6 12.4477 5.55228 12 5 12C4.44772 12 4 12.4477 4 13C4 13.5523 4.44772 14 5 14ZM5 15C6.10457 15 7 14.1046 7 13C7 11.8954 6.10457 11 5 11C3.89543 11 3 11.8954 3 13C3 14.1046 3.89543 15 5 15ZM6 7C6 7.55228 5.55228 8 5 8C4.44772 8 4 7.55228 4 7C4 6.44772 4.44772 6 5 6C5.55228 6 6 6.44772 6 7ZM7 7C7 8.10457 6.10457 9 5 9C3.89543 9 3 8.10457 3 7C3 5.89543 3.89543 5 5 5C6.10457 5 7 5.89543 7 7Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11.642 5.1421C12.1606 4.78075 12.5 4.18001 12.5 3.5C12.5 2.39543 11.6046 1.5 10.5 1.5C9.82004 1.5 9.21933 1.83933 8.85797 2.35789C9.18176 2.13228 9.57543 1.99999 9.99999 1.99999C11.1046 1.99999 12 2.89542 12 3.99999C12 4.42459 11.8677 4.81829 11.642 5.1421ZM16.642 8.1421C17.1606 7.78075 17.5 7.18001 17.5 6.5C17.5 5.39543 16.6046 4.5 15.5 4.5C14.82 4.5 14.2193 4.83933 13.858 5.35789C14.1818 5.13228 14.5754 4.99999 15 4.99999C16.1046 4.99999 17 5.89542 17 6.99999C17 7.42459 16.8677 7.81829 16.642 8.1421ZM13.858 11.3579C14.1818 11.1323 14.5754 11 15 11C16.1046 11 17 11.8954 17 13C17 13.4246 16.8677 13.8183 16.642 14.1421C17.1606 13.7808 17.5 13.18 17.5 12.5C17.5 11.3954 16.6046 10.5 15.5 10.5C14.82 10.5 14.2193 10.8393 13.858 11.3579ZM11.642 17.1421C12.1606 16.7808 12.5 16.18 12.5 15.5C12.5 14.3954 11.6046 13.5 10.5 13.5C9.82004 13.5 9.21933 13.8393 8.85797 14.3579C9.18176 14.1323 9.57543 14 9.99999 14C11.1046 14 12 14.8954 12 16C12 16.4246 11.8677 16.8183 11.642 17.1421ZM6.64202 14.1421C7.16064 13.7808 7.50001 13.18 7.50001 12.5C7.50001 11.3954 6.60458 10.5 5.50001 10.5C4.82004 10.5 4.21933 10.8393 3.85797 11.3579C4.18176 11.1323 4.57543 11 4.99999 11C6.10456 11 6.99999 11.8954 6.99999 13C6.99999 13.4246 6.86767 13.8183 6.64202 14.1421ZM6.64202 8.1421C7.16064 7.78075 7.50001 7.18001 7.50001 6.5C7.50001 5.39543 6.60458 4.5 5.50001 4.5C4.82004 4.5 4.21933 4.83933 3.85797 5.35789C4.18176 5.13228 4.57543 4.99999 4.99999 4.99999C6.10456 4.99999 6.99999 5.89542 6.99999 6.99999C6.99999 7.42459 6.86767 7.81829 6.64202 8.1421Z"
fill="currentColor"
/>
</svg>
),
patternLinear2d: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4 4H6V6H4V4ZM3 3H4H6H7V4V6V7H6H4H3V6V4V3ZM4 9H6V11H4V9ZM3 8H4H6H7V9V11V12H6H4H3V11V9V8ZM6 14H4V16H6V14ZM4 13H3V14V16V17H4H6H7V16V14V13H6H4ZM9 4H11V6H9V4ZM8 3H9H11H12V4V6V7H11H9H8V6V4V3ZM11 9H9V11H11V9ZM9 8H8V9V11V12H9H11H12V11V9V8H11H9ZM14 4H16V6H14V4ZM13 3H14H16H17V4V6V7H16H14H13V6V4V3Z"
fill="currentColor"
/>
</svg>
),
patternLinear3d: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4 4H6V6H4V4ZM3 3H4H6H7V4V6V7H6H4H3V6V4V3ZM4 9H6V11H4V9ZM3 8H4H6H7V9V11V12H6H4H3V11V9V8ZM6 14H4V16H6V14ZM4 13H3V14V16V17H4H6H7V16V14V13H6H4ZM9 4H11V6H9V4ZM8 3H9H11H12V4V6V7H11H9H8V6V4V3ZM11 9H9V11H11V9ZM9 8H8V9V11V12H9H11H12V11V9V8H11H9ZM14 4H16V6H14V4ZM13 3H14H16H17V4V6V7H16H14H13V6V4V3Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.5 2.5H3.5V3H7V6.5H7.5V5.5V3.5V2.5H6.5H4.5ZM4.5 7.5H3.5V8H7V11.5H7.5V10.5V8.5V7.5H6.5H4.5ZM3.5 12.5H4.5H6.5H7.5V13.5V15.5V16.5H7V13H3.5V12.5ZM9.5 2.5H8.5V3H12V6.5H12.5V5.5V3.5V2.5H11.5H9.5ZM8.5 7.5H9.5H11.5H12.5V8.5V10.5V11.5H12V8H8.5V7.5ZM14.5 2.5H13.5V3H17V6.5H17.5V5.5V3.5V2.5H16.5H14.5Z"
fill="currentColor"
/>
</svg>
),
person: ( person: (
<svg <svg
viewBox="0 0 20 20" viewBox="0 0 20 20"

View File

@ -13,7 +13,11 @@ import { engineCommandManager } from '../lib/singletons'
import { Spinner } from './Spinner' import { Spinner } from './Spinner'
const Loading = ({ children }: React.PropsWithChildren) => { interface LoadingProps extends React.PropsWithChildren {
className?: string
}
const Loading = ({ children, className }: LoadingProps) => {
const [error, setError] = useState<ConnectionError>(ConnectionError.Unset) const [error, setError] = useState<ConnectionError>(ConnectionError.Unset)
useEffect(() => { useEffect(() => {
@ -64,7 +68,7 @@ const Loading = ({ children }: React.PropsWithChildren) => {
return ( return (
<div <div
className="body-bg flex flex-col items-center justify-center h-screen" className={`body-bg flex flex-col items-center justify-center h-screen ${className}`}
data-testid="loading" data-testid="loading"
> >
<Spinner /> <Spinner />

View File

@ -76,20 +76,18 @@ import {
resultIsOk, resultIsOk,
} from 'lang/wasm' } from 'lang/wasm'
import { import {
artifactIsPlaneWithPaths,
doesSketchPipeNeedSplitting, doesSketchPipeNeedSplitting,
getNodeFromPath, getNodeFromPath,
isCursorInFunctionDefinition, isCursorInFunctionDefinition,
traverse,
} from 'lang/queryAst' } from 'lang/queryAst'
import { exportFromEngine } from 'lib/exportFromEngine' 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 { useLoaderData, 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 { err, reportRejection, trap, reject } from 'lib/trap' import { err, reportRejection, trap, reject } from 'lib/trap'
import { useCommandsContext } from 'hooks/useCommandsContext' import { useCommandsContext } from 'hooks/useCommandsContext'
import { modelingMachineEvent } from 'editor/manager'
import { import {
ExportIntent, ExportIntent,
EngineConnectionStateType, EngineConnectionStateType,
@ -105,6 +103,7 @@ import {
getPlaneFromArtifact, getPlaneFromArtifact,
} from 'lang/std/artifactGraph' } from 'lang/std/artifactGraph'
import { promptToEditFlow } from 'lib/promptToEdit' import { promptToEditFlow } from 'lib/promptToEdit'
import { kclEditorActor } from 'machines/kclEditorMachine'
type MachineContext<T extends AnyStateMachine> = { type MachineContext<T extends AnyStateMachine> = {
state: StateFrom<T> state: StateFrom<T>
@ -314,22 +313,6 @@ export const ModelingMachineProvider = ({
null null
if (!setSelections) return {} if (!setSelections) return {}
const dispatchSelection = (selection?: EditorSelection) => {
if (!selection) return // TODO less of hack for the below please
if (!editorManager.editorView) return
setTimeout(() => {
if (!editorManager.editorView) return
editorManager.editorView.dispatch({
selection,
annotations: [
modelingMachineEvent,
Transaction.addToHistory.of(false),
],
})
})
}
let selections: Selections = { let selections: Selections = {
graphSelections: [], graphSelections: [],
otherSelections: [], otherSelections: [],
@ -369,7 +352,15 @@ export const ModelingMachineProvider = ({
} = handleSelectionBatch({ } = handleSelectionBatch({
selections, selections,
}) })
codeMirrorSelection && dispatchSelection(codeMirrorSelection) if (codeMirrorSelection) {
kclEditorActor.send({
type: 'setLastSelectionEvent',
data: {
codeMirrorSelection,
scrollIntoView: setSelections.scrollIntoView ?? false,
},
})
}
engineEvents && engineEvents &&
engineEvents.forEach((event) => { engineEvents.forEach((event) => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises // eslint-disable-next-line @typescript-eslint/no-floating-promises
@ -579,6 +570,9 @@ export const ModelingMachineProvider = ({
'Selection is on face': ({ context: { selectionRanges }, event }) => { 'Selection is on face': ({ context: { selectionRanges }, event }) => {
if (event.type !== 'Enter sketch') return false if (event.type !== 'Enter sketch') return false
if (event.data?.forceNewSketch) return false if (event.data?.forceNewSketch) return false
if (artifactIsPlaneWithPaths(selectionRanges)) {
return true
}
if ( if (
isCursorInFunctionDefinition( isCursorInFunctionDefinition(
kclManager.ast, kclManager.ast,

View File

@ -0,0 +1,381 @@
import { Diagnostic } from '@codemirror/lint'
import { useMachine, useSelector } from '@xstate/react'
import { ContextMenu, ContextMenuItem } from 'components/ContextMenu'
import { CustomIcon, CustomIconName } from 'components/CustomIcon'
import Loading from 'components/Loading'
import { useModelingContext } from 'hooks/useModelingContext'
import { useKclContext } from 'lang/KclProvider'
import { codeRefFromRange, getArtifactFromRange } from 'lang/std/artifactGraph'
import { sourceRangeFromRust } from 'lang/wasm'
import {
filterOperations,
getOperationIcon,
getOperationLabel,
} from 'lib/operations'
import { editorManager, engineCommandManager, kclManager } from 'lib/singletons'
import { ComponentProps, useEffect, useMemo, useRef, useState } from 'react'
import { Operation } from 'wasm-lib/kcl/bindings/Operation'
import { Actor, Prop } from 'xstate'
import { featureTreeMachine } from 'machines/featureTreeMachine'
import {
editorIsMountedSelector,
kclEditorActor,
selectionEventSelector,
} from 'machines/kclEditorMachine'
export const FeatureTreePane = () => {
const isEditorMounted = useSelector(kclEditorActor, editorIsMountedSelector)
const lastSelectionEvent = useSelector(kclEditorActor, selectionEventSelector)
const { send: modelingSend, state: modelingState } = useModelingContext()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_featureTreeState, featureTreeSend] = useMachine(
featureTreeMachine.provide({
guards: {
codePaneIsOpen: () =>
modelingState.context.store.openPanes.includes('code') &&
editorManager.editorView !== null,
},
actions: {
openCodePane: () => {
modelingSend({
type: 'Set context',
data: {
openPanes: [...modelingState.context.store.openPanes, 'code'],
},
})
},
sendEditFlowStart: () => {
modelingSend({ type: 'Enter sketch' })
},
scrollToError: () => {
editorManager.scrollToFirstErrorDiagnosticIfExists()
},
sendSelectionEvent: ({ context }) => {
if (!context.targetSourceRange) {
return
}
const artifact = context.targetSourceRange
? getArtifactFromRange(
context.targetSourceRange,
engineCommandManager.artifactGraph
)
: null
if (!artifact || !('codeRef' in artifact)) {
modelingSend({
type: 'Set selection',
data: {
selectionType: 'singleCodeCursor',
selection: {
codeRef: codeRefFromRange(
context.targetSourceRange,
kclManager.ast
),
},
scrollIntoView: true,
},
})
} else {
modelingSend({
type: 'Set selection',
data: {
selectionType: 'singleCodeCursor',
selection: {
artifact: artifact,
codeRef: codeRefFromRange(
context.targetSourceRange,
kclManager.ast
),
},
scrollIntoView: true,
},
})
}
},
},
})
)
// If there are parse errors we show the last successful operations
// and overlay a message on top of the pane
const parseErrors = kclManager.errors.filter((e) => e.kind !== 'engine')
// If there are engine errors we show the successful operations
// Errors return an operation list, so use the longest one if there are multiple
const longestErrorOperationList = kclManager.errors.reduce((acc, error) => {
return error.operations && error.operations.length > acc.length
? error.operations
: acc
}, [] as Operation[])
const unfilteredOperationList = !parseErrors.length
? !kclManager.errors.length
? kclManager.execState.operations
: longestErrorOperationList
: kclManager.lastSuccessfulOperations
// We filter out operations that are not useful to show in the feature tree
const operationList = filterOperations(unfilteredOperationList)
// Watch for changes in the open panes and send an event to the feature tree machine
useEffect(() => {
const codeOpen = modelingState.context.store.openPanes.includes('code')
if (codeOpen && isEditorMounted) {
featureTreeSend({ type: 'codePaneOpened' })
}
}, [modelingState.context.store.openPanes, isEditorMounted])
// Watch for changes in the selection and send an event to the feature tree machine
useEffect(() => {
featureTreeSend({ type: 'selected' })
}, [lastSelectionEvent])
function goToError() {
featureTreeSend({ type: 'goToError' })
}
return (
<div className="relative">
<section
data-testid="debug-panel"
className="absolute inset-0 p-1 box-border overflow-auto"
>
{kclManager.isExecuting ? (
<Loading className="h-full">Building feature tree...</Loading>
) : (
<>
{parseErrors.length > 0 && (
<div
className={`absolute inset-0 rounded-lg p-2 ${
operationList.length &&
`bg-destroy-10/40 dark:bg-destroy-80/40`
}`}
>
<div className="text-sm bg-destroy-80 text-chalkboard-10 py-1 px-2 rounded flex gap-2 items-center">
<p className="flex-1">
Errors found in KCL code.
<br />
Please fix them before continuing.
</p>
<button
onClick={goToError}
className="bg-chalkboard-10 text-destroy-80 p-1 rounded-sm flex-none hover:bg-chalkboard-10 hover:border-destroy-70 hover:text-destroy-80 border-transparent"
>
View error
</button>
</div>
</div>
)}
{operationList.map((operation) => {
const key = `${operation.type}-${
'name' in operation ? operation.name : 'anonymous'
}-${
'sourceRange' in operation ? operation.sourceRange[0] : 'start'
}`
return (
<OperationItem
key={key}
item={operation}
send={featureTreeSend}
/>
)
})}
</>
)}
</section>
</div>
)
}
export const visibilityMap = new Map<string, boolean>()
interface VisibilityToggleProps {
entityId: string
initialVisibility: boolean
onVisibilityChange?: () => void
}
/**
* A button that toggles the visibility of an entity
* tied to an artifact in the feature tree.
* TODO: this is unimplemented and will be used for
* default planes after we fix them and add them to the artifact graph / feature tree
*/
const VisibilityToggle = (props: VisibilityToggleProps) => {
const [visible, setVisible] = useState(props.initialVisibility)
function handleToggleVisible() {
setVisible(!visible)
visibilityMap.set(props.entityId, !visible)
props.onVisibilityChange?.()
}
return (
<button
onClick={handleToggleVisible}
className="border-transparent p-0 m-0"
>
<CustomIcon
name={visible ? 'eyeOpen' : 'eyeCrossedOut'}
className={`w-5 h-5 ${
visible
? 'hidden group-hover/item:block group-focus-within/item:block'
: 'text-chalkboard-50'
}`}
/>
</button>
)
}
/**
* More generic version of OperationListItem,
* to be used for default planes after we fix them and
* add them to the artifact graph / feature tree
*/
const OperationItemWrapper = ({
icon,
name,
visibilityToggle,
menuItems,
errors,
className,
...props
}: React.HTMLAttributes<HTMLButtonElement> & {
icon: CustomIconName
name: string
visibilityToggle?: VisibilityToggleProps
menuItems?: ComponentProps<typeof ContextMenu>['items']
errors?: Diagnostic[]
}) => {
const menuRef = useRef<HTMLDivElement>(null)
return (
<div
ref={menuRef}
className="flex select-none items-center group/item my-0 py-0.5 px-1 focus-within:bg-primary/10 hover:bg-primary/5"
>
<button
{...props}
className={`reset flex-1 flex items-center gap-2 border-transparent dark:border-transparent text-left text-base ${className}`}
>
<CustomIcon name={icon} className="w-5 h-5 block" />
{name}
</button>
{errors && errors.length > 0 && (
<em className="text-destroy-80 text-xs">has error</em>
)}
{visibilityToggle && <VisibilityToggle {...visibilityToggle} />}
{menuItems && (
<ContextMenu menuTargetElement={menuRef} items={menuItems} />
)}
</div>
)
}
/**
* A button with an icon, name, and context menu
* for an operation in the feature tree.
*/
const OperationItem = (props: {
item: Operation
send: Prop<Actor<typeof featureTreeMachine>, 'send'>
}) => {
const kclContext = useKclContext()
const name =
'name' in props.item && props.item.name !== null
? getOperationLabel(props.item)
: 'anonymous'
const errors = useMemo(() => {
return kclContext.diagnostics.filter(
(diag) =>
diag.severity === 'error' &&
'sourceRange' in props.item &&
diag.from >= props.item.sourceRange[0] &&
diag.to <= props.item.sourceRange[1]
)
}, [kclContext.diagnostics.length])
function selectOperation() {
if (props.item.type === 'UserDefinedFunctionReturn') {
return
}
props.send({
type: 'selectOperation',
data: {
targetSourceRange: sourceRangeFromRust(props.item.sourceRange),
},
})
}
/**
* For now we can only enter the "edit" flow for the startSketchOn operation.
* TODO: https://github.com/KittyCAD/modeling-app/issues/4442
*/
function enterEditFlow() {
if (
props.item.type === 'StdLibCall' &&
props.item.name === 'startSketchOn'
) {
props.send({
type: 'enterEditFlow',
data: {
targetSourceRange: sourceRangeFromRust(props.item.sourceRange),
},
})
}
}
const menuItems = useMemo(
() => [
<ContextMenuItem
onClick={() => {
if (props.item.type === 'UserDefinedFunctionReturn') {
return
}
props.send({
type: 'goToKclSource',
data: {
targetSourceRange: sourceRangeFromRust(props.item.sourceRange),
},
})
}}
>
View KCL source code
</ContextMenuItem>,
...(props.item.type === 'UserDefinedFunctionCall'
? [
<ContextMenuItem
onClick={() => {
if (props.item.type !== 'UserDefinedFunctionCall') {
return
}
const functionRange = props.item.functionSourceRange
// For some reason, the cursor goes to the end of the source
// range we select. So set the end equal to the beginning.
functionRange[1] = functionRange[0]
props.send({
type: 'goToKclSource',
data: {
targetSourceRange: sourceRangeFromRust(functionRange),
},
})
}}
>
View function definition
</ContextMenuItem>,
]
: []),
],
[props.item, props.send]
)
return (
<OperationItemWrapper
icon={getOperationIcon(props.item)}
name={name}
menuItems={menuItems}
onClick={selectOperation}
onDoubleClick={enterEditFlow}
errors={errors}
/>
)
}

Some files were not shown because too many files have changed in this diff Show More