diff --git a/.env.development b/.env.development index 47ce20bfc..7b474799a 100644 --- a/.env.development +++ b/.env.development @@ -7,7 +7,6 @@ VITE_KC_API_WS_MODELING_URL=wss://api.dev.zoo.dev/ws/modeling/commands VITE_KC_API_BASE_URL=https://api.dev.zoo.dev VITE_KC_SITE_BASE_URL=https://dev.zoo.dev VITE_KC_SITE_APP_URL=https://app.dev.zoo.dev -VITE_KC_SKIP_AUTH=false VITE_KC_CONNECTION_TIMEOUT_MS=5000 #VITE_WASM_URL="optional way of overriding the wasm url, particular for unit tests which need this if you running not on the default 3000 port" #VITE_KC_DEV_TOKEN="optional token to skip auth in the app" diff --git a/.env.production b/.env.production index 6adcecc5d..49aee03e9 100644 --- a/.env.production +++ b/.env.production @@ -3,5 +3,4 @@ VITE_KC_API_WS_MODELING_URL=wss://api.zoo.dev/ws/modeling/commands VITE_KC_API_BASE_URL=https://api.zoo.dev VITE_KC_SITE_BASE_URL=https://zoo.dev VITE_KC_SITE_APP_URL=https://app.zoo.dev -VITE_KC_SKIP_AUTH=false VITE_KC_CONNECTION_TIMEOUT_MS=15000 diff --git a/.github/workflows/build-apps.yml b/.github/workflows/build-apps.yml index 839ec313f..fc0aace03 100644 --- a/.github/workflows/build-apps.yml +++ b/.github/workflows/build-apps.yml @@ -43,7 +43,7 @@ jobs: - name: Download Wasm Cache id: download-wasm if: ${{ github.event_name == 'pull_request' && steps.filter.outputs.rust == 'false' }} - uses: dawidd6/action-download-artifact@v7 + uses: dawidd6/action-download-artifact@v11 continue-on-error: true with: github_token: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/cargo-test.yml b/.github/workflows/cargo-test.yml index 8eb8fb446..d38867655 100644 --- a/.github/workflows/cargo-test.yml +++ b/.github/workflows/cargo-test.yml @@ -25,8 +25,8 @@ jobs: - runner=8cpu-linux-x64 - extras=s3-cache steps: - - uses: runs-on/action@v1 - - uses: actions/create-github-app-token@v1 + - uses: runs-on/action@v2 + - uses: actions/create-github-app-token@v2 id: app-token with: app-id: ${{ secrets.MODELING_APP_GH_APP_ID }} @@ -149,8 +149,8 @@ jobs: partitionIndex: [1, 2, 3, 4, 5, 6] partitionTotal: [6] steps: - - uses: runs-on/action@v1 - - uses: actions/create-github-app-token@v1 + - uses: runs-on/action@v2 + - uses: actions/create-github-app-token@v2 id: app-token with: app-id: ${{ secrets.MODELING_APP_GH_APP_ID }} @@ -207,8 +207,8 @@ jobs: - runner=32cpu-linux-x64 - extras=s3-cache steps: - - uses: runs-on/action@v1 - - uses: actions/create-github-app-token@v1 + - uses: runs-on/action@v2 + - uses: actions/create-github-app-token@v2 id: app-token with: app-id: ${{ secrets.MODELING_APP_GH_APP_ID }} diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 32cee74b9..b173db5db 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -46,7 +46,7 @@ jobs: - name: Download Wasm cache id: download-wasm if: ${{ github.event_name != 'schedule' && steps.filter.outputs.rust == 'false' }} - uses: dawidd6/action-download-artifact@v7 + uses: dawidd6/action-download-artifact@v11 continue-on-error: true with: github_token: ${{secrets.GITHUB_TOKEN}} @@ -110,7 +110,7 @@ jobs: steps: - - uses: actions/create-github-app-token@v1 + - uses: actions/create-github-app-token@v2 id: app-token with: app-id: ${{ secrets.MODELING_APP_GH_APP_ID }} @@ -230,7 +230,7 @@ jobs: steps: - - uses: actions/create-github-app-token@v1 + - uses: actions/create-github-app-token@v2 id: app-token with: app-id: ${{ secrets.MODELING_APP_GH_APP_ID }} diff --git a/.github/workflows/generate-website-docs.yml b/.github/workflows/generate-website-docs.yml index d5cd67609..c3f209101 100644 --- a/.github/workflows/generate-website-docs.yml +++ b/.github/workflows/generate-website-docs.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/create-github-app-token@v1 + - uses: actions/create-github-app-token@v2 id: app-token with: # required diff --git a/.github/workflows/kcl-python-bindings.yml b/.github/workflows/kcl-python-bindings.yml index 2a31bb257..2cd0e9011 100644 --- a/.github/workflows/kcl-python-bindings.yml +++ b/.github/workflows/kcl-python-bindings.yml @@ -113,7 +113,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v6 - uses: actions-rust-lang/setup-rust-toolchain@v1 - uses: taiki-e/install-action@just - name: Run tests @@ -130,7 +130,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install the latest version of uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v6 - name: Install codespell run: | uv venv .venv @@ -161,7 +161,7 @@ jobs: with: path: rust/kcl-python-bindings - name: Install the latest version of uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v6 - name: do uv things run: | cd rust/kcl-python-bindings diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 12805fd5d..bc07de134 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -120,6 +120,36 @@ jobs: - run: npm run circular-deps:diff + npm-url-checker: + runs-on: ubuntu-latest + needs: npm-build-wasm + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'npm' + - run: npm install + + - name: Download all artifacts + uses: actions/download-artifact@v4 + + - name: Copy prepared wasm + run: | + ls -R prepared-wasm + cp prepared-wasm/kcl_wasm_lib_bg.wasm public + mkdir rust/kcl-wasm-lib/pkg + cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg + + - name: Copy prepared ts-rs bindings + run: | + ls -R prepared-ts-rs-bindings + mkdir rust/kcl-lib/bindings + cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/ + + - run: npm run url-checker:diff + python-codespell: runs-on: ubuntu-22.04 steps: diff --git a/.gitignore b/.gitignore index 25e5edc45..562e471c2 100644 --- a/.gitignore +++ b/.gitignore @@ -87,4 +87,4 @@ venv .vscode-test .biome/ -.million +.million \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6427419d3..d124a1bf1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -235,6 +235,47 @@ To display logging (to the terminal or console) set `ZOO_LOG=1`. This will log s To enable memory metrics, build with `--features dhat-heap`. +## Running scripts + +There are multiple scripts under the folder path `./scripts` which can be used in various settings. + +### Pattern for a static file, npm run commands, and CI-CD checks + +If you want to implement a static checker follow this pattern. Two static checkers we have are circular dependency checks in our typescript code and url checker to see if any hard coded URL is the typescript application 404s. We have a set of known files in `./scripts/known/*.txt` which is the baseline. + +If you improve the baseline, run the overwrite command and commit the new smaller baseline. Try not to make the baseline bigger, the CI CD will complain. +These baselines are to hold us to higher standards and help implement automated testing against the repository + +#### Output result to stdout +- `npm run circular-deps` +- `npm run url-checker` + +- create a `.sh` file that will run the static checker then output the result to `stdout` + +#### Overwrite result to known .txt file on disk + +If the application needs to overwrite the known file on disk use this pattern. This known .txt file will be source controlled as the baseline + +- `npm run circular-deps:overwrite` +- `npm run url-checker:overwrite` + +#### Diff baseline and current + +These commands will write a /tmp/ file on disk and compare it to the known file in the repository. This command will also be used in the CI CD pipeline for automated checks + +- create a `diff-.sh` file that is the script to diff your tmp file to the baseline +e.g. `diff-url-checker.sh` +```bash +#!/bin/bash +set -euo pipefail + +npm run url-checker > /tmp/urls.txt +diff --ignore-blank-lines -w /tmp/urls.txt ./scripts/known/urls.txt +``` + +- `npm run circular-deps:diff` +- `npm run url-checker:diff` + ## Proposing changes Before you submit a contribution PR to this repo, please ensure that: diff --git a/docs/kcl-std/functions/std-sketch-planeOf.md b/docs/kcl-std/functions/std-sketch-planeOf.md new file mode 100644 index 000000000..111a03db3 --- /dev/null +++ b/docs/kcl-std/functions/std-sketch-planeOf.md @@ -0,0 +1,48 @@ +--- +title: "planeOf" +subtitle: "Function in std::sketch" +excerpt: "Find the plane a face lies on. Returns an error if the face doesn't lie on any plane (for example, the curved face of a cylinder)" +layout: manual +--- + +Find the plane a face lies on. Returns an error if the face doesn't lie on any plane (for example, the curved face of a cylinder) + +```kcl +planeOf( + @solid: Solid, + face: TaggedFace, +): Plane +``` + + + +### Arguments + +| Name | Type | Description | Required | +|----------|------|-------------|----------| +| `solid` | [`Solid`](/docs/kcl-std/types/std-types-Solid) | The solid whose face is being queried. | Yes | +| `face` | [`TaggedFace`](/docs/kcl-std/types/std-types-TaggedFace) | Find the plane which this face lies on. | Yes | + +### Returns + +[`Plane`](/docs/kcl-std/types/std-types-Plane) - An abstract plane. + + +### Examples + +```kcl +triangle = startSketchOn(XY) + |> polygon(radius = 3, numSides = 3, center = [0, 0]) + |> extrude(length = 2) + +// Find the plane of the triangle's top face. +topPlane = planeOf(triangle, face = END) + +// Create a new plane, 10 units above the triangle's top face. +startSketchOn(offsetPlane(topPlane, offset = 10)) + +``` + +![Rendered example of planeOf 0](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAYAAADPfd1WAABNq0lEQVR4Ae3AA6AkWZbG8f937o3IzKdyS2Oubdu2bdu2bdu2bWmMnpZKr54yMyLu+Xa3anqmhztr1a8+6EEP4qqrrrrqqquuuuqqq6666qqrrrrqqquu+j+JylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuur/vBd7sRd7bZ6Pf/iHf/htrrrqqquuuuqqq676v4zKVVddddVVV1111VX/p73O67zOe3/4h3/4d128ePF2HuDEiRM3f/3Xf/37/NZv/dZ3c9VVV1111VVXXXXV/1XoQQ96EFddddVVV1111VVX/d90zTXXPPibvumbnv60pz3tD7/lW77lbXmAD/qgD/rJhz70oa/6IR/yIQ+57777buWqq6666qqrrrrqqv+LqFx11VVXXXXVVVdd9X/eiRMnbn7Hd3zHr+EBHvrQh74qV1111VVXXXXVVVf9X0flqquuuuqqq6666qr/077+67/+fT78wz/8u17u5V7unXguP/IjP/LZ9913361cddVVV1111VVXXfV/FXrQgx7EVVddddVVV1111VX/d11zzTUPfrEXe7HX5vn4rd/6re/mqquuuuqqq6666qr/y9CDHvQgrrrqqquuuuqqq6666qqrrrrqqquuuuqq/5Mox48f56qrrrrqqquuuuqq/x8+/MM//Lse/OAHvzSgs2fP3spVV1111VVXXXXVVf/XUbnqqquuuuqqq6666v+Fb/qmb3r6Nddc82Ce6UM+5EMect99993KVVddddVVV1111VX/lxFcddVVV1111VVXXfX/wjXXXPNgHuBzP/dzf5urrrrqqquuuuqqq/6vI7jqqquuuuqqq6666v+Ff/iHf/htnum+++679YM/+IMfzFVXXXXVVVddddVV/9ehBz3oQVx11VVXXXXVVVdd9f/D67zO67z3mTNnHvSjP/qjn8NVV1111VVXXXXVVf8foAc96EFcddVVV1111VVXXXXVVVddddVVV1111VX/J1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/Kv4R/WxcCe3yxu8AAAAASUVORK5CYII=) + + diff --git a/docs/kcl-std/functions/std-sketch-rectangle.md b/docs/kcl-std/functions/std-sketch-rectangle.md new file mode 100644 index 000000000..5654a837b --- /dev/null +++ b/docs/kcl-std/functions/std-sketch-rectangle.md @@ -0,0 +1,55 @@ +--- +title: "rectangle" +subtitle: "Function in std::sketch" +excerpt: "Sketch a rectangle." +layout: manual +--- + +Sketch a rectangle. + +```kcl +rectangle( + @sketchOrSurface: Sketch | Plane | Face, + width: number(Length), + height: number(Length), + center?: Point2d, + corner?: Point2d, +): Sketch +``` + + + +### Arguments + +| Name | Type | Description | Required | +|----------|------|-------------|----------| +| `sketchOrSurface` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) or [`Plane`](/docs/kcl-std/types/std-types-Plane) or [`Face`](/docs/kcl-std/types/std-types-Face) | Sketch to extend, or plane or surface to sketch on. | Yes | +| `width` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Rectangle's width along X axis. | Yes | +| `height` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Rectangle's height along Y axis. | Yes | +| `center` | [`Point2d`](/docs/kcl-std/types/std-types-Point2d) | The center of the rectangle. Incompatible with `corner`. | No | +| `corner` | [`Point2d`](/docs/kcl-std/types/std-types-Point2d) | The corner of the rectangle. Incompatible with `center`. This will be the corner which is most negative on both X and Y axes. | No | + +### Returns + +[`Sketch`](/docs/kcl-std/types/std-types-Sketch) - A sketch is a collection of paths. + + +### Examples + +```kcl +exampleSketch = startSketchOn(-XZ) + |> rectangle(center = [0, 0], width = 10, height = 5) + +``` + +![Rendered example of rectangle 0](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAYAAADPfd1WAABebUlEQVR4Ae3AA6AkWZbG8f937o3IzKdyS2Oubdu2bdu2bdu2bWmMnpZKr54yMyLu+Xa3anqmhztr1a8+6EEP4qqrrrrqqquuuuqqq6666qqrrrrqqquu+j+JylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqmf5pm/6pqdz1VVXXXXVVVdd9X+YJNk2DyBJts0DSJJt8wCSZNs8gCTZNg8gSbbNA0iSbfMAkmTbPIAk2TYPIEm2zQNIkm3zAJJk2wAf8iEf8hCuuuqqq64CoHLVVVddddVln/u5n/tbr/u6r/tgrrrqqquuuuqqq676X++bvumbnv4hH/IhD+Gqq6666ioqV1111VVXPct6vWZ7e5urrrrqqquuuuqqq/73GoaBq6666qqrnoXKVVddddVVV1111VVXXXXVVVf9H7K/v88//MM//DZXXXXVVVcBEFx11VVXXXXZ3//93//2wcEBV1111VVXXXXVVVf97zYMA/fdd9+tXHXVVVddBUBw1VVXXXXVZf/wD//wO3fffTdXXXXVVVddddVVV/3vNgwDZ8+efQZXXXXVVVcBULnqqquuuuqqq6666qqrrrrqqv9D9vf3+Yd/+Iff5qqrrrrqKgAqV1111VVXXXb27Nlbueqqq6666qqrrrrqf71hGLjqqquuuupZCK666qqrrrrsvvvuu/W+++67dRgGrrrqqquuuuqqq6763+2+++67lauuuuqqqwAIrrrqqquuepazZ8/eul6vueqqq6666qqrrrrqf6fz58/zW7/1W9/NVVddddVV9yO46qqrrrrqWe67775bDw4OuOqqq6666qqrrrrqqquuuuqq/yMIrrrqqquuepb77rvv1rvvvpurrrrqqquuuuqqq/53Ojg44B/+4R9+h6uuuuqqq+5H5aqrrrrqqmf5h3/4h9/hqquuuuqqq6666qqrrrrqqqv+76By1VVXXXXVs5w9e/ZWrrrqqquuuuqqq676X2t/f59/+Id/+G2uuuqqq666H5Wrrrrqqque5b777ruVq6666qqrrrrqqqv+1xqGgfvuu+9Wrrrqqquuuh/BVVddddVVz+Ef/uEffnt/f5+rrrrqqquuuuqqq/53OTg44B/+4R9+m6uuuuqqqx6IylVXXXXVVVddddVVV1111VVX/R+wXq+57777buWqq6666qoHIrjqqquuuuo5/P3f//1vP/nJT+aqq6666qqrrrrqqv9dhmHgvvvuu5WrrrrqqqseiMpVV1111VXP4R/+4R9+h6uuuuqqq6666qqr/tcZhoGzZ88+g6uuuuqqqx6IylVXXXXVVc/h7Nmzt3LVVVddddVVV1111f86+/v7/MM//MNvc9VVV1111QNRueqqq6666qqrrrrqqquuuuqq/wOGYeC+++67lauuuuqqqx6I4Kqrrrrqqudw33333XrffffdOgwDV1111VVXXXXVVVf97zAMA1ddddVVVz1fBFddddVVVz2Ps2fP3rper7nqqquuuuqqq6666n+H/f19fuu3fut7uOqqq6666rkRXHXVVVdd9Tz+/u///rcPDg646qqrrrrqqquuuuqqq6666qr/5Qiuuuqqq656HmfPnn3G3XffzVVXXXXVVVddddVV/zscHBzwD//wD7/NVVddddVVz43KVVddddVVz+O+++67lauuuuqqq6666qqr/tdYr9ecPXv2Vq666qqrrnpuVK666qqrrnoeZ8+evZWrrrrqqquuuuqqq/7XGIaB++6771auuuqqq656bgRXXXXVVVc9j/vuu+9WgGEYuOqqq6666qqrrrrqf75hGLjvvvtu5aqrrrrqqudGcNVVV1111fP1D//wD7+9Xq+56qqrrrrqqquuuup/tv39ff7hH/7ht7nqqquuuur5oXLVVVddddXzdd999906DANXXXXVVVddddVVV/3PNgwD9913361cddVVV131/BBcddVVV131fN133323PuMZz+Cqq6666qqrrrrqqv/ZDg4OuO+++27lqquuuuqq54fKVVddddVVz9c//MM//A5XXXXVVVddddVVV/2vcPbs2Wdw1VVXXXXV80Plqquuuuqq5+vs2bO3ctVVV1111VVXXXXV/3j7+/v8wz/8w29z1VVXXXXV80Plqquuuuqqq6666qqrrrrqqqv+FxuGgfvuu+9Wrrrqqquuen4Irrrqqquuer7uu+++W//hH/7ht/f397nqqquuuuqqq6666n+mYRi47777buWqq6666qoXhMpVV1111VVXXXXVVVddddVVV/0vtb+/zz/8wz/8NlddddVVV70gBFddddVVV71Af//3f//bBwcHXHXVVVddddVVV131P9MwDNx33323ctVVV1111QtCcNVVV1111Qv0D//wD79z9913c9VVV1111VVXXXXV/0zDMHD27NlncNVVV1111QtC5aqrrrrqqquuuuqqq6666qqr/pfa39/nH/7hH36bq6666qqrXhAqV1111VVXvUBnz569lauuuuqqq6666qqr/scahoGrrrrqqqteKIKrrrrqqqteoPvuu+/W++6779ZhGLjqqquuuuqqq6666n+m++6771auuuqqq656QQiuuuqqq656oc6ePXvrer3mqquuuuqqq6666qr/Wc6fP89v/dZvfTdXXXXVVVe9MARXXXXVVVe9UPfdd9+tBwcHXHXVVVddddVVV1111VVXXXXV/0IEV1111VVXvVD33XffrXfffTdXXXXVVVddddVVV/3PcnBwwD/8wz/8DlddddVVV70wVK666qqrrnqh/uEf/uF3uOqqq6666qqrrrrqqquuuuqq/52oXHXVVVdd9UKdPXv2Vq666qqrrrrqqquu+h9nf3+ff/iHf/htrrrqqquuemGoXHXVVVdd9ULdd999t3LVVVddddVVV1111f84wzBw33333cpVV1111VUvDMFVV1111VX/on/4h3/47f39fa666qqrrrrqqquu+p/h4OCAf/iHf/htrrrqqquu+pdQueqqq6666qqrrrrqqquuuuqq/2XW6zX33XffrVx11VVXXfUvIbjqqquuuupf9Pd///e//eQnP5mrrrrqqquuuuqqq/5nGIaB++6771auuuqqq676l1C56qqrrrrqX/QP//APv8NVV1111VVXXXXVVf9jDMPA2bNnn8FVV1111VX/EipXXXXVVVf9i86ePXsrV1111VVXXXXVVVf9j7G/v88//MM//DZXXXXVVVf9S6hcddVVV1111VVXXXXVVVddddX/MsMwcN99993KVVddddVV/xKCq6666qqr/kX33Xffrffdd9+twzBw1VVXXXXVVVddddV/r2EYuOqqq6666kVGcNVVV1111Yvk7Nmzt67Xa6666qqrrrrqqquu+u+1v7/Pb/3Wb30PV1111VVXvSgIrrrqqquuepH8/d///W8fHBxw1VVXXXXVVVddddVVV1111VX/ixBcddVVV131Ijl79uwz7r77bq666qqrrrrqqquu+u91cHDAP/zDP/w2V1111VVXvSioXHXVVVdd9SK57777buWqq6666qqrrrrqqv926/Was2fP3spVV1111VUvCipXXXXVVVe9SM6ePXsrV1111VVXXXXVVVf9txuGgfvuu+9WrrrqqquuelEQXHXVVVdd9SK57777bgUYhoGrrrrqqquuuuqqq/77DMPAfffddytXXXXVVVe9KAiuuuqqq656kf3DP/zDb6/Xa6666qqrrrrqqquu+u+xv7/PP/zDP/w2V1111VVXvaioXHXVVVdd9SK77777bh2Ggauuuuqqq6666qqr/nsMw8B99913K1ddddVVV72oCK666qqrrnqR3Xfffbc+4xnP4Kqrrrrqqquuuuqq/x4HBwfcd999t3LVVVddddWLispVV1111VUvsn/4h3/4Ha666qqrrrrqqquu+m919uzZZ3DVVVddddWLispVV1111VUvsrNnz97KVVddddVVV1111VX/bfb39/mHf/iH3+aqq6666qoXFZWrrrrqqquuuuqqq6666qqrrvpfYhgG7rvvvlu56qqrrrrqRUVw1VVXXXXVi+y+++679R/+4R9+e39/n6uuuuqqq6666qqr/msNw8B99913K1ddddVVV/1rULnqqquuuuqqq6666qqrrrrqqv8F9vf3+Yd/+Iff5qqrrrrqqn8Ngquuuuqqq/5V/v7v//63Dw4OuOqqq6666qqrrrrqv9YwDNx33323ctVVV1111b8GwVVXXXXVVf8q//AP//A7d999N1ddddVVV1111VVX/dcahoGzZ88+g6uuuuqqq/41qFx11VVXXXXVVVddddVVV1111f8C+/v7/MM//MNvc9VVV1111b8Glauuuuqqq/5Vzp49eytXXXXVVVddddVVV/2XG4aBq6666qqr/tUIrrrqqquu+le57777br3vvvtuHYaBq6666qqrrrrqqqv+a9133323ctVVV1111b8GwVVXXXXVVf9qZ8+evXW9XnPVVVddddVVV1111X+N8+fP81u/9VvfzVVXXXXVVf9aBFddddVVV/2r3XfffbceHBxw1VVXXXXVVVddddVVV1111VX/wxFcddVVV131r3bffffdevfdd3PVVVddddVVV1111X+Ng4MD/uEf/uF3uOqqq6666l+LylVXXXXVVf9q//AP//A7XHXVVVddddVVV1111VVXXXXV/3xUrrrqqquu+lc7e/bsrVx11VVXXXXVVVdd9V9mf3+ff/iHf/htrrrqqquu+teictVVV1111b/afffddytXXXXVVVddddVVV/2XGYaB++6771auuuqqq6761yK46qqrrrrq3+Qf/uEffnt/f5+rrrrqqquuuuqqq/5zHRwc8A//8A+/zVVXXXXVVf8WVK666qqrrrrqqquuuuqqq6666n+w9XrNfffddytXXXXVVVf9WxBcddVVV131b/L3f//3v/3kJz+Zq6666qqrrrrqqqv+cw3DwH333XcrV1111VVX/VtQueqqq6666t/kH/7hH36Hq6666qqrrrrqqqv+0w3DwNmzZ5/BVVddddVV/xZUrrrqqquu+jc5e/bsrVx11VVXXXXVVVdd9Z9uf3+ff/iHf/htrrrqqquu+regctVVV1111VVXXXXVVVddddVV/4MNw8B99913K1ddddVVV/1bEFx11VVXXfVvct99991633333ToMA1ddddVVV1111VVX/ecYhoGrrrrqqqv+XQiuuuqqq676Nzt79uyt6/Waq6666qqrrrrqqqv+c+zv7/Nbv/Vb38NVV1111VX/VgRXXXXVVVf9m/393//9bx8cHHDVVVddddVVV1111VVXXXXVVf9DEVx11VVXXfVvdvbs2WfcfffdXHXVVVddddVVV131n+Pg4IB/+Id/+G2uuuqqq676t6Jy1VVXXXXVv9l99913K1ddddVVV1111VVX/adZr9ecPXv2Vq666qqrrvq3onLVVVddddW/2dmzZ2/lqquuuuqqq6666qr/NMMwcN99993KVVddddVV/1YEV1111VVX/Zvdd999twIMw8BVV1111VVXXXXVVf/xhmHgvvvuu5Wrrrrqqqv+rQiuuuqqq676d/mHf/iH316v11x11VVXXXXVVVdd9R9rf3+ff/iHf/htrrrqqquu+vegctVVV1111b/Lfffdd+swDFx11VVXXXXVVVdd9R9rGAbuu+++W7nqqquuuurfg+Cqq6666qp/l/vuu+/WZzzjGVx11VVXXXXVVVdd9R/r4OCA++6771auuuqqq67696By1VVXXXXVv8s//MM//A5XXXXVVVddddVVV/2nOHv27DO46qqrrrrq34PKVVddddVV/y5nz569lauuuuqqq6666qqr/sPt7+/zD//wD7/NVVddddVV/x5Urrrqqquuuuqqq6666qqrrrrqf6BhGLjvvvtu5aqrrrrqqn8Pgquuuuqqq/5d7rvvvlv/4R/+4bf39/e56qqrrrrqqquuuuo/xjAM3Hfffbdy1VVXXXXVvxeVq6666qqrrrrqqquuuuqqq676H2Z/f59/+Id/+G2uuuqqq6769yK46qqrrrrq3+3v//7vf/vg4ICrrrrqqquuuuqqq/5jDMPAfffddytXXXXVVVf9exFcddVVV1317/YP//APv3P33Xdz1VVXXXXVVVddddV/jGEYOHv27DO46qqrrrrq34vKVVddddVVV1111VVXXXXVVVf9D7O/v88//MM//DZXXXXVVVf9e1G56qqrrrrq3+3s2bO3ctVVV1111VVXXXXVf5hhGLjqqquuuuo/BMFVV1111VX/bvfdd9+t9913363DMHDVVVddddVVV1111X+M++6771auuuqqq6769yK46qqrrrrqP8TZs2dvXa/XXHXVVVddddVVV13173P+/Hl+67d+67u56qqrrrrqPwLBVVddddVV/yHuu+++Ww8ODrjqqquuuuqqq6666qqrrrrqqv9BCK666qqrrvoPcd9999169913c9VVV1111VVXXXXVv8/BwQH/8A//8DtcddVVV131H4HKVVddddVV/yH+4R/+4Xe46qqrrrrqqquuuuqqq6666qr/WahcddVVV131H+Ls2bO3ctVVV1111VVXXXXVv9v+/j7/8A//8NtcddVVV131H4HKVVddddVV/yHuu+++W7nqqquuuuqqq6666t9tGAbuu+++W7nqqquuuuo/AsFVV1111VX/Yf7hH/7ht/f397nqqquuuuqqq6666t/m4OCAf/iHf/htrrrqqquu+o9C5aqrrrrqqquuuuqqq6666qqr/odYr9fcd999t3LVVVddddV/FIKrrrrqqqv+w/z93//9bz/5yU/mqquuuuqqq6666qp/m2EYuO+++27lqquuuuqq/yhUrrrqqquu+g/zD//wD7/DVVddddVVV1111VX/ZsMwcPbs2Wdw1VVXXXXVfxQqV1111VVX/Yc5e/bsrVx11VVXXXXVVVdd9W+2v7/PP/zDP/w2V1111VVX/UehctVVV1111VVXXXXVVVddddVV/0MMw8B99913K1ddddVVV/1HIbjqqquuuuo/zH333Xfrfffdd+swDFx11VVXXXXVVVdd9a8zDANXXXXVVVf9hyO46qqrrrrqP9TZs2dvXa/XXHXVVVddddVVV131r7O/v89v/dZvfQ9XXXXVVVf9RyK46qqrrrrqP9Tf//3f//bBwQFXXXXVVVddddVVV1111VVXXfU/AMFVV1111VX/oc6ePfuMu+++m6uuuuqqq6666qqr/nUODg74h3/4h9/mqquuuuqq/0hUrrrqqquu+g9133333cpVV1111VVXXXXVVf9q6/Was2fP3spVV1111VX/kahcddVVV131H+rs2bO3ctVVV1111VVXXXXVv9owDNx33323ctVVV1111X8kgquuuuqqq/5D3XfffbcCDMPAVVddddVVV1111VUvumEYuO+++27lqquuuuqq/0gEV1111VVX/Yf7h3/4h99er9dcddVVV1111VVXXfWi2d/f5x/+4R9+m6uuuuqqq/6jUbnqqquuuuo/3H333XfrMAxcddVVV1111VVXXfWiGYaB++6771auuuqqq676j0Zw1VVXXXXVf7j77rvv1mc84xlcddVVV1111VVXXfWiOTg44L777ruVq6666qqr/qNRueqqq6666j/cP/zDP/wOV1111VVXXXXVVVf9q5w9e/YZXHXVVVdd9R+NylVXXXXVVf/hzp49eytXXXXVVVddddVVV73I9vf3+Yd/+Iff5qqrrrrqqv9oVK666qqrrrrqqquuuuqqq6666r/ZMAzcd999t3LVVVddddV/NIKrrrrqqqv+w9133323/sM//MNv7+/vc9VVV1111VVXXXXVCzcMA/fdd9+tXHXVVVdd9Z+BylVXXXXVVVddddVVV1111VVX/Tfa39/nH/7hH36bq6666qqr/jMQXHXVVVdd9Z/i7//+73/74OCAq6666qqrrrrqqqteuGEYuO+++27lqquuuuqq/wwEV1111VVX/af4h3/4h9+5++67ueqqq6666qqrrrrqhRuGgbNnzz6Dq6666qqr/jNQueqqq6666qqrrrrqqquuuuqq/0b7+/v8wz/8w29z1VVXXXXVfwYqV1111VVX/ac4e/bsrVx11VVXXXXVVVdd9S8ahoGrrrrqqqv+0xBcddVVV131n+K+++679b777rt1GAauuuqqq6666qqrrnrh7rvvvlu56qqrrrrqPwPBVVddddVV/2nOnj1763q95qqrrrrqqquuuuqq5+/8+fP81m/91ndz1VVXXXXVfxaCq6666qqr/tPcd999tx4cHHDVVVddddVVV1111VVXXXXVVf9NCK666qqrrvpPc99999169913c9VVV1111VVXXXXV83dwcMA//MM//A5XXXXVVVf9Z6Fy1VVXXXXVf5p/+Id/+B2uuuqqq6666qqrrrrqqquuuuq/D5Wrrrrqqqv+05w9e/ZWrrrqqquuuuqqq656gfb39/mHf/iH3+aqq6666qr/LFSuuuqqq676T3PffffdylVXXXXVVVddddVVL9AwDNx33323ctVVV1111X8Wgquuuuqqq/5T/cM//MNv7+/vc9VVV1111VVXXXXVczo4OOAf/uEffpurrrrqqqv+M1G56qqrrrrqqquuuuqqq6666qr/Buv1mvvuu+9Wrrrqqquu+s9EcNVVV1111X+qv//7v//tJz/5yVx11VVXXXXVVVdd9ZyGYeC+++67lauuuuqqq/4zUbnqqquuuuo/1T/8wz/8DlddddVVV1111VVXPY9hGDh79uwzuOqqq6666j8Tlauuuuqqq/5TnT179lauuuqqq6666qqrrnoe+/v7/MM//MNvc9VVV1111X8mKlddddVVV1111VVXXXXVVVdd9d9gGAbuu+++W7nqqquuuuo/E8FVV1111VX/qe67775b77vvvlv39/e56qqrrrrqqquuuuqKYRi46qqrrrrqvwTBVVddddVV/+nOnj17K1ddddVVV1111VVXPcv+/j6/9Vu/9T1cddVVV131n43gqquuuuqq/3R///d//9sHBwdcddVVV1111VVXXXXVVVddddV/MYKrrrrqqqv+0509e/YZd999N1ddddVVV1111VVXXXFwcMA//MM//DZXXXXVVVf9Z6Ny1VVXXXXVf7r77rvvVq666qqrrrrqqquuepb9/X3Onj17K1ddddVVV/1no3LVVVddddV/urNnz97KVVddddVVV1111VXP4b777ruVq6666qqr/rMRXHXVVVdd9Z/uvvvuuxVgGAauuuqqq6666qqrroJhGLjvvvtu5aqrrrrqqv9sBFddddVVV/2X+Id/+IffXq/XXHXVVVddddVVV/1/t7+/zz/8wz/8NlddddVVV/1XoHLVVVddddV/ifvuu+/WYRi46qqrrrrqqquu+v9uGAbuu+++W7nqqquuuuq/AsFVV1111VX/Je67775bn/GMZ3DVVVddddVVV131/93BwQH33XffrVx11VVXXfVfgcpVV1111VX/Jf7hH/7hd7jqqquuuuqqq6666rKzZ88+g6uuuuqqq/4rULnqqquuuuq/xNmzZ2/lqquuuuqqq6666ir29/f5h3/4h9/mqquuuuqq/wpUrrrqqquuuuqqq6666qqrrrrqv9AwDNx33323ctVVV1111X8Fgquuuuqqq/5L3Hfffbf+wz/8w2/v7+9z1VVXXXXVVVdd9f/VMAzcd999t3LVVVddddV/FSpXXXXVVVddddVVV1111VVXXfVfZH9/n3/4h3/4ba666qqrrvqvQnDVVVddddV/mb//+7//7YODA6666qqrrrrqqqv+vxqGgfvuu+9Wrrrqqquu+q9CcNVVV1111X+Zf/iHf/idu+++m6uuuuqqq6666qr/r4Zh4OzZs8/gqquuuuqq/ypUrrrqqquuuuqqq6666qqrrrrqv8j+/j7/8A//8NtcddVVV131X4XKVVddddVV/2XOnj17K1ddddVVV1111VX/jw3DwFVXXXXVVf+lCK666qqrrvovc999991633333ToMA1ddddVVV1111VX/X9133323ctVVV1111X8Vgquuuuqqq/5LnT179tb1es1VV1111VVXXXXV/zfnz5/nt37rt76bq6666qqr/isRXHXVVVdd9V/qvvvuu/Xg4ICrrrrqqquuuuqqq6666qqrrvovQHDVVVddddV/qfvuu+/Wu+++m6uuuuqqq6666qr/bw4ODviHf/iH3+Gqq6666qr/SlSuuuqqq676L/UP//APv8NVV1111VVXXXXVVVddddVVV/3XoHLVVVddddV/qbNnz97KVVddddVVV1111f9D+/v7/MM//MNvc9VVV1111X8lKlddddVVV/2Xuu+++27lqquuuuqqq6666v+hYRi47777buWqq6666qr/SgRXXXXVVVf9l/uHf/iH397f3+eqq6666qqrrrrq/4uDgwP+4R/+4be56qqrrrrqvxqVq6666qqrrrrqqquuuuqqq676T7Zer7nvvvtu5aqrrrrqqv9qBFddddVVV/2X+/u///vffvKTn8xVV1111VVXXXXV/xfDMHDffffdylVXXXXVVf/VqFx11VVXXfVf7h/+4R9+h6uuuuqqq6666qr/R4Zh4OzZs8/gqquuuuqq/2pUrrrqqquu+i939uzZW7nqqquuuuqqq676f2R/f59/+Id/+G2uuuqqq676r0blqquuuuqqq6666qqrrrrqqqv+kw3DwH333XcrV1111VVX/VcjuOqqq6666r/cfffdd+t999136/7+PlddddVVV1111VX/1w3DwFVXXXXVVf9tCK666qqrrvpvcfbs2Vu56qqrrrrqqquu+n9gf3+f3/qt3/oerrrqqquu+u9AcNVVV1111X+Lv//7v//tg4MDrrrqqquuuuqqq6666qqrrrrqPxHBVVddddVV/y3Onj37jLvvvpurrrrqqquuuuqq/+sODg74h3/4h9/mqquuuuqq/w5Urrrqqquu+m9x33333cpVV1111VVXXXXV/wP7+/ucPXv2Vq666qqrrvrvQOWqq6666qr/FmfPnr2Vq6666qqrrrrqqv8n7rvvvlu56qqrrrrqvwPBVVddddVV/y3uu+++WwGGYeCqq6666qqrrrrq/7JhGLjvvvtu5aqrrrrqqv8OBFddddVVV/23+Yd/+IffXq/XXHXVVVddddVVV/1ftb+/zz/8wz/8NlddddVVV/13oXLVVVddddV/m/vuu+/WYRi46qqrrrrqqquu+r9qGAbuu+++W7nqqquuuuq/C8FVV1111VX/be67775bn/GMZ3DVVVddddVVV131f9XBwQH33XffrVx11VVXXfXfhcpVV1111VX/bf7hH/7hd7jqqquuuuqqq676P+7s2bPP4Kqrrrrqqv8uVK666qqrrvpvc/bs2Vu56qqrrrrqqquu+j9sf3+ff/iHf/htrrrqqquu+u9C5aqrrrrqqquuuuqqq6666qqr/pMMw8B99913K1ddddVVV/13Ibjqqquuuuq/zX333XfrP/zDP/z2/v4+V1111VVXXXXVVf/XDMPAfffddytXXXXVVVf9d6Jy1VVXXXXVVVddddVVV1111VX/Cfb39/mHf/iH3+aqq6666qr/TgRXXXXVVVf9t/r7v//73z44OOCqq6666qqrrrrq/5phGLjvvvtu5aqrrrrqqv9OBFddddVVV/23+od/+Iffufvuu7nqqquuuuqqq676v2YYBs6ePfsMrrrqqquu+u9E5aqrrrrqqquuuuqqq6666qqr/hPs7+/zD//wD7/NVVddddVV/52oXHXVVVdd9d/q7Nmzt3LVVVddddVVV131f9AwDFx11VVXXfXfjuCqq6666qr/Vvfdd9+t9913363DMHDVVVddddVVV131f8199913K1ddddVVV/13Irjqqquuuuq/3dmzZ29dr9dcddVVV1111VVX/V9x/vx5fuu3fuu7ueqqq6666r8bwVVXXXXVVf/t/v7v//63Dw4OuOqqq6666qqrrrrqqquuuuqq/2AEV1111VVX/Y9w9913c9VVV1111VVXXfV/xcHBAf/wD//wO1x11VVXXfXfjcpVV1111VX/7f7hH/7hd7jqqquuuuqqq6666qqrrrrqqv94VK666qqrrvpvd/bs2Vu56qqrrrrqqquu+j9kf3+ff/iHf/htrrrqqquu+u9G5aqrrrrqqv929913361cddVVV1111VVX/R8yDAP33XffrVx11VVXXfXfjeCqq6666qr/Ef7hH/7ht/f397nqqquuuuqqq6763+7g4IB/+Id/+G2uuuqqq676n4DKVVddddVVV1111VVXXXXVVVf9B1qv19x33323ctVVV1111f8EBFddddVVV/2P8Pd///e//eQnP5mrrrrqqquuuuqq/+2GYeC+++67lauuuuqqq/4noHLVVVddddX/CP/wD//wO1x11VVXXXXVVVf9HzAMA2fPnn0GV1111VVX/U9A5aqrrrrqqv8Rzp49eytXXXXVVVddddVV/wfs7+/zD//wD7/NVVddddVV/xNQueqqq6666qqrrrrqqquuuuqq/0DDMHDffffdylVXXXXVVf8TEFx11VVXXfU/wn333Xfrfffdd+v+/j5XXXXVVVddddVV/1sNw8BVV1111VX/oxBcddVVV131P8bZs2dv5aqrrrrqqquuuup/sf39fX7rt37re7jqqquuuup/CoKrrrrqqqv+x/j7v//73z44OOCqq6666qqrrrrqqquuuuqqq/6DEFx11VVXXfU/xtmzZ59x9913c9VVV1111VVXXfW/1cHBAf/wD//w21x11VVXXfU/BZWrrrrqqqv+x7jvvvtu5aqrrrrqqquuuup/sf39fc6ePXsrV1111VVX/U9B5aqrrrrqqv8xzp49eytXXXXVVVddddVV/8vdd999t3LVVVddddX/FARXXXXVVVf9j3HffffdCjAMA1ddddVVV1111VX/Gw3DwH333XcrV1111VVX/U9BcNVVV1111f8o//AP//Db6/Waq6666qqrrrrqqv9t9vf3+Yd/+Iff5qqrrrrqqv9JqFx11VVXXfU/yn333XfrMAxcddVVV1111VVX/W8zDAP33XffrVx11VVXXfU/CcFVV1111VX/o9x33323PuMZz+Cqq6666qqrrrrqf5uDgwP+4R/+4Xe46qqrrrrqfxIqV1111VVX/Y/yD//wD7/DVVddddVVV1111VVXXXXVVVf9x6By1VVXXXXV/yhnz569lauuuuqqq6666qr/hfb39/mHf/iH3+aqq6666qr/SahcddVVV1111VVXXXXVVVddddV/gGEYuO+++27lqquuuuqq/0kIrrrqqquu+h/lvvvuu/Uf/uEffnt/f5+rrrrqqquuuuqq/y2GYeC+++67lauuuuqqq/6noXLVVVddddVVV1111VVXXXXVVf9O+/v7/MM//MNvc9VVV1111f80BFddddVVV/2P8/d///e/fXBwwFVXXXXVVVddddX/FsMwcN99993KVVddddVV/9MQXHXVVVdd9T/OP/zDP/zO3XffzVVXXXXVVVddddX/FsMwcPbs2Wdw1VVXXXXV/zRUrrrqqquuuuqqq6666qqrrrrq32l/f59/+Id/+G2uuuqqq676n4bKVVddddVV/+OcPXv2Vq666qqrrrrqqqv+FxmGgauuuuqqq/5HIrjqqquuuup/nPvuu+/W++6779ZhGLjqqquuuuqqq6763+K+++67lauuuuqqq/6nIbjqqquuuup/pLNnz966Xq+56qqrrrrqqquu+p/u/Pnz/NZv/dZ3c9VVV1111f9EBFddddVVV/2P9Pd///e/fXBwwFVXXXXVVVddddVVV1111VVX/TsQXHXVVVdd9T/W3XffzVVXXXXVVVddddX/dAcHB/zDP/zD73DVVVddddX/RFSuuuqqq676H+kf/uEffoerrrrqqquuuuqqq6666qqrrvr3oXLVVVddddX/SGfPnr2Vq6666qqrrrrqqv8F9vf3+Yd/+Iff5qqrrrrqqv+JqFx11VVXXfU/0n333XcrV1111VVXXXXVVf8LDMPAfffddytXXXXVVVf9T0Rw1VVXXXXV/1j/8A//8Nv7+/tcddVVV1111VVX/U91cHDAP/zDP/w2V1111VVX/U9F5aqrrrrqqquuuuqqq6666qqr/o3W6zX33XffrVx11VVXXfU/FcFVV1111VX/Y/393//9bz/5yU/mqquuuuqqq6666n+qYRi47777buWqq6666qr/qahcddVVV131P9Y//MM//A5XXXXVVVddddVV/4MNw8DZs2efwVVXXXXVVf9TUbnqqquuuup/rLNnz97KVVddddVVV1111f9g+/v7/MM//MNvc9VVV1111f9UVK666qqrrrrqqquuuuqqq6666t9oGAbuu+++W7nqqquuuup/KoKrrrrqqqv+x7rvvvtuve+++27d39/nqquuuuqqq6666n+aYRi46qqrrrrqfzyCq6666qqr/kc7e/bsrVx11VVXXXXVVVf9D7S/v89v/dZvfQ9XXXXVVVf9T0Zw1VVXXXXV/2h///d//9sHBwdcddVVV1111VVXXXXVVVddddW/AcFVV1111VX/o509e/YZd999N1ddddVVV1111VX/0xwcHPAP//APv81VV1111VX/k1G56qqrrrrqf7T77rvvVq666qqrrrrqqqv+B9rf3+fs2bO3ctVVV1111f9kVK666qqrrvof7ezZs7dy1VVXXXXVVVdd9T/UfffddytXXXXVVVf9T0Zw1VVXXXXV/2j33XffrQDDMHDVVVddddVVV131P8kwDNx33323ctVVV1111f9kBFddddVVV/2P9w//8A+/vV6vueqqq6666qqrrvqfYn9/n3/4h3/4ba666qqrrvqfjspVV1111VX/49133323DsPAVVddddVVV1111f8UwzBw33333cpVV1111VX/0xFcddVVV131P95999136zOe8Qyuuuqqq6666qqr/qc4ODjgH/7hH36Hq6666qqr/qejctVVV1111f94//AP//A7XHXVVVddddVVV1111VVXXXXVvx6Vq6666qqr/sc7e/bsrVx11VVXXXXVVVf9D7K/v88//MM//DZXXXXVVVf9T0flqquuuuqqq6666qqrrrrqqqv+lYZh4L777ruVq6666qqr/qcjuOqqq6666n+8++6779Z/+Id/+O39/X2uuuqqq6666qqr/rsNw8B99913K1ddddVVV/1vQOWqq6666qqrrrrqqquuuuqqq/4V9vf3+Yd/+Iff5qqrrrrqqv8NCK666qqrrvpf4e///u9/++DggKuuuuqqq6666qr/bsMwcN99993KVVddddVV/xsQXHXVVVdd9b/CP/zDP/zO3XffzVVXXXXVVVddddV/t2EYOHv27DO46qqrrrrqfwMqV1111VVXXXXVVVddddVVV131r7C/v88//MM//DZXXXXVVVf9b0Dlqquuuuqq/xXOnj17K1ddddVVV1111VX/AwzDwFVXXXXVVf9rEFx11VVXXfW/wn333Xfrfffdd+swDFx11VVXXXXVVVf9d7vvvvtu5aqrrrrqqv8NCK666qqrrvpf4+zZs7eu12uuuuqqq6666qqr/rucP3+e3/qt3/purrrqqquu+t+C4Kqrrrrqqv81/v7v//63Dw4OuOqqq6666qqrrrrqqquuuuqqFxHBVVddddVV/6vcfffdXHXVVVddddVVV/13ecYznsE//MM//A5XXXXVVVf9b0Hlqquuuuqq/zV++7d/+3vOnj37jA//8A//Lq666qqrrrrqqqv+G/zIj/zIZ//Wb/3Wd3PVVVddddX/FuhBD3oQV1111VVXXXXVVVddddVVV1111VVXXXXV/0lUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V/GPl+emO0MXBNcAAAAASUVORK5CYII=) + +```kcl +exampleSketch = startSketchOn(-XZ) + |> rectangle(corner = [0, 0], width = 10, height = 5) + +``` + +![Rendered example of rectangle 1](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAYAAADPfd1WAABebUlEQVR4Ae3AA6AkWZbG8f937o3IzKdyS2Oubdu2bdu2bdu2bWmMnpZKr54yMyLu+Xa3anqmhztr1a8+6EEP4qqrrrrqqquuuuqqq6666qqrrrrqqquu+j+JylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqmf5pm/6pqdz1VVXXXXVVVdd9X+YJNk2DyBJts0DSJJt8wCSZNs8gCTZNg8gSbbNA0iSbfMAkmTbPIAk2TYPIEm2zQNIkm3zAJJk2wAf8iEf8hCuuuqqq64CoHLVVVddddVln/u5n/tbr/u6r/tgrrrqqquuuuqqq676X++bvumbnv4hH/IhD+Gqq6666ioqV1111VVXPct6vWZ7e5urrrrqqquuuuqqq/73GoaBq6666qqrnoXKVVddddVVV1111VVXXXXVVVf9H7K/v88//MM//DZXXXXVVVcBEFx11VVXXXXZ3//93//2wcEBV1111VVXXXXVVVf97zYMA/fdd9+tXHXVVVddBUBw1VVXXXXVZf/wD//wO3fffTdXXXXVVVddddVVV/3vNgwDZ8+efQZXXXXVVVcBULnqqquuuuqqq6666qqrrrrqqv9D9vf3+Yd/+Iff5qqrrrrqKgAqV1111VVXXXb27Nlbueqqq6666qqrrrrqf71hGLjqqquuuupZCK666qqrrrrsvvvuu/W+++67dRgGrrrqqquuuuqqq6763+2+++67lauuuuqqqwAIrrrqqquuepazZ8/eul6vueqqq6666qqrrrrqf6fz58/zW7/1W9/NVVddddVV9yO46qqrrrrqWe67775bDw4OuOqqq6666qqrrrrqqquuuuqq/yMIrrrqqquuepb77rvv1rvvvpurrrrqqquuuuqqq/53Ojg44B/+4R9+h6uuuuqqq+5H5aqrrrrqqmf5h3/4h9/hqquuuuqqq6666qqrrrrqqqv+76By1VVXXXXVs5w9e/ZWrrrqqquuuuqqq676X2t/f59/+Id/+G2uuuqqq666H5Wrrrrqqque5b777ruVq6666qqrrrrqqqv+1xqGgfvuu+9Wrrrqqquuuh/BVVddddVVz+Ef/uEffnt/f5+rrrrqqquuuuqqq/53OTg44B/+4R9+m6uuuuqqqx6IylVXXXXVVVddddVVV1111VVX/R+wXq+57777buWqq6666qoHIrjqqquuuuo5/P3f//1vP/nJT+aqq6666qqrrrrqqv9dhmHgvvvuu5WrrrrqqqseiMpVV1111VXP4R/+4R9+h6uuuuqqq6666qqr/tcZhoGzZ88+g6uuuuqqqx6IylVXXXXVVc/h7Nmzt3LVVVddddVVV1111f86+/v7/MM//MNvc9VVV1111QNRueqqq6666qqrrrrqqquuuuqq/wOGYeC+++67lauuuuqqqx6I4Kqrrrrqqudw33333XrffffdOgwDV1111VVXXXXVVVf97zAMA1ddddVVVz1fBFddddVVVz2Ps2fP3rper7nqqquuuuqqq6666n+H/f19fuu3fut7uOqqq6666rkRXHXVVVdd9Tz+/u///rcPDg646qqrrrrqqquuuuqqq6666qr/5Qiuuuqqq656HmfPnn3G3XffzVVXXXXVVVddddVV/zscHBzwD//wD7/NVVddddVVz43KVVddddVVz+O+++67lauuuuqqq6666qqr/tdYr9ecPXv2Vq666qqrrnpuVK666qqrrnoeZ8+evZWrrrrqqquuuuqqq/7XGIaB++6771auuuqqq656bgRXXXXVVVc9j/vuu+9WgGEYuOqqq6666qqrrrrqf75hGLjvvvtu5aqrrrrqqudGcNVVV1111fP1D//wD7+9Xq+56qqrrrrqqquuuup/tv39ff7hH/7ht7nqqquuuur5oXLVVVddddXzdd999906DANXXXXVVVddddVVV/3PNgwD9913361cddVVV131/BBcddVVV131fN133323PuMZz+Cqq6666qqrrrrqqv/ZDg4OuO+++27lqquuuuqq54fKVVddddVVz9c//MM//A5XXXXVVVddddVVV/2vcPbs2Wdw1VVXXXXV80Plqquuuuqq5+vs2bO3ctVVV1111VVXXXXV/3j7+/v8wz/8w29z1VVXXXXV80Plqquuuuqqq6666qqrrrrqqqv+FxuGgfvuu+9Wrrrqqquuen4Irrrqqquuer7uu+++W//hH/7ht/f397nqqquuuuqqq6666n+mYRi47777buWqq6666qoXhMpVV1111VVXXXXVVVddddVVV/0vtb+/zz/8wz/8NlddddVVV70gBFddddVVV71Af//3f//bBwcHXHXVVVddddVVV131P9MwDNx33323ctVVV1111QtCcNVVV1111Qv0D//wD79z9913c9VVV1111VVXXXXV/0zDMHD27NlncNVVV1111QtC5aqrrrrqqquuuuqqq6666qqr/pfa39/nH/7hH36bq6666qqrXhAqV1111VVXvUBnz569lauuuuqqq6666qqr/scahoGrrrrqqqteKIKrrrrqqqteoPvuu+/W++6779ZhGLjqqquuuuqqq6666n+m++6771auuuqqq656QQiuuuqqq656oc6ePXvrer3mqquuuuqqq6666qr/Wc6fP89v/dZvfTdXXXXVVVe9MARXXXXVVVe9UPfdd9+tBwcHXHXVVVddddVVV1111VVXXXXV/0IEV1111VVXvVD33XffrXfffTdXXXXVVVddddVVV/3PcnBwwD/8wz/8DlddddVVV70wVK666qqrrnqh/uEf/uF3uOqqq6666qqrrrrqqquuuuqq/52oXHXVVVdd9UKdPXv2Vq666qqrrrrqqquu+h9nf3+ff/iHf/htrrrqqquuemGoXHXVVVdd9ULdd999t3LVVVddddVVV1111f84wzBw33333cpVV1111VUvDMFVV1111VX/on/4h3/47f39fa666qqrrrrqqquu+p/h4OCAf/iHf/htrrrqqquu+pdQueqqq6666qqrrrrqqquuuuqq/2XW6zX33XffrVx11VVXXfUvIbjqqquuuupf9Pd///e//eQnP5mrrrrqqquuuuqqq/5nGIaB++6771auuuqqq676l1C56qqrrrrqX/QP//APv8NVV1111VVXXXXVVf9jDMPA2bNnn8FVV1111VX/EipXXXXVVVf9i86ePXsrV1111VVXXXXVVVf9j7G/v88//MM//DZXXXXVVVf9S6hcddVVV1111VVXXXXVVVddddX/MsMwcN99993KVVddddVV/xKCq6666qqr/kX33Xffrffdd9+twzBw1VVXXXXVVVddddV/r2EYuOqqq6666kVGcNVVV1111Yvk7Nmzt67Xa6666qqrrrrqqquu+u+1v7/Pb/3Wb30PV1111VVXvSgIrrrqqquuepH8/d///W8fHBxw1VVXXXXVVVddddVVV1111VX/ixBcddVVV131Ijl79uwz7r77bq666qqrrrrqqquu+u91cHDAP/zDP/w2V1111VVXvSioXHXVVVdd9SK57777buWqq6666qqrrrrqqv926/Was2fP3spVV1111VUvCipXXXXVVVe9SM6ePXsrV1111VVXXXXVVVf9txuGgfvuu+9WrrrqqquuelEQXHXVVVdd9SK57777bgUYhoGrrrrqqquuuuqqq/77DMPAfffddytXXXXVVVe9KAiuuuqqq656kf3DP/zDb6/Xa6666qqrrrrqqquu+u+xv7/PP/zDP/w2V1111VVXvaioXHXVVVdd9SK77777bh2Ggauuuuqqq6666qqr/nsMw8B99913K1ddddVVV72oCK666qqrrnqR3Xfffbc+4xnP4Kqrrrrqqquuuuqq/x4HBwfcd999t3LVVVddddWLispVV1111VUvsn/4h3/4Ha666qqrrrrqqquu+m919uzZZ3DVVVddddWLispVV1111VUvsrNnz97KVVddddVVV1111VX/bfb39/mHf/iH3+aqq6666qoXFZWrrrrqqquuuuqqq6666qqrrvpfYhgG7rvvvlu56qqrrrrqRUVw1VVXXXXVi+y+++679R/+4R9+e39/n6uuuuqqq6666qqr/msNw8B99913K1ddddVVV/1rULnqqquuuuqqq6666qqrrrrqqv8F9vf3+Yd/+Iff5qqrrrrqqn8Ngquuuuqqq/5V/v7v//63Dw4OuOqqq6666qqrrrrqv9YwDNx33323ctVVV1111b8GwVVXXXXVVf8q//AP//A7d999N1ddddVVV1111VVX/dcahoGzZ88+g6uuuuqqq/41qFx11VVXXXXVVVddddVVV1111f8C+/v7/MM//MNvc9VVV1111b8Glauuuuqqq/5Vzp49eytXXXXVVVddddVVV/2XG4aBq6666qqr/tUIrrrqqquu+le57777br3vvvtuHYaBq6666qqrrrrqqqv+a9133323ctVVV1111b8GwVVXXXXVVf9qZ8+evXW9XnPVVVddddVVV1111X+N8+fP81u/9VvfzVVXXXXVVf9aBFddddVVV/2r3XfffbceHBxw1VVXXXXVVVddddVVV1111VX/wxFcddVVV131r3bffffdevfdd3PVVVddddVVV1111X+Ng4MD/uEf/uF3uOqqq6666l+LylVXXXXVVf9q//AP//A7XHXVVVddddVVV1111VVXXXXV/3xUrrrqqquu+lc7e/bsrVx11VVXXXXVVVdd9V9mf3+ff/iHf/htrrrqqquu+teictVVV1111b/afffddytXXXXVVVddddVVV/2XGYaB++6771auuuqqq6761yK46qqrrrrq3+Qf/uEffnt/f5+rrrrqqquuuuqqq/5zHRwc8A//8A+/zVVXXXXVVf8WVK666qqrrrrqqquuuuqqq6666n+w9XrNfffddytXXXXVVVf9WxBcddVVV131b/L3f//3v/3kJz+Zq6666qqrrrrqqqv+cw3DwH333XcrV1111VVX/VtQueqqq6666t/kH/7hH36Hq6666qqrrrrqqqv+0w3DwNmzZ5/BVVddddVV/xZUrrrqqquu+jc5e/bsrVx11VVXXXXVVVdd9Z9uf3+ff/iHf/htrrrqqquu+regctVVV1111VVXXXXVVVddddVV/4MNw8B99913K1ddddVVV/1bEFx11VVXXfVvct99991633333ToMA1ddddVVV1111VVX/ecYhoGrrrrqqqv+XQiuuuqqq676Nzt79uyt6/Waq6666qqrrrrqqqv+c+zv7/Nbv/Vb38NVV1111VX/VgRXXXXVVVf9m/393//9bx8cHHDVVVddddVVV1111VVXXXXVVf9DEVx11VVXXfVvdvbs2WfcfffdXHXVVVddddVVV131n+Pg4IB/+Id/+G2uuuqqq676t6Jy1VVXXXXVv9l99913K1ddddVVV1111VVX/adZr9ecPXv2Vq666qqrrvq3onLVVVddddW/2dmzZ2/lqquuuuqqq6666qr/NMMwcN99993KVVddddVV/1YEV1111VVX/Zvdd999twIMw8BVV1111VVXXXXVVf/xhmHgvvvuu5Wrrrrqqqv+rQiuuuqqq676d/mHf/iH316v11x11VVXXXXVVVdd9R9rf3+ff/iHf/htrrrqqquu+vegctVVV1111b/Lfffdd+swDFx11VVXXXXVVVdd9R9rGAbuu+++W7nqqquuuurfg+Cqq6666qp/l/vuu+/WZzzjGVx11VVXXXXVVVdd9R/r4OCA++6771auuuqqq67696By1VVXXXXVv8s//MM//A5XXXXVVVddddVVV/2nOHv27DO46qqrrrrq34PKVVddddVV/y5nz569lauuuuqqq6666qqr/sPt7+/zD//wD7/NVVddddVV/x5Urrrqqquuuuqqq6666qqrrrrqf6BhGLjvvvtu5aqrrrrqqn8Pgquuuuqqq/5d7rvvvlv/4R/+4bf39/e56qqrrrrqqquuuuo/xjAM3Hfffbdy1VVXXXXVvxeVq6666qqrrrrqqquuuuqqq676H2Z/f59/+Id/+G2uuuqqq6769yK46qqrrrrq3+3v//7vf/vg4ICrrrrqqquuuuqqq/5jDMPAfffddytXXXXVVVf9exFcddVVV1317/YP//APv3P33Xdz1VVXXXXVVVddddV/jGEYOHv27DO46qqrrrrq34vKVVddddVVV1111VVXXXXVVVf9D7O/v88//MM//DZXXXXVVVf9e1G56qqrrrrq3+3s2bO3ctVVV1111VVXXXXVf5hhGLjqqquuuuo/BMFVV1111VX/bvfdd9+t9913363DMHDVVVddddVVV1111X+M++6771auuuqqq6769yK46qqrrrrqP8TZs2dvXa/XXHXVVVddddVVV13173P+/Hl+67d+67u56qqrrrrqPwLBVVddddVV/yHuu+++Ww8ODrjqqquuuuqqq6666qqrrrrqqv9BCK666qqrrvoPcd9999169913c9VVV1111VVXXXXVv8/BwQH/8A//8DtcddVVV131H4HKVVddddVV/yH+4R/+4Xe46qqrrrrqqquuuuqqq6666qr/WahcddVVV131H+Ls2bO3ctVVV1111VVXXXXVv9v+/j7/8A//8NtcddVVV131H4HKVVddddVV/yHuu+++W7nqqquuuuqqq6666t9tGAbuu+++W7nqqquuuuo/AsFVV1111VX/Yf7hH/7ht/f397nqqquuuuqqq6666t/m4OCAf/iHf/htrrrqqquu+o9C5aqrrrrqqquuuuqqq6666qqr/odYr9fcd999t3LVVVddddV/FIKrrrrqqqv+w/z93//9bz/5yU/mqquuuuqqq6666qp/m2EYuO+++27lqquuuuqq/yhUrrrqqquu+g/zD//wD7/DVVddddVVV1111VX/ZsMwcPbs2Wdw1VVXXXXVfxQqV1111VVX/Yc5e/bsrVx11VVXXXXVVVdd9W+2v7/PP/zDP/w2V1111VVX/UehctVVV1111VVXXXXVVVddddVV/0MMw8B99913K1ddddVVV/1HIbjqqquuuuo/zH333Xfrfffdd+swDFx11VVXXXXVVVdd9a8zDANXXXXVVVf9hyO46qqrrrrqP9TZs2dvXa/XXHXVVVddddVVV131r7O/v89v/dZvfQ9XXXXVVVf9RyK46qqrrrrqP9Tf//3f//bBwQFXXXXVVVddddVVV1111VVXXfU/AMFVV1111VX/oc6ePfuMu+++m6uuuuqqq6666qqr/nUODg74h3/4h9/mqquuuuqq/0hUrrrqqquu+g9133333cpVV1111VVXXXXVVf9q6/Was2fP3spVV1111VX/kahcddVVV131H+rs2bO3ctVVV1111VVXXXXVv9owDNx33323ctVVV1111X8kgquuuuqqq/5D3XfffbcCDMPAVVddddVVV1111VUvumEYuO+++27lqquuuuqq/0gEV1111VVX/Yf7h3/4h99er9dcddVVV1111VVXXfWi2d/f5x/+4R9+m6uuuuqqq/6jUbnqqquuuuo/3H333XfrMAxcddVVV1111VVXXfWiGYaB++6771auuuqqq676j0Zw1VVXXXXVf7j77rvv1mc84xlcddVVV1111VVXXfWiOTg44L777ruVq6666qqr/qNRueqqq6666j/cP/zDP/wOV1111VVXXXXVVVf9q5w9e/YZXHXVVVdd9R+NylVXXXXVVf/hzp49eytXXXXVVVddddVVV73I9vf3+Yd/+Iff5qqrrrrqqv9oVK666qqrrrrqqquuuuqqq6666r/ZMAzcd999t3LVVVddddV/NIKrrrrqqqv+w9133323/sM//MNv7+/vc9VVV1111VVXXXXVCzcMA/fdd9+tXHXVVVdd9Z+BylVXXXXVVVddddVVV1111VVX/Tfa39/nH/7hH36bq6666qqr/jMQXHXVVVdd9Z/i7//+73/74OCAq6666qqrrrrqqqteuGEYuO+++27lqquuuuqq/wwEV1111VVX/af4h3/4h9+5++67ueqqq6666qqrrrrqhRuGgbNnzz6Dq6666qqr/jNQueqqq6666qqrrrrqqquuuuqq/0b7+/v8wz/8w29z1VVXXXXVfwYqV1111VVX/ac4e/bsrVx11VVXXXXVVVdd9S8ahoGrrrrqqqv+0xBcddVVV131n+K+++679b777rt1GAauuuqqq6666qqrrnrh7rvvvlu56qqrrrrqPwPBVVddddVV/2nOnj1763q95qqrrrrqqquuuuqq5+/8+fP81m/91ndz1VVXXXXVfxaCq6666qqr/tPcd999tx4cHHDVVVddddVVV1111VVXXXXVVf9NCK666qqrrvpPc99999169913c9VVV1111VVXXXXV83dwcMA//MM//A5XXXXVVVf9Z6Fy1VVXXXXVf5p/+Id/+B2uuuqqq6666qqrrrrqqquuuuq/D5Wrrrrqqqv+05w9e/ZWrrrqqquuuuqqq656gfb39/mHf/iH3+aqq6666qr/LFSuuuqqq676T3PffffdylVXXXXVVVddddVVL9AwDNx33323ctVVV1111X8Wgquuuuqqq/5T/cM//MNv7+/vc9VVV1111VVXXXXVczo4OOAf/uEffpurrrrqqqv+M1G56qqrrrrqqquuuuqqq6666qr/Buv1mvvuu+9Wrrrqqquu+s9EcNVVV1111X+qv//7v//tJz/5yVx11VVXXXXVVVdd9ZyGYeC+++67lauuuuqqq/4zUbnqqquuuuo/1T/8wz/8DlddddVVV1111VVXPY9hGDh79uwzuOqqq6666j8Tlauuuuqqq/5TnT179lauuuqqq6666qqrrnoe+/v7/MM//MNvc9VVV1111X8mKlddddVVV1111VVXXXXVVVdd9d9gGAbuu+++W7nqqquuuuo/E8FVV1111VX/qe67775b77vvvlv39/e56qqrrrrqqquuuuqKYRi46qqrrrrqvwTBVVddddVV/+nOnj17K1ddddVVV1111VVXPcv+/j6/9Vu/9T1cddVVV131n43gqquuuuqq/3R///d//9sHBwdcddVVV1111VVXXXXVVVddddV/MYKrrrrqqqv+0509e/YZd999N1ddddVVV1111VVXXXFwcMA//MM//DZXXXXVVVf9Z6Ny1VVXXXXVf7r77rvvVq666qqrrrrqqquuepb9/X3Onj17K1ddddVVV/1no3LVVVddddV/urNnz97KVVddddVVV1111VXP4b777ruVq6666qqr/rMRXHXVVVdd9Z/uvvvuuxVgGAauuuqqq6666qqrroJhGLjvvvtu5aqrrrrqqv9sBFddddVVV/2X+Id/+IffXq/XXHXVVVddddVVV/1/t7+/zz/8wz/8NlddddVVV/1XoHLVVVddddV/ifvuu+/WYRi46qqrrrrqqquu+v9uGAbuu+++W7nqqquuuuq/AsFVV1111VX/Je67775bn/GMZ3DVVVddddVVV131/93BwQH33XffrVx11VVXXfVfgcpVV1111VX/Jf7hH/7hd7jqqquuuuqqq6666rKzZ88+g6uuuuqqq/4rULnqqquuuuq/xNmzZ2/lqquuuuqqq6666ir29/f5h3/4h9/mqquuuuqq/wpUrrrqqquuuuqqq6666qqrrrrqv9AwDNx33323ctVVV1111X8Fgquuuuqqq/5L3Hfffbf+wz/8w2/v7+9z1VVXXXXVVVdd9f/VMAzcd999t3LVVVddddV/FSpXXXXVVVddddVVV1111VVXXfVfZH9/n3/4h3/4ba666qqrrvqvQnDVVVddddV/mb//+7//7YODA6666qqrrrrqqqv+vxqGgfvuu+9Wrrrqqquu+q9CcNVVV1111X+Zf/iHf/idu+++m6uuuuqqq6666qr/r4Zh4OzZs8/gqquuuuqq/ypUrrrqqquuuuqqq6666qqrrrrqv8j+/j7/8A//8NtcddVVV131X4XKVVddddVV/2XOnj17K1ddddVVV1111VX/jw3DwFVXXXXVVf+lCK666qqrrvovc999991633333ToMA1ddddVVV1111VX/X9133323ctVVV1111X8Vgquuuuqqq/5LnT179tb1es1VV1111VVXXXXV/zfnz5/nt37rt76bq6666qqr/isRXHXVVVdd9V/qvvvuu/Xg4ICrrrrqqquuuuqqq6666qqrrvovQHDVVVddddV/qfvuu+/Wu+++m6uuuuqqq6666qr/bw4ODviHf/iH3+Gqq6666qr/SlSuuuqqq676L/UP//APv8NVV1111VVXXXXVVVddddVVV/3XoHLVVVddddV/qbNnz97KVVddddVVV1111f9D+/v7/MM//MNvc9VVV1111X8lKlddddVVV/2Xuu+++27lqquuuuqqq6666v+hYRi47777buWqq6666qr/SgRXXXXVVVf9l/uHf/iH397f3+eqq6666qqrrrrq/4uDgwP+4R/+4be56qqrrrrqvxqVq6666qqrrrrqqquuuuqqq676T7Zer7nvvvtu5aqrrrrqqv9qBFddddVVV/2X+/u///vffvKTn8xVV1111VVXXXXV/xfDMHDffffdylVXXXXVVf/VqFx11VVXXfVf7h/+4R9+h6uuuuqqq6666qr/R4Zh4OzZs8/gqquuuuqq/2pUrrrqqquu+i939uzZW7nqqquuuuqqq676f2R/f59/+Id/+G2uuuqqq676r0blqquuuuqqq6666qqrrrrqqqv+kw3DwH333XcrV1111VVX/VcjuOqqq6666r/cfffdd+t999136/7+PlddddVVV1111VX/1w3DwFVXXXXVVf9tCK666qqrrvpvcfbs2Vu56qqrrrrqqquu+n9gf3+f3/qt3/oerrrqqquu+u9AcNVVV1111X+Lv//7v//tg4MDrrrqqquuuuqqq6666qqrrrrqPxHBVVddddVV/y3Onj37jLvvvpurrrrqqquuuuqq/+sODg74h3/4h9/mqquuuuqq/w5Urrrqqquu+m9x33333cpVV1111VVXXXXV/wP7+/ucPXv2Vq666qqrrvrvQOWqq6666qr/FmfPnr2Vq6666qqrrrrqqv8n7rvvvlu56qqrrrrqvwPBVVddddVV/y3uu+++WwGGYeCqq6666qqrrrrq/7JhGLjvvvtu5aqrrrrqqv8OBFddddVVV/23+Yd/+IffXq/XXHXVVVddddVVV/1ftb+/zz/8wz/8NlddddVVV/13oXLVVVddddV/m/vuu+/WYRi46qqrrrrqqquu+r9qGAbuu+++W7nqqquuuuq/C8FVV1111VX/be67775bn/GMZ3DVVVddddVVV131f9XBwQH33XffrVx11VVXXfXfhcpVV1111VX/bf7hH/7hd7jqqquuuuqqq676P+7s2bPP4Kqrrrrqqv8uVK666qqrrvpvc/bs2Vu56qqrrrrqqquu+j9sf3+ff/iHf/htrrrqqquu+u9C5aqrrrrqqquuuuqqq6666qqr/pMMw8B99913K1ddddVVV/13Ibjqqquuuuq/zX333XfrP/zDP/z2/v4+V1111VVXXXXVVf/XDMPAfffddytXXXXVVVf9d6Jy1VVXXXXVVVddddVVV1111VX/Cfb39/mHf/iH3+aqq6666qr/TgRXXXXVVVf9t/r7v//73z44OOCqq6666qqrrrrq/5phGLjvvvtu5aqrrrrqqv9OBFddddVVV/23+od/+Iffufvuu7nqqquuuuqqq676v2YYBs6ePfsMrrrqqquu+u9E5aqrrrrqqquuuuqqq6666qqr/hPs7+/zD//wD7/NVVddddVV/52oXHXVVVdd9d/q7Nmzt3LVVVddddVVV131f9AwDFx11VVXXfXfjuCqq6666qr/Vvfdd9+t9913363DMHDVVVddddVVV131f8199913K1ddddVVV/13Irjqqquuuuq/3dmzZ29dr9dcddVVV1111VVX/V9x/vx5fuu3fuu7ueqqq6666r8bwVVXXXXVVf/t/v7v//63Dw4OuOqqq6666qqrrrrqqquuuuqq/2AEV1111VVX/Y9w9913c9VVV1111VVXXfV/xcHBAf/wD//wO1x11VVXXfXfjcpVV1111VX/7f7hH/7hd7jqqquuuuqqq6666qqrrrrqqv94VK666qqrrvpvd/bs2Vu56qqrrrrqqquu+j9kf3+ff/iHf/htrrrqqquu+u9G5aqrrrrqqv929913361cddVVV1111VVX/R8yDAP33XffrVx11VVXXfXfjeCqq6666qr/Ef7hH/7ht/f397nqqquuuuqqq6763+7g4IB/+Id/+G2uuuqqq676n4DKVVddddVVV1111VVXXXXVVVf9B1qv19x33323ctVVV1111f8EBFddddVVV/2P8Pd///e//eQnP5mrrrrqqquuuuqq/+2GYeC+++67lauuuuqqq/4noHLVVVddddX/CP/wD//wO1x11VVXXXXVVVf9HzAMA2fPnn0GV1111VVX/U9A5aqrrrrqqv8Rzp49eytXXXXVVVddddVV/wfs7+/zD//wD7/NVVddddVV/xNQueqqq6666qqrrrrqqquuuuqq/0DDMHDffffdylVXXXXVVf8TEFx11VVXXfU/wn333Xfrfffdd+v+/j5XXXXVVVddddVV/1sNw8BVV1111VX/oxBcddVVV131P8bZs2dv5aqrrrrqqquuuup/sf39fX7rt37re7jqqquuuup/CoKrrrrqqqv+x/j7v//73z44OOCqq6666qqrrrrqqquuuuqqq/6DEFx11VVXXfU/xtmzZ59x9913c9VVV1111VVXXfW/1cHBAf/wD//w21x11VVXXfU/BZWrrrrqqqv+x7jvvvtu5aqrrrrqqquuuup/sf39fc6ePXsrV1111VVX/U9B5aqrrrrqqv8xzp49eytXXXXVVVddddVV/8vdd999t3LVVVddddX/FARXXXXVVVf9j3HffffdCjAMA1ddddVVV1111VX/Gw3DwH333XcrV1111VVX/U9BcNVVV1111f8o//AP//Db6/Waq6666qqrrrrqqv9t9vf3+Yd/+Iff5qqrrrrqqv9JqFx11VVXXfU/yn333XfrMAxcddVVV1111VVX/W8zDAP33XffrVx11VVXXfU/CcFVV1111VX/o9x33323PuMZz+Cqq6666qqrrrrqf5uDgwP+4R/+4Xe46qqrrrrqfxIqV1111VVX/Y/yD//wD7/DVVddddVVV1111VVXXXXVVVf9x6By1VVXXXXV/yhnz569lauuuuqqq6666qr/hfb39/mHf/iH3+aqq6666qr/SahcddVVV1111VVXXXXVVVddddV/gGEYuO+++27lqquuuuqq/0kIrrrqqquu+h/lvvvuu/Uf/uEffnt/f5+rrrrqqquuuuqq/y2GYeC+++67lauuuuqqq/6noXLVVVddddVVV1111VVXXXXVVf9O+/v7/MM//MNvc9VVV1111f80BFddddVVV/2P8/d///e/fXBwwFVXXXXVVVddddX/FsMwcN99993KVVddddVV/9MQXHXVVVdd9T/OP/zDP/zO3XffzVVXXXXVVVddddX/FsMwcPbs2Wdw1VVXXXXV/zRUrrrqqquuuuqqq6666qqrrrrq32l/f59/+Id/+G2uuuqqq676n4bKVVddddVV/+OcPXv2Vq666qqrrrrqqqv+FxmGgauuuuqqq/5HIrjqqquuuup/nPvuu+/W++6779ZhGLjqqquuuuqqq6763+K+++67lauuuuqqq/6nIbjqqquuuup/pLNnz966Xq+56qqrrrrqqquu+p/u/Pnz/NZv/dZ3c9VVV1111f9EBFddddVVV/2P9Pd///e/fXBwwFVXXXXVVVddddVVV1111VVX/TsQXHXVVVdd9T/W3XffzVVXXXXVVVddddX/dAcHB/zDP/zD73DVVVddddX/RFSuuuqqq676H+kf/uEffoerrrrqqquuuuqqq6666qqrrvr3oXLVVVddddX/SGfPnr2Vq6666qqrrrrqqv8F9vf3+Yd/+Iff5qqrrrrqqv+JqFx11VVXXfU/0n333XcrV1111VVXXXXVVf8LDMPAfffddytXXXXVVVf9T0Rw1VVXXXXV/1j/8A//8Nv7+/tcddVVV1111VVX/U91cHDAP/zDP/w2V1111VVX/U9F5aqrrrrqqquuuuqqq6666qqr/o3W6zX33XffrVx11VVXXfU/FcFVV1111VX/Y/393//9bz/5yU/mqquuuuqqq6666n+qYRi47777buWqq6666qr/qahcddVVV131P9Y//MM//A5XXXXVVVddddVV/4MNw8DZs2efwVVXXXXVVf9TUbnqqquuuup/rLNnz97KVVddddVVV1111f9g+/v7/MM//MNvc9VVV1111f9UVK666qqrrrrqqquuuuqqq6666t9oGAbuu+++W7nqqquuuup/KoKrrrrqqqv+x7rvvvtuve+++27d39/nqquuuuqqq6666n+aYRi46qqrrrrqfzyCq6666qqr/kc7e/bsrVx11VVXXXXVVVf9D7S/v89v/dZvfQ9XXXXVVVf9T0Zw1VVXXXXV/2h///d//9sHBwdcddVVV1111VVXXXXVVVddddW/AcFVV1111VX/o509e/YZd999N1ddddVVV1111VX/0xwcHPAP//APv81VV1111VX/k1G56qqrrrrqf7T77rvvVq666qqrrrrqqqv+B9rf3+fs2bO3ctVVV1111f9kVK666qqrrvof7ezZs7dy1VVXXXXVVVdd9T/UfffddytXXXXVVVf9T0Zw1VVXXXXV/2j33XffrQDDMHDVVVddddVVV131P8kwDNx33323ctVVV1111f9kBFddddVVV/2P9w//8A+/vV6vueqqq6666qqrrvqfYn9/n3/4h3/4ba666qqrrvqfjspVV1111VX/49133323DsPAVVddddVVV1111f8UwzBw33333cpVV1111VX/0xFcddVVV131P95999136zOe8Qyuuuqqq6666qqr/qc4ODjgH/7hH36Hq6666qqr/qejctVVV1111f94//AP//A7XHXVVVddddVVV1111VVXXXXVvx6Vq6666qqr/sc7e/bsrVx11VVXXXXVVVf9D7K/v88//MM//DZXXXXVVVf9T0flqquuuuqqq6666qqrrrrqqqv+lYZh4L777ruVq6666qqr/qcjuOqqq6666n+8++6779Z/+Id/+O39/X2uuuqqq6666qqr/rsNw8B99913K1ddddVVV/1vQOWqq6666qqrrrrqqquuuuqqq/4V9vf3+Yd/+Iff5qqrrrrqqv8NCK666qqrrvpf4e///u9/++DggKuuuuqqq6666qr/bsMwcN99993KVVddddVV/xsQXHXVVVdd9b/CP/zDP/zO3XffzVVXXXXVVVddddV/t2EYOHv27DO46qqrrrrqfwMqV1111VVXXXXVVVddddVVV131r7C/v88//MM//DZXXXXVVVf9b0Dlqquuuuqq/xXOnj17K1ddddVVV1111VX/AwzDwFVXXXXVVf9rEFx11VVXXfW/wn333Xfrfffdd+swDFx11VVXXXXVVVf9d7vvvvtu5aqrrrrqqv8NCK666qqrrvpf4+zZs7eu12uuuuqqq6666qqr/rucP3+e3/qt3/purrrqqquu+t+C4Kqrrrrqqv81/v7v//63Dw4OuOqqq6666qqrrrrqqquuuuqqFxHBVVddddVV/6vcfffdXHXVVVddddVVV/13ecYznsE//MM//A5XXXXVVVf9b0Hlqquuuuqq/zV++7d/+3vOnj37jA//8A//Lq666qqrrrrqqqv+G/zIj/zIZ//Wb/3Wd3PVVVddddX/FuhBD3oQV1111VVXXXXVVVddddVVV1111VVXXXXV/0lUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V/GPl+emO0MXBNcAAAAASUVORK5CYII=) + + diff --git a/docs/kcl-std/index.md b/docs/kcl-std/index.md index 6d125d707..0435fa2f3 100644 --- a/docs/kcl-std/index.md +++ b/docs/kcl-std/index.md @@ -75,10 +75,12 @@ layout: manual * [`patternCircular2d`](/docs/kcl-std/functions/std-sketch-patternCircular2d) * [`patternLinear2d`](/docs/kcl-std/functions/std-sketch-patternLinear2d) * [`patternTransform2d`](/docs/kcl-std/functions/std-sketch-patternTransform2d) + * [`planeOf`](/docs/kcl-std/functions/std-sketch-planeOf) * [`polygon`](/docs/kcl-std/functions/std-sketch-polygon) * [`profileStart`](/docs/kcl-std/functions/std-sketch-profileStart) * [`profileStartX`](/docs/kcl-std/functions/std-sketch-profileStartX) * [`profileStartY`](/docs/kcl-std/functions/std-sketch-profileStartY) + * [`rectangle`](/docs/kcl-std/functions/std-sketch-rectangle) * [`revolve`](/docs/kcl-std/functions/std-sketch-revolve) * [`segAng`](/docs/kcl-std/functions/std-sketch-segAng) * [`segEnd`](/docs/kcl-std/functions/std-sketch-segEnd) diff --git a/docs/kcl-std/modules/std-sketch.md b/docs/kcl-std/modules/std-sketch.md index a3321c3c1..950cdd6f6 100644 --- a/docs/kcl-std/modules/std-sketch.md +++ b/docs/kcl-std/modules/std-sketch.md @@ -40,10 +40,12 @@ This module contains functions for creating and manipulating sketches, and makin * [`patternCircular2d`](/docs/kcl-std/functions/std-sketch-patternCircular2d) * [`patternLinear2d`](/docs/kcl-std/functions/std-sketch-patternLinear2d) * [`patternTransform2d`](/docs/kcl-std/functions/std-sketch-patternTransform2d) +* [`planeOf`](/docs/kcl-std/functions/std-sketch-planeOf) * [`polygon`](/docs/kcl-std/functions/std-sketch-polygon) * [`profileStart`](/docs/kcl-std/functions/std-sketch-profileStart) * [`profileStartX`](/docs/kcl-std/functions/std-sketch-profileStartX) * [`profileStartY`](/docs/kcl-std/functions/std-sketch-profileStartY) +* [`rectangle`](/docs/kcl-std/functions/std-sketch-rectangle) * [`revolve`](/docs/kcl-std/functions/std-sketch-revolve) * [`segAng`](/docs/kcl-std/functions/std-sketch-segAng) * [`segEnd`](/docs/kcl-std/functions/std-sketch-segEnd) diff --git a/e2e/playwright/command-bar-tests.spec.ts b/e2e/playwright/command-bar-tests.spec.ts index 3e4d28391..4cea2adc4 100644 --- a/e2e/playwright/command-bar-tests.spec.ts +++ b/e2e/playwright/command-bar-tests.spec.ts @@ -525,7 +525,9 @@ test.describe('Command bar tests', () => { const projectName = 'test' const beforeKclCode = `a = 5 b = a * a -c = 3 + a` +c = 3 + a +theta = 45deg +` await context.folderSetupFn(async (dir) => { const testProject = join(dir, projectName) await fsp.mkdir(testProject, { recursive: true }) @@ -615,9 +617,45 @@ c = 3 + a` stage: 'commandBarClosed', }) }) + await test.step(`Edit a parameter with explicit units via command bar`, async () => { + await cmdBar.cmdBarOpenBtn.click() + await cmdBar.chooseCommand('edit parameter') + await cmdBar + .selectOption({ + name: 'theta', + }) + .click() + await cmdBar.expectState({ + stage: 'arguments', + commandName: 'Edit parameter', + currentArgKey: 'value', + currentArgValue: '45deg', + headerArguments: { + Name: 'theta', + Value: '', + }, + highlightedHeaderArg: 'value', + }) + await cmdBar.argumentInput + .locator('[contenteditable]') + .fill('45deg + 1deg') + await cmdBar.progressCmdBar() + await cmdBar.expectState({ + stage: 'review', + commandName: 'Edit parameter', + headerArguments: { + Name: 'theta', + Value: '46deg', + }, + }) + await cmdBar.progressCmdBar() + await cmdBar.expectState({ + stage: 'commandBarClosed', + }) + }) await editor.expectEditor.toContain( - `a = 5b = a * amyParameter001 = ${newValue}c = 3 + a` + `a = 5b = a * amyParameter001 = ${newValue}c = 3 + atheta = 45deg + 1deg` ) }) diff --git a/e2e/playwright/point-click.spec.ts b/e2e/playwright/point-click.spec.ts index 780269cd6..31e4ffc0b 100644 --- a/e2e/playwright/point-click.spec.ts +++ b/e2e/playwright/point-click.spec.ts @@ -136,17 +136,17 @@ test.describe('Point-and-click tests', () => { highlightedHeaderArg: 'length', commandName: 'Extrude', }) - await page.keyboard.insertText('width - 0.001') + await page.keyboard.insertText('width - 0.001in') await cmdBar.progressCmdBar() await cmdBar.expectState({ stage: 'review', headerArguments: { - Length: '4.999', + Length: '4.999in', }, commandName: 'Extrude', }) await cmdBar.progressCmdBar() - await editor.expectEditor.toContain('extrude(length = width - 0.001)') + await editor.expectEditor.toContain('extrude(length = width - 0.001in)') }) await test.step(`Edit second extrude via feature tree`, async () => { diff --git a/interface.d.ts b/interface.d.ts index 8b6af29bd..1a2904e2b 100644 --- a/interface.d.ts +++ b/interface.d.ts @@ -79,7 +79,6 @@ export interface IElectronAPI { VITE_KC_API_BASE_URL: string VITE_KC_SITE_BASE_URL: string VITE_KC_SITE_APP_URL: string - VITE_KC_SKIP_AUTH: string VITE_KC_CONNECTION_TIMEOUT_MS: string VITE_KC_DEV_TOKEN: string NODE_ENV: string diff --git a/jest-component-unit-tests/billing.jesttest.tsx b/jest-component-unit-tests/billing.jesttest.tsx index 263cf6e13..3ce869f8f 100644 --- a/jest-component-unit-tests/billing.jesttest.tsx +++ b/jest-component-unit-tests/billing.jesttest.tsx @@ -125,18 +125,57 @@ test('Shows a loading spinner when uninitialized credit count', async () => { await expect(queryByTestId('spinner')).toBeVisible() }) -test('Shows the total credits for Unknown subscription', async () => { - const data = { - balance: { - monthlyApiCreditsRemaining: 10, - stableApiCreditsRemaining: 25, - }, - subscriptions: { - monthlyPayAsYouGoApiCreditsTotal: 20, - name: "unknown", - } +const unKnownTierData = { + balance: { + monthlyApiCreditsRemaining: 10, + stableApiCreditsRemaining: 25, + }, + subscriptions: { + monthlyPayAsYouGoApiCreditsTotal: 20, + name: "unknown", } +} +const freeTierData = { + balance: { + monthlyApiCreditsRemaining: 10, + stableApiCreditsRemaining: 0, + }, + subscriptions: { + monthlyPayAsYouGoApiCreditsTotal: 20, + name: "free", + } +} + +const proTierData = { + // These are all ignored + balance: { + monthlyApiCreditsRemaining: 10, + stableApiCreditsRemaining: 0, + }, + subscriptions: { + // This should be ignored because it's Pro tier. + monthlyPayAsYouGoApiCreditsTotal: 20, + name: "pro", + } +} + +const enterpriseTierData = { + // These are all ignored, user is part of an org. + balance: { + monthlyApiCreditsRemaining: 10, + stableApiCreditsRemaining: 0, + }, + subscriptions: { + // This should be ignored because it's Pro tier. + monthlyPayAsYouGoApiCreditsTotal: 20, + // This should be ignored because the user is part of an Org. + name: "free", + } +} + +test('Shows the total credits for Unknown subscription', async () => { + const data = unKnownTierData server.use( http.get('*/user/payment/balance', (req, res, ctx) => { return HttpResponse.json(createUserPaymentBalanceResponse(data.balance)) @@ -166,17 +205,7 @@ test('Shows the total credits for Unknown subscription', async () => { }) test('Progress bar reflects ratio left of Free subscription', async () => { - const data = { - balance: { - monthlyApiCreditsRemaining: 10, - stableApiCreditsRemaining: 0, - }, - subscriptions: { - monthlyPayAsYouGoApiCreditsTotal: 20, - name: "free", - } - } - + const data = freeTierData server.use( http.get('*/user/payment/balance', (req, res, ctx) => { return HttpResponse.json(createUserPaymentBalanceResponse(data.balance)) @@ -212,19 +241,7 @@ test('Progress bar reflects ratio left of Free subscription', async () => { }) }) test('Shows infinite credits for Pro subscription', async () => { - const data = { - // These are all ignored - balance: { - monthlyApiCreditsRemaining: 10, - stableApiCreditsRemaining: 0, - }, - subscriptions: { - // This should be ignored because it's Pro tier. - monthlyPayAsYouGoApiCreditsTotal: 20, - name: "pro", - } - } - + const data = proTierData server.use( http.get('*/user/payment/balance', (req, res, ctx) => { return HttpResponse.json(createUserPaymentBalanceResponse(data.balance)) @@ -255,19 +272,7 @@ test('Shows infinite credits for Pro subscription', async () => { await expect(queryByTestId('billing-remaining-progress-bar-inline')).toBe(null) }) test('Shows infinite credits for Enterprise subscription', async () => { - const data = { - // These are all ignored, user is part of an org. - balance: { - monthlyApiCreditsRemaining: 10, - stableApiCreditsRemaining: 0, - }, - subscriptions: { - // This should be ignored because it's Pro tier. - monthlyPayAsYouGoApiCreditsTotal: 20, - // This should be ignored because the user is part of an Org. - name: "free", - } - } + const data = enterpriseTierData server.use( http.get('*/user/payment/balance', (req, res, ctx) => { @@ -297,3 +302,58 @@ test('Shows infinite credits for Enterprise subscription', async () => { await expect(queryByTestId('infinity')).toBeVisible() await expect(queryByTestId('billing-remaining-progress-bar-inline')).toBe(null) }) + +test('Show upgrade button if credits are not infinite', async () => { + const data = freeTierData + server.use( + http.get('*/user/payment/balance', (req, res, ctx) => { + return HttpResponse.json(createUserPaymentBalanceResponse(data.balance)) + }), + http.get('*/user/payment/subscriptions', (req, res, ctx) => { + return HttpResponse.json(createUserPaymentSubscriptionsResponse(data.subscriptions)) + }), + http.get('*/org', (req, res, ctx) => { + return new HttpResponse(403) + }), + ) + + const billingActor = createActor(billingMachine, { input: BILLING_CONTEXT_DEFAULTS }).start() + + const { queryByTestId } = render() + + await act(() => { + billingActor.send({ type: BillingTransition.Update, apiToken: "it doesn't matter wtf this is :)" }) + }) + + await expect(queryByTestId('billing-upgrade-button')).toBeVisible() +}) + +test('Hide upgrade button if credits are infinite', async () => { + const data = enterpriseTierData + server.use( + http.get('*/user/payment/balance', (req, res, ctx) => { + return HttpResponse.json(createUserPaymentBalanceResponse(data.balance)) + }), + http.get('*/user/payment/subscriptions', (req, res, ctx) => { + return HttpResponse.json(createUserPaymentSubscriptionsResponse(data.subscriptions)) + }), + // Ok finally the first use of an org lol + http.get('*/org', (req, res, ctx) => { + return HttpResponse.json(createOrgResponse()) + }), + ) + + const billingActor = createActor(billingMachine, { input: BILLING_CONTEXT_DEFAULTS }).start() + + const { queryByTestId } = render() + + await act(() => { + billingActor.send({ type: BillingTransition.Update, apiToken: "it doesn't matter wtf this is :)" }) + }) + + await expect(queryByTestId('billing-upgrade-button')).toBe(null) +}) diff --git a/package-lock.json b/package-lock.json index f6d219b48..798907d5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3652,303 +3652,285 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", + "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz", + "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz", + "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz", + "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz", + "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz", + "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz", + "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz", + "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz", + "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz", + "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz", + "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==", "cpu": [ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz", + "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==", "cpu": [ "loong64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz", + "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==", "cpu": [ "mips64el" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz", + "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz", + "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz", + "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==", "cpu": [ "s390x" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz", + "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", - "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz", + "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "netbsd" @@ -3958,31 +3940,29 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz", + "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", - "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz", + "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openbsd" @@ -3992,88 +3972,83 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz", + "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz", + "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz", + "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz", + "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==", "cpu": [ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz", + "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -13244,42 +13219,43 @@ "optional": true }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", + "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", "dev": true, "hasInstallScript": true, - "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "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" + "@esbuild/aix-ppc64": "0.25.3", + "@esbuild/android-arm": "0.25.3", + "@esbuild/android-arm64": "0.25.3", + "@esbuild/android-x64": "0.25.3", + "@esbuild/darwin-arm64": "0.25.3", + "@esbuild/darwin-x64": "0.25.3", + "@esbuild/freebsd-arm64": "0.25.3", + "@esbuild/freebsd-x64": "0.25.3", + "@esbuild/linux-arm": "0.25.3", + "@esbuild/linux-arm64": "0.25.3", + "@esbuild/linux-ia32": "0.25.3", + "@esbuild/linux-loong64": "0.25.3", + "@esbuild/linux-mips64el": "0.25.3", + "@esbuild/linux-ppc64": "0.25.3", + "@esbuild/linux-riscv64": "0.25.3", + "@esbuild/linux-s390x": "0.25.3", + "@esbuild/linux-x64": "0.25.3", + "@esbuild/netbsd-arm64": "0.25.3", + "@esbuild/netbsd-x64": "0.25.3", + "@esbuild/openbsd-arm64": "0.25.3", + "@esbuild/openbsd-x64": "0.25.3", + "@esbuild/sunos-x64": "0.25.3", + "@esbuild/win32-arm64": "0.25.3", + "@esbuild/win32-ia32": "0.25.3", + "@esbuild/win32-x64": "0.25.3" } }, "node_modules/escalade": { @@ -25297,6 +25273,412 @@ } } }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "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" + } + }, "node_modules/vite/node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -26279,7 +26661,7 @@ "@vscode/test-electron": "^2.4.1", "@vscode/vsce": "^3.3.2", "cross-env": "^7.0.3", - "esbuild": "^0.25.2", + "esbuild": "^0.25.3", "glob": "^11.0.1", "mocha": "^11.1.0", "typescript": "^5.8.3" @@ -26288,397 +26670,6 @@ "vscode": "^1.97.0" } }, - "rust/kcl-language-server/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", - "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/android-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", - "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/android-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", - "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/android-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", - "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", - "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/darwin-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", - "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", - "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", - "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/linux-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", - "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/linux-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", - "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/linux-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", - "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/linux-loong64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", - "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", - "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", - "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", - "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/linux-s390x": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", - "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/linux-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", - "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", - "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", - "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/sunos-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", - "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/win32-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", - "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/win32-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", - "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "rust/kcl-language-server/node_modules/@esbuild/win32-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", - "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, "rust/kcl-language-server/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -26689,47 +26680,6 @@ "balanced-match": "^1.0.0" } }, - "rust/kcl-language-server/node_modules/esbuild": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", - "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.2", - "@esbuild/android-arm": "0.25.2", - "@esbuild/android-arm64": "0.25.2", - "@esbuild/android-x64": "0.25.2", - "@esbuild/darwin-arm64": "0.25.2", - "@esbuild/darwin-x64": "0.25.2", - "@esbuild/freebsd-arm64": "0.25.2", - "@esbuild/freebsd-x64": "0.25.2", - "@esbuild/linux-arm": "0.25.2", - "@esbuild/linux-arm64": "0.25.2", - "@esbuild/linux-ia32": "0.25.2", - "@esbuild/linux-loong64": "0.25.2", - "@esbuild/linux-mips64el": "0.25.2", - "@esbuild/linux-ppc64": "0.25.2", - "@esbuild/linux-riscv64": "0.25.2", - "@esbuild/linux-s390x": "0.25.2", - "@esbuild/linux-x64": "0.25.2", - "@esbuild/netbsd-arm64": "0.25.2", - "@esbuild/netbsd-x64": "0.25.2", - "@esbuild/openbsd-arm64": "0.25.2", - "@esbuild/openbsd-x64": "0.25.2", - "@esbuild/sunos-x64": "0.25.2", - "@esbuild/win32-arm64": "0.25.2", - "@esbuild/win32-ia32": "0.25.2", - "@esbuild/win32-x64": "0.25.2" - } - }, "rust/kcl-language-server/node_modules/glob": { "version": "11.0.1", "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", diff --git a/package.json b/package.json index 5d56502a2..07ebc3da8 100644 --- a/package.json +++ b/package.json @@ -110,8 +110,11 @@ "remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\" || echo \"sed for both mac and linux\"", "lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src", "lint": "eslint --max-warnings 0 --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src", + "url-checker":"./scripts/url-checker.sh", + "url-checker:overwrite":"npm run url-checker > scripts/known/urls.txt", + "url-checker:diff":"./scripts/diff-url-checker.sh", "circular-deps": "dpdm --no-warning --no-tree -T --skip-dynamic-imports=circular src/index.tsx", - "circular-deps:overwrite": "npm run circular-deps | sed '$d' | grep -v '^npm run' > known-circular.txt", + "circular-deps:overwrite": "npm run circular-deps | sed '$d' | grep -v '^npm run' > scripts/known/circular.txt", "circular-deps:diff": "./scripts/diff-circular-deps.sh", "circular-deps:diff:nodejs": "npm run circular-deps:diff || node ./scripts/diff.js", "files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json", diff --git a/packages/codemirror-lsp-client/package.json b/packages/codemirror-lsp-client/package.json index 3462e18cf..7c937934e 100644 --- a/packages/codemirror-lsp-client/package.json +++ b/packages/codemirror-lsp-client/package.json @@ -29,7 +29,7 @@ "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/node": "^22.14.1", + "@types/node": "^24.0.7", "ts-node": "^10.9.2" } } diff --git a/public/kcl-samples/screenshots/surgical-drill-guide.png b/public/kcl-samples/screenshots/surgical-drill-guide.png index e69de29bb..b75b445e2 100644 Binary files a/public/kcl-samples/screenshots/surgical-drill-guide.png and b/public/kcl-samples/screenshots/surgical-drill-guide.png differ diff --git a/public/kcl-samples/screenshots/teapot.png b/public/kcl-samples/screenshots/teapot.png index e69de29bb..e6e5b5b5f 100644 Binary files a/public/kcl-samples/screenshots/teapot.png and b/public/kcl-samples/screenshots/teapot.png differ diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 975f569b7..4b883a65a 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -178,7 +178,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -189,7 +189,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -211,7 +211,7 @@ checksum = "e12882f59de5360c748c4cbf569a042d5fb0eb515f7bea9c1f470b47f6ffbd73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -514,7 +514,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -740,7 +740,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -751,7 +751,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -810,7 +810,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -831,7 +831,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -841,7 +841,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -906,7 +906,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -944,7 +944,7 @@ checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -1119,7 +1119,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -1223,7 +1223,7 @@ dependencies = [ "inflections", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -1599,7 +1599,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -1814,7 +1814,7 @@ dependencies = [ [[package]] name = "kcl-bumper" -version = "0.1.83" +version = "0.1.84" dependencies = [ "anyhow", "clap", @@ -1825,26 +1825,26 @@ dependencies = [ [[package]] name = "kcl-derive-docs" -version = "0.1.83" +version = "0.1.84" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] name = "kcl-directory-test-macro" -version = "0.1.83" +version = "0.1.84" dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] name = "kcl-language-server" -version = "0.2.83" +version = "0.2.84" dependencies = [ "anyhow", "clap", @@ -1865,7 +1865,7 @@ dependencies = [ [[package]] name = "kcl-language-server-release" -version = "0.1.83" +version = "0.1.84" dependencies = [ "anyhow", "clap", @@ -1885,7 +1885,7 @@ dependencies = [ [[package]] name = "kcl-lib" -version = "0.2.83" +version = "0.2.84" dependencies = [ "anyhow", "approx 0.5.1", @@ -1962,7 +1962,7 @@ dependencies = [ [[package]] name = "kcl-python-bindings" -version = "0.3.83" +version = "0.3.84" dependencies = [ "anyhow", "kcl-lib", @@ -1977,7 +1977,7 @@ dependencies = [ [[package]] name = "kcl-test-server" -version = "0.1.83" +version = "0.1.84" dependencies = [ "anyhow", "hyper 0.14.32", @@ -1990,7 +1990,7 @@ dependencies = [ [[package]] name = "kcl-to-core" -version = "0.1.83" +version = "0.1.84" dependencies = [ "anyhow", "async-trait", @@ -2004,7 +2004,7 @@ dependencies = [ [[package]] name = "kcl-wasm-lib" -version = "0.1.83" +version = "0.1.84" dependencies = [ "anyhow", "bson", @@ -2102,7 +2102,7 @@ dependencies = [ "kittycad-modeling-cmds-macros-impl", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -2112,7 +2112,7 @@ source = "git+https://github.com/KittyCAD/modeling-api.git?branch=ben/conics#b8b dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -2308,7 +2308,7 @@ checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -2637,7 +2637,7 @@ dependencies = [ "regex", "regex-syntax 0.8.5", "structmeta", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -2651,7 +2651,7 @@ dependencies = [ "regex", "regex-syntax 0.8.5", "structmeta", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -2707,7 +2707,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -2751,7 +2751,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -2806,7 +2806,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -2918,7 +2918,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -2978,7 +2978,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -2991,7 +2991,7 @@ dependencies = [ "proc-macro2", "pyo3-build-config", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -3489,7 +3489,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -3553,7 +3553,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -3564,7 +3564,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -3588,14 +3588,14 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] @@ -3812,7 +3812,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta-derive", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -3823,7 +3823,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -3845,7 +3845,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -3888,9 +3888,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.103" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -3914,7 +3914,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -3938,7 +3938,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -4047,7 +4047,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -4058,7 +4058,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -4170,7 +4170,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -4214,9 +4214,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", @@ -4235,9 +4235,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap 2.9.0", "serde", @@ -4338,7 +4338,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -4366,7 +4366,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -4446,7 +4446,7 @@ checksum = "e9d4ed7b4c18cc150a6a0a1e9ea1ecfa688791220781af6e119f9599a8502a0a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", "termcolor", ] @@ -4632,7 +4632,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -4703,7 +4703,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", "wasm-bindgen-shared", ] @@ -4739,7 +4739,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4774,7 +4774,7 @@ checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -5064,7 +5064,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", "synstructure", ] @@ -5109,7 +5109,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -5120,7 +5120,7 @@ checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -5140,7 +5140,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", "synstructure", ] @@ -5161,7 +5161,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] @@ -5183,7 +5183,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.104", ] [[package]] diff --git a/rust/justfile b/rust/justfile index f098f05de..b85c94480 100644 --- a/rust/justfile +++ b/rust/justfile @@ -8,6 +8,9 @@ lint: # Ensure we can build without extra feature flags. cargo clippy -p kcl-lib --all-targets -- -D warnings +lint-fix: + cargo clippy --workspace --all-targets --all-features --fix + # Run the stdlib docs generation redo-kcl-stdlib-docs-no-imgs: EXPECTORATE=overwrite {{cnr}} {{kcl_lib_flags}} docs::gen_std_tests::test_generate_stdlib diff --git a/rust/kcl-bumper/Cargo.toml b/rust/kcl-bumper/Cargo.toml index 16dee4e08..db2c82531 100644 --- a/rust/kcl-bumper/Cargo.toml +++ b/rust/kcl-bumper/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "kcl-bumper" -version = "0.1.83" +version = "0.1.84" edition = "2021" repository = "https://github.com/KittyCAD/modeling-api" rust-version = "1.76" @@ -19,7 +19,7 @@ anyhow = { workspace = true } clap = { workspace = true, features = ["derive"] } semver = "1.0.25" serde = { workspace = true } -toml_edit = "0.22.26" +toml_edit = "0.22.27" [lints] workspace = true diff --git a/rust/kcl-derive-docs/Cargo.toml b/rust/kcl-derive-docs/Cargo.toml index 9040377df..4e5b29f16 100644 --- a/rust/kcl-derive-docs/Cargo.toml +++ b/rust/kcl-derive-docs/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "kcl-derive-docs" description = "A tool for generating documentation from Rust derive macros" -version = "0.1.83" +version = "0.1.84" edition = "2021" license = "MIT" repository = "https://github.com/KittyCAD/modeling-app" @@ -14,7 +14,7 @@ bench = false [dependencies] proc-macro2 = "1" quote = "1" -syn = { version = "2.0.103", features = ["full"] } +syn = { version = "2.0.104", features = ["full"] } [lints] workspace = true diff --git a/rust/kcl-derive-docs/src/example_tests.rs b/rust/kcl-derive-docs/src/example_tests.rs index 75faebca4..c048a8a0c 100644 --- a/rust/kcl-derive-docs/src/example_tests.rs +++ b/rust/kcl-derive-docs/src/example_tests.rs @@ -97,8 +97,11 @@ pub const TEST_NAMES: &[&str] = &[ "std-offsetPlane-2", "std-offsetPlane-3", "std-offsetPlane-4", + "std-sketch-planeOf-0", "std-sketch-circle-0", "std-sketch-circle-1", + "std-sketch-rectangle-0", + "std-sketch-rectangle-1", "std-sketch-patternTransform2d-0", "std-sketch-revolve-0", "std-sketch-revolve-1", diff --git a/rust/kcl-directory-test-macro/Cargo.toml b/rust/kcl-directory-test-macro/Cargo.toml index 113de027a..860fcb1b1 100644 --- a/rust/kcl-directory-test-macro/Cargo.toml +++ b/rust/kcl-directory-test-macro/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "kcl-directory-test-macro" description = "A tool for generating tests from a directory of kcl files" -version = "0.1.83" +version = "0.1.84" edition = "2021" license = "MIT" repository = "https://github.com/KittyCAD/modeling-app" @@ -14,7 +14,7 @@ bench = false convert_case = "0.8.0" proc-macro2 = "1" quote = "1" -syn = { version = "2.0.103", features = ["full"] } +syn = { version = "2.0.104", features = ["full"] } [lints] workspace = true diff --git a/rust/kcl-language-server-release/Cargo.toml b/rust/kcl-language-server-release/Cargo.toml index f7d51e4e1..801060edc 100644 --- a/rust/kcl-language-server-release/Cargo.toml +++ b/rust/kcl-language-server-release/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kcl-language-server-release" -version = "0.1.83" +version = "0.1.84" edition = "2021" authors = ["KittyCAD Inc "] publish = false diff --git a/rust/kcl-language-server/Cargo.toml b/rust/kcl-language-server/Cargo.toml index 5b529de7a..88ee106ea 100644 --- a/rust/kcl-language-server/Cargo.toml +++ b/rust/kcl-language-server/Cargo.toml @@ -2,7 +2,7 @@ name = "kcl-language-server" description = "A language server for KCL." authors = ["KittyCAD Inc "] -version = "0.2.83" +version = "0.2.84" edition = "2021" license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/rust/kcl-language-server/package.json b/rust/kcl-language-server/package.json index 9bd17fa6f..a67c5769e 100644 --- a/rust/kcl-language-server/package.json +++ b/rust/kcl-language-server/package.json @@ -123,7 +123,7 @@ "@vscode/test-electron": "^2.4.1", "@vscode/vsce": "^3.3.2", "cross-env": "^7.0.3", - "esbuild": "^0.25.2", + "esbuild": "^0.25.3", "glob": "^11.0.1", "mocha": "^11.1.0", "typescript": "^5.8.3" diff --git a/rust/kcl-lib/Cargo.toml b/rust/kcl-lib/Cargo.toml index 4f5d24874..ee0b2cacc 100644 --- a/rust/kcl-lib/Cargo.toml +++ b/rust/kcl-lib/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "kcl-lib" description = "KittyCAD Language implementation and tools" -version = "0.2.83" +version = "0.2.84" edition = "2024" license = "MIT" repository = "https://github.com/KittyCAD/modeling-app" @@ -74,7 +74,7 @@ sha2 = "0.10.9" tabled = { version = "0.20.0", optional = true } tempfile = "3.20" thiserror = "2.0.0" -toml = "0.8.22" +toml = "0.8.23" ts-rs = { version = "11.0.1", features = [ "uuid-impl", "url-impl", diff --git a/rust/kcl-lib/src/execution/cad_op.rs b/rust/kcl-lib/src/execution/cad_op.rs index 6c277258b..585a50d04 100644 --- a/rust/kcl-lib/src/execution/cad_op.rs +++ b/rust/kcl-lib/src/execution/cad_op.rs @@ -226,10 +226,7 @@ impl From<&KclValue> for OpKclValue { match value { KclValue::Uuid { value, .. } => Self::Uuid { value: *value }, KclValue::Bool { value, .. } => Self::Bool { value: *value }, - KclValue::Number { value, ty, .. } => Self::Number { - value: *value, - ty: ty.clone(), - }, + KclValue::Number { value, ty, .. } => Self::Number { value: *value, ty: *ty }, KclValue::String { value, .. } => Self::String { value: value.clone() }, KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => { let value = value.iter().map(Self::from).collect(); diff --git a/rust/kcl-lib/src/execution/exec_ast.rs b/rust/kcl-lib/src/execution/exec_ast.rs index 34639033d..f1ee7cc47 100644 --- a/rust/kcl-lib/src/execution/exec_ast.rs +++ b/rust/kcl-lib/src/execution/exec_ast.rs @@ -864,6 +864,9 @@ impl BinaryPart { BinaryPart::CallExpressionKw(call_expression) => call_expression.execute(exec_state, ctx).await, BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await, BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state, ctx).await, + BinaryPart::ArrayExpression(e) => e.execute(exec_state, ctx).await, + BinaryPart::ArrayRangeExpression(e) => e.execute(exec_state, ctx).await, + BinaryPart::ObjectExpression(e) => e.execute(exec_state, ctx).await, BinaryPart::IfExpression(e) => e.get_result(exec_state, ctx).await, BinaryPart::AscribedExpression(e) => e.get_result(exec_state, ctx).await, } @@ -1046,6 +1049,16 @@ impl Node { (KclValue::Solid { value }, Property::String(prop), false) if prop == "sketch" => Ok(KclValue::Sketch { value: Box::new(value.sketch), }), + (geometry @ KclValue::Solid { .. }, Property::String(prop), false) if prop == "tags" => { + // This is a common mistake. + Err(KclError::new_semantic(KclErrorDetails::new( + format!( + "Property `{prop}` not found on {}. You can get a solid's tags through its sketch, as in, `exampleSolid.sketch.tags`.", + geometry.human_friendly_type() + ), + vec![self.clone().into()], + ))) + } (KclValue::Sketch { value: sk }, Property::String(prop), false) if prop == "tags" => Ok(KclValue::Object { meta: vec![Metadata { source_range: SourceRange::from(self.clone()), @@ -1056,6 +1069,12 @@ impl Node { .map(|(k, tag)| (k.to_owned(), KclValue::TagIdentifier(Box::new(tag.to_owned())))) .collect(), }), + (geometry @ (KclValue::Sketch { .. } | KclValue::Solid { .. }), Property::String(property), false) => { + Err(KclError::new_semantic(KclErrorDetails::new( + format!("Property `{property}` not found on {}", geometry.human_friendly_type()), + vec![self.clone().into()], + ))) + } (being_indexed, _, _) => Err(KclError::new_semantic(KclErrorDetails::new( format!( "Only arrays can be indexed, but you're trying to index {}", @@ -1297,7 +1316,7 @@ impl Node { Ok(KclValue::Number { value: -value, meta, - ty: ty.clone(), + ty: *ty, }) } KclValue::Plane { value } => { @@ -1329,7 +1348,7 @@ impl Node { .map(|v| match v { KclValue::Number { value, ty, meta } => Ok(KclValue::Number { value: *value * -1.0, - ty: ty.clone(), + ty: *ty, meta: meta.clone(), }), _ => Err(err()), @@ -1350,7 +1369,7 @@ impl Node { .map(|v| match v { KclValue::Number { value, ty, meta } => Ok(KclValue::Number { value: *value * -1.0, - ty: ty.clone(), + ty: *ty, meta: meta.clone(), }), _ => Err(err()), @@ -1544,7 +1563,7 @@ impl Node { .into_iter() .map(|num| KclValue::Number { value: num as f64, - ty: start_ty.clone(), + ty: start_ty, meta: meta.clone(), }) .collect(), diff --git a/rust/kcl-lib/src/execution/fn_call.rs b/rust/kcl-lib/src/execution/fn_call.rs index 778140385..581873646 100644 --- a/rust/kcl-lib/src/execution/fn_call.rs +++ b/rust/kcl-lib/src/execution/fn_call.rs @@ -401,7 +401,7 @@ impl FunctionDefinition<'_> { impl FunctionBody<'_> { fn prep_mem(&self, exec_state: &mut ExecState) { match self { - FunctionBody::Rust(_) => exec_state.mut_stack().push_new_env_for_rust_call(), + FunctionBody::Rust(_) => exec_state.mut_stack().push_new_root_env(true), FunctionBody::Kcl(_, memory) => exec_state.mut_stack().push_new_env_for_call(*memory), } } diff --git a/rust/kcl-lib/src/execution/geometry.rs b/rust/kcl-lib/src/execution/geometry.rs index a75590e79..60ff86fa1 100644 --- a/rust/kcl-lib/src/execution/geometry.rs +++ b/rust/kcl-lib/src/execution/geometry.rs @@ -958,6 +958,7 @@ impl From for Point3D { Self { x: p.x, y: p.y, z: p.z } } } + impl From for kittycad_modeling_cmds::shared::Point3d { fn from(p: Point3d) -> Self { Self { @@ -1023,12 +1024,12 @@ pub struct BasePath { impl BasePath { pub fn get_to(&self) -> [TyF64; 2] { let ty: NumericType = self.units.into(); - [TyF64::new(self.to[0], ty.clone()), TyF64::new(self.to[1], ty)] + [TyF64::new(self.to[0], ty), TyF64::new(self.to[1], ty)] } pub fn get_from(&self) -> [TyF64; 2] { let ty: NumericType = self.units.into(); - [TyF64::new(self.from[0], ty.clone()), TyF64::new(self.from[1], ty)] + [TyF64::new(self.from[0], ty), TyF64::new(self.from[1], ty)] } } @@ -1049,7 +1050,7 @@ pub struct GeoMeta { #[ts(export)] #[serde(tag = "type")] pub enum Path { - /// A path that goes to a point. + /// A straight line which ends at the given point. ToPoint { #[serde(flatten)] base: BasePath, @@ -1269,14 +1270,14 @@ impl Path { pub fn get_from(&self) -> [TyF64; 2] { let p = &self.get_base().from; let ty: NumericType = self.get_base().units.into(); - [TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)] + [TyF64::new(p[0], ty), TyF64::new(p[1], ty)] } /// Where does this path segment end? pub fn get_to(&self) -> [TyF64; 2] { let p = &self.get_base().to; let ty: NumericType = self.get_base().units.into(); - [TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)] + [TyF64::new(p[0], ty), TyF64::new(p[1], ty)] } /// The path segment start point and its type. diff --git a/rust/kcl-lib/src/execution/kcl_value.rs b/rust/kcl-lib/src/execution/kcl_value.rs index 71c2dba66..9f9a07451 100644 --- a/rust/kcl-lib/src/execution/kcl_value.rs +++ b/rust/kcl-lib/src/execution/kcl_value.rs @@ -415,15 +415,41 @@ impl KclValue { /// Put the point into a KCL value. pub fn from_point2d(p: [f64; 2], ty: NumericType, meta: Vec) -> Self { + let [x, y] = p; Self::Tuple { value: vec![ Self::Number { - value: p[0], + value: x, meta: meta.clone(), - ty: ty.clone(), + ty, }, Self::Number { - value: p[1], + value: y, + meta: meta.clone(), + ty, + }, + ], + meta, + } + } + + /// Put the point into a KCL value. + pub fn from_point3d(p: [f64; 3], ty: NumericType, meta: Vec) -> Self { + let [x, y, z] = p; + Self::Tuple { + value: vec![ + Self::Number { + value: x, + meta: meta.clone(), + ty, + }, + Self::Number { + value: y, + meta: meta.clone(), + ty, + }, + Self::Number { + value: z, meta: meta.clone(), ty, }, @@ -448,7 +474,7 @@ impl KclValue { pub fn as_int_with_ty(&self) -> Option<(i64, NumericType)> { match self { - KclValue::Number { value, ty, .. } => crate::try_f64_to_i64(*value).map(|i| (i, ty.clone())), + KclValue::Number { value, ty, .. } => crate::try_f64_to_i64(*value).map(|i| (i, *ty)), _ => None, } } @@ -562,7 +588,7 @@ impl KclValue { pub fn as_ty_f64(&self) -> Option { match self { - KclValue::Number { value, ty, .. } => Some(TyF64::new(*value, ty.clone())), + KclValue::Number { value, ty, .. } => Some(TyF64::new(*value, *ty)), _ => None, } } diff --git a/rust/kcl-lib/src/execution/memory.rs b/rust/kcl-lib/src/execution/memory.rs index a420a6922..85ecb1c51 100644 --- a/rust/kcl-lib/src/execution/memory.rs +++ b/rust/kcl-lib/src/execution/memory.rs @@ -541,22 +541,6 @@ impl Stack { self.push_new_env_for_call(snapshot); } - /// Push a new stack frame on to the call stack for callees which should not read or write - /// from memory. - /// - /// This is suitable for calling standard library functions or other functions written in Rust - /// which will use 'Rust memory' rather than KCL's memory and cannot reach into the wider - /// environment. - /// - /// Trying to read or write from this environment will panic with an index out of bounds. - pub fn push_new_env_for_rust_call(&mut self) { - self.call_stack.push(self.current_env); - // Rust functions shouldn't try to set or access anything in their environment, so don't - // waste time and space on a new env. Using usize::MAX means we'll get an overflow if we - // try to access anything rather than a silent error. - self.current_env = EnvironmentRef(usize::MAX, 0); - } - /// Push a new stack frame on to the call stack with no connection to a parent environment. /// /// Suitable for executing a separate module. @@ -683,7 +667,7 @@ impl Stack { env.contains_key(var) } - /// Get a key from the first KCL (i.e., non-Rust) stack frame on the call stack. + /// Get a key from the first stack frame on the call stack. pub fn get_from_call_stack(&self, key: &str, source_range: SourceRange) -> Result<(usize, &KclValue), KclError> { if !self.current_env.skip_env() { return Ok((self.current_env.1, self.get(key, source_range)?)); @@ -695,7 +679,7 @@ impl Stack { } } - unreachable!("It can't be Rust frames all the way down"); + unreachable!("No frames on the stack?"); } /// Iterate over all keys in the current environment which satisfy the provided predicate. @@ -1217,24 +1201,6 @@ mod test { assert_get_from(mem, "c", 5, callee); } - #[test] - fn rust_env() { - let mem = &mut Stack::new_for_tests(); - mem.add("a".to_owned(), val(1), sr()).unwrap(); - mem.add("b".to_owned(), val(3), sr()).unwrap(); - let sn = mem.snapshot(); - - mem.push_new_env_for_rust_call(); - mem.push_new_env_for_call(sn); - assert_get(mem, "b", 3); - mem.add("b".to_owned(), val(4), sr()).unwrap(); - assert_get(mem, "b", 4); - - mem.pop_env(); - mem.pop_env(); - assert_get(mem, "b", 3); - } - #[test] fn deep_call_env() { let mem = &mut Stack::new_for_tests(); diff --git a/rust/kcl-lib/src/execution/mod.rs b/rust/kcl-lib/src/execution/mod.rs index 5c6f4c209..8f3647716 100644 --- a/rust/kcl-lib/src/execution/mod.rs +++ b/rust/kcl-lib/src/execution/mod.rs @@ -1920,6 +1920,22 @@ shape = layer() |> patternTransform(instances = 10, transform = transform) ); } + #[tokio::test(flavor = "multi_thread")] + async fn pass_std_to_std() { + let ast = r#"sketch001 = startSketchOn(XY) +profile001 = circle(sketch001, center = [0, 0], radius = 2) +extrude001 = extrude(profile001, length = 5) +extrudes = patternLinear3d( + extrude001, + instances = 3, + distance = 5, + axis = [1, 1, 0], +) +clone001 = map(extrudes, f = clone) +"#; + parse_execute(ast).await.unwrap(); + } + #[tokio::test(flavor = "multi_thread")] async fn test_zero_param_fn() { let ast = r#"sigmaAllow = 35000 // psi diff --git a/rust/kcl-lib/src/execution/types.rs b/rust/kcl-lib/src/execution/types.rs index 6de88d39b..6ea937ab5 100644 --- a/rust/kcl-lib/src/execution/types.rs +++ b/rust/kcl-lib/src/execution/types.rs @@ -460,7 +460,7 @@ impl fmt::Display for PrimitiveType { } } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ts_rs::TS, JsonSchema)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, ts_rs::TS, JsonSchema)] #[ts(export)] #[serde(tag = "type")] pub enum NumericType { @@ -575,7 +575,7 @@ impl NumericType { match (&ty, &i.ty) { (Any, Default { .. }) if i.n == 0.0 => {} (Any, t) => { - ty = t.clone(); + ty = *t; } (_, Unknown) | (Default { .. }, Default { .. }) => return (result, Unknown), @@ -598,7 +598,7 @@ impl NumericType { } if ty == Any && !input.is_empty() { - ty = input[0].ty.clone(); + ty = input[0].ty; } (result, ty) @@ -722,7 +722,7 @@ impl NumericType { if ty.subtype(self) { return Ok(KclValue::Number { value: *value, - ty: ty.clone(), + ty: *ty, meta: meta.clone(), }); } @@ -736,7 +736,7 @@ impl NumericType { (Any, _) => Ok(KclValue::Number { value: *value, - ty: self.clone(), + ty: *self, meta: meta.clone(), }), @@ -744,7 +744,7 @@ impl NumericType { // means accept any number rather than force the current default. (_, Default { .. }) => Ok(KclValue::Number { value: *value, - ty: ty.clone(), + ty: *ty, meta: meta.clone(), }), @@ -840,6 +840,18 @@ pub enum UnitType { Angle(UnitAngle), } +impl UnitType { + pub(crate) fn to_suffix(self) -> Option { + match self { + UnitType::Count => Some("_".to_owned()), + UnitType::Length(UnitLen::Unknown) => None, + UnitType::Angle(UnitAngle::Unknown) => None, + UnitType::Length(l) => Some(l.to_string()), + UnitType::Angle(a) => Some(a.to_string()), + } + } +} + impl std::fmt::Display for UnitType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -1479,7 +1491,7 @@ impl KclValue { pub fn principal_type(&self) -> Option { match self { KclValue::Bool { .. } => Some(RuntimeType::Primitive(PrimitiveType::Boolean)), - KclValue::Number { ty, .. } => Some(RuntimeType::Primitive(PrimitiveType::Number(ty.clone()))), + KclValue::Number { ty, .. } => Some(RuntimeType::Primitive(PrimitiveType::Number(*ty))), KclValue::String { .. } => Some(RuntimeType::Primitive(PrimitiveType::String)), KclValue::Object { value, .. } => { let properties = value diff --git a/rust/kcl-lib/src/fmt.rs b/rust/kcl-lib/src/fmt.rs index a7a20fa20..2caa721f9 100644 --- a/rust/kcl-lib/src/fmt.rs +++ b/rust/kcl-lib/src/fmt.rs @@ -45,6 +45,31 @@ pub fn format_number_literal(value: f64, suffix: NumericSuffix) -> Result Result { + match ty { + NumericType::Default { .. } => Ok(value.to_string()), + // There isn't a syntactic suffix for these. For unknown, we don't want + // to ever generate the unknown suffix. We currently warn on it, and we + // may remove it in the future. + NumericType::Unknown | NumericType::Any => Err(FormatNumericTypeError::Invalid(ty)), + NumericType::Known(unit_type) => unit_type + .to_suffix() + .map(|suffix| format!("{value}{suffix}")) + .ok_or(FormatNumericTypeError::Invalid(ty)), + } +} + #[cfg(test)] mod tests { use pretty_assertions::assert_eq; @@ -134,4 +159,74 @@ mod tests { Err(FormatNumericSuffixError::Invalid(NumericSuffix::Unknown)) ); } + + #[test] + fn test_format_number_value() { + assert_eq!( + format_number_value( + 1.0, + NumericType::Default { + len: Default::default(), + angle: Default::default() + } + ), + Ok("1".to_owned()) + ); + assert_eq!( + format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLen::Unknown))), + Err(FormatNumericTypeError::Invalid(NumericType::Known(UnitType::Length( + UnitLen::Unknown + )))) + ); + assert_eq!( + format_number_value(1.0, NumericType::Known(UnitType::Angle(UnitAngle::Unknown))), + Err(FormatNumericTypeError::Invalid(NumericType::Known(UnitType::Angle( + UnitAngle::Unknown + )))) + ); + assert_eq!( + format_number_value(1.0, NumericType::Known(UnitType::Count)), + Ok("1_".to_owned()) + ); + assert_eq!( + format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLen::Mm))), + Ok("1mm".to_owned()) + ); + assert_eq!( + format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLen::Cm))), + Ok("1cm".to_owned()) + ); + assert_eq!( + format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLen::M))), + Ok("1m".to_owned()) + ); + assert_eq!( + format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLen::Inches))), + Ok("1in".to_owned()) + ); + assert_eq!( + format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLen::Feet))), + Ok("1ft".to_owned()) + ); + assert_eq!( + format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLen::Yards))), + Ok("1yd".to_owned()) + ); + assert_eq!( + format_number_value(1.0, NumericType::Known(UnitType::Angle(UnitAngle::Degrees))), + Ok("1deg".to_owned()) + ); + assert_eq!( + format_number_value(1.0, NumericType::Known(UnitType::Angle(UnitAngle::Radians))), + Ok("1rad".to_owned()) + ); + assert_eq!( + format_number_value(1.0, NumericType::Unknown), + Err(FormatNumericTypeError::Invalid(NumericType::Unknown)) + ); + assert_eq!( + format_number_value(1.0, NumericType::Any), + Err(FormatNumericTypeError::Invalid(NumericType::Any)) + ); + } } diff --git a/rust/kcl-lib/src/lib.rs b/rust/kcl-lib/src/lib.rs index 7aebd7d20..4b11113b5 100644 --- a/rust/kcl-lib/src/lib.rs +++ b/rust/kcl-lib/src/lib.rs @@ -140,7 +140,7 @@ pub mod std_utils { pub mod pretty { pub use crate::{ - fmt::{format_number_literal, human_display_number}, + fmt::{format_number_literal, format_number_value, human_display_number}, parsing::token::NumericSuffix, }; } diff --git a/rust/kcl-lib/src/lsp/kcl/hover.rs b/rust/kcl-lib/src/lsp/kcl/hover.rs index 0db697c83..1b3e1b2c8 100644 --- a/rust/kcl-lib/src/lsp/kcl/hover.rs +++ b/rust/kcl-lib/src/lsp/kcl/hover.rs @@ -149,6 +149,9 @@ impl BinaryPart { BinaryPart::UnaryExpression(unary_expression) => { unary_expression.get_hover_value_for_position(pos, code, opts) } + BinaryPart::ArrayExpression(e) => e.get_hover_value_for_position(pos, code, opts), + BinaryPart::ArrayRangeExpression(e) => e.get_hover_value_for_position(pos, code, opts), + BinaryPart::ObjectExpression(e) => e.get_hover_value_for_position(pos, code, opts), BinaryPart::IfExpression(e) => e.get_hover_value_for_position(pos, code, opts), BinaryPart::AscribedExpression(e) => e.expr.get_hover_value_for_position(pos, code, opts), BinaryPart::MemberExpression(member_expression) => { diff --git a/rust/kcl-lib/src/parsing/ast/digest.rs b/rust/kcl-lib/src/parsing/ast/digest.rs index 1526a13f8..76850717d 100644 --- a/rust/kcl-lib/src/parsing/ast/digest.rs +++ b/rust/kcl-lib/src/parsing/ast/digest.rs @@ -161,6 +161,9 @@ impl BinaryPart { BinaryPart::CallExpressionKw(ce) => ce.compute_digest(), BinaryPart::UnaryExpression(ue) => ue.compute_digest(), BinaryPart::MemberExpression(me) => me.compute_digest(), + BinaryPart::ArrayExpression(e) => e.compute_digest(), + BinaryPart::ArrayRangeExpression(e) => e.compute_digest(), + BinaryPart::ObjectExpression(e) => e.compute_digest(), BinaryPart::IfExpression(e) => e.compute_digest(), BinaryPart::AscribedExpression(e) => e.compute_digest(), } diff --git a/rust/kcl-lib/src/parsing/ast/mod.rs b/rust/kcl-lib/src/parsing/ast/mod.rs index 147004e81..697b9b39a 100644 --- a/rust/kcl-lib/src/parsing/ast/mod.rs +++ b/rust/kcl-lib/src/parsing/ast/mod.rs @@ -51,6 +51,9 @@ impl BinaryPart { BinaryPart::CallExpressionKw(call_expression) => call_expression.module_id, BinaryPart::UnaryExpression(unary_expression) => unary_expression.module_id, BinaryPart::MemberExpression(member_expression) => member_expression.module_id, + BinaryPart::ArrayExpression(e) => e.module_id, + BinaryPart::ArrayRangeExpression(e) => e.module_id, + BinaryPart::ObjectExpression(e) => e.module_id, BinaryPart::IfExpression(e) => e.module_id, BinaryPart::AscribedExpression(e) => e.module_id, } diff --git a/rust/kcl-lib/src/parsing/ast/types/mod.rs b/rust/kcl-lib/src/parsing/ast/types/mod.rs index bd44f7181..ee2849d96 100644 --- a/rust/kcl-lib/src/parsing/ast/types/mod.rs +++ b/rust/kcl-lib/src/parsing/ast/types/mod.rs @@ -1214,6 +1214,9 @@ impl From<&BinaryPart> for Expr { BinaryPart::CallExpressionKw(call_expression) => Expr::CallExpressionKw(call_expression.clone()), BinaryPart::UnaryExpression(unary_expression) => Expr::UnaryExpression(unary_expression.clone()), BinaryPart::MemberExpression(member_expression) => Expr::MemberExpression(member_expression.clone()), + BinaryPart::ArrayExpression(e) => Expr::ArrayExpression(e.clone()), + BinaryPart::ArrayRangeExpression(e) => Expr::ArrayRangeExpression(e.clone()), + BinaryPart::ObjectExpression(e) => Expr::ObjectExpression(e.clone()), BinaryPart::IfExpression(e) => Expr::IfExpression(e.clone()), BinaryPart::AscribedExpression(e) => Expr::AscribedExpression(e.clone()), } @@ -1281,6 +1284,9 @@ pub enum BinaryPart { CallExpressionKw(BoxNode), UnaryExpression(BoxNode), MemberExpression(BoxNode), + ArrayExpression(BoxNode), + ArrayRangeExpression(BoxNode), + ObjectExpression(BoxNode), IfExpression(BoxNode), AscribedExpression(BoxNode), } @@ -1307,6 +1313,9 @@ impl BinaryPart { BinaryPart::CallExpressionKw(call_expression) => call_expression.get_constraint_level(), BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_constraint_level(), BinaryPart::MemberExpression(member_expression) => member_expression.get_constraint_level(), + BinaryPart::ArrayExpression(e) => e.get_constraint_level(), + BinaryPart::ArrayRangeExpression(e) => e.get_constraint_level(), + BinaryPart::ObjectExpression(e) => e.get_constraint_level(), BinaryPart::IfExpression(e) => e.get_constraint_level(), BinaryPart::AscribedExpression(e) => e.expr.get_constraint_level(), } @@ -1320,6 +1329,9 @@ impl BinaryPart { BinaryPart::CallExpressionKw(call_expression) => call_expression.replace_value(source_range, new_value), BinaryPart::UnaryExpression(unary_expression) => unary_expression.replace_value(source_range, new_value), BinaryPart::MemberExpression(_) => {} + BinaryPart::ArrayExpression(e) => e.replace_value(source_range, new_value), + BinaryPart::ArrayRangeExpression(e) => e.replace_value(source_range, new_value), + BinaryPart::ObjectExpression(e) => e.replace_value(source_range, new_value), BinaryPart::IfExpression(e) => e.replace_value(source_range, new_value), BinaryPart::AscribedExpression(e) => e.expr.replace_value(source_range, new_value), } @@ -1333,6 +1345,9 @@ impl BinaryPart { BinaryPart::CallExpressionKw(call_expression) => call_expression.start, BinaryPart::UnaryExpression(unary_expression) => unary_expression.start, BinaryPart::MemberExpression(member_expression) => member_expression.start, + BinaryPart::ArrayExpression(e) => e.start, + BinaryPart::ArrayRangeExpression(e) => e.start, + BinaryPart::ObjectExpression(e) => e.start, BinaryPart::IfExpression(e) => e.start, BinaryPart::AscribedExpression(e) => e.start, } @@ -1346,6 +1361,9 @@ impl BinaryPart { BinaryPart::CallExpressionKw(call_expression) => call_expression.end, BinaryPart::UnaryExpression(unary_expression) => unary_expression.end, BinaryPart::MemberExpression(member_expression) => member_expression.end, + BinaryPart::ArrayExpression(e) => e.end, + BinaryPart::ArrayRangeExpression(e) => e.end, + BinaryPart::ObjectExpression(e) => e.end, BinaryPart::IfExpression(e) => e.end, BinaryPart::AscribedExpression(e) => e.end, } @@ -1360,6 +1378,9 @@ impl BinaryPart { BinaryPart::CallExpressionKw(call_expression) => call_expression.rename_identifiers(old_name, new_name), BinaryPart::UnaryExpression(unary_expression) => unary_expression.rename_identifiers(old_name, new_name), BinaryPart::MemberExpression(member_expression) => member_expression.rename_identifiers(old_name, new_name), + BinaryPart::ArrayExpression(e) => e.rename_identifiers(old_name, new_name), + BinaryPart::ArrayRangeExpression(e) => e.rename_identifiers(old_name, new_name), + BinaryPart::ObjectExpression(e) => e.rename_identifiers(old_name, new_name), BinaryPart::IfExpression(if_expression) => if_expression.rename_identifiers(old_name, new_name), BinaryPart::AscribedExpression(e) => e.expr.rename_identifiers(old_name, new_name), } diff --git a/rust/kcl-lib/src/parsing/parser.rs b/rust/kcl-lib/src/parsing/parser.rs index c8b277a6b..6e3ec4c96 100644 --- a/rust/kcl-lib/src/parsing/parser.rs +++ b/rust/kcl-lib/src/parsing/parser.rs @@ -624,9 +624,6 @@ fn operand(i: &mut TokenSlice) -> ModalResult { Expr::FunctionExpression(_) | Expr::PipeExpression(_) | Expr::PipeSubstitution(_) - | Expr::ArrayExpression(_) - | Expr::ArrayRangeExpression(_) - | Expr::ObjectExpression(_) | Expr::LabelledExpression(..) => return Err(CompilationError::fatal(source_range, TODO_783)), Expr::None(_) => { return Err(CompilationError::fatal( @@ -652,6 +649,9 @@ fn operand(i: &mut TokenSlice) -> ModalResult { Expr::BinaryExpression(x) => BinaryPart::BinaryExpression(x), Expr::CallExpressionKw(x) => BinaryPart::CallExpressionKw(x), Expr::MemberExpression(x) => BinaryPart::MemberExpression(x), + Expr::ArrayExpression(x) => BinaryPart::ArrayExpression(x), + Expr::ArrayRangeExpression(x) => BinaryPart::ArrayRangeExpression(x), + Expr::ObjectExpression(x) => BinaryPart::ObjectExpression(x), Expr::IfExpression(x) => BinaryPart::IfExpression(x), Expr::AscribedExpression(x) => BinaryPart::AscribedExpression(x), }; @@ -2115,6 +2115,8 @@ fn possible_operands(i: &mut TokenSlice) -> ModalResult { literal.map(Expr::Literal), fn_call_kw.map(Box::new).map(Expr::CallExpressionKw), name.map(Box::new).map(Expr::Name), + array, + object.map(Box::new).map(Expr::ObjectExpression), binary_expr_in_parens.map(Box::new).map(Expr::BinaryExpression), unnecessarily_bracketed, )) @@ -3398,6 +3400,27 @@ mod tests { operand.parse(tokens).unwrap(); } + #[test] + fn parse_binary_operator_on_array() { + let tokens = crate::parsing::token::lex("[0] + 1", ModuleId::default()).unwrap(); + let tokens = tokens.as_slice(); + binary_expression.parse(tokens).unwrap(); + } + + #[test] + fn parse_binary_operator_on_object() { + let tokens = crate::parsing::token::lex("{ a = 1 } + 2", ModuleId::default()).unwrap(); + let tokens = tokens.as_slice(); + binary_expression.parse(tokens).unwrap(); + } + + #[test] + fn parse_call_array_operator() { + let tokens = crate::parsing::token::lex("f([0] + 1)", ModuleId::default()).unwrap(); + let tokens = tokens.as_slice(); + fn_call_kw.parse(tokens).unwrap(); + } + #[test] fn weird_program_just_a_pipe() { let tokens = crate::parsing::token::lex("|", ModuleId::default()).unwrap(); diff --git a/rust/kcl-lib/src/simulation_tests.rs b/rust/kcl-lib/src/simulation_tests.rs index 776e364ae..fb223f65c 100644 --- a/rust/kcl-lib/src/simulation_tests.rs +++ b/rust/kcl-lib/src/simulation_tests.rs @@ -779,6 +779,27 @@ mod add_lots { super::execute(TEST_NAME, false).await } } +mod add_arrays { + const TEST_NAME: &str = "add_arrays"; + + /// Test parsing KCL. + #[test] + fn parse() { + super::parse(TEST_NAME) + } + + /// Test that parsing and unparsing KCL produces the original KCL input. + #[tokio::test(flavor = "multi_thread")] + async fn unparse() { + super::unparse(TEST_NAME).await + } + + /// Test that KCL is executed correctly. + #[tokio::test(flavor = "multi_thread")] + async fn kcl_test_execute() { + super::execute(TEST_NAME, false).await + } +} mod argument_error { //! The argument error points to the problematic argument in the call site, //! not the function definition that the variable points to. @@ -887,6 +908,27 @@ mod invalid_index_fractional { super::execute(TEST_NAME, false).await } } +mod property_access_not_found_on_solid { + const TEST_NAME: &str = "property_access_not_found_on_solid"; + + /// Test parsing KCL. + #[test] + fn parse() { + super::parse(TEST_NAME) + } + + /// Test that parsing and unparsing KCL produces the original KCL input. + #[tokio::test(flavor = "multi_thread")] + async fn unparse() { + super::unparse(TEST_NAME).await + } + + /// Test that KCL is executed correctly. + #[tokio::test(flavor = "multi_thread")] + async fn kcl_test_execute() { + super::execute(TEST_NAME, true).await + } +} mod invalid_member_object { const TEST_NAME: &str = "invalid_member_object"; @@ -3632,3 +3674,66 @@ mod non_english_identifiers { super::execute(TEST_NAME, true).await } } +mod rect { + const TEST_NAME: &str = "rect"; + + /// Test parsing KCL. + #[test] + fn parse() { + super::parse(TEST_NAME) + } + + /// Test that parsing and unparsing KCL produces the original KCL input. + #[tokio::test(flavor = "multi_thread")] + async fn unparse() { + super::unparse(TEST_NAME).await + } + + /// Test that KCL is executed correctly. + #[tokio::test(flavor = "multi_thread")] + async fn kcl_test_execute() { + super::execute(TEST_NAME, true).await + } +} +mod rect_helper { + const TEST_NAME: &str = "rect_helper"; + + /// Test parsing KCL. + #[test] + fn parse() { + super::parse(TEST_NAME) + } + + /// Test that parsing and unparsing KCL produces the original KCL input. + #[tokio::test(flavor = "multi_thread")] + async fn unparse() { + super::unparse(TEST_NAME).await + } + + /// Test that KCL is executed correctly. + #[tokio::test(flavor = "multi_thread")] + async fn kcl_test_execute() { + super::execute(TEST_NAME, true).await + } +} +mod plane_of { + const TEST_NAME: &str = "plane_of"; + + /// Test parsing KCL. + #[test] + fn parse() { + super::parse(TEST_NAME) + } + + /// Test that parsing and unparsing KCL produces the original KCL input. + #[tokio::test(flavor = "multi_thread")] + async fn unparse() { + super::unparse(TEST_NAME).await + } + + /// Test that KCL is executed correctly. + #[tokio::test(flavor = "multi_thread")] + async fn kcl_test_execute() { + super::execute(TEST_NAME, true).await + } +} diff --git a/rust/kcl-lib/src/std/args.rs b/rust/kcl-lib/src/std/args.rs index a73355fe7..bb5ad806c 100644 --- a/rust/kcl-lib/src/std/args.rs +++ b/rust/kcl-lib/src/std/args.rs @@ -340,12 +340,12 @@ impl Args { let x = KclValue::Number { value: p[0], meta: vec![meta], - ty: ty.clone(), + ty, }; let y = KclValue::Number { value: p[1], meta: vec![meta], - ty: ty.clone(), + ty, }; let ty = RuntimeType::Primitive(PrimitiveType::Number(ty)); @@ -1038,7 +1038,7 @@ impl<'a> FromKclValue<'a> for u64 { impl<'a> FromKclValue<'a> for TyF64 { fn from_kcl_val(arg: &'a KclValue) -> Option { match arg { - KclValue::Number { value, ty, .. } => Some(TyF64::new(*value, ty.clone())), + KclValue::Number { value, ty, .. } => Some(TyF64::new(*value, *ty)), _ => None, } } diff --git a/rust/kcl-lib/src/std/mod.rs b/rust/kcl-lib/src/std/mod.rs index cb3f85067..5322d04f5 100644 --- a/rust/kcl-lib/src/std/mod.rs +++ b/rust/kcl-lib/src/std/mod.rs @@ -288,6 +288,14 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp |e, a| Box::pin(crate::std::sketch::elliptic_point(e, a)), StdFnProps::default("std::sketch::ellipticPoint"), ), + ("sketch", "rectangle") => ( + |e, a| Box::pin(crate::std::shapes::rectangle(e, a)), + StdFnProps::default("std::sketch::rectangle"), + ), + ("sketch", "planeOf") => ( + |e, a| Box::pin(crate::std::planes::plane_of(e, a)), + StdFnProps::default("std::sketch::planeOf"), + ), ("sketch", "extrude") => ( |e, a| Box::pin(crate::std::extrude::extrude(e, a)), StdFnProps::default("std::sketch::extrude").include_in_feature_tree(), diff --git a/rust/kcl-lib/src/std/patterns.rs b/rust/kcl-lib/src/std/patterns.rs index 454385f83..b1807553b 100644 --- a/rust/kcl-lib/src/std/patterns.rs +++ b/rust/kcl-lib/src/std/patterns.rs @@ -408,7 +408,7 @@ impl GeometryTrait for Sketch { exec_state: &mut ExecState, ) -> Result<[TyF64; 3], KclError> { let [x, y] = array_to_point2d(val, source_ranges, exec_state)?; - let ty = x.ty.clone(); + let ty = x.ty; Ok([x, y, TyF64::new(0.0, ty)]) } diff --git a/rust/kcl-lib/src/std/planes.rs b/rust/kcl-lib/src/std/planes.rs index 59389196e..25fe4cce3 100644 --- a/rust/kcl-lib/src/std/planes.rs +++ b/rust/kcl-lib/src/std/planes.rs @@ -1,15 +1,123 @@ //! Standard library plane helpers. use kcmc::{ModelingCmd, each_cmd as mcmd, length_unit::LengthUnit, shared::Color}; -use kittycad_modeling_cmds as kcmc; +use kittycad_modeling_cmds::{self as kcmc, ok_response::OkModelingCmdResponse, websocket::OkWebSocketResponseData}; -use super::{args::TyF64, sketch::PlaneData}; +use super::{ + args::TyF64, + sketch::{FaceTag, PlaneData}, +}; use crate::{ - errors::KclError, - execution::{ExecState, KclValue, ModelingCmdMeta, Plane, PlaneType, types::RuntimeType}, + UnitLen, + errors::{KclError, KclErrorDetails}, + execution::{ExecState, KclValue, Metadata, ModelingCmdMeta, Plane, PlaneType, types::RuntimeType}, std::Args, }; +/// Find the plane of a given face. +pub async fn plane_of(exec_state: &mut ExecState, args: Args) -> Result { + let solid = args.get_unlabeled_kw_arg("solid", &RuntimeType::solid(), exec_state)?; + let face = args.get_kw_arg("face", &RuntimeType::tagged_face(), exec_state)?; + + inner_plane_of(solid, face, exec_state, &args) + .await + .map(Box::new) + .map(|value| KclValue::Plane { value }) +} + +async fn inner_plane_of( + solid: crate::execution::Solid, + face: FaceTag, + exec_state: &mut ExecState, + args: &Args, +) -> Result { + // Support mock execution + // Return an arbitrary (incorrect) plane and a non-fatal error. + if args.ctx.no_engine_commands().await { + let plane_id = exec_state.id_generator().next_uuid(); + exec_state.err(crate::CompilationError { + source_range: args.source_range, + message: "The engine isn't available, so returning an arbitrary incorrect plane".to_owned(), + suggestion: None, + severity: crate::errors::Severity::Error, + tag: crate::errors::Tag::None, + }); + return Ok(Plane { + artifact_id: plane_id.into(), + id: plane_id, + // Engine doesn't know about the ID we created, so set this to Uninit. + value: PlaneType::Uninit, + info: crate::execution::PlaneInfo { + origin: Default::default(), + x_axis: Default::default(), + y_axis: Default::default(), + }, + meta: vec![Metadata { + source_range: args.source_range, + }], + }); + } + + // Query the engine to learn what plane, if any, this face is on. + let face_id = face.get_face_id(&solid, exec_state, args, true).await?; + let meta = args.into(); + let cmd = ModelingCmd::FaceIsPlanar(mcmd::FaceIsPlanar { object_id: face_id }); + let plane_resp = exec_state.send_modeling_cmd(meta, cmd).await?; + let OkWebSocketResponseData::Modeling { + modeling_response: OkModelingCmdResponse::FaceIsPlanar(planar), + } = plane_resp + else { + return Err(KclError::new_semantic(KclErrorDetails::new( + format!( + "Engine returned invalid response, it should have returned FaceIsPlanar but it returned {plane_resp:#?}" + ), + vec![args.source_range], + ))); + }; + // Destructure engine's response to check if the face was on a plane. + let not_planar: Result<_, KclError> = Err(KclError::new_semantic(KclErrorDetails::new( + "The face you provided doesn't lie on any plane. It might be curved.".to_owned(), + vec![args.source_range], + ))); + let Some(x_axis) = planar.x_axis else { return not_planar }; + let Some(y_axis) = planar.y_axis else { return not_planar }; + let Some(origin) = planar.origin else { return not_planar }; + + // Engine always returns measurements in mm. + let engine_units = UnitLen::Mm; + let x_axis = crate::execution::Point3d { + x: x_axis.x, + y: x_axis.y, + z: x_axis.z, + units: engine_units, + }; + let y_axis = crate::execution::Point3d { + x: y_axis.x, + y: y_axis.y, + z: y_axis.z, + units: engine_units, + }; + let origin = crate::execution::Point3d { + x: origin.x.0, + y: origin.y.0, + z: origin.z.0, + units: engine_units, + }; + + // Engine doesn't send back an ID, so let's just make a new plane ID. + let plane_id = exec_state.id_generator().next_uuid(); + Ok(Plane { + artifact_id: plane_id.into(), + id: plane_id, + // Engine doesn't know about the ID we created, so set this to Uninit. + value: PlaneType::Uninit, + info: crate::execution::PlaneInfo { origin, x_axis, y_axis }, + meta: vec![Metadata { + source_range: args.source_range, + }], + }) +} + /// Offset a plane by a distance along its normal. pub async fn offset_plane(exec_state: &mut ExecState, args: Args) -> Result { let std_plane = args.get_unlabeled_kw_arg("plane", &RuntimeType::plane(), exec_state)?; diff --git a/rust/kcl-lib/src/std/segment.rs b/rust/kcl-lib/src/std/segment.rs index 1734c7344..7ad1d3b8f 100644 --- a/rust/kcl-lib/src/std/segment.rs +++ b/rust/kcl-lib/src/std/segment.rs @@ -18,7 +18,7 @@ pub async fn segment_end(exec_state: &mut ExecState, args: Args) -> Result Result<[TyF64; 2], KclError> { @@ -31,7 +31,7 @@ fn inner_segment_end(tag: &TagIdentifier, exec_state: &mut ExecState, args: Args })?; let (p, ty) = path.end_point_components(); // Docs generation isn't smart enough to handle ([f64; 2], NumericType). - let point = [TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)]; + let point = [TyF64::new(p[0], ty), TyF64::new(p[1], ty)]; Ok(point) } @@ -81,7 +81,7 @@ pub async fn segment_start(exec_state: &mut ExecState, args: Args) -> Result Result<[TyF64; 2], KclError> { @@ -94,7 +94,7 @@ fn inner_segment_start(tag: &TagIdentifier, exec_state: &mut ExecState, args: Ar })?; let (p, ty) = path.start_point_components(); // Docs generation isn't smart enough to handle ([f64; 2], NumericType). - let point = [TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)]; + let point = [TyF64::new(p[0], ty), TyF64::new(p[1], ty)]; Ok(point) } diff --git a/rust/kcl-lib/src/std/shapes.rs b/rust/kcl-lib/src/std/shapes.rs index 8f7a85493..0fc35b53a 100644 --- a/rust/kcl-lib/src/std/shapes.rs +++ b/rust/kcl-lib/src/std/shapes.rs @@ -38,6 +38,119 @@ pub enum SketchOrSurface { Sketch(Box), } +/// Sketch a rectangle. +pub async fn rectangle(exec_state: &mut ExecState, args: Args) -> Result { + let sketch_or_surface = + args.get_unlabeled_kw_arg("sketchOrSurface", &RuntimeType::sketch_or_surface(), exec_state)?; + let center = args.get_kw_arg_opt("center", &RuntimeType::point2d(), exec_state)?; + let corner = args.get_kw_arg_opt("corner", &RuntimeType::point2d(), exec_state)?; + let width: TyF64 = args.get_kw_arg("width", &RuntimeType::length(), exec_state)?; + let height: TyF64 = args.get_kw_arg("height", &RuntimeType::length(), exec_state)?; + + inner_rectangle(sketch_or_surface, center, corner, width, height, exec_state, args) + .await + .map(Box::new) + .map(|value| KclValue::Sketch { value }) +} + +async fn inner_rectangle( + sketch_or_surface: SketchOrSurface, + center: Option<[TyF64; 2]>, + corner: Option<[TyF64; 2]>, + width: TyF64, + height: TyF64, + exec_state: &mut ExecState, + args: Args, +) -> Result { + let sketch_surface = match sketch_or_surface { + SketchOrSurface::SketchSurface(surface) => surface, + SketchOrSurface::Sketch(s) => s.on, + }; + + // Find the corner in the negative quadrant + let (ty, corner) = match (center, corner) { + (Some(center), None) => ( + center[0].ty, + [center[0].n - width.n / 2.0, center[1].n - height.n / 2.0], + ), + (None, Some(corner)) => (corner[0].ty, [corner[0].n, corner[1].n]), + (None, None) => { + return Err(KclError::new_semantic(KclErrorDetails::new( + "You must supply either `corner` or `center` arguments, but not both".to_string(), + vec![args.source_range], + ))); + } + (Some(_), Some(_)) => { + return Err(KclError::new_semantic(KclErrorDetails::new( + "You must supply either `corner` or `center` arguments, but not both".to_string(), + vec![args.source_range], + ))); + } + }; + let units = ty.expect_length(); + let corner_t = [TyF64::new(corner[0], ty), TyF64::new(corner[1], ty)]; + + // Start the sketch then draw the 4 lines. + let sketch = + crate::std::sketch::inner_start_profile(sketch_surface, corner_t, None, exec_state, args.clone()).await?; + let sketch_id = sketch.id; + let deltas = [[width.n, 0.0], [0.0, height.n], [-width.n, 0.0], [0.0, -height.n]]; + let ids = [ + exec_state.next_uuid(), + exec_state.next_uuid(), + exec_state.next_uuid(), + exec_state.next_uuid(), + ]; + for (id, delta) in ids.iter().copied().zip(deltas) { + exec_state + .batch_modeling_cmd( + ModelingCmdMeta::from_args_id(&args, id), + ModelingCmd::from(mcmd::ExtendPath { + path: sketch.id.into(), + segment: PathSegment::Line { + end: KPoint2d::from(untyped_point_to_mm(delta, units)) + .with_z(0.0) + .map(LengthUnit), + relative: true, + }, + }), + ) + .await?; + } + exec_state + .batch_modeling_cmd( + ModelingCmdMeta::from_args_id(&args, sketch_id), + ModelingCmd::from(mcmd::ClosePath { path_id: sketch.id }), + ) + .await?; + + // Update the sketch in KCL memory. + let mut new_sketch = sketch.clone(); + fn add(a: [f64; 2], b: [f64; 2]) -> [f64; 2] { + [a[0] + b[0], a[1] + b[1]] + } + let a = (corner, add(corner, deltas[0])); + let b = (a.1, add(a.1, deltas[1])); + let c = (b.1, add(b.1, deltas[2])); + let d = (c.1, add(c.1, deltas[3])); + for (id, (from, to)) in ids.into_iter().zip([a, b, c, d]) { + let current_path = Path::ToPoint { + base: BasePath { + from, + to, + tag: None, + units, + geo_meta: GeoMeta { + id, + metadata: args.source_range.into(), + }, + }, + }; + new_sketch.paths.push(current_path); + } + Ok(new_sketch) +} + /// Sketch a circle. pub async fn circle(exec_state: &mut ExecState, args: Args) -> Result { let sketch_or_surface = @@ -71,7 +184,7 @@ async fn inner_circle( let radius = get_radius(radius, diameter, args.source_range)?; let from = [center_u[0] + radius.to_length_units(units), center_u[1]]; - let from_t = [TyF64::new(from[0], ty.clone()), TyF64::new(from[1], ty)]; + let from_t = [TyF64::new(from[0], ty), TyF64::new(from[1], ty)]; let sketch = crate::std::sketch::inner_start_profile(sketch_surface, from_t, None, exec_state, args.clone()).await?; @@ -156,7 +269,7 @@ async fn inner_circle_three_point( exec_state: &mut ExecState, args: Args, ) -> Result { - let ty = p1[0].ty.clone(); + let ty = p1[0].ty; let units = ty.expect_length(); let p1 = point_to_len_unit(p1, units); @@ -172,10 +285,7 @@ async fn inner_circle_three_point( SketchOrSurface::Sketch(group) => group.on, }; - let from = [ - TyF64::new(center[0] + radius, ty.clone()), - TyF64::new(center[1], ty.clone()), - ]; + let from = [TyF64::new(center[0] + radius, ty), TyF64::new(center[1], ty)]; let sketch = crate::std::sketch::inner_start_profile(sketch_surface, from.clone(), None, exec_state, args.clone()).await?; diff --git a/rust/kcl-lib/src/std/sketch.rs b/rust/kcl-lib/src/std/sketch.rs index f14bf0dcf..1c16a13d7 100644 --- a/rust/kcl-lib/src/std/sketch.rs +++ b/rust/kcl-lib/src/std/sketch.rs @@ -602,7 +602,7 @@ async fn inner_angled_line_of_x_length( } let to = get_y_component(Angle::from_degrees(angle_degrees), length.n); - let to = [TyF64::new(to[0], length.ty.clone()), TyF64::new(to[1], length.ty)]; + let to = [TyF64::new(to[0], length.ty), TyF64::new(to[1], length.ty)]; let new_sketch = straight_line(StraightLineParams::relative(to, sketch, tag), exec_state, args).await?; @@ -669,7 +669,7 @@ async fn inner_angled_line_of_y_length( } let to = get_x_component(Angle::from_degrees(angle_degrees), length.n); - let to = [TyF64::new(to[0], length.ty.clone()), TyF64::new(to[1], length.ty)]; + let to = [TyF64::new(to[0], length.ty), TyF64::new(to[1], length.ty)]; let new_sketch = straight_line(StraightLineParams::relative(to, sketch, tag), exec_state, args).await?; diff --git a/rust/kcl-lib/src/unparser.rs b/rust/kcl-lib/src/unparser.rs index 352a9ec1f..42c4b9a24 100644 --- a/rust/kcl-lib/src/unparser.rs +++ b/rust/kcl-lib/src/unparser.rs @@ -363,6 +363,9 @@ impl BinaryPart { BinaryPart::MemberExpression(member_expression) => { member_expression.recast(options, indentation_level, ctxt) } + BinaryPart::ArrayExpression(e) => e.recast(options, indentation_level, ctxt), + BinaryPart::ArrayRangeExpression(e) => e.recast(options, indentation_level, ctxt), + BinaryPart::ObjectExpression(e) => e.recast(options, indentation_level, ctxt), BinaryPart::IfExpression(e) => e.recast(options, indentation_level, ExprContext::Other), BinaryPart::AscribedExpression(e) => e.recast(options, indentation_level, ExprContext::Other), } @@ -745,6 +748,9 @@ impl UnaryExpression { BinaryPart::Literal(_) | BinaryPart::Name(_) | BinaryPart::MemberExpression(_) + | BinaryPart::ArrayExpression(_) + | BinaryPart::ArrayRangeExpression(_) + | BinaryPart::ObjectExpression(_) | BinaryPart::IfExpression(_) | BinaryPart::AscribedExpression(_) | BinaryPart::CallExpressionKw(_) => { diff --git a/rust/kcl-lib/src/walk/ast_node.rs b/rust/kcl-lib/src/walk/ast_node.rs index 01d8950ce..c62bc0fc3 100644 --- a/rust/kcl-lib/src/walk/ast_node.rs +++ b/rust/kcl-lib/src/walk/ast_node.rs @@ -220,6 +220,9 @@ impl<'tree> From<&'tree types::BinaryPart> for Node<'tree> { types::BinaryPart::CallExpressionKw(ce) => ce.as_ref().into(), types::BinaryPart::UnaryExpression(ue) => ue.as_ref().into(), types::BinaryPart::MemberExpression(me) => me.as_ref().into(), + types::BinaryPart::ArrayExpression(e) => e.as_ref().into(), + types::BinaryPart::ArrayRangeExpression(e) => e.as_ref().into(), + types::BinaryPart::ObjectExpression(e) => e.as_ref().into(), types::BinaryPart::IfExpression(e) => e.as_ref().into(), types::BinaryPart::AscribedExpression(e) => e.as_ref().into(), } diff --git a/rust/kcl-lib/std/sketch.kcl b/rust/kcl-lib/std/sketch.kcl index 20b4e64ec..5dcd75b78 100644 --- a/rust/kcl-lib/std/sketch.kcl +++ b/rust/kcl-lib/std/sketch.kcl @@ -234,6 +234,39 @@ export fn startProfile( tag?: TagDecl, ): Sketch {} +/// Sketch a rectangle. +/// +/// ``` +/// exampleSketch = startSketchOn(-XZ) +/// |> rectangle(center = [0, 0], width = 10, height = 5) +// |> extrude(length = 2) +/// ``` +/// +/// ``` +/// exampleSketch = startSketchOn(-XZ) +/// |> rectangle(corner = [0, 0], width = 10, height = 5) +// |> extrude(length = 2) +/// ``` +@(impl = std_rust) +export fn rectangle( + /// Sketch to extend, or plane or surface to sketch on. + @sketchOrSurface: Sketch | Plane | Face, + /// Rectangle's width along X axis. + width: number(Length), + /// Rectangle's height along Y axis. + height: number(Length), + /// The center of the rectangle. + /// Incompatible with `corner`. + @(snippetArray = ["0", "0"]) + center?: Point2d, + /// The corner of the rectangle. + /// Incompatible with `center`. + /// This will be the corner which is most negative on + /// both X and Y axes. + @(snippetArray = ["0", "0"]) + corner?: Point2d, +): Sketch {} + /// Construct a 2-dimensional circle, of the specified radius, centered at /// the provided (x, y) origin point. /// @@ -1962,7 +1995,6 @@ export fn subtract2d( /// The shape(s) which should be cut out of the sketch. tool: [Sketch; 1+], ): Sketch {} - /// Add a conic section to an existing sketch. /// @@ -2152,3 +2184,24 @@ export fn ellipticPoint( /// The y value. Calculates x and returns (x, y). Incompatible with `x`. y?: number(Length), ): Point2d {} + +/// Find the plane a face lies on. +/// Returns an error if the face doesn't lie on any plane (for example, the curved face of a cylinder) +///```kcl +/// triangle = startSketchOn(XY) +/// |> polygon(radius = 3, numSides = 3, center = [0, 0]) +/// |> extrude(length = 2) +/// +/// // Find the plane of the triangle's top face. +/// topPlane = planeOf(triangle, face = END) +/// +/// // Create a new plane, 10 units above the triangle's top face. +/// startSketchOn(offsetPlane(topPlane, offset = 10)) +/// ``` +@(impl = std_rust) +export fn planeOf( + /// The solid whose face is being queried. + @solid: Solid, + /// Find the plane which this face lies on. + face: TaggedFace, +): Plane {} diff --git a/rust/kcl-lib/tests/add_arrays/artifact_commands.snap b/rust/kcl-lib/tests/add_arrays/artifact_commands.snap new file mode 100644 index 000000000..a0448a9cd --- /dev/null +++ b/rust/kcl-lib/tests/add_arrays/artifact_commands.snap @@ -0,0 +1,18 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Artifact commands add_arrays.kcl +--- +{ + "rust/kcl-lib/tests/add_arrays/input.kcl": [], + "std::appearance": [], + "std::array": [], + "std::math": [], + "std::prelude": [], + "std::sketch": [], + "std::solid": [], + "std::sweep": [], + "std::transform": [], + "std::turns": [], + "std::types": [], + "std::units": [] +} diff --git a/rust/kcl-lib/tests/add_arrays/artifact_graph_flowchart.snap b/rust/kcl-lib/tests/add_arrays/artifact_graph_flowchart.snap new file mode 100644 index 000000000..c9f05dab0 --- /dev/null +++ b/rust/kcl-lib/tests/add_arrays/artifact_graph_flowchart.snap @@ -0,0 +1,6 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Artifact graph flowchart add_arrays.kcl +extension: md +snapshot_kind: binary +--- diff --git a/rust/kcl-lib/tests/add_arrays/artifact_graph_flowchart.snap.md b/rust/kcl-lib/tests/add_arrays/artifact_graph_flowchart.snap.md new file mode 100644 index 000000000..13e533509 --- /dev/null +++ b/rust/kcl-lib/tests/add_arrays/artifact_graph_flowchart.snap.md @@ -0,0 +1,3 @@ +```mermaid +flowchart LR +``` diff --git a/rust/kcl-lib/tests/add_arrays/ast.snap b/rust/kcl-lib/tests/add_arrays/ast.snap new file mode 100644 index 000000000..87d65a56e --- /dev/null +++ b/rust/kcl-lib/tests/add_arrays/ast.snap @@ -0,0 +1,106 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Result of parsing add_arrays.kcl +--- +{ + "Ok": { + "body": [ + { + "commentStart": 0, + "declaration": { + "commentStart": 0, + "end": 0, + "id": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "answer", + "start": 0, + "type": "Identifier" + }, + "init": { + "commentStart": 0, + "end": 0, + "left": { + "commentStart": 0, + "elements": [ + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "0", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 0.0, + "suffix": "None" + } + }, + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "1", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 1.0, + "suffix": "None" + } + } + ], + "end": 0, + "moduleId": 0, + "start": 0, + "type": "ArrayExpression", + "type": "ArrayExpression" + }, + "moduleId": 0, + "operator": "+", + "right": { + "commentStart": 0, + "elements": [ + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "2", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 2.0, + "suffix": "None" + } + } + ], + "end": 0, + "moduleId": 0, + "start": 0, + "type": "ArrayExpression", + "type": "ArrayExpression" + }, + "start": 0, + "type": "BinaryExpression", + "type": "BinaryExpression" + }, + "moduleId": 0, + "start": 0, + "type": "VariableDeclarator" + }, + "end": 0, + "kind": "const", + "moduleId": 0, + "start": 0, + "type": "VariableDeclaration", + "type": "VariableDeclaration" + } + ], + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0 + } +} diff --git a/rust/kcl-lib/tests/add_arrays/execution_error.snap b/rust/kcl-lib/tests/add_arrays/execution_error.snap new file mode 100644 index 000000000..0724d8eef --- /dev/null +++ b/rust/kcl-lib/tests/add_arrays/execution_error.snap @@ -0,0 +1,12 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Error from executing add_arrays.kcl +--- +KCL Semantic error + + × semantic: Expected a number, but found an array of `number`, `number` + ╭──── + 1 │ answer = [0, 1] + [2] + · ───┬── + · ╰── tests/add_arrays/input.kcl + ╰──── diff --git a/rust/kcl-lib/tests/add_arrays/input.kcl b/rust/kcl-lib/tests/add_arrays/input.kcl new file mode 100644 index 000000000..6699ec5f9 --- /dev/null +++ b/rust/kcl-lib/tests/add_arrays/input.kcl @@ -0,0 +1 @@ +answer = [0, 1] + [2] diff --git a/rust/kcl-lib/tests/add_arrays/ops.snap b/rust/kcl-lib/tests/add_arrays/ops.snap new file mode 100644 index 000000000..32038be77 --- /dev/null +++ b/rust/kcl-lib/tests/add_arrays/ops.snap @@ -0,0 +1,96 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Operations executed add_arrays.kcl +--- +{ + "rust/kcl-lib/tests/add_arrays/input.kcl": [], + "std::appearance": [], + "std::array": [], + "std::math": [ + { + "type": "VariableDeclaration", + "name": "PI", + "value": { + "type": "Number", + "value": 3.141592653589793, + "ty": { + "type": "Unknown" + } + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + }, + { + "type": "VariableDeclaration", + "name": "E", + "value": { + "type": "Number", + "value": 2.718281828459045, + "ty": { + "type": "Known", + "type": "Count" + } + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + }, + { + "type": "VariableDeclaration", + "name": "TAU", + "value": { + "type": "Number", + "value": 6.283185307179586, + "ty": { + "type": "Known", + "type": "Count" + } + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + } + ], + "std::prelude": [ + { + "type": "VariableDeclaration", + "name": "START", + "value": { + "type": "String", + "value": "start" + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + }, + { + "type": "VariableDeclaration", + "name": "END", + "value": { + "type": "String", + "value": "end" + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + } + ], + "std::sketch": [], + "std::solid": [], + "std::sweep": [], + "std::transform": [], + "std::turns": [], + "std::types": [], + "std::units": [] +} diff --git a/rust/kcl-lib/tests/add_arrays/unparsed.snap b/rust/kcl-lib/tests/add_arrays/unparsed.snap new file mode 100644 index 000000000..18ba47ca7 --- /dev/null +++ b/rust/kcl-lib/tests/add_arrays/unparsed.snap @@ -0,0 +1,5 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Result of unparsing add_arrays.kcl +--- +answer = [0, 1] + [2] diff --git a/rust/kcl-lib/tests/outputs/serial_test_example_fn_std-sketch-planeOf0.png b/rust/kcl-lib/tests/outputs/serial_test_example_fn_std-sketch-planeOf0.png new file mode 100644 index 000000000..df07af627 Binary files /dev/null and b/rust/kcl-lib/tests/outputs/serial_test_example_fn_std-sketch-planeOf0.png differ diff --git a/rust/kcl-lib/tests/outputs/serial_test_example_fn_std-sketch-rectangle0.png b/rust/kcl-lib/tests/outputs/serial_test_example_fn_std-sketch-rectangle0.png new file mode 100644 index 000000000..be2d109a1 Binary files /dev/null and b/rust/kcl-lib/tests/outputs/serial_test_example_fn_std-sketch-rectangle0.png differ diff --git a/rust/kcl-lib/tests/outputs/serial_test_example_fn_std-sketch-rectangle1.png b/rust/kcl-lib/tests/outputs/serial_test_example_fn_std-sketch-rectangle1.png new file mode 100644 index 000000000..be2d109a1 Binary files /dev/null and b/rust/kcl-lib/tests/outputs/serial_test_example_fn_std-sketch-rectangle1.png differ diff --git a/rust/kcl-lib/tests/plane_of/artifact_commands.snap b/rust/kcl-lib/tests/plane_of/artifact_commands.snap new file mode 100644 index 000000000..8a38a1a13 --- /dev/null +++ b/rust/kcl-lib/tests/plane_of/artifact_commands.snap @@ -0,0 +1,381 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Artifact commands plane_of.kcl +--- +{ + "rust/kcl-lib/tests/plane_of/input.kcl": [ + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "make_plane", + "origin": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "x_axis": { + "x": 1.0, + "y": 0.0, + "z": 0.0 + }, + "y_axis": { + "x": 0.0, + "y": 1.0, + "z": 0.0 + }, + "size": 60.0, + "clobber": false, + "hide": true + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "enable_sketch_mode", + "entity_id": "[uuid]", + "ortho": false, + "animated": false, + "adjust_camera": false, + "planar_normal": { + "x": 0.0, + "y": 0.0, + "z": 1.0 + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "start_path" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "move_path_pen", + "path": "[uuid]", + "to": { + "x": 2743.2, + "y": 0.0, + "z": 0.0 + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "sketch_mode_disable" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": -1371.5999999999995, + "y": 2375.680887661472, + "z": 0.0 + }, + "relative": false + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": -1371.6000000000013, + "y": -2375.680887661472, + "z": 0.0 + }, + "relative": false + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": 2743.2, + "y": 0.0, + "z": 0.0 + }, + "relative": false + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "close_path", + "path_id": "[uuid]" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "enable_sketch_mode", + "entity_id": "[uuid]", + "ortho": false, + "animated": false, + "adjust_camera": false, + "planar_normal": { + "x": 0.0, + "y": 0.0, + "z": 1.0 + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extrude", + "target": "[uuid]", + "distance": 1828.8, + "faces": null, + "opposite": "None" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "sketch_mode_disable" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "object_bring_to_front", + "object_id": "[uuid]" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "solid3d_get_extrusion_face_info", + "object_id": "[uuid]", + "edge_id": "[uuid]" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "solid3d_get_adjacency_info", + "object_id": "[uuid]", + "edge_id": "[uuid]" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "face_is_planar", + "object_id": "[uuid]" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "make_plane", + "origin": { + "x": 0.0, + "y": 0.0, + "z": 3657.6 + }, + "x_axis": { + "x": 1.0, + "y": 0.0, + "z": 0.0 + }, + "y_axis": { + "x": 0.0, + "y": 1.0, + "z": 0.0 + }, + "size": 100.0, + "clobber": false, + "hide": false + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "plane_set_color", + "plane_id": "[uuid]", + "color": { + "r": 0.6, + "g": 0.6, + "b": 0.6, + "a": 0.3 + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "object_visible", + "object_id": "[uuid]", + "hidden": true + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "enable_sketch_mode", + "entity_id": "[uuid]", + "ortho": false, + "animated": false, + "adjust_camera": false, + "planar_normal": { + "x": 0.0, + "y": 0.0, + "z": 1.0 + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "start_path" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "move_path_pen", + "path": "[uuid]", + "to": { + "x": 1828.8, + "y": 0.0, + "z": 0.0 + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "sketch_mode_disable" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": 0.00000000000011198170331403397, + "y": 1828.8, + "z": 0.0 + }, + "relative": false + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": -1828.8, + "y": 0.00000000000022396340662806795, + "z": 0.0 + }, + "relative": false + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": -0.0000000000003359451099421019, + "y": -1828.8, + "z": 0.0 + }, + "relative": false + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": 1828.8, + "y": 0.0, + "z": 0.0 + }, + "relative": false + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "close_path", + "path_id": "[uuid]" + } + } + ], + "std::appearance": [], + "std::array": [], + "std::math": [], + "std::prelude": [], + "std::sketch": [], + "std::solid": [], + "std::sweep": [], + "std::transform": [], + "std::turns": [], + "std::types": [], + "std::units": [] +} diff --git a/rust/kcl-lib/tests/plane_of/artifact_graph_flowchart.snap b/rust/kcl-lib/tests/plane_of/artifact_graph_flowchart.snap new file mode 100644 index 000000000..4d1e0089e --- /dev/null +++ b/rust/kcl-lib/tests/plane_of/artifact_graph_flowchart.snap @@ -0,0 +1,6 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Artifact graph flowchart plane_of.kcl +extension: md +snapshot_kind: binary +--- diff --git a/rust/kcl-lib/tests/plane_of/artifact_graph_flowchart.snap.md b/rust/kcl-lib/tests/plane_of/artifact_graph_flowchart.snap.md new file mode 100644 index 000000000..17cc9b7e7 --- /dev/null +++ b/rust/kcl-lib/tests/plane_of/artifact_graph_flowchart.snap.md @@ -0,0 +1,105 @@ +```mermaid +flowchart LR + subgraph path2 [Path] + 2["Path
[64, 114, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }] + 3["Segment
[64, 114, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }] + 4["Segment
[64, 114, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }] + 5["Segment
[64, 114, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }] + 6["Segment
[64, 114, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }] + 7[Solid2d] + end + subgraph path21 [Path] + 21["Path
[311, 361, 0]"] + %% [ProgramBodyItem { index: 2 }, ExpressionStatementExpr, PipeBodyItem { index: 1 }] + 22["Segment
[311, 361, 0]"] + %% [ProgramBodyItem { index: 2 }, ExpressionStatementExpr, PipeBodyItem { index: 1 }] + 23["Segment
[311, 361, 0]"] + %% [ProgramBodyItem { index: 2 }, ExpressionStatementExpr, PipeBodyItem { index: 1 }] + 24["Segment
[311, 361, 0]"] + %% [ProgramBodyItem { index: 2 }, ExpressionStatementExpr, PipeBodyItem { index: 1 }] + 25["Segment
[311, 361, 0]"] + %% [ProgramBodyItem { index: 2 }, ExpressionStatementExpr, PipeBodyItem { index: 1 }] + 26["Segment
[311, 361, 0]"] + %% [ProgramBodyItem { index: 2 }, ExpressionStatementExpr, PipeBodyItem { index: 1 }] + 27[Solid2d] + end + 1["Plane
[41, 58, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }] + 8["Sweep Extrusion
[120, 139, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 2 }] + 9[Wall] + %% face_code_ref=Missing NodePath + 10[Wall] + %% face_code_ref=Missing NodePath + 11[Wall] + %% face_code_ref=Missing NodePath + 12["Cap Start"] + %% face_code_ref=Missing NodePath + 13["Cap End"] + %% face_code_ref=Missing NodePath + 14["SweepEdge Opposite"] + 15["SweepEdge Adjacent"] + 16["SweepEdge Opposite"] + 17["SweepEdge Adjacent"] + 18["SweepEdge Opposite"] + 19["SweepEdge Adjacent"] + 20["Plane
[277, 304, 0]"] + %% [ProgramBodyItem { index: 2 }, ExpressionStatementExpr, PipeBodyItem { index: 0 }, CallKwUnlabeledArg] + 28["StartSketchOnPlane
[263, 305, 0]"] + %% [ProgramBodyItem { index: 2 }, ExpressionStatementExpr, PipeBodyItem { index: 0 }] + 1 --- 2 + 2 --- 3 + 2 --- 4 + 2 --- 5 + 2 --- 6 + 2 --- 7 + 2 ---- 8 + 3 --- 9 + 3 x--> 12 + 3 --- 14 + 3 --- 15 + 4 --- 10 + 4 x--> 12 + 4 --- 16 + 4 --- 17 + 5 --- 11 + 5 x--> 12 + 5 --- 18 + 5 --- 19 + 8 --- 9 + 8 --- 10 + 8 --- 11 + 8 --- 12 + 8 --- 13 + 8 --- 14 + 8 --- 15 + 8 --- 16 + 8 --- 17 + 8 --- 18 + 8 --- 19 + 9 --- 14 + 9 --- 15 + 19 <--x 9 + 15 <--x 10 + 10 --- 16 + 10 --- 17 + 17 <--x 11 + 11 --- 18 + 11 --- 19 + 14 <--x 13 + 16 <--x 13 + 18 <--x 13 + 20 --- 21 + 20 <--x 28 + 21 --- 22 + 21 --- 23 + 21 --- 24 + 21 --- 25 + 21 --- 26 + 21 --- 27 +``` diff --git a/rust/kcl-lib/tests/plane_of/ast.snap b/rust/kcl-lib/tests/plane_of/ast.snap new file mode 100644 index 000000000..7db45f037 --- /dev/null +++ b/rust/kcl-lib/tests/plane_of/ast.snap @@ -0,0 +1,705 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Result of parsing plane_of.kcl +--- +{ + "Ok": { + "body": [ + { + "commentStart": 0, + "declaration": { + "commentStart": 0, + "end": 0, + "id": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "tri", + "start": 0, + "type": "Identifier" + }, + "init": { + "body": [ + { + "arguments": [], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "startSketchOn", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "XY", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name", + "type": "Name" + } + }, + { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "radius", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "3", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 3.0, + "suffix": "None" + } + } + }, + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "numSides", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "3", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 3.0, + "suffix": "None" + } + } + }, + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "center", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "elements": [ + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "0", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 0.0, + "suffix": "None" + } + }, + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "0", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 0.0, + "suffix": "None" + } + } + ], + "end": 0, + "moduleId": 0, + "start": 0, + "type": "ArrayExpression", + "type": "ArrayExpression" + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "polygon", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + }, + { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "length", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "2", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 2.0, + "suffix": "None" + } + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "extrude", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + } + ], + "commentStart": 0, + "end": 0, + "moduleId": 0, + "nonCodeMeta": { + "nonCodeNodes": { + "2": [ + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "NonCodeNode", + "value": { + "type": "newLineBlockComment", + "value": "Get the plane which `tri` ends on.", + "style": "line" + } + } + ] + }, + "startNodes": [] + }, + "start": 0, + "type": "PipeExpression", + "type": "PipeExpression" + }, + "moduleId": 0, + "start": 0, + "type": "VariableDeclarator" + }, + "end": 0, + "kind": "const", + "moduleId": 0, + "start": 0, + "type": "VariableDeclaration", + "type": "VariableDeclaration" + }, + { + "commentStart": 0, + "declaration": { + "commentStart": 0, + "end": 0, + "id": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "p0", + "start": 0, + "type": "Identifier" + }, + "init": { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "face", + "start": 0, + "type": "Identifier" + }, + "arg": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "END", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name", + "type": "Name" + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "planeOf", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "tri", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name", + "type": "Name" + } + }, + "moduleId": 0, + "start": 0, + "type": "VariableDeclarator" + }, + "end": 0, + "kind": "const", + "moduleId": 0, + "start": 0, + "type": "VariableDeclaration", + "type": "VariableDeclaration" + }, + { + "commentStart": 0, + "end": 0, + "expression": { + "body": [ + { + "arguments": [], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "startSketchOn", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "offset", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "2", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 2.0, + "suffix": "None" + } + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "offsetPlane", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "p0", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name", + "type": "Name" + } + } + }, + { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "radius", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "2", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 2.0, + "suffix": "None" + } + } + }, + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "numSides", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "4", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 4.0, + "suffix": "None" + } + } + }, + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "center", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "elements": [ + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "0", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 0.0, + "suffix": "None" + } + }, + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "0", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 0.0, + "suffix": "None" + } + } + ], + "end": 0, + "moduleId": 0, + "start": 0, + "type": "ArrayExpression", + "type": "ArrayExpression" + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "polygon", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + } + ], + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "PipeExpression", + "type": "PipeExpression" + }, + "moduleId": 0, + "preComments": [ + "", + "", + "// Offset that plane by 2, then draw a square on it." + ], + "start": 0, + "type": "ExpressionStatement", + "type": "ExpressionStatement" + } + ], + "commentStart": 0, + "end": 0, + "innerAttrs": [ + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "settings", + "start": 0, + "type": "Identifier" + }, + "properties": [ + { + "commentStart": 0, + "end": 0, + "key": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "defaultLengthUnit", + "start": 0, + "type": "Identifier" + }, + "moduleId": 0, + "start": 0, + "type": "ObjectProperty", + "value": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "yd", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name", + "type": "Name" + } + } + ], + "start": 0, + "type": "Annotation" + } + ], + "moduleId": 0, + "nonCodeMeta": { + "nonCodeNodes": { + "2": [ + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "NonCodeNode", + "value": { + "type": "newLine" + } + } + ] + }, + "startNodes": [ + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "NonCodeNode", + "value": { + "type": "newLine" + } + } + ] + }, + "start": 0 + } +} diff --git a/rust/kcl-lib/tests/plane_of/input.kcl b/rust/kcl-lib/tests/plane_of/input.kcl new file mode 100644 index 000000000..bca0bfa3a --- /dev/null +++ b/rust/kcl-lib/tests/plane_of/input.kcl @@ -0,0 +1,13 @@ +@settings(defaultLengthUnit = yd) + +tri = startSketchOn(XY) + |> polygon(radius = 3, numSides = 3, center = [0, 0]) + |> extrude(length = 2) + +// Get the plane which `tri` ends on. +p0 = planeOf(tri, face = END) + +// Offset that plane by 2, then draw a square on it. +startSketchOn(offsetPlane(p0, offset = 2)) + |> polygon(radius = 2, numSides = 4, center = [0, 0]) + diff --git a/rust/kcl-lib/tests/plane_of/ops.snap b/rust/kcl-lib/tests/plane_of/ops.snap new file mode 100644 index 000000000..adfad681b --- /dev/null +++ b/rust/kcl-lib/tests/plane_of/ops.snap @@ -0,0 +1,254 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Operations executed plane_of.kcl +--- +{ + "rust/kcl-lib/tests/plane_of/input.kcl": [ + { + "type": "StdLibCall", + "name": "startSketchOn", + "unlabeledArg": { + "value": { + "type": "Plane", + "artifact_id": "[uuid]" + }, + "sourceRange": [] + }, + "labeledArgs": {}, + "nodePath": { + "steps": [ + { + "type": "ProgramBodyItem", + "index": 0 + }, + { + "type": "VariableDeclarationDeclaration" + }, + { + "type": "VariableDeclarationInit" + }, + { + "type": "PipeBodyItem", + "index": 0 + } + ] + }, + "sourceRange": [] + }, + { + "type": "StdLibCall", + "name": "extrude", + "unlabeledArg": { + "value": { + "type": "Sketch", + "value": { + "artifactId": "[uuid]" + } + }, + "sourceRange": [] + }, + "labeledArgs": { + "length": { + "value": { + "type": "Number", + "value": 2.0, + "ty": { + "type": "Default", + "len": { + "type": "Yards" + }, + "angle": { + "type": "Degrees" + } + } + }, + "sourceRange": [] + } + }, + "nodePath": { + "steps": [ + { + "type": "ProgramBodyItem", + "index": 0 + }, + { + "type": "VariableDeclarationDeclaration" + }, + { + "type": "VariableDeclarationInit" + }, + { + "type": "PipeBodyItem", + "index": 2 + } + ] + }, + "sourceRange": [] + }, + { + "type": "StdLibCall", + "name": "offsetPlane", + "unlabeledArg": { + "value": { + "type": "Plane", + "artifact_id": "[uuid]" + }, + "sourceRange": [] + }, + "labeledArgs": { + "offset": { + "value": { + "type": "Number", + "value": 2.0, + "ty": { + "type": "Default", + "len": { + "type": "Yards" + }, + "angle": { + "type": "Degrees" + } + } + }, + "sourceRange": [] + } + }, + "nodePath": { + "steps": [ + { + "type": "ProgramBodyItem", + "index": 2 + }, + { + "type": "ExpressionStatementExpr" + }, + { + "type": "PipeBodyItem", + "index": 0 + }, + { + "type": "CallKwUnlabeledArg" + } + ] + }, + "sourceRange": [] + }, + { + "type": "StdLibCall", + "name": "startSketchOn", + "unlabeledArg": { + "value": { + "type": "Plane", + "artifact_id": "[uuid]" + }, + "sourceRange": [] + }, + "labeledArgs": {}, + "nodePath": { + "steps": [ + { + "type": "ProgramBodyItem", + "index": 2 + }, + { + "type": "ExpressionStatementExpr" + }, + { + "type": "PipeBodyItem", + "index": 0 + } + ] + }, + "sourceRange": [] + } + ], + "std::appearance": [], + "std::array": [], + "std::math": [ + { + "type": "VariableDeclaration", + "name": "PI", + "value": { + "type": "Number", + "value": 3.141592653589793, + "ty": { + "type": "Unknown" + } + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + }, + { + "type": "VariableDeclaration", + "name": "E", + "value": { + "type": "Number", + "value": 2.718281828459045, + "ty": { + "type": "Known", + "type": "Count" + } + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + }, + { + "type": "VariableDeclaration", + "name": "TAU", + "value": { + "type": "Number", + "value": 6.283185307179586, + "ty": { + "type": "Known", + "type": "Count" + } + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + } + ], + "std::prelude": [ + { + "type": "VariableDeclaration", + "name": "START", + "value": { + "type": "String", + "value": "start" + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + }, + { + "type": "VariableDeclaration", + "name": "END", + "value": { + "type": "String", + "value": "end" + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + } + ], + "std::sketch": [], + "std::solid": [], + "std::sweep": [], + "std::transform": [], + "std::turns": [], + "std::types": [], + "std::units": [] +} diff --git a/rust/kcl-lib/tests/plane_of/program_memory.snap b/rust/kcl-lib/tests/plane_of/program_memory.snap new file mode 100644 index 000000000..ddd84bc7c --- /dev/null +++ b/rust/kcl-lib/tests/plane_of/program_memory.snap @@ -0,0 +1,192 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Variables in memory after executing plane_of.kcl +--- +{ + "p0": { + "type": "Plane", + "value": { + "artifactId": "[uuid]", + "id": "[uuid]", + "origin": { + "x": 0.0, + "y": 0.0, + "z": 1828.8, + "units": { + "type": "Mm" + } + }, + "value": "Uninit", + "xAxis": { + "x": 1.0, + "y": 0.0, + "z": 0.0, + "units": { + "type": "Mm" + } + }, + "yAxis": { + "x": 0.0, + "y": 1.0, + "z": 0.0, + "units": { + "type": "Mm" + } + } + } + }, + "tri": { + "type": "Solid", + "value": { + "type": "Solid", + "id": "[uuid]", + "artifactId": "[uuid]", + "value": [ + { + "faceId": "[uuid]", + "id": "[uuid]", + "sourceRange": [], + "tag": null, + "type": "extrudePlane" + }, + { + "faceId": "[uuid]", + "id": "[uuid]", + "sourceRange": [], + "tag": null, + "type": "extrudePlane" + }, + { + "faceId": "[uuid]", + "id": "[uuid]", + "sourceRange": [], + "tag": null, + "type": "extrudePlane" + } + ], + "sketch": { + "type": "Sketch", + "id": "[uuid]", + "paths": [ + { + "__geoMeta": { + "id": "[uuid]", + "sourceRange": [] + }, + "from": [ + 3.0, + 0.0 + ], + "tag": null, + "to": [ + -1.4999999999999993, + 2.598076211353316 + ], + "type": "ToPoint", + "units": { + "type": "Yards" + } + }, + { + "__geoMeta": { + "id": "[uuid]", + "sourceRange": [] + }, + "from": [ + -1.4999999999999993, + 2.598076211353316 + ], + "tag": null, + "to": [ + -1.5000000000000013, + -2.5980762113533156 + ], + "type": "ToPoint", + "units": { + "type": "Yards" + } + }, + { + "__geoMeta": { + "id": "[uuid]", + "sourceRange": [] + }, + "from": [ + -1.5000000000000013, + -2.5980762113533156 + ], + "tag": null, + "to": [ + 3.0, + 0.0 + ], + "type": "ToPoint", + "units": { + "type": "Yards" + } + } + ], + "on": { + "artifactId": "[uuid]", + "id": "[uuid]", + "origin": { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "units": { + "type": "Mm" + } + }, + "type": "plane", + "value": "XY", + "xAxis": { + "x": 1.0, + "y": 0.0, + "z": 0.0, + "units": { + "type": "Unknown" + } + }, + "yAxis": { + "x": 0.0, + "y": 1.0, + "z": 0.0, + "units": { + "type": "Unknown" + } + } + }, + "start": { + "from": [ + 3.0, + 0.0 + ], + "to": [ + 3.0, + 0.0 + ], + "units": { + "type": "Yards" + }, + "tag": null, + "__geoMeta": { + "id": "[uuid]", + "sourceRange": [] + } + }, + "artifactId": "[uuid]", + "originalId": "[uuid]", + "units": { + "type": "Yards" + } + }, + "height": 2.0, + "startCapId": "[uuid]", + "endCapId": "[uuid]", + "units": { + "type": "Yards" + }, + "sectional": false + } + } +} diff --git a/rust/kcl-lib/tests/plane_of/rendered_model.png b/rust/kcl-lib/tests/plane_of/rendered_model.png new file mode 100644 index 000000000..441e64b83 Binary files /dev/null and b/rust/kcl-lib/tests/plane_of/rendered_model.png differ diff --git a/rust/kcl-lib/tests/plane_of/unparsed.snap b/rust/kcl-lib/tests/plane_of/unparsed.snap new file mode 100644 index 000000000..56ef43432 --- /dev/null +++ b/rust/kcl-lib/tests/plane_of/unparsed.snap @@ -0,0 +1,16 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Result of unparsing plane_of.kcl +--- +@settings(defaultLengthUnit = yd) + +tri = startSketchOn(XY) + |> polygon(radius = 3, numSides = 3, center = [0, 0]) + |> extrude(length = 2) + +// Get the plane which `tri` ends on. +p0 = planeOf(tri, face = END) + +// Offset that plane by 2, then draw a square on it. +startSketchOn(offsetPlane(p0, offset = 2)) + |> polygon(radius = 2, numSides = 4, center = [0, 0]) diff --git a/rust/kcl-lib/tests/property_access_not_found_on_solid/artifact_commands.snap b/rust/kcl-lib/tests/property_access_not_found_on_solid/artifact_commands.snap new file mode 100644 index 000000000..4c6e3b052 --- /dev/null +++ b/rust/kcl-lib/tests/property_access_not_found_on_solid/artifact_commands.snap @@ -0,0 +1,206 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Artifact commands property_access_not_found_on_solid.kcl +--- +{ + "rust/kcl-lib/tests/property_access_not_found_on_solid/input.kcl": [ + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "make_plane", + "origin": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "x_axis": { + "x": 1.0, + "y": 0.0, + "z": 0.0 + }, + "y_axis": { + "x": 0.0, + "y": 1.0, + "z": 0.0 + }, + "size": 60.0, + "clobber": false, + "hide": true + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "enable_sketch_mode", + "entity_id": "[uuid]", + "ortho": false, + "animated": false, + "adjust_camera": false, + "planar_normal": { + "x": 0.0, + "y": 0.0, + "z": 1.0 + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "start_path" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "move_path_pen", + "path": "[uuid]", + "to": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "sketch_mode_disable" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": 10.0, + "y": 10.0, + "z": 0.0 + }, + "relative": true + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": -20.0, + "y": 0.0, + "z": 0.0 + }, + "relative": true + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": 10.0, + "y": -10.0, + "z": 0.0 + }, + "relative": true + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "close_path", + "path_id": "[uuid]" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "enable_sketch_mode", + "entity_id": "[uuid]", + "ortho": false, + "animated": false, + "adjust_camera": false, + "planar_normal": { + "x": 0.0, + "y": 0.0, + "z": 1.0 + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extrude", + "target": "[uuid]", + "distance": 5.0, + "faces": null, + "opposite": "None" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "sketch_mode_disable" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "object_bring_to_front", + "object_id": "[uuid]" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "solid3d_get_extrusion_face_info", + "object_id": "[uuid]", + "edge_id": "[uuid]" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "solid3d_get_adjacency_info", + "object_id": "[uuid]", + "edge_id": "[uuid]" + } + } + ], + "std::appearance": [], + "std::array": [], + "std::math": [], + "std::prelude": [], + "std::sketch": [], + "std::solid": [], + "std::sweep": [], + "std::transform": [], + "std::turns": [], + "std::types": [], + "std::units": [] +} diff --git a/rust/kcl-lib/tests/property_access_not_found_on_solid/artifact_graph_flowchart.snap b/rust/kcl-lib/tests/property_access_not_found_on_solid/artifact_graph_flowchart.snap new file mode 100644 index 000000000..5a6d20b91 --- /dev/null +++ b/rust/kcl-lib/tests/property_access_not_found_on_solid/artifact_graph_flowchart.snap @@ -0,0 +1,6 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Artifact graph flowchart property_access_not_found_on_solid.kcl +extension: md +snapshot_kind: binary +--- diff --git a/rust/kcl-lib/tests/property_access_not_found_on_solid/artifact_graph_flowchart.snap.md b/rust/kcl-lib/tests/property_access_not_found_on_solid/artifact_graph_flowchart.snap.md new file mode 100644 index 000000000..4a45dfcec --- /dev/null +++ b/rust/kcl-lib/tests/property_access_not_found_on_solid/artifact_graph_flowchart.snap.md @@ -0,0 +1,78 @@ +```mermaid +flowchart LR + subgraph path2 [Path] + 2["Path
[52, 77, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 0 }, ReturnStatementArg, PipeBodyItem { index: 1 }] + 3["Segment
[85, 119, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 0 }, ReturnStatementArg, PipeBodyItem { index: 2 }] + 4["Segment
[127, 147, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 0 }, ReturnStatementArg, PipeBodyItem { index: 3 }] + 5["Segment
[155, 176, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 0 }, ReturnStatementArg, PipeBodyItem { index: 4 }] + 6["Segment
[184, 191, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 0 }, ReturnStatementArg, PipeBodyItem { index: 5 }] + 7[Solid2d] + end + 1["Plane
[27, 44, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 0 }, ReturnStatementArg, PipeBodyItem { index: 0 }] + 8["Sweep Extrusion
[199, 235, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 0 }, ReturnStatementArg, PipeBodyItem { index: 6 }] + 9[Wall] + %% face_code_ref=Missing NodePath + 10[Wall] + %% face_code_ref=Missing NodePath + 11[Wall] + %% face_code_ref=Missing NodePath + 12["Cap Start"] + %% face_code_ref=Missing NodePath + 13["Cap End"] + %% face_code_ref=Missing NodePath + 14["SweepEdge Opposite"] + 15["SweepEdge Adjacent"] + 16["SweepEdge Opposite"] + 17["SweepEdge Adjacent"] + 18["SweepEdge Opposite"] + 19["SweepEdge Adjacent"] + 1 --- 2 + 2 --- 3 + 2 --- 4 + 2 --- 5 + 2 --- 6 + 2 --- 7 + 2 ---- 8 + 3 --- 9 + 3 x--> 12 + 3 --- 14 + 3 --- 15 + 4 --- 10 + 4 x--> 12 + 4 --- 16 + 4 --- 17 + 5 --- 11 + 5 x--> 12 + 5 --- 18 + 5 --- 19 + 8 --- 9 + 8 --- 10 + 8 --- 11 + 8 --- 12 + 8 --- 13 + 8 --- 14 + 8 --- 15 + 8 --- 16 + 8 --- 17 + 8 --- 18 + 8 --- 19 + 9 --- 14 + 9 --- 15 + 19 <--x 9 + 15 <--x 10 + 10 --- 16 + 10 --- 17 + 17 <--x 11 + 11 --- 18 + 11 --- 19 + 14 <--x 13 + 16 <--x 13 + 18 <--x 13 +``` diff --git a/rust/kcl-lib/tests/property_access_not_found_on_solid/ast.snap b/rust/kcl-lib/tests/property_access_not_found_on_solid/ast.snap new file mode 100644 index 000000000..26dbee524 --- /dev/null +++ b/rust/kcl-lib/tests/property_access_not_found_on_solid/ast.snap @@ -0,0 +1,780 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Result of parsing property_access_not_found_on_solid.kcl +--- +{ + "Ok": { + "body": [ + { + "commentStart": 0, + "declaration": { + "commentStart": 0, + "end": 0, + "id": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "myFunction", + "start": 0, + "type": "Identifier" + }, + "init": { + "body": { + "body": [ + { + "argument": { + "body": [ + { + "arguments": [], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "startSketchOn", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "XY", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name", + "type": "Name" + } + }, + { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "at", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "elements": [ + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "0", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 0.0, + "suffix": "None" + } + }, + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "0", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 0.0, + "suffix": "None" + } + } + ], + "end": 0, + "moduleId": 0, + "start": 0, + "type": "ArrayExpression", + "type": "ArrayExpression" + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "startProfile", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + }, + { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "end", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "elements": [ + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "10", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 10.0, + "suffix": "None" + } + }, + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "10", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 10.0, + "suffix": "None" + } + } + ], + "end": 0, + "moduleId": 0, + "start": 0, + "type": "ArrayExpression", + "type": "ArrayExpression" + } + }, + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "tag", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "TagDeclarator", + "type": "TagDeclarator", + "value": "seg01" + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "line", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + }, + { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "end", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "elements": [ + { + "argument": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "20", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 20.0, + "suffix": "None" + } + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "operator": "-", + "start": 0, + "type": "UnaryExpression", + "type": "UnaryExpression" + }, + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "0", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 0.0, + "suffix": "None" + } + } + ], + "end": 0, + "moduleId": 0, + "start": 0, + "type": "ArrayExpression", + "type": "ArrayExpression" + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "line", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + }, + { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "end", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "elements": [ + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "10", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 10.0, + "suffix": "None" + } + }, + { + "argument": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "10", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 10.0, + "suffix": "None" + } + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "operator": "-", + "start": 0, + "type": "UnaryExpression", + "type": "UnaryExpression" + } + ], + "end": 0, + "moduleId": 0, + "start": 0, + "type": "ArrayExpression", + "type": "ArrayExpression" + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "line", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + }, + { + "arguments": [], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "close", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + }, + { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "length", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "5", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 5.0, + "suffix": "None" + } + } + }, + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "tagEnd", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "TagDeclarator", + "type": "TagDeclarator", + "value": "end01" + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "extrude", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + } + ], + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "PipeExpression", + "type": "PipeExpression" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "ReturnStatement", + "type": "ReturnStatement" + } + ], + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0 + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "params": [], + "start": 0, + "type": "FunctionExpression", + "type": "FunctionExpression" + }, + "moduleId": 0, + "start": 0, + "type": "VariableDeclarator" + }, + "end": 0, + "kind": "fn", + "moduleId": 0, + "start": 0, + "type": "VariableDeclaration", + "type": "VariableDeclaration" + }, + { + "commentStart": 0, + "declaration": { + "commentStart": 0, + "end": 0, + "id": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "myShape", + "start": 0, + "type": "Identifier" + }, + "init": { + "arguments": [], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "myFunction", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + }, + "moduleId": 0, + "start": 0, + "type": "VariableDeclarator" + }, + "end": 0, + "kind": "const", + "moduleId": 0, + "start": 0, + "type": "VariableDeclaration", + "type": "VariableDeclaration" + }, + { + "commentStart": 0, + "declaration": { + "commentStart": 0, + "end": 0, + "id": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "sketch001", + "start": 0, + "type": "Identifier" + }, + "init": { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "face", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "computed": false, + "end": 0, + "moduleId": 0, + "object": { + "commentStart": 0, + "computed": false, + "end": 0, + "moduleId": 0, + "object": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "myShape", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name", + "type": "Name" + }, + "property": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "tags", + "start": 0, + "type": "Identifier", + "type": "Identifier" + }, + "start": 0, + "type": "MemberExpression", + "type": "MemberExpression" + }, + "property": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "seg01", + "start": 0, + "type": "Identifier", + "type": "Identifier" + }, + "start": 0, + "type": "MemberExpression", + "type": "MemberExpression" + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "startSketchOn", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "myShape", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name", + "type": "Name" + } + }, + "moduleId": 0, + "start": 0, + "type": "VariableDeclarator" + }, + "end": 0, + "kind": "const", + "moduleId": 0, + "start": 0, + "type": "VariableDeclaration", + "type": "VariableDeclaration" + } + ], + "commentStart": 0, + "end": 0, + "moduleId": 0, + "nonCodeMeta": { + "nonCodeNodes": { + "0": [ + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "NonCodeNode", + "value": { + "type": "newLine" + } + } + ], + "1": [ + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "NonCodeNode", + "value": { + "type": "newLine" + } + } + ], + "2": [ + { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "NonCodeNode", + "value": { + "type": "newLine" + } + } + ] + }, + "startNodes": [] + }, + "start": 0 + } +} diff --git a/rust/kcl-lib/tests/property_access_not_found_on_solid/execution_error.snap b/rust/kcl-lib/tests/property_access_not_found_on_solid/execution_error.snap new file mode 100644 index 000000000..2dd58ad6d --- /dev/null +++ b/rust/kcl-lib/tests/property_access_not_found_on_solid/execution_error.snap @@ -0,0 +1,15 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Error from executing property_access_not_found_on_solid.kcl +--- +KCL Semantic error + + × semantic: Property `tags` not found on a solid. You can get a solid's tags + │ through its sketch, as in, `exampleSolid.sketch.tags`. + ╭─[13:43] + 12 │ + 13 │ sketch001 = startSketchOn(myShape, face = myShape.tags.seg01) + · ──────┬───── + · ╰── tests/property_access_not_found_on_solid/input.kcl + 14 │ + ╰──── diff --git a/rust/kcl-lib/tests/property_access_not_found_on_solid/input.kcl b/rust/kcl-lib/tests/property_access_not_found_on_solid/input.kcl new file mode 100644 index 000000000..35b1d360c --- /dev/null +++ b/rust/kcl-lib/tests/property_access_not_found_on_solid/input.kcl @@ -0,0 +1,14 @@ +fn myFunction() { + return startSketchOn(XY) + |> startProfile(at = [0, 0]) + |> line(end = [10, 10], tag = $seg01) + |> line(end = [-20, 0]) + |> line(end = [10, -10]) + |> close() + |> extrude(length = 5, tagEnd = $end01) +} + +myShape = myFunction() + +sketch001 = startSketchOn(myShape, face = myShape.tags.seg01) + diff --git a/rust/kcl-lib/tests/property_access_not_found_on_solid/ops.snap b/rust/kcl-lib/tests/property_access_not_found_on_solid/ops.snap new file mode 100644 index 000000000..693b13f70 --- /dev/null +++ b/rust/kcl-lib/tests/property_access_not_found_on_solid/ops.snap @@ -0,0 +1,233 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Operations executed property_access_not_found_on_solid.kcl +--- +{ + "rust/kcl-lib/tests/property_access_not_found_on_solid/input.kcl": [ + { + "type": "GroupBegin", + "group": { + "type": "FunctionCall", + "name": "myFunction", + "functionSourceRange": [], + "unlabeledArg": null, + "labeledArgs": {} + }, + "nodePath": { + "steps": [ + { + "type": "ProgramBodyItem", + "index": 1 + }, + { + "type": "VariableDeclarationDeclaration" + }, + { + "type": "VariableDeclarationInit" + } + ] + }, + "sourceRange": [] + }, + { + "type": "StdLibCall", + "name": "startSketchOn", + "unlabeledArg": { + "value": { + "type": "Plane", + "artifact_id": "[uuid]" + }, + "sourceRange": [] + }, + "labeledArgs": {}, + "nodePath": { + "steps": [ + { + "type": "ProgramBodyItem", + "index": 0 + }, + { + "type": "VariableDeclarationDeclaration" + }, + { + "type": "VariableDeclarationInit" + }, + { + "type": "FunctionExpressionBody" + }, + { + "type": "FunctionExpressionBodyItem", + "index": 0 + }, + { + "type": "ReturnStatementArg" + }, + { + "type": "PipeBodyItem", + "index": 0 + } + ] + }, + "sourceRange": [] + }, + { + "type": "StdLibCall", + "name": "extrude", + "unlabeledArg": { + "value": { + "type": "Sketch", + "value": { + "artifactId": "[uuid]" + } + }, + "sourceRange": [] + }, + "labeledArgs": { + "length": { + "value": { + "type": "Number", + "value": 5.0, + "ty": { + "type": "Default", + "len": { + "type": "Mm" + }, + "angle": { + "type": "Degrees" + } + } + }, + "sourceRange": [] + }, + "tagEnd": { + "value": { + "type": "TagDeclarator", + "name": "end01" + }, + "sourceRange": [] + } + }, + "nodePath": { + "steps": [ + { + "type": "ProgramBodyItem", + "index": 0 + }, + { + "type": "VariableDeclarationDeclaration" + }, + { + "type": "VariableDeclarationInit" + }, + { + "type": "FunctionExpressionBody" + }, + { + "type": "FunctionExpressionBodyItem", + "index": 0 + }, + { + "type": "ReturnStatementArg" + }, + { + "type": "PipeBodyItem", + "index": 6 + } + ] + }, + "sourceRange": [] + }, + { + "type": "GroupEnd" + } + ], + "std::appearance": [], + "std::array": [], + "std::math": [ + { + "type": "VariableDeclaration", + "name": "PI", + "value": { + "type": "Number", + "value": 3.141592653589793, + "ty": { + "type": "Unknown" + } + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + }, + { + "type": "VariableDeclaration", + "name": "E", + "value": { + "type": "Number", + "value": 2.718281828459045, + "ty": { + "type": "Known", + "type": "Count" + } + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + }, + { + "type": "VariableDeclaration", + "name": "TAU", + "value": { + "type": "Number", + "value": 6.283185307179586, + "ty": { + "type": "Known", + "type": "Count" + } + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + } + ], + "std::prelude": [ + { + "type": "VariableDeclaration", + "name": "START", + "value": { + "type": "String", + "value": "start" + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + }, + { + "type": "VariableDeclaration", + "name": "END", + "value": { + "type": "String", + "value": "end" + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + } + ], + "std::sketch": [], + "std::solid": [], + "std::sweep": [], + "std::transform": [], + "std::turns": [], + "std::types": [], + "std::units": [] +} diff --git a/rust/kcl-lib/tests/property_access_not_found_on_solid/unparsed.snap b/rust/kcl-lib/tests/property_access_not_found_on_solid/unparsed.snap new file mode 100644 index 000000000..106565aac --- /dev/null +++ b/rust/kcl-lib/tests/property_access_not_found_on_solid/unparsed.snap @@ -0,0 +1,17 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Result of unparsing property_access_not_found_on_solid.kcl +--- +fn myFunction() { + return startSketchOn(XY) + |> startProfile(at = [0, 0]) + |> line(end = [10, 10], tag = $seg01) + |> line(end = [-20, 0]) + |> line(end = [10, -10]) + |> close() + |> extrude(length = 5, tagEnd = $end01) +} + +myShape = myFunction() + +sketch001 = startSketchOn(myShape, face = myShape.tags.seg01) diff --git a/rust/kcl-lib/tests/rect/artifact_commands.snap b/rust/kcl-lib/tests/rect/artifact_commands.snap new file mode 100644 index 000000000..fd195b67a --- /dev/null +++ b/rust/kcl-lib/tests/rect/artifact_commands.snap @@ -0,0 +1,163 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Artifact commands rect.kcl +--- +{ + "rust/kcl-lib/tests/rect/input.kcl": [ + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "make_plane", + "origin": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "x_axis": { + "x": 1.0, + "y": 0.0, + "z": 0.0 + }, + "y_axis": { + "x": 0.0, + "y": 1.0, + "z": 0.0 + }, + "size": 60.0, + "clobber": false, + "hide": true + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "enable_sketch_mode", + "entity_id": "[uuid]", + "ortho": false, + "animated": false, + "adjust_camera": false, + "planar_normal": { + "x": 0.0, + "y": 0.0, + "z": 1.0 + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "start_path" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "move_path_pen", + "path": "[uuid]", + "to": { + "x": -2.0, + "y": -1.0, + "z": 0.0 + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "sketch_mode_disable" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": 3.0, + "y": 0.0, + "z": 0.0 + }, + "relative": true + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": 0.00000000000000012246467991473532, + "y": 2.0, + "z": 0.0 + }, + "relative": true + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": -3.0, + "y": -0.0, + "z": 0.0 + }, + "relative": true + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": -2.0, + "y": -1.0, + "z": 0.0 + }, + "relative": false + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "close_path", + "path_id": "[uuid]" + } + } + ], + "std::appearance": [], + "std::array": [], + "std::math": [], + "std::prelude": [], + "std::sketch": [], + "std::solid": [], + "std::sweep": [], + "std::transform": [], + "std::turns": [], + "std::types": [], + "std::units": [] +} diff --git a/rust/kcl-lib/tests/rect/artifact_graph_flowchart.snap b/rust/kcl-lib/tests/rect/artifact_graph_flowchart.snap new file mode 100644 index 000000000..626a011cd --- /dev/null +++ b/rust/kcl-lib/tests/rect/artifact_graph_flowchart.snap @@ -0,0 +1,6 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Artifact graph flowchart rect.kcl +extension: md +snapshot_kind: binary +--- diff --git a/rust/kcl-lib/tests/rect/artifact_graph_flowchart.snap.md b/rust/kcl-lib/tests/rect/artifact_graph_flowchart.snap.md new file mode 100644 index 000000000..4414db6b1 --- /dev/null +++ b/rust/kcl-lib/tests/rect/artifact_graph_flowchart.snap.md @@ -0,0 +1,27 @@ +```mermaid +flowchart LR + subgraph path2 [Path] + 2["Path
[27, 54, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }] + 3["Segment
[60, 103, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 2 }] + 4["Segment
[109, 155, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 3 }] + 5["Segment
[161, 211, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 4 }] + 6["Segment
[217, 273, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 5 }] + 7["Segment
[279, 286, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 6 }] + 8[Solid2d] + end + 1["Plane
[4, 21, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }] + 1 --- 2 + 2 --- 3 + 2 --- 4 + 2 --- 5 + 2 --- 6 + 2 --- 7 + 2 --- 8 +``` diff --git a/rust/kcl-lib/tests/rect/ast.snap b/rust/kcl-lib/tests/rect/ast.snap new file mode 100644 index 000000000..42dc11681 --- /dev/null +++ b/rust/kcl-lib/tests/rect/ast.snap @@ -0,0 +1,701 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Result of parsing rect.kcl +--- +{ + "Ok": { + "body": [ + { + "commentStart": 0, + "declaration": { + "commentStart": 0, + "end": 0, + "id": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "x", + "start": 0, + "type": "Identifier" + }, + "init": { + "body": [ + { + "arguments": [], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "startSketchOn", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "XY", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name", + "type": "Name" + } + }, + { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "at", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "elements": [ + { + "argument": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "2", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 2.0, + "suffix": "None" + } + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "operator": "-", + "start": 0, + "type": "UnaryExpression", + "type": "UnaryExpression" + }, + { + "argument": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "1", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 1.0, + "suffix": "None" + } + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "operator": "-", + "start": 0, + "type": "UnaryExpression", + "type": "UnaryExpression" + } + ], + "end": 0, + "moduleId": 0, + "start": 0, + "type": "ArrayExpression", + "type": "ArrayExpression" + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "startProfile", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + }, + { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "angle", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "0", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 0.0, + "suffix": "None" + } + } + }, + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "length", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "3", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 3.0, + "suffix": "None" + } + } + }, + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "tag", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "TagDeclarator", + "type": "TagDeclarator", + "value": "a" + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "angledLine", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + }, + { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "angle", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "left": { + "arguments": [], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "segAng", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "a", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name", + "type": "Name" + } + }, + "moduleId": 0, + "operator": "+", + "right": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "90", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 90.0, + "suffix": "None" + } + }, + "start": 0, + "type": "BinaryExpression", + "type": "BinaryExpression" + } + }, + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "length", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "2", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 2.0, + "suffix": "None" + } + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "angledLine", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + }, + { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "angle", + "start": 0, + "type": "Identifier" + }, + "arg": { + "arguments": [], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "segAng", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "a", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name", + "type": "Name" + } + } + }, + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "length", + "start": 0, + "type": "Identifier" + }, + "arg": { + "argument": { + "arguments": [], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "segLen", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "a", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name", + "type": "Name" + } + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "operator": "-", + "start": 0, + "type": "UnaryExpression", + "type": "UnaryExpression" + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "angledLine", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + }, + { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "endAbsolute", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "elements": [ + { + "arguments": [], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "profileStartX", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "PipeSubstitution", + "type": "PipeSubstitution" + } + }, + { + "arguments": [], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "profileStartY", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "PipeSubstitution", + "type": "PipeSubstitution" + } + } + ], + "end": 0, + "moduleId": 0, + "start": 0, + "type": "ArrayExpression", + "type": "ArrayExpression" + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "line", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + }, + { + "arguments": [], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "close", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + } + ], + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "PipeExpression", + "type": "PipeExpression" + }, + "moduleId": 0, + "start": 0, + "type": "VariableDeclarator" + }, + "end": 0, + "kind": "const", + "moduleId": 0, + "start": 0, + "type": "VariableDeclaration", + "type": "VariableDeclaration" + } + ], + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0 + } +} diff --git a/rust/kcl-lib/tests/rect/input.kcl b/rust/kcl-lib/tests/rect/input.kcl new file mode 100644 index 000000000..2ed70845f --- /dev/null +++ b/rust/kcl-lib/tests/rect/input.kcl @@ -0,0 +1,7 @@ +x = startSketchOn(XY) + |> startProfile(at = [-2, -1]) + |> angledLine(angle = 0, length = 3, tag = $a) + |> angledLine(angle = segAng(a) + 90, length = 2) + |> angledLine(angle = segAng(a), length = -segLen(a)) + |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) + |> close() diff --git a/rust/kcl-lib/tests/rect/ops.snap b/rust/kcl-lib/tests/rect/ops.snap new file mode 100644 index 000000000..cf120d1ec --- /dev/null +++ b/rust/kcl-lib/tests/rect/ops.snap @@ -0,0 +1,128 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Operations executed rect.kcl +--- +{ + "rust/kcl-lib/tests/rect/input.kcl": [ + { + "type": "StdLibCall", + "name": "startSketchOn", + "unlabeledArg": { + "value": { + "type": "Plane", + "artifact_id": "[uuid]" + }, + "sourceRange": [] + }, + "labeledArgs": {}, + "nodePath": { + "steps": [ + { + "type": "ProgramBodyItem", + "index": 0 + }, + { + "type": "VariableDeclarationDeclaration" + }, + { + "type": "VariableDeclarationInit" + }, + { + "type": "PipeBodyItem", + "index": 0 + } + ] + }, + "sourceRange": [] + } + ], + "std::appearance": [], + "std::array": [], + "std::math": [ + { + "type": "VariableDeclaration", + "name": "PI", + "value": { + "type": "Number", + "value": 3.141592653589793, + "ty": { + "type": "Unknown" + } + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + }, + { + "type": "VariableDeclaration", + "name": "E", + "value": { + "type": "Number", + "value": 2.718281828459045, + "ty": { + "type": "Known", + "type": "Count" + } + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + }, + { + "type": "VariableDeclaration", + "name": "TAU", + "value": { + "type": "Number", + "value": 6.283185307179586, + "ty": { + "type": "Known", + "type": "Count" + } + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + } + ], + "std::prelude": [ + { + "type": "VariableDeclaration", + "name": "START", + "value": { + "type": "String", + "value": "start" + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + }, + { + "type": "VariableDeclaration", + "name": "END", + "value": { + "type": "String", + "value": "end" + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + } + ], + "std::sketch": [], + "std::solid": [], + "std::sweep": [], + "std::transform": [], + "std::turns": [], + "std::types": [], + "std::units": [] +} diff --git a/rust/kcl-lib/tests/rect/program_memory.snap b/rust/kcl-lib/tests/rect/program_memory.snap new file mode 100644 index 000000000..a93bc3091 --- /dev/null +++ b/rust/kcl-lib/tests/rect/program_memory.snap @@ -0,0 +1,181 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Variables in memory after executing rect.kcl +--- +{ + "a": { + "type": "TagIdentifier", + "type": "TagIdentifier", + "value": "a" + }, + "x": { + "type": "Sketch", + "value": { + "type": "Sketch", + "id": "[uuid]", + "paths": [ + { + "__geoMeta": { + "id": "[uuid]", + "sourceRange": [] + }, + "from": [ + -2.0, + -1.0 + ], + "tag": { + "commentStart": 100, + "end": 102, + "moduleId": 0, + "start": 100, + "type": "TagDeclarator", + "value": "a" + }, + "to": [ + 1.0, + -1.0 + ], + "type": "ToPoint", + "units": { + "type": "Mm" + } + }, + { + "__geoMeta": { + "id": "[uuid]", + "sourceRange": [] + }, + "from": [ + 1.0, + -1.0 + ], + "tag": null, + "to": [ + 1.0000000000000002, + 1.0 + ], + "type": "ToPoint", + "units": { + "type": "Mm" + } + }, + { + "__geoMeta": { + "id": "[uuid]", + "sourceRange": [] + }, + "from": [ + 1.0000000000000002, + 1.0 + ], + "tag": null, + "to": [ + -1.9999999999999998, + 1.0 + ], + "type": "ToPoint", + "units": { + "type": "Mm" + } + }, + { + "__geoMeta": { + "id": "[uuid]", + "sourceRange": [] + }, + "from": [ + -1.9999999999999998, + 1.0 + ], + "tag": null, + "to": [ + -2.0, + -1.0 + ], + "type": "ToPoint", + "units": { + "type": "Mm" + } + }, + { + "__geoMeta": { + "id": "[uuid]", + "sourceRange": [] + }, + "from": [ + -2.0, + -1.0 + ], + "tag": null, + "to": [ + -2.0, + -1.0 + ], + "type": "ToPoint", + "units": { + "type": "Mm" + } + } + ], + "on": { + "artifactId": "[uuid]", + "id": "[uuid]", + "origin": { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "units": { + "type": "Mm" + } + }, + "type": "plane", + "value": "XY", + "xAxis": { + "x": 1.0, + "y": 0.0, + "z": 0.0, + "units": { + "type": "Unknown" + } + }, + "yAxis": { + "x": 0.0, + "y": 1.0, + "z": 0.0, + "units": { + "type": "Unknown" + } + } + }, + "start": { + "from": [ + -2.0, + -1.0 + ], + "to": [ + -2.0, + -1.0 + ], + "units": { + "type": "Mm" + }, + "tag": null, + "__geoMeta": { + "id": "[uuid]", + "sourceRange": [] + } + }, + "tags": { + "a": { + "type": "TagIdentifier", + "value": "a" + } + }, + "artifactId": "[uuid]", + "originalId": "[uuid]", + "units": { + "type": "Mm" + } + } + } +} diff --git a/rust/kcl-lib/tests/rect/rendered_model.png b/rust/kcl-lib/tests/rect/rendered_model.png new file mode 100644 index 000000000..d67607ac6 Binary files /dev/null and b/rust/kcl-lib/tests/rect/rendered_model.png differ diff --git a/rust/kcl-lib/tests/rect/unparsed.snap b/rust/kcl-lib/tests/rect/unparsed.snap new file mode 100644 index 000000000..99637e205 --- /dev/null +++ b/rust/kcl-lib/tests/rect/unparsed.snap @@ -0,0 +1,11 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Result of unparsing rect.kcl +--- +x = startSketchOn(XY) + |> startProfile(at = [-2, -1]) + |> angledLine(angle = 0, length = 3, tag = $a) + |> angledLine(angle = segAng(a) + 90, length = 2) + |> angledLine(angle = segAng(a), length = -segLen(a)) + |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) + |> close() diff --git a/rust/kcl-lib/tests/rect_helper/artifact_commands.snap b/rust/kcl-lib/tests/rect_helper/artifact_commands.snap new file mode 100644 index 000000000..3071652d6 --- /dev/null +++ b/rust/kcl-lib/tests/rect_helper/artifact_commands.snap @@ -0,0 +1,163 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Artifact commands rect_helper.kcl +--- +{ + "rust/kcl-lib/tests/rect_helper/input.kcl": [ + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "make_plane", + "origin": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "x_axis": { + "x": 1.0, + "y": 0.0, + "z": 0.0 + }, + "y_axis": { + "x": 0.0, + "y": 1.0, + "z": 0.0 + }, + "size": 60.0, + "clobber": false, + "hide": true + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "enable_sketch_mode", + "entity_id": "[uuid]", + "ortho": false, + "animated": false, + "adjust_camera": false, + "planar_normal": { + "x": 0.0, + "y": 0.0, + "z": 1.0 + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "start_path" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "move_path_pen", + "path": "[uuid]", + "to": { + "x": -2.0, + "y": -1.0, + "z": 0.0 + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "sketch_mode_disable" + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": 3.0, + "y": 0.0, + "z": 0.0 + }, + "relative": true + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": 0.0, + "y": 2.0, + "z": 0.0 + }, + "relative": true + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": -3.0, + "y": 0.0, + "z": 0.0 + }, + "relative": true + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "extend_path", + "path": "[uuid]", + "segment": { + "type": "line", + "end": { + "x": 0.0, + "y": -2.0, + "z": 0.0 + }, + "relative": true + } + } + }, + { + "cmdId": "[uuid]", + "range": [], + "command": { + "type": "close_path", + "path_id": "[uuid]" + } + } + ], + "std::appearance": [], + "std::array": [], + "std::math": [], + "std::prelude": [], + "std::sketch": [], + "std::solid": [], + "std::sweep": [], + "std::transform": [], + "std::turns": [], + "std::types": [], + "std::units": [] +} diff --git a/rust/kcl-lib/tests/rect_helper/artifact_graph_flowchart.snap b/rust/kcl-lib/tests/rect_helper/artifact_graph_flowchart.snap new file mode 100644 index 000000000..fa21abca9 --- /dev/null +++ b/rust/kcl-lib/tests/rect_helper/artifact_graph_flowchart.snap @@ -0,0 +1,6 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Artifact graph flowchart rect_helper.kcl +extension: md +snapshot_kind: binary +--- diff --git a/rust/kcl-lib/tests/rect_helper/artifact_graph_flowchart.snap.md b/rust/kcl-lib/tests/rect_helper/artifact_graph_flowchart.snap.md new file mode 100644 index 000000000..a082bd808 --- /dev/null +++ b/rust/kcl-lib/tests/rect_helper/artifact_graph_flowchart.snap.md @@ -0,0 +1,24 @@ +```mermaid +flowchart LR + subgraph path2 [Path] + 2["Path
[25, 76, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }] + 3["Segment
[25, 76, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }] + 4["Segment
[25, 76, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }] + 5["Segment
[25, 76, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }] + 6["Segment
[25, 76, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }] + 7[Solid2d] + end + 1["Plane
[4, 21, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }] + 1 --- 2 + 2 --- 3 + 2 --- 4 + 2 --- 5 + 2 --- 6 + 2 --- 7 +``` diff --git a/rust/kcl-lib/tests/rect_helper/ast.snap b/rust/kcl-lib/tests/rect_helper/ast.snap new file mode 100644 index 000000000..9c157979f --- /dev/null +++ b/rust/kcl-lib/tests/rect_helper/ast.snap @@ -0,0 +1,233 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Result of parsing rect_helper.kcl +--- +{ + "Ok": { + "body": [ + { + "commentStart": 0, + "declaration": { + "commentStart": 0, + "end": 0, + "id": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "x", + "start": 0, + "type": "Identifier" + }, + "init": { + "body": [ + { + "arguments": [], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "startSketchOn", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "XY", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name", + "type": "Name" + } + }, + { + "arguments": [ + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "width", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "3", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 3.0, + "suffix": "None" + } + } + }, + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "height", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "2", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 2.0, + "suffix": "None" + } + } + }, + { + "type": "LabeledArg", + "label": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "corner", + "start": 0, + "type": "Identifier" + }, + "arg": { + "commentStart": 0, + "elements": [ + { + "argument": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "2", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 2.0, + "suffix": "None" + } + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "operator": "-", + "start": 0, + "type": "UnaryExpression", + "type": "UnaryExpression" + }, + { + "argument": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "raw": "1", + "start": 0, + "type": "Literal", + "type": "Literal", + "value": { + "value": 1.0, + "suffix": "None" + } + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "operator": "-", + "start": 0, + "type": "UnaryExpression", + "type": "UnaryExpression" + } + ], + "end": 0, + "moduleId": 0, + "start": 0, + "type": "ArrayExpression", + "type": "ArrayExpression" + } + } + ], + "callee": { + "abs_path": false, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": { + "commentStart": 0, + "end": 0, + "moduleId": 0, + "name": "rectangle", + "start": 0, + "type": "Identifier" + }, + "path": [], + "start": 0, + "type": "Name" + }, + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "CallExpressionKw", + "type": "CallExpressionKw", + "unlabeled": null + } + ], + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0, + "type": "PipeExpression", + "type": "PipeExpression" + }, + "moduleId": 0, + "start": 0, + "type": "VariableDeclarator" + }, + "end": 0, + "kind": "const", + "moduleId": 0, + "start": 0, + "type": "VariableDeclaration", + "type": "VariableDeclaration" + } + ], + "commentStart": 0, + "end": 0, + "moduleId": 0, + "start": 0 + } +} diff --git a/rust/kcl-lib/tests/rect_helper/input.kcl b/rust/kcl-lib/tests/rect_helper/input.kcl new file mode 100644 index 000000000..3e041e728 --- /dev/null +++ b/rust/kcl-lib/tests/rect_helper/input.kcl @@ -0,0 +1,2 @@ +x = startSketchOn(XY) +|> rectangle(width = 3, height = 2, corner = [-2, -1]) diff --git a/rust/kcl-lib/tests/rect_helper/ops.snap b/rust/kcl-lib/tests/rect_helper/ops.snap new file mode 100644 index 000000000..a34c67bb0 --- /dev/null +++ b/rust/kcl-lib/tests/rect_helper/ops.snap @@ -0,0 +1,128 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Operations executed rect_helper.kcl +--- +{ + "rust/kcl-lib/tests/rect_helper/input.kcl": [ + { + "type": "StdLibCall", + "name": "startSketchOn", + "unlabeledArg": { + "value": { + "type": "Plane", + "artifact_id": "[uuid]" + }, + "sourceRange": [] + }, + "labeledArgs": {}, + "nodePath": { + "steps": [ + { + "type": "ProgramBodyItem", + "index": 0 + }, + { + "type": "VariableDeclarationDeclaration" + }, + { + "type": "VariableDeclarationInit" + }, + { + "type": "PipeBodyItem", + "index": 0 + } + ] + }, + "sourceRange": [] + } + ], + "std::appearance": [], + "std::array": [], + "std::math": [ + { + "type": "VariableDeclaration", + "name": "PI", + "value": { + "type": "Number", + "value": 3.141592653589793, + "ty": { + "type": "Unknown" + } + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + }, + { + "type": "VariableDeclaration", + "name": "E", + "value": { + "type": "Number", + "value": 2.718281828459045, + "ty": { + "type": "Known", + "type": "Count" + } + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + }, + { + "type": "VariableDeclaration", + "name": "TAU", + "value": { + "type": "Number", + "value": 6.283185307179586, + "ty": { + "type": "Known", + "type": "Count" + } + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + } + ], + "std::prelude": [ + { + "type": "VariableDeclaration", + "name": "START", + "value": { + "type": "String", + "value": "start" + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + }, + { + "type": "VariableDeclaration", + "name": "END", + "value": { + "type": "String", + "value": "end" + }, + "visibility": "export", + "nodePath": { + "steps": [] + }, + "sourceRange": [] + } + ], + "std::sketch": [], + "std::solid": [], + "std::sweep": [], + "std::transform": [], + "std::turns": [], + "std::types": [], + "std::units": [] +} diff --git a/rust/kcl-lib/tests/rect_helper/program_memory.snap b/rust/kcl-lib/tests/rect_helper/program_memory.snap new file mode 100644 index 000000000..d2fd3a45a --- /dev/null +++ b/rust/kcl-lib/tests/rect_helper/program_memory.snap @@ -0,0 +1,144 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Variables in memory after executing rect_helper.kcl +--- +{ + "x": { + "type": "Sketch", + "value": { + "type": "Sketch", + "id": "[uuid]", + "paths": [ + { + "__geoMeta": { + "id": "[uuid]", + "sourceRange": [] + }, + "from": [ + -2.0, + -1.0 + ], + "tag": null, + "to": [ + 1.0, + -1.0 + ], + "type": "ToPoint", + "units": { + "type": "Mm" + } + }, + { + "__geoMeta": { + "id": "[uuid]", + "sourceRange": [] + }, + "from": [ + 1.0, + -1.0 + ], + "tag": null, + "to": [ + 1.0, + 1.0 + ], + "type": "ToPoint", + "units": { + "type": "Mm" + } + }, + { + "__geoMeta": { + "id": "[uuid]", + "sourceRange": [] + }, + "from": [ + 1.0, + 1.0 + ], + "tag": null, + "to": [ + -2.0, + 1.0 + ], + "type": "ToPoint", + "units": { + "type": "Mm" + } + }, + { + "__geoMeta": { + "id": "[uuid]", + "sourceRange": [] + }, + "from": [ + -2.0, + 1.0 + ], + "tag": null, + "to": [ + -2.0, + -1.0 + ], + "type": "ToPoint", + "units": { + "type": "Mm" + } + } + ], + "on": { + "artifactId": "[uuid]", + "id": "[uuid]", + "origin": { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "units": { + "type": "Mm" + } + }, + "type": "plane", + "value": "XY", + "xAxis": { + "x": 1.0, + "y": 0.0, + "z": 0.0, + "units": { + "type": "Unknown" + } + }, + "yAxis": { + "x": 0.0, + "y": 1.0, + "z": 0.0, + "units": { + "type": "Unknown" + } + } + }, + "start": { + "from": [ + -2.0, + -1.0 + ], + "to": [ + -2.0, + -1.0 + ], + "units": { + "type": "Mm" + }, + "tag": null, + "__geoMeta": { + "id": "[uuid]", + "sourceRange": [] + } + }, + "artifactId": "[uuid]", + "originalId": "[uuid]", + "units": { + "type": "Mm" + } + } + } +} diff --git a/rust/kcl-lib/tests/rect_helper/rendered_model.png b/rust/kcl-lib/tests/rect_helper/rendered_model.png new file mode 100644 index 000000000..d67607ac6 Binary files /dev/null and b/rust/kcl-lib/tests/rect_helper/rendered_model.png differ diff --git a/rust/kcl-lib/tests/rect_helper/unparsed.snap b/rust/kcl-lib/tests/rect_helper/unparsed.snap new file mode 100644 index 000000000..f8376774d --- /dev/null +++ b/rust/kcl-lib/tests/rect_helper/unparsed.snap @@ -0,0 +1,6 @@ +--- +source: kcl-lib/src/simulation_tests.rs +description: Result of unparsing rect_helper.kcl +--- +x = startSketchOn(XY) + |> rectangle(width = 3, height = 2, corner = [-2, -1]) diff --git a/rust/kcl-lib/tests/subtract_regression12/rendered_model.png b/rust/kcl-lib/tests/subtract_regression12/rendered_model.png index 5aae217e3..c541dac7c 100644 Binary files a/rust/kcl-lib/tests/subtract_regression12/rendered_model.png and b/rust/kcl-lib/tests/subtract_regression12/rendered_model.png differ diff --git a/rust/kcl-python-bindings/Cargo.toml b/rust/kcl-python-bindings/Cargo.toml index 8d6cde759..a7184becf 100644 --- a/rust/kcl-python-bindings/Cargo.toml +++ b/rust/kcl-python-bindings/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kcl-python-bindings" -version = "0.3.83" +version = "0.3.84" edition = "2021" repository = "https://github.com/kittycad/modeling-app" exclude = ["tests/*", "files/*", "venv/*"] diff --git a/rust/kcl-test-server/Cargo.toml b/rust/kcl-test-server/Cargo.toml index 5a16659f0..bb8505841 100644 --- a/rust/kcl-test-server/Cargo.toml +++ b/rust/kcl-test-server/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "kcl-test-server" description = "A test server for KCL" -version = "0.1.83" +version = "0.1.84" edition = "2021" license = "MIT" diff --git a/rust/kcl-to-core/Cargo.toml b/rust/kcl-to-core/Cargo.toml index 8f73372a7..c5a89ff86 100644 --- a/rust/kcl-to-core/Cargo.toml +++ b/rust/kcl-to-core/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "kcl-to-core" description = "Utility methods to convert kcl to engine core executable tests" -version = "0.1.83" +version = "0.1.84" edition = "2021" license = "MIT" repository = "https://github.com/KittyCAD/modeling-app" diff --git a/rust/kcl-wasm-lib/Cargo.toml b/rust/kcl-wasm-lib/Cargo.toml index 77670e8d7..9905ce187 100644 --- a/rust/kcl-wasm-lib/Cargo.toml +++ b/rust/kcl-wasm-lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kcl-wasm-lib" -version = "0.1.83" +version = "0.1.84" edition = "2021" repository = "https://github.com/KittyCAD/modeling-app" rust-version = "1.83" @@ -25,7 +25,7 @@ kittycad = { workspace = true } kittycad-modeling-cmds = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true, features = ["sync", "rt"] } -toml = "0.8.22" +toml = "0.8.23" tower-lsp = { workspace = true, features = ["runtime-agnostic"] } uuid = { workspace = true, features = ["v4", "js", "serde"] } wasm-bindgen = "0.2.99" diff --git a/rust/kcl-wasm-lib/src/wasm.rs b/rust/kcl-wasm-lib/src/wasm.rs index c44f8c634..ae9095d05 100644 --- a/rust/kcl-wasm-lib/src/wasm.rs +++ b/rust/kcl-wasm-lib/src/wasm.rs @@ -61,6 +61,37 @@ pub fn format_number_literal(value: f64, suffix_json: &str) -> Result Result { + console_error_panic_hook::set_once(); + + // ts-rs can't handle tuple types, so it mashes all of these types together. + if let Ok(ty) = serde_json::from_str::(numeric_type_json) { + if let Ok(formatted) = kcl_lib::pretty::format_number_value(value, ty) { + return Ok(formatted); + } + } + if let Ok(unit_type) = serde_json::from_str::(numeric_type_json) { + let ty = NumericType::Known(unit_type); + if let Ok(formatted) = kcl_lib::pretty::format_number_value(value, ty) { + return Ok(formatted); + } + } + if let Ok(unit_len) = serde_json::from_str::(numeric_type_json) { + let ty = NumericType::Known(UnitType::Length(unit_len)); + if let Ok(formatted) = kcl_lib::pretty::format_number_value(value, ty) { + return Ok(formatted); + } + } + if let Ok(unit_angle) = serde_json::from_str::(numeric_type_json) { + let ty = NumericType::Known(UnitType::Angle(unit_angle)); + if let Ok(formatted) = kcl_lib::pretty::format_number_value(value, ty) { + return Ok(formatted); + } + } + Err(format!("Invalid type: {numeric_type_json}")) +} + #[wasm_bindgen] pub fn human_display_number(value: f64, ty_json: &str) -> Result { console_error_panic_hook::set_once(); diff --git a/scripts/diff-circular-deps.sh b/scripts/diff-circular-deps.sh index 425b363b2..9d95765e6 100755 --- a/scripts/diff-circular-deps.sh +++ b/scripts/diff-circular-deps.sh @@ -2,4 +2,4 @@ set -euo pipefail npm run circular-deps | sed '$d' > /tmp/circular-deps.txt -diff --ignore-blank-lines -w /tmp/circular-deps.txt ./known-circular.txt +diff --ignore-blank-lines -w /tmp/circular-deps.txt ./scripts/known/circular.txt diff --git a/scripts/diff-url-checker.sh b/scripts/diff-url-checker.sh new file mode 100755 index 000000000..21b4e4fa2 --- /dev/null +++ b/scripts/diff-url-checker.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -euo pipefail + +npm run url-checker > /tmp/urls.txt +diff --ignore-blank-lines -w /tmp/urls.txt ./scripts/known/urls.txt diff --git a/known-circular.txt b/scripts/known/circular.txt similarity index 100% rename from known-circular.txt rename to scripts/known/circular.txt diff --git a/scripts/known/urls.txt b/scripts/known/urls.txt new file mode 100644 index 000000000..2b08be00e --- /dev/null +++ b/scripts/known/urls.txt @@ -0,0 +1,21 @@ + +> zoo-modeling-app@0.0.0 url-checker +> ./scripts/url-checker.sh + +URL STATUS +000 https://${BASE_URL} +301 https://discord.gg/JQEpHR7Nt2 +404 https://github.com/KittyCAD/engine/issues/3528 +404 https://github.com/KittyCAD/modeling-app/commit/${ref} +302 https://github.com/KittyCAD/modeling-app/issues/new/choose +302 https://github.com/KittyCAD/modeling-app/issues/new?template=bug_report.yml +302 https://github.com/KittyCAD/modeling-app/issues/new?title=${title}&body=${body} +404 https://github.com/KittyCAD/modeling-app/releases/tag/v${version} +521 https://placekitten.com/200/200 +302 https://reactrouter.com/en/6.16.0/routers/picking-a-router#using-v64-data-apis +302 https://stackoverflow.com/a/57390160/22753272 +302 https://stackoverflow.com/a/58436959/22753272 +303 https://text-to-cad.zoo.dev/dashboard +307 https://zoo.dev/ +308 https://zoo.dev/docs/api/ml/generate-a-cad-model-from-text +308 https://zoo.dev/docs/kcl diff --git a/scripts/url-checker.sh b/scripts/url-checker.sh new file mode 100755 index 000000000..450e86214 --- /dev/null +++ b/scripts/url-checker.sh @@ -0,0 +1,56 @@ +#!/bin/bash +set -euo pipefail +trap 'echo "$BASH_COMMAND"' ERR + +remove_after_space () { + sed 's/ .*//' +} + +remove_after_backtick () { + sed 's/`.*//' +} + +remove_after_end_paren () { + sed 's/).*//' +} + +remove_after_double_quote () { + sed 's/".*//' +} + +remove_after_gt () { + sed 's/>.*//' +} + +remove_after_comma () { + sed 's/,.*//' +} + +# Search all src/**/*.ts files +val1=$(grep -Eoh "(https)://[^']+" src/**/*.ts | remove_after_space | remove_after_backtick | remove_after_end_paren | remove_after_double_quote | remove_after_gt | remove_after_comma) + +# Search all src/**/*.tsx files +val2=$(grep -Eoh "(https)://[^']+" src/**/*.tsx | remove_after_space | remove_after_backtick | remove_after_end_paren | remove_after_double_quote | remove_after_gt | remove_after_comma) + +# Required a newline between them when combining since there is not one at the end of val1 +combined="$val1"$'\n'"$val2" + +# Merge both ts and tsx results and unique them +uniqued=$(echo "$combined" | sort | uniq) + +# All urls and status codes +all="URL\tSTATUS\n" + +# All non 200 urls and status codes +problematic="URL\tSTATUS\n" +while read line; do + # || true this curl request to bypass any failures and not have the scrip panic. + # the set -euo pipefail will cause a panic if a curl fails + status=$(curl -o /dev/null -s -w "%{http_code}\n" $line || true) + all+="$status\t$line\n" + if [[ "$status" -ne 200 ]]; then + # list status first over line because of white space formatting, less annoying for diffing + problematic+="$status\t$line\n" + fi +done < <(echo "$uniqued") +echo -e $problematic | column -t diff --git a/src/Toolbar.tsx b/src/Toolbar.tsx index 6be7284a9..657081eb7 100644 --- a/src/Toolbar.tsx +++ b/src/Toolbar.tsx @@ -394,7 +394,7 @@ export function Toolbar({
{isInTemporaryWorkspace && (
-
+
Temporary workspace
) diff --git a/src/components/CommandBar/CommandBarHeaderFooter.tsx b/src/components/CommandBar/CommandBarHeaderFooter.tsx index 8934aeaaa..19731edd9 100644 --- a/src/components/CommandBar/CommandBarHeaderFooter.tsx +++ b/src/components/CommandBar/CommandBarHeaderFooter.tsx @@ -12,7 +12,7 @@ import type { } from '@src/lib/commandTypes' import type { Selections } from '@src/lib/selections' import { getSelectionTypeDisplayText } from '@src/lib/selections' -import { roundOff } from '@src/lib/utils' +import { roundOffWithUnits } from '@src/lib/utils' import { commandBarActor, useCommandBarState } from '@src/lib/singletons' function CommandBarHeaderFooter({ @@ -163,10 +163,8 @@ function CommandBarHeaderFooter({ arg.inputType === 'selectionMixed' ? ( getSelectionTypeDisplayText(argValue as Selections) ) : arg.inputType === 'kcl' ? ( - roundOff( - Number( - (argValue as KclCommandValue).valueCalculated - ), + roundOffWithUnits( + (argValue as KclCommandValue).valueCalculated, 4 ) ) : arg.inputType === 'text' && diff --git a/src/components/CommandBar/CommandBarKclInput.tsx b/src/components/CommandBar/CommandBarKclInput.tsx index 257206681..113d4c814 100644 --- a/src/components/CommandBar/CommandBarKclInput.tsx +++ b/src/components/CommandBar/CommandBarKclInput.tsx @@ -21,13 +21,13 @@ import { Spinner } from '@src/components/Spinner' import { createLocalName, createVariableDeclaration } from '@src/lang/create' import { getNodeFromPath } from '@src/lang/queryAst' import type { SourceRange, VariableDeclarator } from '@src/lang/wasm' -import { isPathToNode } from '@src/lang/wasm' +import { formatNumberValue, isPathToNode } from '@src/lang/wasm' import type { CommandArgument, KclCommandValue } from '@src/lib/commandTypes' import { kclManager } from '@src/lib/singletons' import { getSystemTheme } from '@src/lib/theme' import { err } from '@src/lib/trap' import { useCalculateKclExpression } from '@src/lib/useCalculateKclExpression' -import { roundOff } from '@src/lib/utils' +import { roundOff, roundOffWithUnits } from '@src/lib/utils' import { varMentions } from '@src/lib/varCompletionExtension' import { useSettings } from '@src/lib/singletons' import { commandBarActor, useCommandBarState } from '@src/lib/singletons' @@ -128,10 +128,22 @@ function CommandBarKclInput({ sourceRange: sourceRangeForPrevVariables, }) - const varMentionData: Completion[] = prevVariables.map((v) => ({ - label: v.key, - detail: String(roundOff(Number(v.value))), - })) + const varMentionData: Completion[] = prevVariables.map((v) => { + const roundedWithUnits = (() => { + if (typeof v.value !== 'number' || !v.ty) { + return undefined + } + const numWithUnits = formatNumberValue(v.value, v.ty) + if (err(numWithUnits)) { + return undefined + } + return roundOffWithUnits(numWithUnits) + })() + return { + label: v.key, + detail: roundedWithUnits ?? String(roundOff(Number(v.value))), + } + }) const varMentionsExtension = varMentions(varMentionData) const { setContainer, view } = useCodeMirror({ @@ -282,7 +294,7 @@ function CommandBarKclInput({ ) : calcResult === 'NAN' ? ( "Can't calculate" ) : ( - roundOff(Number(calcResult), 4) + roundOffWithUnits(calcResult, 4) )} diff --git a/src/env.ts b/src/env.ts index bde829f58..f3f0af864 100644 --- a/src/env.ts +++ b/src/env.ts @@ -11,7 +11,6 @@ export const VITE_KC_API_WS_MODELING_URL = env.VITE_KC_API_WS_MODELING_URL as export const VITE_KC_API_BASE_URL = env.VITE_KC_API_BASE_URL export const VITE_KC_SITE_BASE_URL = env.VITE_KC_SITE_BASE_URL export const VITE_KC_SITE_APP_URL = env.VITE_KC_SITE_APP_URL -export const VITE_KC_SKIP_AUTH = env.VITE_KC_SKIP_AUTH as string | undefined export const VITE_KC_CONNECTION_TIMEOUT_MS = env.VITE_KC_CONNECTION_TIMEOUT_MS as string | undefined export const VITE_KC_DEV_TOKEN = env.VITE_KC_DEV_TOKEN as string | undefined diff --git a/src/lang/queryAst.test.ts b/src/lang/queryAst.test.ts index e71d39ddc..94d570af7 100644 --- a/src/lang/queryAst.test.ts +++ b/src/lang/queryAst.test.ts @@ -63,11 +63,36 @@ variableBelowShouldNotBeIncluded = 3 execState.variables, topLevelRange(rangeStart, rangeStart) ) + const defaultTy = { + type: 'Default', + angle: { + type: 'Degrees', + }, + len: { + type: 'Mm', + }, + } expect(variables).toEqual([ - { key: 'baseThick', value: 1 }, - { key: 'armAngle', value: 60 }, - { key: 'baseThickHalf', value: 0.5 }, - { key: 'halfArmAngle', value: 30 }, + { + key: 'baseThick', + value: 1, + ty: defaultTy, + }, + { + key: 'armAngle', + value: 60, + ty: defaultTy, + }, + { + key: 'baseThickHalf', + value: 0.5, + ty: defaultTy, + }, + { + key: 'halfArmAngle', + value: 30, + ty: defaultTy, + }, // no arrExpShouldNotBeIncluded, variableBelowShouldNotBeIncluded etc ]) // there are 4 number variables and 2 non-number variables before the sketch var diff --git a/src/lang/queryAst.ts b/src/lang/queryAst.ts index d89d7b1c8..1ea158cec 100644 --- a/src/lang/queryAst.ts +++ b/src/lang/queryAst.ts @@ -55,6 +55,7 @@ import type { OpKclValue, Operation } from '@rust/kcl-lib/bindings/Operation' import { ARG_INDEX_FIELD, LABELED_ARG_FIELD } from '@src/lang/queryAstConstants' import type { KclCommandValue } from '@src/lib/commandTypes' import type { UnaryExpression } from 'typescript' +import type { NumericType } from '@rust/kcl-lib/bindings/NumericType' /** * Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type. @@ -306,6 +307,7 @@ export function traverse( export interface PrevVariable { key: string value: T + ty: NumericType | undefined } export function findAllPreviousVariablesPath( @@ -353,6 +355,7 @@ export function findAllPreviousVariablesPath( variables.push({ key: varName, value: varValue.value, + ty: varValue.type === 'Number' ? varValue.ty : undefined, }) }) diff --git a/src/lang/wasm.ts b/src/lang/wasm.ts index ebdb544b5..ee2d05f9a 100644 --- a/src/lang/wasm.ts +++ b/src/lang/wasm.ts @@ -45,6 +45,7 @@ import { default_app_settings, default_project_settings, format_number_literal, + format_number_value, get_kcl_version, get_tangential_arc_to_info, human_display_number, @@ -448,6 +449,23 @@ export function formatNumberLiteral( } } +/** + * Format a number from a KclValue such that it could be parsed as KCL. + */ +export function formatNumberValue( + value: number, + numericType: NumericType +): string | Error { + try { + return format_number_value(value, JSON.stringify(numericType)) + } catch (e) { + return new Error( + `Error formatting number value: value=${value}, numericType=${numericType}`, + { cause: e } + ) + } +} + /** * Debug display a number with suffix, for human consumption only. */ diff --git a/src/lib/kclHelpers.test.ts b/src/lib/kclHelpers.test.ts index c3b79a50e..3bcfc58eb 100644 --- a/src/lib/kclHelpers.test.ts +++ b/src/lib/kclHelpers.test.ts @@ -2,13 +2,20 @@ import type { ParseResult } from '@src/lang/wasm' import { getCalculatedKclExpressionValue } from '@src/lib/kclHelpers' describe('KCL expression calculations', () => { - it('calculates a simple expression', async () => { + it('calculates a simple expression without units', async () => { const actual = await getCalculatedKclExpressionValue('1 + 2') const coercedActual = actual as Exclude expect(coercedActual).not.toHaveProperty('errors') expect(coercedActual.valueAsString).toEqual('3') expect(coercedActual?.astNode).toBeDefined() }) + it('calculates a simple expression with units', async () => { + const actual = await getCalculatedKclExpressionValue('1deg + 30deg') + const coercedActual = actual as Exclude + expect(coercedActual).not.toHaveProperty('errors') + expect(coercedActual.valueAsString).toEqual('31deg') + expect(coercedActual?.astNode).toBeDefined() + }) it('returns NAN for an invalid expression', async () => { const actual = await getCalculatedKclExpressionValue('1 + x') const coercedActual = actual as Exclude diff --git a/src/lib/kclHelpers.ts b/src/lib/kclHelpers.ts index 284ce10d0..3244fa16a 100644 --- a/src/lib/kclHelpers.ts +++ b/src/lib/kclHelpers.ts @@ -1,5 +1,10 @@ import { executeAstMock } from '@src/lang/langHelpers' -import { type CallExpressionKw, parse, resultIsOk } from '@src/lang/wasm' +import { + type CallExpressionKw, + formatNumberValue, + parse, + resultIsOk, +} from '@src/lang/wasm' import type { KclCommandValue, KclExpression } from '@src/lib/commandTypes' import { rustContext } from '@src/lib/singletons' import { err } from '@src/lib/trap' @@ -32,12 +37,27 @@ export async function getCalculatedKclExpressionValue(value: string) { const variableDeclaratorAstNode = resultDeclaration?.type === 'VariableDeclaration' && resultDeclaration?.declaration.init - const resultRawValue = execState.variables[DUMMY_VARIABLE_NAME]?.value + const varValue = execState.variables[DUMMY_VARIABLE_NAME] + // If the value is a number, attempt to format it with units. + const resultValueWithUnits = (() => { + if (!varValue || varValue.type !== 'Number') { + return undefined + } + const formatted = formatNumberValue(varValue.value, varValue.ty) + if (err(formatted)) return undefined + return formatted + })() + // Prefer the formatted value with units. Fallback to the raw value. + const resultRawValue = varValue?.value + const valueAsString = resultValueWithUnits + ? resultValueWithUnits + : typeof resultRawValue === 'number' + ? String(resultRawValue) + : 'NAN' return { astNode: variableDeclaratorAstNode, - valueAsString: - typeof resultRawValue === 'number' ? String(resultRawValue) : 'NAN', + valueAsString, } } diff --git a/src/lib/useCalculateKclExpression.ts b/src/lib/useCalculateKclExpression.ts index 5bcbf5cf9..15184ab89 100644 --- a/src/lib/useCalculateKclExpression.ts +++ b/src/lib/useCalculateKclExpression.ts @@ -79,7 +79,7 @@ export function useCalculateKclExpression({ isValueParsable = false } const initialCalcResult: number | string = - Number.isNaN(Number(value)) || !isValueParsable ? 'NAN' : value + Number.isNaN(parseFloat(value)) || !isValueParsable ? 'NAN' : value const [calcResult, setCalcResult] = useState(initialCalcResult) const [newVariableName, _setNewVariableName] = useState('') const [isNewVariableNameUnique, setIsNewVariableNameUnique] = useState(true) diff --git a/src/lib/utils.test.ts b/src/lib/utils.test.ts index cf96a8423..a5b850f44 100644 --- a/src/lib/utils.test.ts +++ b/src/lib/utils.test.ts @@ -8,6 +8,7 @@ import { isOverlap, onDragNumberCalculation, roundOff, + roundOffWithUnits, simulateOnMouseDragMatch, } from '@src/lib/utils' @@ -43,6 +44,48 @@ describe('testing roundOff', () => { }) }) +describe('roundOffWithUnits', () => { + it('works with no units', () => { + expect(roundOffWithUnits('1.23456789')).toBe('1.23') + expect(roundOffWithUnits('1.23456789', 3)).toBe('1.235') + expect(roundOffWithUnits('1.', 3)).toBe('1') + expect(roundOffWithUnits('-1.23456789')).toBe('-1.23') + expect(roundOffWithUnits('-1.23456789', 3)).toBe('-1.235') + expect(roundOffWithUnits('-1.', 3)).toBe('-1') + }) + it('works with standard units', () => { + expect(roundOffWithUnits('1.23456789mm', 3)).toBe('1.235mm') + expect(roundOffWithUnits('1.23456789m', 3)).toBe('1.235m') + expect(roundOffWithUnits('1.23456789in', 3)).toBe('1.235in') + expect(roundOffWithUnits('1.23456789_', 3)).toBe('1.235_') + expect(roundOffWithUnits('1._', 3)).toBe('1_') + expect(roundOffWithUnits('-1.23456789mm', 3)).toBe('-1.235mm') + expect(roundOffWithUnits('-1.23456789m', 3)).toBe('-1.235m') + expect(roundOffWithUnits('-1.23456789in', 3)).toBe('-1.235in') + expect(roundOffWithUnits('-1.23456789_', 3)).toBe('-1.235_') + expect(roundOffWithUnits('-1._', 3)).toBe('-1_') + expect(roundOffWithUnits('1.23456789e3mm', 3)).toBe('1234.568mm') + expect(roundOffWithUnits('1.23456789e3m', 3)).toBe('1234.568m') + expect(roundOffWithUnits('1.23456789e3in', 3)).toBe('1234.568in') + expect(roundOffWithUnits('1.23456789e3_', 3)).toBe('1234.568_') + expect(roundOffWithUnits('1.e3_', 3)).toBe('1000_') + expect(roundOffWithUnits('1e3_', 3)).toBe('1000_') + expect(roundOffWithUnits('1.23456789e-3mm', 3)).toBe('0.001mm') + expect(roundOffWithUnits('1.23456789e-3m', 3)).toBe('0.001m') + expect(roundOffWithUnits('1.23456789e-3in', 3)).toBe('0.001in') + expect(roundOffWithUnits('1.23456789e-3_', 3)).toBe('0.001_') + expect(roundOffWithUnits('1.e-3_', 3)).toBe('0.001_') + expect(roundOffWithUnits('1e-3_', 3)).toBe('0.001_') + }) + it('works with weird units', () => { + expect(roundOffWithUnits('1.23456789_?', 3)).toBe('1.235_?') + expect(roundOffWithUnits('-1.23456789_?', 3)).toBe('-1.235_?') + }) + it('returns the original string when used with something not parsable as a number', () => { + expect(roundOffWithUnits('foo', 3)).toBe('foo') + }) +}) + describe('testing hasLeadingZero', () => { it('.1 should have no leading zero', () => { const actual = hasLeadingZero('.1') diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 49b4e59c2..2b0e1a06f 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -328,6 +328,32 @@ export function roundOff(num: number, precision: number = 2): number { return Math.round(num * x) / x } +export function roundOffWithUnits( + numWithUnits: string, + precision: number = 2 +): string { + const match = numWithUnits.match( + /^([+-]?[\d.]+(?:[eE][+-]?\d+)?)([a-zA-Z_?]+)$/ + ) + let num: string + let suffix: string + if (match) { + num = match[1] + suffix = match[2] ?? '' + } else { + // If no match, assume it's just a number with no units. + num = numWithUnits + suffix = '' + } + const parsedNum = parseFloat(num) + if (Number.isNaN(parsedNum)) { + // If parsing fails, return the original string. + return numWithUnits + } + const roundedNum = roundOff(parsedNum, precision) + return `${roundedNum}${suffix}` +} + /** * Determine if the number as a string has any precision in the decimal places * '1' -> 0 diff --git a/src/lib/wasm_lib_wrapper.ts b/src/lib/wasm_lib_wrapper.ts index 1af4c9d97..8fee6b6c9 100644 --- a/src/lib/wasm_lib_wrapper.ts +++ b/src/lib/wasm_lib_wrapper.ts @@ -13,6 +13,7 @@ import type { default_app_settings as DefaultAppSettings, default_project_settings as DefaultProjectSettings, format_number_literal as FormatNumberLiteral, + format_number_value as FormatNumberValue, human_display_number as HumanDisplayNumber, get_kcl_version as GetKclVersion, get_tangential_arc_to_info as GetTangentialArcToInfo, @@ -59,6 +60,9 @@ export const recast_wasm: typeof RecastWasm = (...args) => { export const format_number_literal: typeof FormatNumberLiteral = (...args) => { return getModule().format_number_literal(...args) } +export const format_number_value: typeof FormatNumberValue = (...args) => { + return getModule().format_number_value(...args) +} export const human_display_number: typeof HumanDisplayNumber = (...args) => { return getModule().human_display_number(...args) } diff --git a/src/machines/authMachine.ts b/src/machines/authMachine.ts index 45b2d328b..97acd35a4 100644 --- a/src/machines/authMachine.ts +++ b/src/machines/authMachine.ts @@ -1,10 +1,5 @@ import type { Models } from '@kittycad/lib' -import { - DEV, - VITE_KC_API_BASE_URL, - VITE_KC_DEV_TOKEN, - VITE_KC_SKIP_AUTH, -} from '@src/env' +import { VITE_KC_API_BASE_URL, VITE_KC_DEV_TOKEN } from '@src/env' import { assign, fromPromise, setup } from 'xstate' import { COOKIE_NAME, OAUTH2_DEVICE_CLIENT_ID } from '@src/lib/constants' @@ -21,26 +16,6 @@ import { } from '@src/lib/withBaseURL' import { ACTOR_IDS } from '@src/machines/machineConstants' -const SKIP_AUTH = VITE_KC_SKIP_AUTH === 'true' && DEV - -const LOCAL_USER: Models['User_type'] = { - id: '8675309', - name: 'Test User', - email: 'kittycad.sidebar.test@example.com', - image: 'https://placekitten.com/200/200', - created_at: 'yesteryear', - updated_at: 'today', - company: 'Test Company', - discord: 'Test User#1234', - github: 'testuser', - phone: '555-555-5555', - first_name: 'Test', - last_name: 'User', - can_train_on_data: false, - is_service_account: false, - deletion_scheduled: false, -} - export interface UserContext { user?: Models['User_type'] token: string @@ -165,19 +140,6 @@ async function getUser(input: { token?: string }) { if (!token && isDesktop()) return Promise.reject(new Error('No token found')) if (token) headers['Authorization'] = `Bearer ${token}` - if (SKIP_AUTH) { - // For local tests - if (localStorage.getItem('FORCE_NO_IMAGE')) { - LOCAL_USER.image = '' - } - - markOnce('code/didAuth') - return { - user: LOCAL_USER, - token, - } - } - const userPromise = isDesktop() ? getUserDesktop(token, VITE_KC_API_BASE_URL) : fetch(url, { diff --git a/src/main.ts b/src/main.ts index 27bf5b8f9..c130963b9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -75,7 +75,6 @@ process.env.VITE_KC_API_WS_MODELING_URL ??= viteEnv.VITE_KC_API_WS_MODELING_URL process.env.VITE_KC_API_BASE_URL ??= viteEnv.VITE_KC_API_BASE_URL process.env.VITE_KC_SITE_BASE_URL ??= viteEnv.VITE_KC_SITE_BASE_URL process.env.VITE_KC_SITE_APP_URL ??= viteEnv.VITE_KC_SITE_APP_URL -process.env.VITE_KC_SKIP_AUTH ??= viteEnv.VITE_KC_SKIP_AUTH process.env.VITE_KC_CONNECTION_TIMEOUT_MS ??= viteEnv.VITE_KC_CONNECTION_TIMEOUT_MS diff --git a/src/menu/designRole.ts b/src/menu/designRole.ts index 74eb6ba66..598b68349 100644 --- a/src/menu/designRole.ts +++ b/src/menu/designRole.ts @@ -9,7 +9,7 @@ export const modelingDesignRole = ( label: 'Design', submenu: [ { - label: 'Start sketch', + label: 'Start Sketch', id: 'Design.Start sketch', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -19,7 +19,7 @@ export const modelingDesignRole = ( }, { type: 'separator' }, { - label: 'Create an offset plane', + label: 'Create an Offset Plane', id: 'Design.Create an offset plane', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -28,7 +28,7 @@ export const modelingDesignRole = ( }, }, { - label: 'Create a helix', + label: 'Create a Helix', id: 'Design.Create a helix', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -37,7 +37,7 @@ export const modelingDesignRole = ( }, }, { - label: 'Create a parameter', + label: 'Create a Parameter', id: 'Design.Create a parameter', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -47,7 +47,7 @@ export const modelingDesignRole = ( }, { type: 'separator' }, { - label: 'Create an additive feature', + label: 'Create an Additive Feature', id: 'Design.Create an additive feature', submenu: [ { @@ -89,7 +89,7 @@ export const modelingDesignRole = ( ], }, { - label: 'Apply modification feature', + label: 'Apply Modification Feature', id: 'Design.Apply modification feature', submenu: [ { @@ -123,7 +123,7 @@ export const modelingDesignRole = ( }, { type: 'separator' }, { - label: 'Insert from project file', + label: 'Insert from Project File', id: 'Design.Insert from project file', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { diff --git a/src/menu/editRole.ts b/src/menu/editRole.ts index 469332f1f..eb80617ad 100644 --- a/src/menu/editRole.ts +++ b/src/menu/editRole.ts @@ -30,7 +30,7 @@ export const projectEditRole = ( label: 'Edit', submenu: [ { - label: 'Rename project', + label: 'Rename Project', id: 'Edit.Rename project', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -39,7 +39,7 @@ export const projectEditRole = ( }, }, { - label: 'Delete project', + label: 'Delete Project', id: 'Edit.Delete project', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -49,7 +49,7 @@ export const projectEditRole = ( }, { type: 'separator' }, { - label: 'Change project directory', + label: 'Change Project Directory', id: 'Edit.Change project directory', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -102,7 +102,7 @@ export const modelingEditRole = ( }, }, { - label: 'Edit parameter', + label: 'Edit Parameter', id: 'Edit.Edit parameter', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -111,7 +111,7 @@ export const modelingEditRole = ( }, }, { - label: 'Format code', + label: 'Format Code', id: 'Edit.Format code', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -121,7 +121,7 @@ export const modelingEditRole = ( }, { type: 'separator' }, { - label: 'Rename project', + label: 'Rename Project', id: 'Edit.Rename project', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -130,7 +130,7 @@ export const modelingEditRole = ( }, }, { - label: 'Delete project', + label: 'Delete Project', id: 'Edit.Delete project', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -140,7 +140,7 @@ export const modelingEditRole = ( }, { type: 'separator' }, { - label: 'Change project directory', + label: 'Change Project Directory', id: 'Edit.Change project directory', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { diff --git a/src/menu/fileRole.ts b/src/menu/fileRole.ts index 08774c4b1..904fecae4 100644 --- a/src/menu/fileRole.ts +++ b/src/menu/fileRole.ts @@ -13,7 +13,7 @@ export const projectFileRole = ( label: 'File', submenu: [ { - label: 'Create project', + label: 'Create Project', id: 'File.Create project', accelerator: 'CommandOrControl+N', click: () => { @@ -23,7 +23,7 @@ export const projectFileRole = ( }, }, { - label: 'Open project', + label: 'Open Project', id: 'File.Open project', accelerator: 'CommandOrControl+P', click: () => { @@ -36,7 +36,7 @@ export const projectFileRole = ( // Appears to be only Windows and Mac OS specific. Linux does not have support { type: 'separator' }, { - label: 'Add file to project', + label: 'Add File to Project', id: 'File.Add file to project', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -58,7 +58,7 @@ export const projectFileRole = ( label: 'Preferences', submenu: [ { - label: 'User settings', + label: 'User Settings', id: 'File.Preferences.User settings', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -76,7 +76,7 @@ export const projectFileRole = ( }, }, { - label: 'User default units', + label: 'User Default Units', id: 'File.Preferences.User default units', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -94,7 +94,7 @@ export const projectFileRole = ( }, }, { - label: 'Theme color', + label: 'Theme Color', id: 'File.Preferences.Theme color', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -107,7 +107,7 @@ export const projectFileRole = ( { type: 'separator' }, // Last in list { - label: 'Sign out', + label: 'Sign Out', id: 'File.Sign out', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -128,7 +128,7 @@ export const modelingFileRole = ( submenu: [ // TODO: Once a safe command bar create new file and folder is implemented we can turn these on // { - // label: 'Create new file', + // label: 'Create New File', // click: () => { // typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { // menuLabel: 'File.Create new file', @@ -136,7 +136,7 @@ export const modelingFileRole = ( // }, // }, // { - // label: 'Create new folder', + // label: 'Create New Folder', // click: () => { // typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { // menuLabel: 'File.Create new folder', @@ -144,7 +144,7 @@ export const modelingFileRole = ( // }, // }, { - label: 'Create project', + label: 'Create Project', id: 'File.Create project', accelerator: 'CommandOrControl+N', click: () => { @@ -154,7 +154,7 @@ export const modelingFileRole = ( }, }, { - label: 'Open project', + label: 'Open Project', id: 'File.Open project', accelerator: 'CommandOrControl+P', click: () => { @@ -167,7 +167,7 @@ export const modelingFileRole = ( // Appears to be only Windows and Mac OS specific. Linux does not have support { type: 'separator' }, { - label: 'Add file to project', + label: 'Add File to Project', id: 'File.Add file to project', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -176,7 +176,7 @@ export const modelingFileRole = ( }, }, { - label: 'Export current part', + label: 'Export Current Part', id: 'File.Export current part', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -189,7 +189,7 @@ export const modelingFileRole = ( label: 'Preferences', submenu: [ { - label: 'Project settings', + label: 'Project Settings', id: 'File.Preferences.Project settings', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -198,7 +198,7 @@ export const modelingFileRole = ( }, }, { - label: 'User settings', + label: 'User Settings', id: 'File.Preferences.User settings', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -216,7 +216,7 @@ export const modelingFileRole = ( }, }, { - label: 'User default units', + label: 'User Default Units', id: 'File.Preferences.User default units', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -234,7 +234,7 @@ export const modelingFileRole = ( }, }, { - label: 'Theme color', + label: 'Theme Color', id: 'File.Preferences.Theme color', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -247,7 +247,7 @@ export const modelingFileRole = ( { type: 'separator' }, // Last in list { - label: 'Sign out', + label: 'Sign Out', id: 'File.Sign out', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { diff --git a/src/menu/helpRole.ts b/src/menu/helpRole.ts index 2353a5392..8c05b8eed 100644 --- a/src/menu/helpRole.ts +++ b/src/menu/helpRole.ts @@ -14,7 +14,7 @@ export const helpRole = ( submenu: [ { id: 'Help.Show all commands', - label: 'Show all commands', + label: 'Show All Commands', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { menuLabel: 'Help.Command Palette...', @@ -22,7 +22,7 @@ export const helpRole = ( }, }, { - label: 'KCL code samples', + label: 'KCL Code Samples', id: 'Help.KCL code samples', click: () => { shell @@ -31,13 +31,13 @@ export const helpRole = ( }, }, { - label: 'KCL docs', + label: 'KCL Docs', click: () => { shell.openExternal('https://zoo.dev/docs/kcl').catch(reportRejection) }, }, { - label: 'Get started with Text-to-CAD', + label: 'Get Started with Text-to-CAD', click: () => { shell .openExternal('https://text-to-cad.zoo.dev/dashboard') @@ -46,7 +46,7 @@ export const helpRole = ( }, { type: 'separator' }, { - label: 'Ask the community discord', + label: 'Ask the Community Discord', click: () => { shell .openExternal('https://discord.gg/JQEpHR7Nt2') @@ -54,7 +54,7 @@ export const helpRole = ( }, }, { - label: 'Ask the community discourse', + label: 'Ask the Community Discourse', click: () => { shell .openExternal('https://community.zoo.dev/') @@ -63,7 +63,7 @@ export const helpRole = ( }, { type: 'separator' }, { - label: 'Report a bug', + label: 'Report a Bug', id: 'Help.Report a bug', click: () => { shell @@ -74,7 +74,7 @@ export const helpRole = ( }, }, { - label: 'Request a feature', + label: 'Request a Feature', click: () => { shell .openExternal( @@ -86,7 +86,7 @@ export const helpRole = ( { type: 'separator' }, { id: 'Help.Replay onboarding tutorial', - label: 'Replay onboarding tutorial', + label: 'Replay Onboarding Tutorial', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { menuLabel: 'Help.Replay onboarding tutorial', @@ -99,7 +99,7 @@ export const helpRole = ( { role: 'forceReload' }, { type: 'separator' }, { - label: 'Show release notes', + label: 'Show Release Notes', click: () => { shell .openExternal('https://github.com/KittyCAD/modeling-app/releases') @@ -107,14 +107,14 @@ export const helpRole = ( }, }, { - label: 'Check for updates', + label: 'Check for Updates', click: () => { getAutoUpdater().checkForUpdates().catch(reportRejection) }, }, { type: 'separator' }, { - label: 'Manage account', + label: 'Manage Account', click: () => { shell.openExternal('https://zoo.dev/account').catch(reportRejection) }, diff --git a/src/menu/roles.ts b/src/menu/roles.ts index f340ecc45..085f9bd19 100644 --- a/src/menu/roles.ts +++ b/src/menu/roles.ts @@ -12,47 +12,47 @@ type HeaderLabel = | 'View' type FileRoleLabel = - | 'Open project' - | 'Create project' - | 'Import file from URL' + | 'Open Project' + | 'Create Project' + | 'Import File from URL' | 'Preferences' - | 'User settings' + | 'User Settings' | 'Keybindings' - | 'Sign out' + | 'Sign Out' | 'Theme' - | 'Theme color' - | 'Export current part' - | 'Create new file' - | 'Create new folder' - | 'Share part via Zoo link' - | 'Project settings' - | 'Add file to project' - | 'User default units' + | 'Theme Color' + | 'Export Current Part' + | 'Create New File' + | 'Create New Folder' + | 'Share Part via Zoo Link' + | 'Project Settings' + | 'Add File to Project' + | 'User Default Units' type EditRoleLabel = - | 'Rename project' - | 'Delete project' - | 'Change project directory' + | 'Rename Project' + | 'Delete Project' + | 'Change Project Directory' | 'Undo' | 'Redo' | 'Speech' - | 'Edit parameter' + | 'Edit Parameter' | 'Modify with Zoo Text-To-CAD' - | 'Format code' + | 'Format Code' type HelpRoleLabel = - | 'Report a bug' - | 'Request a feature' - | 'Ask the community discord' - | 'Ask the community discourse' - | 'KCL code samples' - | 'KCL docs' - | 'Replay onboarding tutorial' - | 'Show release notes' - | 'Check for updates' - | 'Manage account' - | 'Get started with Text-to-CAD' - | 'Show all commands' + | 'Report a Bug' + | 'Request a Feature' + | 'Ask the Community Discord' + | 'Ask the Community Discourse' + | 'KCL Code Samples' + | 'KCL Docs' + | 'Replay Onboarding Tutorial' + | 'Show Release Notes' + | 'Check for Updates' + | 'Manage Account' + | 'Get Started with Text-to-CAD' + | 'Show All Commands' type ViewRoleLabel = | 'Command Palette...' @@ -64,37 +64,37 @@ type ViewRoleLabel = | 'Variables' | 'Logs' | 'Debug' - | 'Standard views' - | 'Orthographic view' - | 'Perspective view' - | 'Right view' - | 'Back view' - | 'Top view' - | 'Left view' - | 'Front view' - | 'Bottom view' - | 'Reset view' - | 'Center view on selection' + | 'Standard Views' + | 'Orthographic View' + | 'Perspective View' + | 'Right View' + | 'Back View' + | 'Top View' + | 'Left View' + | 'Front View' + | 'Bottom View' + | 'Reset View' + | 'Center View on Selection' | 'Refresh' - | 'Named views' - | 'Create named view' - | 'Load named view' - | 'Delete named view' + | 'Named Views' + | 'Create Named View' + | 'Load Named View' + | 'Delete Named View' type DesignRoleLabel = | 'Design' - | 'Create a parameter' - | 'Insert from project file' + | 'Create a Parameter' + | 'Insert from Project File' | 'Create with Zoo Text-To-CAD' - | 'Start sketch' - | 'Create an offset plane' - | 'Create a helix' - | 'Create an additive feature' + | 'Start Sketch' + | 'Create an Offset Plane' + | 'Create a Helix' + | 'Create an Additive Feature' | 'Extrude' | 'Revolve' | 'Sweep' | 'Loft' - | 'Apply modification feature' + | 'Apply Modification Feature' | 'Fillet' | 'Chamfer' | 'Shell' diff --git a/src/menu/viewRole.ts b/src/menu/viewRole.ts index 0dde901c3..1d0c2fa1c 100644 --- a/src/menu/viewRole.ts +++ b/src/menu/viewRole.ts @@ -74,7 +74,7 @@ export const modelingViewRole = ( }, { type: 'separator' }, { - label: 'Orthographic view', + label: 'Orthographic View', id: 'View.Orthographic view', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -83,7 +83,7 @@ export const modelingViewRole = ( }, }, { - label: 'Perspective view', + label: 'Perspective View', id: 'View.Perspective view', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -93,11 +93,11 @@ export const modelingViewRole = ( }, { type: 'separator' }, { - label: 'Standard views', + label: 'Standard Views', id: 'View.Standard views', submenu: [ { - label: 'Right view', + label: 'Right View', id: 'View.Standard views.Right view', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -106,7 +106,7 @@ export const modelingViewRole = ( }, }, { - label: 'Back view', + label: 'Back View', id: 'View.Standard views.Back view', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -114,9 +114,8 @@ export const modelingViewRole = ( }) }, }, - { - label: 'Top view', + label: 'Top View', id: 'View.Standard views.Top view', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -124,9 +123,8 @@ export const modelingViewRole = ( }) }, }, - { - label: 'Left view', + label: 'Left View', id: 'View.Standard views.Left view', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -134,9 +132,8 @@ export const modelingViewRole = ( }) }, }, - { - label: 'Front view', + label: 'Front View', id: 'View.Standard views.Front view', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -144,9 +141,8 @@ export const modelingViewRole = ( }) }, }, - { - label: 'Bottom view', + label: 'Bottom View', id: 'View.Standard views.Bottom view', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -156,7 +152,7 @@ export const modelingViewRole = ( }, { type: 'separator' }, { - label: 'Reset view', + label: 'Reset View', id: 'View.Standard views.Reset view', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -164,9 +160,8 @@ export const modelingViewRole = ( }) }, }, - { - label: 'Center view on selection', + label: 'Center View on Selection', id: 'View.Standard views.Center view on selection', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -186,11 +181,11 @@ export const modelingViewRole = ( ], }, { - label: 'Named views', + label: 'Named Views', id: 'View.Named views', submenu: [ { - label: 'Create named view', + label: 'Create Named View', id: 'View.Named views.Create named view', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -198,9 +193,8 @@ export const modelingViewRole = ( }) }, }, - { - label: 'Load named view', + label: 'Load Named View', id: 'View.Named views.Load named view', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { @@ -208,9 +202,8 @@ export const modelingViewRole = ( }) }, }, - { - label: 'Delete named view', + label: 'Delete Named View', id: 'View.Named views.Delete named view', click: () => { typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', { diff --git a/src/preload.ts b/src/preload.ts index 5e7ebe4c5..4eb8036f7 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -292,7 +292,6 @@ contextBridge.exposeInMainWorld('electron', { 'VITE_KC_API_BASE_URL', 'VITE_KC_SITE_BASE_URL', 'VITE_KC_SITE_APP_URL', - 'VITE_KC_SKIP_AUTH', 'VITE_KC_CONNECTION_TIMEOUT_MS', 'VITE_KC_DEV_TOKEN', diff --git a/src/routes/Home.tsx b/src/routes/Home.tsx index 234540de6..de9555ae5 100644 --- a/src/routes/Home.tsx +++ b/src/routes/Home.tsx @@ -66,6 +66,7 @@ import { defaultLocalStatusBarItems, defaultGlobalStatusBarItems, } from '@src/components/StatusBar/defaultStatusBarItems' +import { useSelector } from '@xstate/react' type ReadWriteProjectState = { value: boolean @@ -81,6 +82,8 @@ const Home = () => { const [nativeFileMenuCreated, setNativeFileMenuCreated] = useState(false) const apiToken = useToken() const networkMachineStatus = useNetworkMachineStatus() + const billingContext = useSelector(billingActor, ({ context }) => context) + const hasUnlimitedCredits = billingContext.credits === Infinity // Only create the native file menus on desktop useEffect(() => { @@ -354,11 +357,13 @@ const Home = () => {
    -
  • -
    - -
    -
  • + {!hasUnlimitedCredits && ( +
  • +
    + +
    +
  • + )}
  • { }} className="!bg-primary !text-chalkboard-10 !border-transarent" > - AI-unlocked CAD + ML-unlocked CAD