Compare commits

..

1 Commits

Author SHA1 Message Date
7711bf768c Iterate over binary ops (instead of recursing) 2025-04-08 17:15:33 -05:00
1860 changed files with 394408 additions and 6300279 deletions

View File

@ -1,3 +1,3 @@
[codespell]
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,atleast,ue,afterall,ser,fromM,FromM
skip: **/target,node_modules,build,dist,./out,**/Cargo.lock,./docs/kcl/*.md,./e2e/playwright/lib/console-error-whitelist.ts,.package-lock.json,**/package-lock.json,./openapi/*.json,./packages/codemirror-lang-kcl/test/all.test.ts,./public/kcl-samples,./rust/kcl-lib/tests/kcl_samples,tsconfig.tsbuildinfo,./src/lib/machine-api.d.ts
skip: **/target,node_modules,build,dist,./out,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./packages/codemirror-lang-kcl/test/all.test.ts,./public/kcl-samples,./rust/kcl-lib/tests/kcl_samples,tsconfig.tsbuildinfo

View File

@ -19,11 +19,6 @@
"plugin:jsx-a11y/recommended",
"plugin:react-hooks/recommended"
],
"settings": {
"react": {
"version": "detect"
}
},
"rules": {
"no-array-constructor": "off", // This is wrong; use the @typescript-eslint one instead.
"@typescript-eslint/no-array-constructor": "error",
@ -66,7 +61,6 @@
"jsx-a11y/click-events-have-key-events": "off",
"jsx-a11y/no-autofocus": "off",
"jsx-a11y/no-noninteractive-element-interactions": "off",
"react/no-unknown-property": "error",
"no-restricted-globals": [
"error",
{

View File

@ -7,11 +7,11 @@ if [[ ! -f "test-results/.last-run.json" ]]; then
# If no last run artifact, than run Playwright normally
echo "run playwright normally"
if [[ "$3" == *ubuntu* ]]; then
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test:playwright:electron:ubuntu -- --shard=$1/$2 || true
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu -- --shard=$1/$2 || true
elif [[ "$3" == *windows* ]]; then
npm run test:playwright:electron:windows -- --shard=$1/$2 || true
yarn test:playwright:electron:windows -- --shard=$1/$2 || true
elif [[ "$3" == *macos* ]]; then
npm run test:playwright:electron:macos -- --shard=$1/$2 || true
yarn test:playwright:electron:macos -- --shard=$1/$2 || true
else
echo "Do not run Playwright. Unable to detect os runtime."
exit 1
@ -26,16 +26,16 @@ max_retries=1
# Retry failed tests, doing our own retries because using inbuilt Playwright retries causes connection issues
while [[ $retry -le $max_retries ]]; do
if [[ -f "test-results/.last-run.json" ]]; then
status=$(jq -r '.status' test-results/.last-run.json)
if [[ "$status" == "failed" ]]; then
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
if [[ $failed_tests -gt 0 ]]; then
echo "retried=true" >>$GITHUB_OUTPUT
echo "run playwright with last failed tests and retry $retry"
if [[ "$3" == *ubuntu* ]]; then
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test:playwright:electron:ubuntu -- --last-failed || true
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test:playwright:electron:ubuntu -- --last-failed || true
elif [[ "$3" == *windows* ]]; then
npm run test:playwright:electron:windows -- --last-failed || true
yarn test:playwright:electron:windows -- --last-failed || true
elif [[ "$3" == *macos* ]]; then
npm run test:playwright:electron:macos -- --last-failed || true
yarn test:playwright:electron:macos -- --last-failed || true
else
echo "Do not run playwright. Unable to detect os runtime."
exit 1
@ -56,11 +56,10 @@ done
echo "retried=false" >>$GITHUB_OUTPUT
if [[ -f "test-results/.last-run.json" ]]; then
status=$(jq -r '.status' test-results/.last-run.json)
if [[ "$status" == "failed" ]]; then
# If it still fails after retries, then fail the job
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
if [[ $failed_tests -gt 0 ]]; then
# If it still fails after 3 retries, then fail the job
exit 1
fi
fi
exit 0

View File

@ -13,9 +13,9 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
cache: 'yarn'
- name: Install dependencies
run: npm install
run: yarn
- name: Use correct Rust toolchain
shell: bash
run: |
@ -32,7 +32,7 @@ jobs:
with:
workspaces: rust
- name: build wasm
run: npm run build:wasm
run: yarn build:wasm
# Upload the WASM bundle as an artifact

View File

@ -29,9 +29,9 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
cache: 'yarn'
- run: npm install
- run: yarn install
- id: filter
name: Check for Rust changes
@ -90,20 +90,20 @@ jobs:
- name: Run build:wasm
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
run: "npm run build:wasm"
run: "yarn build:wasm"
- name: Set nightly version, product name, release notes, and icons
if: ${{ env.IS_NIGHTLY == 'true' }}
run: |
export VERSION=${GITHUB_REF_NAME#nightly-v}
npm run files:set-version
npm run files:flip-to-nightly
yarn files:set-version
yarn files:flip-to-nightly
- name: Set release version
if: ${{ env.IS_RELEASE == 'true' }}
run: |
export VERSION=${GITHUB_REF_NAME#v}
npm run files:set-version
yarn files:set-version
- uses: actions/upload-artifact@v4
with:
@ -172,9 +172,16 @@ jobs:
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm' # Set this to npm, npm or pnpm.
cache: 'yarn' # Set this to npm, yarn or pnpm.
- run: npm install
- name: yarn install
# Windows is picky sometimes and fails on fetch. Step takes about ~30s
uses: nick-fields/retry@v3.0.2
with:
shell: bash
timeout_minutes: 2
max_attempts: 3
command: yarn install
- name: Prepare certificate and variables (Windows only)
if: ${{ (env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true') && matrix.os == 'windows-2022' }}
@ -200,20 +207,13 @@ jobs:
smctl.exe keypair ls
C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user
smksp_cert_sync.exe
smctl windows certsync
# This last line `smctl windows certsync` was added after windows codesign failures started happening
# with nightly-v25.4.10. It looks like `smksp_cert_sync.exe` used to do the sync to the local cert store,
# but stopped doing it overnight. This extra call that I randomly got from this azure-related doc page
# https://docs.digicert.com/en/digicert-keylocker/code-signing/sign-with-third-party-signing-tools/windows-applications/sign-azure-apps-with-signtool-using-ksp-library.html#sync-certificates--windows-only--618365
# seems to be doing that extra sync that we need for scripts/sign-win.js to work.
# TODO: we still need to make sign-win.js errors fail the workflow, see issue #6276
shell: cmd
- name: Build the app (debug)
if: ${{ env.IS_RELEASE == 'false' && env.IS_NIGHTLY == 'false' }}
# electron-builder doesn't have a concept of release vs debug,
# this is just not doing any codesign or release yml generation, and points to dev infra
run: npm run tronb:package:dev
run: yarn tronb:package:dev
- name: Build the app (release)
if: ${{ env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true' }}
@ -226,7 +226,14 @@ jobs:
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
run: npm run tronb:package:prod
DEBUG: "electron-notarize*"
# TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures
uses: nick-fields/retry@v3.0.2
with:
shell: bash
timeout_minutes: 10
max_attempts: 3
command: yarn tronb:package:prod
- name: List artifacts in out/
run: ls -R out
@ -281,7 +288,14 @@ jobs:
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
run: npm run tronb:package:prod
DEBUG: "electron-notarize*"
# TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures
uses: nick-fields/retry@v3.0.2
with:
shell: bash
timeout_minutes: 10
max_attempts: 3
command: yarn tronb:package:prod
- uses: actions/upload-artifact@v4
if: ${{ env.IS_RELEASE == 'true' }}
@ -364,7 +378,7 @@ jobs:
NOTES: ${{ needs.prepare-files.outputs.notes }}
PUB_DATE: ${{ github.event.repository.updated_at }}
WEBSITE_DIR: ${{ env.IS_NIGHTLY == 'true' && 'dl.zoo.dev/releases/modeling-app/nightly' || 'dl.zoo.dev/releases/modeling-app' }}
URL_CODED_NAME: ${{ env.IS_NIGHTLY == 'true' && 'Zoo%20Design%20Studio%20%28Nightly%29' || 'Zoo%20Design%20Studio' }}
URL_CODED_NAME: ${{ env.IS_NIGHTLY == 'true' && 'Zoo%20Modeling%20App%20%28Nightly%29' || 'Zoo%20Modeling%20App' }}
run: |
RELEASE_DIR=https://${WEBSITE_DIR}
jq --null-input \
@ -421,7 +435,7 @@ jobs:
export TAG="nightly-${VERSION}"
export PREVIOUS_TAG=$(git tag --list --sort=-committerdate "nightly-v[0-9]*" | head -n2 | tail -n1)
export NOTES=$(./scripts/get-nightly-changelog.sh)
npm run files:set-notes
yarn files:set-notes
- name: Authenticate to Google Cloud
if: ${{ env.IS_NIGHTLY == 'true' }}
@ -446,4 +460,4 @@ jobs:
- name: Invalidate bucket cache on latest*.yml and last_download.json files
if: ${{ env.IS_NIGHTLY == 'true' }}
run: npm run files:invalidate-bucket:nightly
run: yarn files:invalidate-bucket:nightly

View File

@ -26,8 +26,12 @@ name: cargo bench
jobs:
cargo-bench:
name: cargo bench
runs-on: ubuntu-latest
runs-on:
- runs-on=${{ github.run_id }}
- runner=32cpu-linux-x64
- extras=s3-cache
steps:
- uses: runs-on/action@v1
- uses: actions/checkout@v4
- name: Use correct Rust toolchain
shell: bash
@ -46,12 +50,13 @@ jobs:
- name: Build the benchmark target(s)
run: |
cd rust
cargo codspeed build
cargo codspeed build --measurement-mode walltime
- name: Run the benchmarks
uses: CodSpeedHQ/action@v3
with:
working-directory: rust
run: cargo codspeed run
token: ${{ secrets.CODSPEED_TOKEN }}
mode: walltime
env:
KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN }}

View File

@ -13,14 +13,10 @@ concurrency:
cancel-in-progress: true
name: cargo test
jobs:
build-test-artifacts:
name: Build test artifacts
runs-on:
- runs-on=${{ github.run_id }}
- runner=8cpu-linux-x64
- extras=s3-cache
cargotest:
name: cargo test
runs-on: ubuntu-latest-8-cores
steps:
- uses: runs-on/action@v1
- uses: actions/create-github-app-token@v1
id: app-token
with:
@ -43,6 +39,7 @@ jobs:
env:
GH_ACTIONS_AXIOM_TOKEN: ${{ secrets.GH_ACTIONS_AXIOM_TOKEN }}
OS_NAME: ${{ env.OS_NAME }}
- uses: taiki-e/install-action@cargo-llvm-cov
- uses: taiki-e/install-action@nextest
- name: Install just
uses: taiki-e/install-action@just
@ -84,10 +81,11 @@ jobs:
run: |
set -euo pipefail
cd rust
cargo nextest run --workspace --features artifact-graph --retries=2 --no-fail-fast --profile ci simulation_tests::kcl_samples 2>&1 | tee /tmp/github-actions.log
cargo nextest run --workspace --retries=2 --no-fail-fast --profile ci simulation_tests::kcl_samples 2>&1 | tee /tmp/github-actions.log
env:
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
RUST_BACKTRACE: full
RUST_MIN_STACK: 10485760000
- name: Commit differences
if: steps.path-changes.outputs.outside-kcl-samples == 'false' && steps.cargo-test-kcl-samples.outcome == 'failure'
shell: bash
@ -120,67 +118,23 @@ jobs:
# Configure nextest when it's run by insta (via just).
NEXTEST_PROFILE: ci
RUST_BACKTRACE: full
- name: Build and archive tests
run: |
cd rust
cargo nextest archive --workspace --features artifact-graph --archive-file nextest-archive.tar.zst
- name: Upload archive to workflow
uses: actions/upload-artifact@v4
with:
name: nextest-archive
path: rust/nextest-archive.tar.zst
run-test-artifacts:
name: cargo test (shard ${{ matrix.partitionIndex}})
runs-on:
- runs-on=${{ github.run_id }}
- runner=32cpu-linux-x64
- extras=s3-cache
needs: build-test-artifacts
strategy:
fail-fast: false
matrix:
partitionIndex: [1, 2, 3, 4, 5, 6]
partitionTotal: [6]
steps:
- uses: runs-on/action@v1
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
private-key: ${{ secrets.MODELING_APP_GH_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
- uses: actions/checkout@v4
with:
token: ${{ steps.app-token.outputs.token }}
- name: Use correct Rust toolchain
shell: bash
run: |
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
- name: Install rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
cache: false # Configured below.
- name: Start Vector
run: .github/ci-cd-scripts/start-vector-ubuntu.sh
env:
GH_ACTIONS_AXIOM_TOKEN: ${{ secrets.GH_ACTIONS_AXIOM_TOKEN }}
OS_NAME: ${{ env.OS_NAME }}
- uses: taiki-e/install-action@nextest
- name: Download archive
uses: actions/download-artifact@v4
with:
name: nextest-archive
- name: Run tests
RUST_MIN_STACK: 10485760000
- name: cargo test
if: steps.path-changes.outputs.outside-kcl-samples == 'true'
shell: bash
run: |-
cp nextest-archive.tar.zst rust/nextest-archive.tar.zst
ls -lah
cd rust
cargo nextest run\
--retries=2 --no-fail-fast -P ci --archive-file nextest-archive.tar.zst \
--partition count:${{ matrix.partitionIndex}}/${{ matrix.partitionTotal }} \
2>&1 | tee /tmp/github-actions.log
cargo llvm-cov nextest --workspace --lcov --output-path lcov.info --retries=2 --no-fail-fast -P ci 2>&1 | tee /tmp/github-actions.log
env:
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
RUST_MIN_STACK: 10485760000
- name: Upload to codecov.io
if: steps.path-changes.outputs.outside-kcl-samples == 'true'
uses: codecov/codecov-action@v5
with:
token: ${{secrets.CODECOV_TOKEN}}
fail_ci_if_error: true
flags: rust
verbose: true
files: lcov.info

View File

@ -5,7 +5,6 @@ on:
types: [opened, synchronize]
paths:
- 'src/lib/exampleKcl.ts'
- 'public/kcl-samples/bracket/main.kcl'
permissions:
contents: read
@ -23,11 +22,11 @@ jobs:
uses: actions/github-script@v7
with:
script: |
const message = '`public/kcl-samples/bracket/main.kcl` or `src/lib/exampleKcl.ts` has been updated in this PR, please review and update the `src/routes/onboarding`, if needed.';
const message = '`src/lib/exampleKcl.ts` has been updated in this PR, please review and update the `src/routes/onboarding`, if needed.';
const issue_number = context.payload.pull_request.number;
const owner = context.repo.owner;
const repo = context.repo.repo;
const { data: comments } = await github.rest.issues.listComments({
owner,
repo,
@ -44,4 +43,4 @@ jobs:
issue_number,
body: message,
});
}
}

View File

@ -11,7 +11,7 @@ concurrency:
cancel-in-progress: true
jobs:
npm-unit-test:
yarn-unit-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@ -19,11 +19,14 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
cache: 'yarn'
- run: npm install
- run: yarn install
working-directory: packages/codemirror-lang-kcl
- run: npm run tsc --workspace=packages/codemirror-lang-kcl
- run: yarn tsc
working-directory: packages/codemirror-lang-kcl
- name: run unit tests
run: npm run test --workspace=packages/codemirror-lang-kcl
run: yarn test
working-directory: packages/codemirror-lang-kcl

View File

@ -3,6 +3,7 @@ on:
push:
branches:
- main
- all-e2e # this bypasses `fixme()` using `orRunWhenFullSuiteEnabled()`
pull_request:
schedule:
- cron: 0 * * * * # hourly
@ -96,11 +97,11 @@ jobs:
if: needs.conditions.outputs.should-run == 'true'
with:
node-version-file: '.nvmrc'
cache: 'npm'
cache: 'yarn'
- name: Install dependencies
if: needs.conditions.outputs.should-run == 'true'
run: npm install
run: yarn
- name: Download Wasm Cache
id: download-wasm
@ -153,7 +154,7 @@ jobs:
- name: Build Wasm
if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
shell: bash
run: npm run build:wasm
run: yarn build:wasm
- uses: actions/upload-artifact@v4
if: needs.conditions.outputs.should-run == 'true'
@ -196,12 +197,12 @@ jobs:
if: needs.conditions.outputs.should-run == 'true'
with:
node-version-file: '.nvmrc'
cache: 'npm'
cache: 'yarn'
- name: Install dependencies
id: deps-install
if: needs.conditions.outputs.should-run == 'true'
run: npm install
run: yarn
- name: Cache Playwright Browsers
if: needs.conditions.outputs.should-run == 'true'
@ -209,32 +210,27 @@ jobs:
with:
path: |
~/.cache/ms-playwright/
key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }}
key: ${{ runner.os }}-playwright-${{ hashFiles('yarn.lock') }}
- name: Install Playwright Browsers
if: needs.conditions.outputs.should-run == 'true'
run: npm run playwright install --with-deps
run: yarn playwright install --with-deps
- name: build web
if: needs.conditions.outputs.should-run == 'true'
run: npm run tronb:vite:dev
run: yarn tronb:vite:dev
- name: Run ubuntu/chrome snapshots
if: needs.conditions.outputs.should-run == 'true'
uses: nick-fields/retry@v3.0.2
with:
shell: bash
command: npm run test:snapshots
timeout_minutes: 5
max_attempts: 5
command: yarn test:snapshots
timeout_minutes: 30
max_attempts: 3
env:
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
TAB_API_URL: ${{ secrets.TAB_API_URL }}
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
TARGET: web
- uses: actions/upload-artifact@v4
if: ${{ needs.conditions.outputs.should-run == 'true' && !cancelled() && (success() || failure()) }}
@ -277,42 +273,28 @@ jobs:
timeout-minutes: 60
env:
OS_NAME: ${{ contains(matrix.os, 'ubuntu') && 'ubuntu' || (contains(matrix.os, 'windows') && 'windows' || 'macos') }}
name: playwright:electron:${{ contains(matrix.os, 'ubuntu') && 'ubuntu' || (contains(matrix.os, 'windows') && 'windows' || 'macos') }} (shard ${{ matrix.shardIndex }})
name: playwright:electron:${{ contains(matrix.os, 'ubuntu') && 'ubuntu' || (contains(matrix.os, 'windows') && 'windows' || 'macos') }}:${{ matrix.shardIndex }}:${{ matrix.shardTotal }}
strategy:
fail-fast: false
matrix:
# TODO: enable namespace-profile-windows-latest once available
include:
- os: "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
shardIndex: 1
shardTotal: 8
- os: "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
shardIndex: 2
shardTotal: 8
- os: "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
shardIndex: 3
shardTotal: 8
- os: "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
shardIndex: 4
shardTotal: 8
- os: "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
shardIndex: 5
shardTotal: 8
- os: "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
shardIndex: 6
shardTotal: 8
- os: "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
shardIndex: 7
shardTotal: 8
- os: "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
shardIndex: 8
shardTotal: 8
os:
- "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
- namespace-profile-macos-8-cores
- windows-latest-8-cores
shardIndex: [1, 2, 3, 4]
shardTotal: [4]
# Disable macos and windows tests on hourly e2e tests since we only care
# about server side changes.
# Technique from https://github.com/joaomcteixeira/python-project-skeleton/pull/31/files
isScheduled:
- ${{ github.event_name == 'schedule' }}
exclude:
- os: namespace-profile-macos-8-cores
shardIndex: 1
shardTotal: 1
isScheduled: true
- os: windows-latest-8-cores
shardIndex: 1
shardTotal: 1
isScheduled: true
# TODO: add ref here for main and latest release tag
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
@ -334,12 +316,12 @@ jobs:
if: needs.conditions.outputs.should-run == 'true'
with:
node-version-file: '.nvmrc'
cache: 'npm'
cache: 'yarn'
- name: Install dependencies
id: deps-install
if: needs.conditions.outputs.should-run == 'true'
run: npm install
run: yarn
- name: Cache Playwright Browsers
if: needs.conditions.outputs.should-run == 'true'
@ -347,15 +329,15 @@ jobs:
with:
path: |
~/.cache/ms-playwright/
key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }}
key: ${{ runner.os }}-playwright-${{ hashFiles('yarn.lock') }}
- name: Install Playwright Browsers
if: needs.conditions.outputs.should-run == 'true'
run: npm run playwright install --with-deps
run: yarn playwright install --with-deps
- name: Build web
if: needs.conditions.outputs.should-run == 'true'
run: npm run tronb:vite:dev
run: yarn tronb:vite:dev
- name: Start Vector
if: ${{ needs.conditions.outputs.should-run == 'true' && !contains(matrix.os, 'windows') }}
@ -383,11 +365,6 @@ jobs:
env:
FAIL_ON_CONSOLE_ERRORS: true
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
TAB_API_URL: ${{ secrets.TAB_API_URL }}
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
TARGET: desktop
- uses: actions/upload-artifact@v4
if: ${{ needs.conditions.outputs.should-run == 'true' && always() }}

View File

@ -21,10 +21,10 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- run: npm install
- run: npm run generate:machine-api
- run: npm run fmt || true
cache: 'yarn'
- run: yarn install
- run: yarn generate:machine-api
- run: yarn fmt
- name: check for changes
id: git-check
run: |

View File

@ -56,19 +56,21 @@ jobs:
node-version-file: ".nvmrc"
- name: Install dependencies
run: |
npm install
yarn install
cd rust/kcl-language-server
yarn install
- name: Run tests
run: |
cd rust/kcl-language-server
npm run build
npm run test-compile
yarn build
yarn test-compile
ls -la dist
xvfb-run -a npm run test
xvfb-run -a yarn test
if: runner.os == 'Linux'
- name: Run tests
run: |
cd rust/kcl-language-server
npm run test
yarn test
if: runner.os != 'Linux'
build-release:
strategy:
@ -181,20 +183,19 @@ jobs:
- name: Install dependencies
run: |
cd rust/kcl-language-server
# npm will symlink which will cause issues w tarballing later
yarn install
- name: Package Extension (release)
if: startsWith(github.event.ref, 'refs/tags/')
run: |
cd rust/kcl-language-server
npx vsce package --yarn -o "../build/kcl-language-server-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }}
npx vsce package -o "../build/kcl-language-server-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }}
- name: Package Extension (nightly)
if: startsWith(github.event.ref, 'refs/tags/') == false
run: |
cd rust/kcl-language-server
npx vsce package --yarn -o "../build/kcl-language-server-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }} --pre-release
npx vsce package -o "../build/kcl-language-server-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }} --pre-release
- name: remove server
if: matrix.target == 'x86_64-unknown-linux-gnu'
@ -206,13 +207,13 @@ jobs:
if: matrix.target == 'x86_64-unknown-linux-gnu' && startsWith(github.event.ref, 'refs/tags/')
run: |
cd rust/kcl-language-server
npx vsce package --yarn -o ../build/kcl-language-server-no-server.vsix
npx vsce package -o ../build/kcl-language-server-no-server.vsix
- name: Package Extension (no server, nightly)
if: matrix.target == 'x86_64-unknown-linux-gnu' && startsWith(github.event.ref, 'refs/tags/') == false
run: |
cd rust/kcl-language-server
npx vsce package --yarn -o ../build/kcl-language-server-no-server.vsix --pre-release
npx vsce package -o ../build/kcl-language-server-no-server.vsix --pre-release
- name: Upload artifacts
uses: actions/upload-artifact@v4
@ -243,8 +244,8 @@ jobs:
lld \
musl-dev \
nodejs \
npm \
yarn
yarn \
npm
- name: Checkout repository
uses: actions/checkout@v4
@ -271,20 +272,19 @@ jobs:
- name: Install dependencies
run: |
cd rust/kcl-language-server
# npm will symlink which will cause issues w tarballing later
yarn install
- name: Package Extension (release)
if: startsWith(github.event.ref, 'refs/tags/')
run: |
cd rust/kcl-language-server
npx vsce package --yarn -o "../build/kcl-language-server-alpine-x64.vsix" --target alpine-x64
npx vsce package -o "../build/kcl-language-server-alpine-x64.vsix" --target alpine-x64
- name: Package Extension (release)
if: startsWith(github.event.ref, 'refs/tags/') == false
run: |
cd rust/kcl-language-server
npx vsce package --yarn -o "../build/kcl-language-server-alpine-x64.vsix" --target alpine-x64 --pre-release
npx vsce package -o "../build/kcl-language-server-alpine-x64.vsix" --target alpine-x64 --pre-release
- name: Upload artifacts
uses: actions/upload-artifact@v4

View File

@ -1,55 +0,0 @@
name: Test Nix Flake
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
nix-flake-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: cachix/install-nix-action@v31
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: nix flake check for all platforms
run: |
nix flake check --all-systems
nix-build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: cachix/install-nix-action@v31
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: nix build . for x86_64-linux
run: nix build .
nix-build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: cachix/install-nix-action@v31
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: nix build . for x86_64-darwin
run: nix build .

View File

@ -19,7 +19,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
cache: 'yarn'
- name: Find tag workflow id
id: tag_workflow_id
@ -105,7 +105,7 @@ jobs:
- name: Override release notes
env:
NOTES: ${{ github.event.release.body }}
run: npm run files:set-notes
run: yarn files:set-notes
- name: Authenticate to Google Cloud
uses: 'google-github-actions/auth@v2.1.8'
@ -126,7 +126,7 @@ jobs:
destination: 'dl.kittycad.io/releases/modeling-app'
- name: Invalidate bucket cache on latest*.yml and last_download.json files
run: npm run files:invalidate-bucket
run: yarn files:invalidate-bucket
- name: Upload release files to Github
if: ${{ github.event_name == 'release' }}

View File

@ -16,18 +16,18 @@ permissions:
actions: read
jobs:
npm-fmt-check:
yarn-fmt-check:
runs-on: 'ubuntu-22.04'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- run: npm install
- run: npm run fmt:check
cache: 'yarn'
- run: yarn install
- run: yarn fmt-check
npm-build-wasm:
yarn-build-wasm:
# Build the wasm blob once on the fastest runner.
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
steps:
@ -36,10 +36,10 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
cache: 'yarn'
- name: Install dependencies
run: npm install
run: yarn install
- name: Use correct Rust toolchain
shell: bash
@ -62,7 +62,7 @@ jobs:
- name: Build Wasm
shell: bash
run: npm run build:wasm
run: yarn build:wasm
- uses: actions/upload-artifact@v4
with:
@ -76,17 +76,17 @@ jobs:
path: |
rust/kcl-lib/bindings/*
npm-tsc:
yarn-tsc:
runs-on: ubuntu-latest
needs: npm-build-wasm
needs: yarn-build-wasm
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- run: npm install
cache: 'yarn'
- run: yarn install
- name: Download all artifacts
uses: actions/download-artifact@v4
@ -104,19 +104,19 @@ jobs:
mkdir rust/kcl-lib/bindings
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
- run: npm run tsc
- run: yarn tsc
npm-lint:
yarn-lint:
runs-on: ubuntu-latest
needs: npm-build-wasm
needs: yarn-build-wasm
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- run: npm install
cache: 'yarn'
- run: yarn install
- name: Download all artifacts
uses: actions/download-artifact@v4
@ -134,19 +134,19 @@ jobs:
mkdir rust/kcl-lib/bindings
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
- run: npm run lint
- run: yarn lint
npm-circular-dependencies:
yarn-circular-dependencies:
runs-on: ubuntu-latest
needs: npm-build-wasm
needs: yarn-build-wasm
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- run: npm install
cache: 'yarn'
- run: yarn install
- name: Download all artifacts
uses: actions/download-artifact@v4
@ -164,7 +164,7 @@ jobs:
mkdir rust/kcl-lib/bindings
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
- run: npm run circular-deps:diff
- run: yarn circular-deps:diff
python-codespell:
runs-on: ubuntu-22.04
@ -179,18 +179,18 @@ jobs:
- name: Run codespell
run: codespell --config .codespellrc # Edit this file to tweak the typo list and other configuration.
npm-unit-test-kcl-samples:
yarn-unit-test-kcl-samples:
runs-on: ubuntu-latest
needs: npm-build-wasm
needs: yarn-build-wasm
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
cache: 'yarn'
- run: npm install
- run: yarn install
- uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
with:
tool: wasm-pack
@ -211,31 +211,31 @@ jobs:
mkdir rust/kcl-lib/bindings
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
- run: npm run simpleserver:bg
- run: yarn simpleserver:bg
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
- name: Install Chromium Browser
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
run: npm run playwright install chromium --with-deps
run: yarn playwright install chromium --with-deps
- name: run unit tests for kcl samples
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
run: npm run test:unit:kcl-samples
run: yarn test:unit:kcl-samples
env:
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
npm-unit-test:
yarn-unit-test:
runs-on: ubuntu-latest
needs: npm-build-wasm
needs: yarn-build-wasm
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
cache: 'yarn'
- run: npm install
- run: yarn install
- uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
with:
tool: wasm-pack
@ -256,16 +256,16 @@ jobs:
mkdir rust/kcl-lib/bindings
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
- run: npm run simpleserver:bg
- run: yarn simpleserver:bg
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
- name: Install Chromium Browser
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
run: npm run playwright install chromium --with-deps
run: yarn playwright install chromium --with-deps
- name: Run unit tests
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
run: xvfb-run -a npm run test:unit
run: xvfb-run -a yarn test:unit
env:
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}

View File

@ -27,7 +27,7 @@ jobs:
with:
node-version-file: '.nvmrc'
- run: npm install
- run: yarn install
- name: Push tag
run: |

45
.github/workflows/update-e2e-branch.yml vendored Normal file
View File

@ -0,0 +1,45 @@
name: update-e2e-branch
# This is used to sync the `all-e2e` branch with the `main` branch for the
# logic in the test utility `orRunWhenFullSuiteEnabled()` that allows all e2e
# tests to run on a particular branch to analyze failures metrics in Axiom.
on:
schedule:
- cron: '0 * * * *' # runs every hour
permissions:
contents: write
jobs:
update-branch:
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
private-key: ${{ secrets.MODELING_APP_GH_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
- uses: actions/checkout@v4
with:
token: ${{ steps.app-token.outputs.token }}
- name: Sync with main
run: |
# Create the branch
git checkout all-e2e || git checkout -b all-e2e
# Reset to main
git fetch origin
git reset --hard origin/main
# Get a new SHA to prevent overwriting the commit status on main
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git commit --allow-empty --message="[all-e2e] $(git log --max-count=1 --pretty=%B)"
# Overwrite the branch
git remote set-url origin https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/${{ github.repository }}.git
git push --force origin all-e2e

3
.gitignore vendored
View File

@ -26,12 +26,10 @@ yarn-error.log*
.idea
.vscode
.helix
result
# rust
rust/target
rust/kcl-lib/bindings
rust/kcl-language-server/server
public/kcl_wasm_lib_bg.wasm
rust/lcov.info
rust/kcl-wasm-lib/pkg
@ -84,4 +82,3 @@ dist
venv
.vscode-test
.biome/

View File

@ -5,6 +5,6 @@ command = "vscode-eslint-language-server"
[[language]]
name = "typescript"
auto-format = true
formatter = { command = "./node_modules/@biomejs/biome/bin/biome", args = ["format", "--write", "--stdin-file-path=foo.ts"] }
# language-servers = [ { name = "eslint", only-features = [ "diagnostics" ] }, "typescript-language-server" ]
formatter = { command = "node_modules/.bin/prettier", args = ["--parser", "typescript"] }
language-servers = [ { name = "eslint", only-features = [ "diagnostics" ] }, "typescript-language-server" ]

View File

@ -1 +0,0 @@
npm run fmt

4
.husky/pre-push Executable file
View File

@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn fmt-check

1
.npmrc
View File

@ -1 +0,0 @@
legacy-peer-deps=true

View File

@ -1,432 +0,0 @@
# Contributor Guide
## Running a development build
Install a node version manager such as [fnm](https://github.com/Schniz/fnm?tab=readme-ov-#installation).
On Windows, it's also recommended to [upgrade your PowerShell version](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.5#winget), we're using 7.
Then in the repo run the following to install and use the node version specified in `.nvmrc`. You might need to specify your processor architecture with `--arch arm64` or `--arch x64` if it's not autodetected.
```
fnm install --corepack-enabled
fnm use
```
Install the NPM dependencies with:
```
npm install
```
This project uses a lot of Rust compiled to [WASM](https://webassembly.org/) within it. We have package scripts to run rustup, see `package.json` for reference:
```
# macOS/Linux
npm run install:rust
npm run install:wasm-pack:sh
# Windows
npm run install:rust:windows
npm run install:wasm-pack:cargo
```
Then to build the WASM layer, run:
```
# macOS/Linux
npm run build:wasm
# Windows
npm run build:wasm:windows
```
Or if you have the `gh` cli installed and want to download the latest main wasm bundle. Note that on Windows, you need to associate .ps1 files with PowerShell, which can be done via the right click menu, selecting `C:\Program Files\PowerShell\7\pwsh.exe`, and you can install tools like `gh` via `npm run install:tools:windows`.
```
# macOS/Linux
npm run fetch:wasm
# Windows
npm run fetch:wasm:windows
```
That will build the WASM binary and put in the `public` dir (though gitignored).
Finally, to run the web app only, run:
```
npm start
```
If you're not a Zoo employee you won't be able to access the dev environment, you should copy everything from `.env.production` to `.env.development.local` to make it point to production instead, then when you navigate to `localhost:3000` the easiest way to sign in is to paste `localStorage.setItem('TOKEN_PERSIST_KEY', "your-token-from-https://zoo.dev/account/api-tokens")` replacing the with a real token from https://zoo.dev/account/api-tokens of course, then navigate to `localhost:3000` again. Note that navigating to `localhost:3000/signin` removes your token so you will need to set the token again.
### Development environment variables
The Copilot LSP plugin in the editor requires a Zoo API token to run. In production, we authenticate this with a token via cookie in the browser and device auth token in the desktop environment, but this token is inaccessible in the dev browser version because the cookie is considered "cross-site" (from `localhost` to `dev.zoo.dev`). There is an optional environment variable called `VITE_KC_DEV_TOKEN` that you can populate with a dev token in a `.env.development.local` file to not check it into Git, which will use that token instead of other methods for the LSP service.
### Developing in Chrome
Chrome is in the process of rolling out a new default which
[blocks Third-Party Cookies](https://developer.chrome.com/en/docs/privacy-sandbox/third-party-cookie-phase-out/).
If you're having trouble logging into the `modeling-app`, you may need to
enable third-party cookies. You can enable third-party cookies by clicking on
the eye with a slash through it in the URL bar, and clicking on "Enable
Third-Party Cookies".
## Desktop
To spin up the desktop app, `npm install` and `npm run build:wasm` need to have been done before hand then:
```
npm run tron:start
```
This will start the application and hot-reload on changes.
Devtools can be opened with the usual Command-Option-I (macOS) or Ctrl-Shift-I (Linux and Windows).
To package the app for your platform with electron-builder, run `npm run tronb:package:dev` (or `npm run tronb:package:prod` to point to the .env.production variables).
## Checking out commits / Bisecting
Which commands from setup are one off vs. need to be run every time?
The following will need to be run when checking out a new commit and guarantees the build is not stale:
```bash
npm install
npm run build:wasm
npm start # or npm run build:local && npm run serve for slower but more production-like build
```
## Before submitting a PR
Before you submit a contribution PR to this repo, please ensure that:
- There is a corresponding issue for the changes you want to make, so that discussion of approach can be had before work begins.
- You have separated out refactoring commits from feature commits as much as possible
- You have run all of the following commands locally:
- `npm run fmt`
- `npm run tsc`
- `npm run test`
- Here they are all together: `npm run fmt && npm run tsc && npm run test`
## Release a new version
#### 1. Create a 'Cut release $VERSION' issue
It will be used to document changelog discussions and release testing.
https://github.com/KittyCAD/modeling-app/issues/new
#### 2. Push a new tag
Create a new tag and push it to the repo. The `semantic-release.sh` script will automatically bump the minor part, which we use the most. For instance going from `v0.27.0` to `v0.28.0`.
```
VERSION=$(./scripts/semantic-release.sh)
git tag $VERSION
git push origin --tags
```
This will trigger the `build-apps` workflow, set the version, build & sign the apps, and generate release files as well as updater-test artifacts.
The workflow should be listed right away [in this list](https://github.com/KittyCAD/modeling-app/actions/workflows/build-apps.yml?query=event%3Apush)).
#### 3. Manually test artifacts
##### Release builds
The release builds can be found under the `out-{arch}-{platform}` zip files, at the very bottom of the `build-apps` summary page for the workflow (triggered by the tag in 2.).
Manually test against this [list](https://github.com/KittyCAD/modeling-app/issues/3588) across Windows, MacOS, Linux and posting results as comments in the issue.
##### Updater-test builds
The other `build-apps` output in the release `build-apps` workflow (triggered by 2.) is `updater-test-{arch}-{platform}`. It's a semi-automated process: for macOS, Windows, and Linux, download the corresponding updater-test artifact file, install the app, run it, expect an updater prompt to a dummy v0.255.255, install it and check that the app comes back at that version.
The only difference with these builds is that they point to a different update location on the release bucket, with this dummy v0.255.255 always available. This helps ensuring that the version we release will be able to update to the next one available.
If the prompt doesn't show up, start the app in command line to grab the electron-updater logs. This is likely an issue with the current build that needs addressing (or the updater-test location in the storage bucket).
```
# Windows (PowerShell)
& 'C:\Program Files\Zoo Design Studio\Zoo Design Studio.exe'
# macOS
/Applications/Zoo\ Modeling\ App.app/Contents/MacOS/Zoo\ Modeling\ App
# Linux
./Zoo Design Studio-{version}-{arch}-linux.AppImage
```
#### 4. Publish the release
Head over to https://github.com/KittyCAD/modeling-app/releases/new, pick the newly created tag and type it in the _Release title_ field as well.
Hit _Generate release notes_ as a starting point to discuss the changelog in the issue. Once done, make sure _Set as the latest release_ is checked, and hit _Publish release_.
A new `publish-apps-release` will kick in and you should be able to find it [here](https://github.com/KittyCAD/modeling-app/actions?query=event%3Arelease). On success, the files will be uploaded to the public bucket as well as to the GitHub release, and the announcement on Discord will be sent.
#### 5. Close the issue
If everything is well and the release is out to the public, the issue tracking the release shall be closed.
## Fuzzing the parser
Make sure you install cargo fuzz:
```bash
$ cargo install cargo-fuzz
```
```bash
$ cd rust/kcl-lib
# list the fuzz targets
$ cargo fuzz list
# run the parser fuzzer
$ cargo +nightly fuzz run parser
```
For more information on fuzzing you can check out
[this guide](https://rust-fuzz.github.io/book/cargo-fuzz.html).
## Tests
### Playwright tests
You will need a `./e2e/playwright/playwright-secrets.env` file:
```bash
$ touch ./e2e/playwright/playwright-secrets.env
$ cat ./e2e/playwright/playwright-secrets.env
token=<dev.zoo.dev/account/api-tokens>
snapshottoken=<zoo.dev/account/api-tokens>
```
or use `export` to set the environment variables `token` and `snapshottoken`.
#### Snapshot tests (Google Chrome on Ubuntu only)
Only Ubunu and Google Chrome is supported for the set of tests evaluating screenshot snapshots.
If you don't run Ubuntu locally or in a VM, you may use a GitHub Codespace.
```
npm run playwright -- install chrome
npm run test:snapshots
```
You may use `-- --update-snapshots` as needed.
#### Electron flow tests (Chromium on Ubuntu, macOS, Windows)
```
npm run playwright -- install chromium
npm run test:playwright:electron:local
```
You may use `-- -g "my test"` to match specific test titles, or `-- path/to/file.spec.ts` for a test file.
#### Debugger
However, if you want a debugger I recommend using VSCode and the `playwright` extension, as the above command is a cruder debugger that steps into every function call which is annoying.
With the extension you can set a breakpoint after `waitForDefaultPlanesVisibilityChange` in order to skip app loading, then the vscode debugger's "step over" is much better for being able to stay at the right level of abstraction as you debug the code.
If you want to limit to a single browser use `--project="webkit"` or `firefox`, `Google Chrome`
Or comment out browsers in `playwright.config.ts`.
note chromium has encoder compat issues which is why were testing against the branded 'Google Chrome'
You may consider using the VSCode extension, it's useful for running individual threads, but some some reason the "record a test" is locked to chromium with we can't use. A work around is to us the CI `npm run playwright codegen -b wk --load-storage ./store localhost:3000`
<details>
<summary>
Where `./store` should look like this
</summary>
```JSON
{
"cookies": [],
"origins": [
{
"origin": "http://localhost:3000",
"localStorage": [
{
"name": "store",
"value": "{\"state\":{\"openPanes\":[\"code\"]},\"version\":0}"
},
{
"name": "persistCode",
"value": ""
},
{
"name": "TOKEN_PERSIST_KEY",
"value": "your-token"
}
]
}
]
}
```
</details>
However because much of our tests involve clicking in the stream at specific locations, it's code-gen looks `await page.locator('video').click();` when really we need to use a pixel coord, so I think it's of limited use.
### Unit and component tests
If you already haven't, run the following:
```
npm
npm run build:wasm
npm start
```
and finally:
```
npm run test:unit
```
For individual testing:
```
npm run test abstractSyntaxTree -t "unexpected closed curly brace" --silent=false
```
Which will run our suite of [Vitest unit](https://vitest.dev/) and [React Testing Library E2E](https://testing-library.com/docs/react-testing-library/intro) tests, in interactive mode by default.
### Rust tests
**Dependencies**
- `KITTYCAD_API_TOKEN`
- `cargo-nextest`
- `just`
#### Setting KITTYCAD_API_TOKEN
Use the production zoo.dev token, set this environment variable before running the tests
#### Installing cargonextest
```
cd rust
cargo search cargo-nextest
cargo install cargo-nextest
```
#### just
install [`just`](https://github.com/casey/just?tab=readme-ov-file#pre-built-binaries)
#### Running the tests
```bash
# With just
# Make sure KITTYCAD_API_TOKEN=<prod zoo.dev token> is set
# Make sure you installed cargo-nextest
# Make sure you installed just
cd rust
just test
```
```bash
# Without just
# Make sure KITTYCAD_API_TOKEN=<prod zoo.dev token> is set
# Make sure you installed cargo-nextest
cd rust
export RUST_BRACKTRACE="full" && cargo nextest run --workspace --test-threads=1
```
Where `XXX` is an API token from the production engine (NOT the dev environment).
We recommend using [nextest](https://nexte.st/) to run the Rust tests (its faster and is used in CI). Once installed, run the tests using
```
cd rust
KITTYCAD_API_TOKEN=XXX cargo run nextest
```
### Mapping CI CD jobs to local commands
When you see the CI CD fail on jobs you may wonder three things
- Do I have a bug in my code?
- Is the test flaky?
- Is there a bug in `main`?
To answer these questions the following commands will give you confidence to locate the issue.
#### Static Analysis
Part of the CI CD pipeline performs static analysis on the code. Use the following commands to mimic the CI CD jobs.
The following set of commands should get us closer to one and done commands to instantly retest issues.
```
npm run test-setup
```
> Gotcha, are packages up to date and is the wasm built?
```
npm run tsc
npm run fmt:check
npm run lint
npm run test:unit:local
```
> Gotcha: Our unit tests have integration tests in them. You need to run a localhost server to run the unit tests.
#### E2E Tests
**Playwright Electron**
These E2E tests run in electron. There are tests that are skipped if they are ran in a windows, linux, or macos environment. We can use playwright tags to implement test skipping.
```
npm run test:playwright:electron:local
npm run test:playwright:electron:windows:local
npm run test:playwright:electron:macos:local
npm run test:playwright:electron:ubuntu:local
```
> Why does it say local? The CI CD commands that run in the pipeline cannot be ran locally. A single command will not properly setup the testing environment locally.
#### Some notes on CI
The tests are broken into snapshot tests and non-snapshot tests, and they run in that order, they automatically commit new snap shots, so if you see an image commit check it was an intended change. If we have non-determinism in the snapshots such that they are always committing new images, hopefully this annoyance makes us fix them asap, if you notice this happening let Kurt know. But for the odd occasion `git reset --hard HEAD~ && git push -f` is your friend.
How to interpret failing playwright tests?
If your tests fail, click through to the action and see that the tests failed on a line that includes `await page.getByTestId('loading').waitFor({ state: 'detached' })`, this means the test fail because the stream never started. It's you choice if you want to re-run the test, or ignore the failure.
We run on ubuntu and macos, because safari doesn't work on linux because of the dreaded "no RTCPeerConnection variable" error. But linux runs first and then macos for the same reason that we limit the number of parallel tests to 1 because we limit stream connections per user, so tests would start failing we if let them run together.
If something fails on CI you can download the artifact, unzip it and then open `playwright-report/data/<UUID>.zip` with https://trace.playwright.dev/ to see what happened.
#### Getting started writing a playwright test in our app
Besides following the instructions above and using the playwright docs, our app is weird because of the whole stream thing, which means our testing is weird. Because we've just figured out this stuff and therefore docs might go stale quick here's a 15min vid/tutorial
https://github.com/KittyCAD/modeling-app/assets/29681384/6f5e8e85-1003-4fd9-be7f-f36ce833942d
<details>
<summary>
PS: for the debug panel, the following JSON is useful for snapping the camera
</summary>
```JSON
{"type":"modeling_cmd_req","cmd_id":"054e5472-e5e9-4071-92d7-1ce3bac61956","cmd":{"type":"default_camera_look_at","center":{"x":15,"y":0,"z":0},"up":{"x":0,"y":0,"z":1},"vantage":{"x":30,"y":30,"z":30}}}
```
</details>
### Logging
To display logging (to the terminal or console) set `ZOO_LOG=1`. This will log some warnings and simple performance metrics. To view these in test runs, use `-- --nocapture`.
To enable memory metrics, build with `--features dhat-heap`.

View File

@ -5,38 +5,36 @@ all: install build check
# INSTALL
ifeq ($(OS),Windows_NT)
export WINDOWS := true
ifndef MSYSTEM
export POWERSHELL := true
endif
endif
ifdef WINDOWS
CARGO ?= $(USERPROFILE)/.cargo/bin/cargo.exe
WASM_PACK ?= $(USERPROFILE)/.cargo/bin/wasm-pack.exe
CARGO ?= ~/.cargo/bin/cargo.exe
WASM_PACK ?= ~/.cargo/bin/wasm-pack.exe
else
CARGO ?= $(shell which cargo || echo ~/.cargo/bin/cargo)
WASM_PACK ?= $(shell which wasm-pack || echo ~/.cargo/bin/wasm-pack)
CARGO ?= ~/.cargo/bin/cargo
WASM_PACK ?= ~/.cargo/bin/wasm-pack
endif
.PHONY: install
install: node_modules/.package-lock.json $(CARGO) $(WASM_PACK) ## Install dependencies
install: node_modules/.yarn-integrity $(CARGO) $(WASM_PACK) ## Install dependencies
node_modules/.package-lock.json: package.json package-lock.json
npm install
node_modules/.yarn-integrity: package.json yarn.lock
yarn install
ifeq ($(OS),Windows_NT)
@ type nul > $@
else
@ touch $@
endif
$(CARGO):
ifdef WINDOWS
npm run install:rust:windows
ifeq ($(OS),Windows_NT)
yarn install:rust:windows
else
npm run install:rust
yarn install:rust
endif
$(WASM_PACK):
ifdef WINDOWS
npm run install:wasm-pack:cargo
ifeq ($(OS),Windows_NT)
yarn install:wasm-pack:cargo
else
npm run install:wasm-pack:sh
yarn install:wasm-pack:sh
endif
###############################################################################
@ -59,17 +57,17 @@ build-web: install public/kcl_wasm_lib_bg.wasm build/index.html
build-desktop: install public/kcl_wasm_lib_bg.wasm .vite/build/main.js
public/kcl_wasm_lib_bg.wasm: $(CARGO_SOURCES) $(RUST_SOURCES)
ifdef WINDOWS
npm run build:wasm:dev:windows
ifeq ($(OS),Windows_NT)
yarn build:wasm:dev:windows
else
npm run build:wasm:dev
yarn build:wasm:dev
endif
build/index.html: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
npm run build:local
yarn build:local
.vite/build/main.js: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
npm run tronb:vite:dev
yarn tronb:vite:dev
###############################################################################
# CHECK
@ -79,12 +77,12 @@ check: format lint
.PHONY: format
format: install ## Format the code
npm run fmt
yarn fmt
.PHONY: lint
lint: install ## Lint the code
npm run tsc
npm run lint
yarn tsc
yarn lint
###############################################################################
# RUN
@ -96,11 +94,11 @@ run: run-$(TARGET)
.PHONY: run-web
run-web: install build-web ## Start the web app
npm run start
yarn start
.PHONY: run-desktop
run-desktop: install build-desktop ## Start the desktop app
npm run tron:start
yarn tron:start
###############################################################################
# TEST
@ -115,7 +113,7 @@ test: test-unit test-e2e
.PHONY: test-unit
test-unit: install ## Run the unit tests
@ curl -fs localhost:3000 >/dev/null || ( echo "Error: localhost:3000 not available, 'make run-web' first" && exit 1 )
npm run test:unit
yarn test:unit
.PHONY: test-e2e
test-e2e: test-e2e-$(TARGET)
@ -124,17 +122,17 @@ test-e2e: test-e2e-$(TARGET)
test-e2e-web: install build-web ## Run the web e2e tests
@ curl -fs localhost:3000 >/dev/null || ( echo "Error: localhost:3000 not available, 'make run-web' first" && exit 1 )
ifdef E2E_GREP
npm run chrome:test -- --headed --grep="$(E2E_GREP)" --max-failures=$(E2E_FAILURES)
yarn chrome:test --headed --grep="$(E2E_GREP)" --max-failures=$(E2E_FAILURES)
else
npm run chrome:test -- --headed --workers='100%'
yarn chrome:test --headed --workers='100%'
endif
.PHONY: test-e2e-desktop
test-e2e-desktop: install build-desktop ## Run the desktop e2e tests
ifdef E2E_GREP
npm run test:playwright:electron -- --grep="$(E2E_GREP)" --max-failures=$(E2E_FAILURES)
yarn test:playwright:electron --grep="$(E2E_GREP)" --max-failures=$(E2E_FAILURES)
else
npm run test:playwright:electron -- --workers='100%'
yarn test:playwright:electron --workers='100%'
endif
###############################################################################
@ -142,8 +140,8 @@ endif
.PHONY: clean
clean: ## Delete all artifacts
ifdef POWERSHELL
git clean --force -d -x --exclude=.env* --exclude=**/*.env
ifeq ($(OS),Windows_NT)
git clean --force -d -X
else
rm -rf .vite/ build/
rm -rf trace.zip playwright-report/ test-results/
@ -154,7 +152,7 @@ endif
.PHONY: help
help: install
ifdef POWERSHELL
ifeq ($(OS),Windows_NT)
@ powershell -Command "Get-Content $(MAKEFILE_LIST) | Select-String -Pattern '^[^\s]+:.*##\s.*$$' | ForEach-Object { $$line = $$_.Line -split ':.*?##\s+'; Write-Host -NoNewline $$line[0].PadRight(30) -ForegroundColor Cyan; Write-Host $$line[1] }"
else
@ grep -E '^[^[:space:]]+:.*## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
@ -169,5 +167,5 @@ endif
# It should work for you other Linux users.
lee-electron-test:
Xephyr -br -ac -noreset -screen 1200x500 :2 &
DISPLAY=:2 NODE_ENV=development PW_TEST_CONNECT_WS_ENDPOINT=ws://127.0.0.1:4444/ npm run tron:test -g "when using the file tree"
DISPLAY=:2 NODE_ENV=development PW_TEST_CONNECT_WS_ENDPOINT=ws://127.0.0.1:4444/ yarn tron:test -g "when using the file tree"
killall Xephyr

473
README.md
View File

@ -1,8 +1,8 @@
![Zoo Design Studio](/public/zma-logomark-outlined.png)
# Zoo Design Studio
## Zoo Design Studio
[zoo.dev/modeling-app](https://zoo.dev/modeling-app)
download at [zoo.dev/modeling-app/download](https://zoo.dev/modeling-app/download)
A CAD application from the future, brought to you by the [Zoo team](https://zoo.dev).
@ -23,7 +23,7 @@ Design Studio is a _hybrid_ user interface for CAD modeling. You can point-and-c
The 3D view in Design Studio is just a video stream from our hosted geometry engine. The app sends new modeling commands to the engine via WebSockets, which returns back video frames of the view within the engine.
## Technology
## Tools
- UI
- [React](https://react.dev/)
@ -38,16 +38,471 @@ The 3D view in Design Studio is just a video stream from our hosted geometry eng
- Modeling
- [KittyCAD TypeScript client](https://github.com/KittyCAD/kittycad.ts)
## Get Started
[Original demo video](https://drive.google.com/file/d/183_wjqGdzZ8EEZXSqZ3eDcJocYPCyOdC/view?pli=1)
We recommend downloading the latest application binary from our [releases](https://github.com/KittyCAD/modeling-app/releases) page. If you don't see your platform or architecture supported there, please file an issue.
[Original demo slides](https://github.com/user-attachments/files/19010536/demo.pdf)
If you'd like to try out upcoming changes sooner, you can also download those from our [nightly releases](https://zoo.dev/modeling-app/download/nightly) page.
## Get started
## Developing
We recommend downloading the latest application binary from [our Releases page](https://github.com/KittyCAD/modeling-app/releases). If you don't see your platform or architecture supported there, please file an issue.
Finally, if you'd like to run a development build or contribute to the project, please visit our [contributor guide](CONTRIBUTING.md) to get started.
## Running a development build
Install a node version manager such as [fnm](https://github.com/Schniz/fnm?tab=readme-ov-#installation).
On Windows, it's also recommended to [upgrade your PowerShell version](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.5#winget), we're using 7.
Then in the repo run the following to install and use the node version specified in `.nvmrc`. You might need to specify your processor architecture with `--arch arm64` or `--arch x64` if it's not autodetected.
```
fnm install --corepack-enabled
fnm use
```
Install the NPM dependencies with:
```
yarn install
```
This project uses a lot of Rust compiled to [WASM](https://webassembly.org/) within it. We have package scripts to run rustup, see `package.json` for reference:
```
# macOS/Linux
yarn install:rust
yarn install:wasm-pack:sh
# Windows
yarn install:rust:windows
yarn install:wasm-pack:cargo
```
Then to build the WASM layer, run:
```
# macOS/Linux
yarn build:wasm
# Windows
yarn build:wasm:windows
```
Or if you have the `gh` cli installed and want to download the latest main wasm bundle. Note that on Windows, you need to associate .ps1 files with PowerShell, which can be done via the right click menu, selecting `C:\Program Files\PowerShell\7\pwsh.exe`, and you can install tools like `gh` via `yarn install:tools:windows`.
```
# macOS/Linux
yarn fetch:wasm
# Windows
yarn fetch:wasm:windows
```
That will build the WASM binary and put in the `public` dir (though gitignored).
Finally, to run the web app only, run:
```
yarn start
```
If you're not a Zoo employee you won't be able to access the dev environment, you should copy everything from `.env.production` to `.env.development.local` to make it point to production instead, then when you navigate to `localhost:3000` the easiest way to sign in is to paste `localStorage.setItem('TOKEN_PERSIST_KEY', "your-token-from-https://zoo.dev/account/api-tokens")` replacing the with a real token from https://zoo.dev/account/api-tokens of course, then navigate to `localhost:3000` again. Note that navigating to `localhost:3000/signin` removes your token so you will need to set the token again.
### Development environment variables
The Copilot LSP plugin in the editor requires a Zoo API token to run. In production, we authenticate this with a token via cookie in the browser and device auth token in the desktop environment, but this token is inaccessible in the dev browser version because the cookie is considered "cross-site" (from `localhost` to `dev.zoo.dev`). There is an optional environment variable called `VITE_KC_DEV_TOKEN` that you can populate with a dev token in a `.env.development.local` file to not check it into Git, which will use that token instead of other methods for the LSP service.
### Developing in Chrome
Chrome is in the process of rolling out a new default which
[blocks Third-Party Cookies](https://developer.chrome.com/en/docs/privacy-sandbox/third-party-cookie-phase-out/).
If you're having trouble logging into the `modeling-app`, you may need to
enable third-party cookies. You can enable third-party cookies by clicking on
the eye with a slash through it in the URL bar, and clicking on "Enable
Third-Party Cookies".
## Desktop
To spin up the desktop app, `yarn install` and `yarn build:wasm` need to have been done before hand then:
```
yarn tron:start
```
This will start the application and hot-reload on changes.
Devtools can be opened with the usual Command-Option-I (macOS) or Ctrl-Shift-I (Linux and Windows).
To package the app for your platform with electron-builder, run `yarn tronb:package:dev` (or `yarn tronb:package:prod` to point to the .env.production variables).
## Checking out commits / Bisecting
Which commands from setup are one off vs. need to be run every time?
The following will need to be run when checking out a new commit and guarantees the build is not stale:
```bash
yarn install
yarn build:wasm
yarn start # or yarn build:local && yarn serve for slower but more production-like build
```
## Before submitting a PR
Before you submit a contribution PR to this repo, please ensure that:
- There is a corresponding issue for the changes you want to make, so that discussion of approach can be had before work begins.
- You have separated out refactoring commits from feature commits as much as possible
- You have run all of the following commands locally:
- `yarn fmt`
- `yarn tsc`
- `yarn test`
- Here they are all together: `yarn fmt && yarn tsc && yarn test`
## Release a new version
#### 1. Create a 'Cut release $VERSION' issue
It will be used to document changelog discussions and release testing.
https://github.com/KittyCAD/modeling-app/issues/new
#### 2. Push a new tag
Create a new tag and push it to the repo. The `semantic-release.sh` script will automatically bump the minor part, which we use the most. For instance going from `v0.27.0` to `v0.28.0`.
```
VERSION=$(./scripts/semantic-release.sh)
git tag $VERSION
git push origin --tags
```
This will trigger the `build-apps` workflow, set the version, build & sign the apps, and generate release files as well as updater-test artifacts.
The workflow should be listed right away [in this list](https://github.com/KittyCAD/modeling-app/actions/workflows/build-apps.yml?query=event%3Apush)).
#### 3. Manually test artifacts
##### Release builds
The release builds can be found under the `out-{arch}-{platform}` zip files, at the very bottom of the `build-apps` summary page for the workflow (triggered by the tag in 2.).
Manually test against this [list](https://github.com/KittyCAD/modeling-app/issues/3588) across Windows, MacOS, Linux and posting results as comments in the issue.
##### Updater-test builds
The other `build-apps` output in the release `build-apps` workflow (triggered by 2.) is `updater-test-{arch}-{platform}`. It's a semi-automated process: for macOS, Windows, and Linux, download the corresponding updater-test artifact file, install the app, run it, expect an updater prompt to a dummy v0.255.255, install it and check that the app comes back at that version.
The only difference with these builds is that they point to a different update location on the release bucket, with this dummy v0.255.255 always available. This helps ensuring that the version we release will be able to update to the next one available.
If the prompt doesn't show up, start the app in command line to grab the electron-updater logs. This is likely an issue with the current build that needs addressing (or the updater-test location in the storage bucket).
```
# Windows (PowerShell)
& 'C:\Program Files\Zoo Design Studio\Zoo Design Studio.exe'
# macOS
/Applications/Zoo\ Modeling\ App.app/Contents/MacOS/Zoo\ Modeling\ App
# Linux
./Zoo Design Studio-{version}-{arch}-linux.AppImage
```
#### 4. Publish the release
Head over to https://github.com/KittyCAD/modeling-app/releases/new, pick the newly created tag and type it in the _Release title_ field as well.
Hit _Generate release notes_ as a starting point to discuss the changelog in the issue. Once done, make sure _Set as the latest release_ is checked, and hit _Publish release_.
A new `publish-apps-release` will kick in and you should be able to find it [here](https://github.com/KittyCAD/modeling-app/actions?query=event%3Arelease). On success, the files will be uploaded to the public bucket as well as to the GitHub release, and the announcement on Discord will be sent.
#### 5. Close the issue
If everything is well and the release is out to the public, the issue tracking the release shall be closed.
## Fuzzing the parser
Make sure you install cargo fuzz:
```bash
$ cargo install cargo-fuzz
```
```bash
$ cd rust/kcl-lib
# list the fuzz targets
$ cargo fuzz list
# run the parser fuzzer
$ cargo +nightly fuzz run parser
```
For more information on fuzzing you can check out
[this guide](https://rust-fuzz.github.io/book/cargo-fuzz.html).
## Tests
### Playwright tests
You will need a `./e2e/playwright/playwright-secrets.env` file:
```bash
$ touch ./e2e/playwright/playwright-secrets.env
$ cat ./e2e/playwright/playwright-secrets.env
token=<dev.zoo.dev/account/api-tokens>
snapshottoken=<your-snapshot-token>
```
For a portable way to run Playwright you'll need Docker.
#### Generic example
After that, open a terminal and run:
```bash
docker run --network host --rm --init -it playwright/chrome:playwright-x.xx.x
```
and in another terminal, run:
```bash
PW_TEST_CONNECT_WS_ENDPOINT=ws://127.0.0.1:4444/ yarn playwright test --project="Google Chrome" <test suite>
```
#### Specific example
open a terminal and run:
```bash
docker run --network host --rm --init -it playwright/chrome:playwright-1.46.0
```
and in another terminal, run:
```bash
PW_TEST_CONNECT_WS_ENDPOINT=ws://127.0.0.1:4444/ yarn playwright test --project="Google Chrome" e2e/playwright/command-bar-tests.spec.ts
```
run a specific test change the test from `test('...` to `test.only('...`
(note if you commit this, the tests will instantly fail without running any of the tests)
**Gotcha**: running the docker container with a mismatched image against your `./node_modules/playwright` will cause a failure. Make sure the versions are matched and up to date.
run headed
```
yarn playwright test --headed
```
run with step through debugger
```
PWDEBUG=1 yarn playwright test
```
However, if you want a debugger I recommend using VSCode and the `playwright` extension, as the above command is a cruder debugger that steps into every function call which is annoying.
With the extension you can set a breakpoint after `waitForDefaultPlanesVisibilityChange` in order to skip app loading, then the vscode debugger's "step over" is much better for being able to stay at the right level of abstraction as you debug the code.
If you want to limit to a single browser use `--project="webkit"` or `firefox`, `Google Chrome`
Or comment out browsers in `playwright.config.ts`.
note chromium has encoder compat issues which is why were testing against the branded 'Google Chrome'
You may consider using the VSCode extension, it's useful for running individual threads, but some some reason the "record a test" is locked to chromium with we can't use. A work around is to us the CI `yarn playwright codegen -b wk --load-storage ./store localhost:3000`
<details>
<summary>
Where `./store` should look like this
</summary>
```JSON
{
"cookies": [],
"origins": [
{
"origin": "http://localhost:3000",
"localStorage": [
{
"name": "store",
"value": "{\"state\":{\"openPanes\":[\"code\"]},\"version\":0}"
},
{
"name": "persistCode",
"value": ""
},
{
"name": "TOKEN_PERSIST_KEY",
"value": "your-token"
}
]
}
]
}
```
</details>
However because much of our tests involve clicking in the stream at specific locations, it's code-gen looks `await page.locator('video').click();` when really we need to use a pixel coord, so I think it's of limited use.
### Unit and component tests
If you already haven't, run the following:
```
yarn
yarn build:wasm
yarn start
```
and finally:
```
yarn test:unit
```
For individual testing:
```
yarn test abstractSyntaxTree -t "unexpected closed curly brace" --silent=false
```
Which will run our suite of [Vitest unit](https://vitest.dev/) and [React Testing Library E2E](https://testing-library.com/docs/react-testing-library/intro) tests, in interactive mode by default.
### Rust tests
**Dependencies**
- `KITTYCAD_API_TOKEN`
- `cargo-nextest`
- `just`
#### Setting KITTYCAD_API_TOKEN
Use the production zoo.dev token, set this environment variable before running the tests
#### Installing cargonextest
```
cd rust
cargo search cargo-nextest
cargo install cargo-nextest
```
#### just
install [`just`](https://github.com/casey/just?tab=readme-ov-file#pre-built-binaries)
#### Running the tests
```bash
# With just
# Make sure KITTYCAD_API_TOKEN=<prod zoo.dev token> is set
# Make sure you installed cargo-nextest
# Make sure you installed just
cd rust
just test
```
```bash
# Without just
# Make sure KITTYCAD_API_TOKEN=<prod zoo.dev token> is set
# Make sure you installed cargo-nextest
cd rust
export RUST_BRACKTRACE="full" && cargo nextest run --workspace --test-threads=1
```
Where `XXX` is an API token from the production engine (NOT the dev environment).
We recommend using [nextest](https://nexte.st/) to run the Rust tests (its faster and is used in CI). Once installed, run the tests using
```
cd rust
KITTYCAD_API_TOKEN=XXX cargo run nextest
```
### Mapping CI CD jobs to local commands
When you see the CI CD fail on jobs you may wonder three things
- Do I have a bug in my code?
- Is the test flaky?
- Is there a bug in `main`?
To answer these questions the following commands will give you confidence to locate the issue.
#### Static Analysis
Part of the CI CD pipeline performs static analysis on the code. Use the following commands to mimic the CI CD jobs.
The following set of commands should get us closer to one and done commands to instantly retest issues.
```
yarn test-setup
```
> Gotcha, are packages up to date and is the wasm built?
```
yarn tsc
yarn fmt-check
yarn lint
yarn test:unit:local
```
> Gotcha: Our unit tests have integration tests in them. You need to run a localhost server to run the unit tests.
#### E2E Tests
**Playwright Electron**
These E2E tests run in electron. There are tests that are skipped if they are ran in a windows, linux, or macos environment. We can use playwright tags to implement test skipping.
```
yarn test:playwright:electron:local
yarn test:playwright:electron:windows:local
yarn test:playwright:electron:macos:local
yarn test:playwright:electron:ubuntu:local
```
> Why does it say local? The CI CD commands that run in the pipeline cannot be ran locally. A single command will not properly setup the testing environment locally.
#### Some notes on CI
The tests are broken into snapshot tests and non-snapshot tests, and they run in that order, they automatically commit new snap shots, so if you see an image commit check it was an intended change. If we have non-determinism in the snapshots such that they are always committing new images, hopefully this annoyance makes us fix them asap, if you notice this happening let Kurt know. But for the odd occasion `git reset --hard HEAD~ && git push -f` is your friend.
How to interpret failing playwright tests?
If your tests fail, click through to the action and see that the tests failed on a line that includes `await page.getByTestId('loading').waitFor({ state: 'detached' })`, this means the test fail because the stream never started. It's you choice if you want to re-run the test, or ignore the failure.
We run on ubuntu and macos, because safari doesn't work on linux because of the dreaded "no RTCPeerConnection variable" error. But linux runs first and then macos for the same reason that we limit the number of parallel tests to 1 because we limit stream connections per user, so tests would start failing we if let them run together.
If something fails on CI you can download the artifact, unzip it and then open `playwright-report/data/<UUID>.zip` with https://trace.playwright.dev/ to see what happened.
#### Getting started writing a playwright test in our app
Besides following the instructions above and using the playwright docs, our app is weird because of the whole stream thing, which means our testing is weird. Because we've just figured out this stuff and therefore docs might go stale quick here's a 15min vid/tutorial
https://github.com/KittyCAD/modeling-app/assets/29681384/6f5e8e85-1003-4fd9-be7f-f36ce833942d
<details>
<summary>
PS: for the debug panel, the following JSON is useful for snapping the camera
</summary>
```JSON
{"type":"modeling_cmd_req","cmd_id":"054e5472-e5e9-4071-92d7-1ce3bac61956","cmd":{"type":"default_camera_look_at","center":{"x":15,"y":0,"z":0},"up":{"x":0,"y":0,"z":1},"vantage":{"x":30,"y":30,"z":30}}}
```
</details>
## KCL
To contribute to the KittyCAD Language, see the [README](https://github.com/KittyCAD/modeling-app/tree/main/rust/kcl-lib) for KCL.
For how to contribute to KCL, [see our KCL README](https://github.com/KittyCAD/modeling-app/tree/main/rust/kcl-lib).
### Logging
To display logging (to the terminal or console) set `ZOO_LOG=1`. This will log some warnings and simple performance metrics. To view these in test runs, use `-- --nocapture`.
To enable memory metrics, build with `--features dhat-heap`.

View File

@ -1,48 +0,0 @@
{
"$schema": "https://biomejs.dev/schemas/1.6.0/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": {
"noUnusedVariables": "error"
},
"suspicious": {
"noExplicitAny": "warn"
},
"style": {
"useBlockStatements": "error",
"useShorthandArrayType": "error"
}
}
},
"formatter": {
"enabled": true,
"indentWidth": 2,
"indentStyle": "space",
"lineWidth": 80,
"formatWithErrors": true
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"jsxQuoteStyle": "double",
"trailingCommas": "es5",
"semicolons": "asNeeded"
},
"parser": {
"unsafeParameterDecoratorsEnabled": true
}
},
"css": {
"parser": {
"cssModules": true
}
},
"files": {
"ignore": ["**/*.json"]
}
}

49
docs/kcl/abs.md Normal file

File diff suppressed because one or more lines are too long

49
docs/kcl/acos.md Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,17 +8,12 @@ Draw a line segment relative to the current origin using the polar measure of so
```kcl
```js
angledLine(
sketch: [Sketch](/docs/kcl/types/Sketch),
angle: [number](/docs/kcl/types/number),
length?: [number](/docs/kcl/types/number),
lengthX?: [number](/docs/kcl/types/number),
lengthY?: [number](/docs/kcl/types/number),
endAbsoluteX?: [number](/docs/kcl/types/number),
endAbsoluteY?: [number](/docs/kcl/types/number),
tag?: [TagDeclarator](/docs/kcl/types#tag-declaration),
): [Sketch](/docs/kcl/types/Sketch)
data: AngledLineData,
sketch: Sketch,
tag?: TagDeclarator,
): Sketch
```
@ -26,14 +21,9 @@ angledLine(
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | Which sketch should this path be added to? | Yes |
| `angle` | [`number`](/docs/kcl/types/number) | Which angle should the line be drawn at? | Yes |
| `length` | [`number`](/docs/kcl/types/number) | Draw the line this distance along the given angle. Only one of `length`, `lengthX`, `lengthY`, `endAbsoluteX`, `endAbsoluteY` can be given. | No |
| `lengthX` | [`number`](/docs/kcl/types/number) | Draw the line this distance along the X axis. Only one of `length`, `lengthX`, `lengthY`, `endAbsoluteX`, `endAbsoluteY` can be given. | No |
| `lengthY` | [`number`](/docs/kcl/types/number) | Draw the line this distance along the Y axis. Only one of `length`, `lengthX`, `lengthY`, `endAbsoluteX`, `endAbsoluteY` can be given. | No |
| `endAbsoluteX` | [`number`](/docs/kcl/types/number) | Draw the line along the given angle until it reaches this point along the X axis. Only one of `length`, `lengthX`, `lengthY`, `endAbsoluteX`, `endAbsoluteY` can be given. | No |
| `endAbsoluteY` | [`number`](/docs/kcl/types/number) | Draw the line along the given angle until it reaches this point along the Y axis. Only one of `length`, `lengthX`, `lengthY`, `endAbsoluteX`, `endAbsoluteY` can be given. | No |
| `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Create a new tag which refers to this line | No |
| `data` | [`AngledLineData`](/docs/kcl/types/AngledLineData) | Data to draw an angled line. | Yes |
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No |
### Returns
@ -42,11 +32,11 @@ angledLine(
### Examples
```kcl
```js
exampleSketch = startSketchOn(XZ)
|> startProfile(at = [0, 0])
|> startProfileAt([0, 0], %)
|> yLine(endAbsolute = 15)
|> angledLine(angle = 30, length = 15)
|> angledLine({ angle = 30, length = 15 }, %)
|> line(end = [8, -10])
|> yLine(endAbsolute = 0)
|> close()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,14 +8,12 @@ Draw an angled line from the current origin, constructing a line segment such th
```kcl
```js
angledLineThatIntersects(
sketch: [Sketch](/docs/kcl/types/Sketch),
angle: [number](/docs/kcl/types/number),
intersectTag: [TagIdentifier](/docs/kcl/types#tag-identifier),
offset?: [number](/docs/kcl/types/number),
tag?: [TagDeclarator](/docs/kcl/types#tag-declaration),
): [Sketch](/docs/kcl/types/Sketch)
data: AngledLineThatIntersectsData,
sketch: Sketch,
tag?: TagDeclarator,
): Sketch
```
@ -23,11 +21,9 @@ angledLineThatIntersects(
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | Which sketch should this path be added to? | Yes |
| `angle` | [`number`](/docs/kcl/types/number) | Which angle should the line be drawn at? | Yes |
| `intersectTag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The tag of the line to intersect with | Yes |
| `offset` | [`number`](/docs/kcl/types/number) | The offset from the intersecting line. Defaults to 0. | No |
| `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Create a new tag which refers to this line | No |
| `data` | [`AngledLineThatIntersectsData`](/docs/kcl/types/AngledLineThatIntersectsData) | Data for drawing an angled line that intersects with a given line. | Yes |
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No |
### Returns
@ -36,13 +32,17 @@ angledLineThatIntersects(
### Examples
```kcl
```js
exampleSketch = startSketchOn(XZ)
|> startProfile(at = [0, 0])
|> startProfileAt([0, 0], %)
|> line(endAbsolute = [5, 10])
|> line(endAbsolute = [-10, 10], tag = $lineToIntersect)
|> line(endAbsolute = [0, 20])
|> angledLineThatIntersects(angle = 80, intersectTag = lineToIntersect, offset = 10)
|> angledLineThatIntersects({
angle = 80,
intersectTag = lineToIntersect,
offset = 10
}, %)
|> close()
example = extrude(exampleSketch, length = 10)

48
docs/kcl/angledLineToX.md Normal file

File diff suppressed because one or more lines are too long

48
docs/kcl/angledLineToY.md Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

45
docs/kcl/arcTo.md Normal file

File diff suppressed because one or more lines are too long

48
docs/kcl/asin.md Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

45
docs/kcl/assertEqual.md Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

48
docs/kcl/atan.md Normal file

File diff suppressed because one or more lines are too long

52
docs/kcl/atan2.md Normal file

File diff suppressed because one or more lines are too long

View File

@ -8,14 +8,12 @@ Draw a smooth, continuous, curved line segment from the current origin to the de
```kcl
```js
bezierCurve(
sketch: [Sketch](/docs/kcl/types/Sketch),
control1: [[[number](/docs/kcl/types/number)]](/docs/kcl/types/[number](/docs/kcl/types/number)),
control2: [[[number](/docs/kcl/types/number)]](/docs/kcl/types/[number](/docs/kcl/types/number)),
end: [[[number](/docs/kcl/types/number)]](/docs/kcl/types/[number](/docs/kcl/types/number)),
tag?: [TagDeclarator](/docs/kcl/types#tag-declaration),
): [Sketch](/docs/kcl/types/Sketch)
data: BezierData,
sketch: Sketch,
tag?: TagDeclarator,
): Sketch
```
@ -23,11 +21,9 @@ bezierCurve(
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | Which sketch should this path be added to? | Yes |
| `control1` | [`[number]`](/docs/kcl/types/number) | First control point for the cubic | Yes |
| `control2` | [`[number]`](/docs/kcl/types/number) | Second control point for the cubic | Yes |
| `end` | [`[number]`](/docs/kcl/types/number) | How far away (along the X and Y axes) should this line go? | Yes |
| `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Create a new tag which refers to this line | No |
| `data` | [`BezierData`](/docs/kcl/types/BezierData) | Data to draw a bezier curve. | Yes |
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | | No |
### Returns
@ -36,11 +32,15 @@ bezierCurve(
### Examples
```kcl
```js
exampleSketch = startSketchOn(XZ)
|> startProfile(at = [0, 0])
|> startProfileAt([0, 0], %)
|> line(end = [0, 10])
|> bezierCurve(control1 = [5, 0], control2 = [5, 10], end = [10, 10])
|> bezierCurve({
to = [10, 10],
control1 = [5, 0],
control2 = [5, 10]
}, %)
|> line(endAbsolute = [10, 0])
|> close()

46
docs/kcl/ceil.md Normal file

File diff suppressed because one or more lines are too long

95
docs/kcl/chamfer.md Normal file

File diff suppressed because one or more lines are too long

View File

@ -8,14 +8,14 @@ Construct a circle derived from 3 points.
```kcl
```js
circleThreePoint(
sketchSurfaceOrGroup: [[Sketch](/docs/kcl/types/Sketch)OrSurface](/docs/kcl/types/[Sketch](/docs/kcl/types/Sketch)OrSurface),
p1: [[[number](/docs/kcl/types/number)]](/docs/kcl/types/[number](/docs/kcl/types/number)),
p2: [[[number](/docs/kcl/types/number)]](/docs/kcl/types/[number](/docs/kcl/types/number)),
p3: [[[number](/docs/kcl/types/number)]](/docs/kcl/types/[number](/docs/kcl/types/number)),
tag?: [TagDeclarator](/docs/kcl/types#tag-declaration),
): [Sketch](/docs/kcl/types/Sketch)
sketchSurfaceOrGroup: SketchOrSurface,
p1: [number],
p2: [number],
p3: [number],
tag?: TagDeclarator,
): Sketch
```
@ -27,7 +27,7 @@ circleThreePoint(
| `p1` | [`[number]`](/docs/kcl/types/number) | 1st point to derive the circle. | Yes |
| `p2` | [`[number]`](/docs/kcl/types/number) | 2nd point to derive the circle. | Yes |
| `p3` | [`[number]`](/docs/kcl/types/number) | 3rd point to derive the circle. | Yes |
| `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Identifier for the circle to reference elsewhere. | No |
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Identifier for the circle to reference elsewhere. | No |
### Returns
@ -36,7 +36,7 @@ circleThreePoint(
### Examples
```kcl
```js
exampleSketch = startSketchOn(XY)
|> circleThreePoint(p1 = [10, 10], p2 = [20, 8], p3 = [15, 5])
|> extrude(length = 5)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -9,8 +9,6 @@ layout: manual
### `std`
- [`END`](/docs/kcl/consts/std-END)
- [`START`](/docs/kcl/consts/std-START)
- [`X`](/docs/kcl/consts/std-X)
- [`XY`](/docs/kcl/consts/std-XY)
- [`XZ`](/docs/kcl/consts/std-XZ)

View File

@ -1,15 +0,0 @@
---
title: "std::END"
excerpt: "Identifies the ending face of an extrusion. I.e., the new face created by an extrusion."
layout: manual
---
Identifies the ending face of an extrusion. I.e., the new face created by an extrusion.
```kcl
std::END: string = 'end'
```

View File

@ -1,15 +0,0 @@
---
title: "std::START"
excerpt: "Identifies the starting face of an extrusion. I.e., the face which is extruded."
layout: manual
---
Identifies the starting face of an extrusion. I.e., the face which is extruded.
```kcl
std::START: string = 'start'
```

View File

@ -8,7 +8,7 @@ layout: manual
```kcl
```js
std::X
```

View File

@ -8,7 +8,7 @@ layout: manual
```kcl
```js
std::XY
```

View File

@ -8,7 +8,7 @@ layout: manual
```kcl
```js
std::XZ
```

View File

@ -8,7 +8,7 @@ layout: manual
```kcl
```js
std::Y
```

View File

@ -8,7 +8,7 @@ layout: manual
```kcl
```js
std::YZ
```

View File

@ -8,7 +8,7 @@ layout: manual
```kcl
```js
std::Z
```

View File

@ -8,19 +8,19 @@ The value of Eulers number `e`.
```kcl
```js
std::math::E: number = 2.71828182845904523536028747135266250_
```
### Examples
```kcl
```js
exampleSketch = startSketchOn(XZ)
|> startProfile(at = [0, 0])
|> angledLine(
|> startProfileAt([0, 0], %)
|> angledLine({
angle = 30,
length = 2 * E ^ 2,
)
}, %)
|> yLine(endAbsolute = 0)
|> close()

View File

@ -8,17 +8,17 @@ The value of `pi`, Archimedes constant (π).
```kcl
std::math::PI: number(_?) = 3.14159265358979323846264338327950288_?
```js
std::math::PI: number = 3.14159265358979323846264338327950288_
```
### Examples
```kcl
```js
circumference = 70
exampleSketch = startSketchOn(XZ)
|> circle(center = [0, 0], radius = (circumference / (2 * PI)): number(mm))
|> circle(center = [0, 0], radius = circumference / (2 * PI))
example = extrude(exampleSketch, length = 5)
```

View File

@ -8,19 +8,19 @@ The value of `tau`, the full circle constant (τ). Equal to 2π.
```kcl
```js
std::math::TAU: number = 6.28318530717958647692528676655900577_
```
### Examples
```kcl
```js
exampleSketch = startSketchOn(XZ)
|> startProfile(at = [0, 0])
|> angledLine(
|> startProfileAt([0, 0], %)
|> angledLine({
angle = 50,
length = 10 * TAU,
)
}, %)
|> yLine(endAbsolute = 0)
|> close()

View File

@ -8,7 +8,7 @@ layout: manual
```kcl
```js
std::turns::HALF_TURN: number(deg) = 180deg
```

View File

@ -8,7 +8,7 @@ layout: manual
```kcl
```js
std::turns::QUARTER_TURN: number(deg) = 90deg
```

View File

@ -8,7 +8,7 @@ layout: manual
```kcl
```js
std::turns::THREE_QUARTER_TURN: number(deg) = 270deg
```

View File

@ -8,7 +8,7 @@ layout: manual
```kcl
```js
std::turns::ZERO: number = 0
```

View File

@ -29,8 +29,8 @@ e(): number
```js
exampleSketch = startSketchOn(XZ)
|> startProfile(at = [0, 0])
|> angledLine(angle = 30, length = 2 * e() ^ 2)
|> startProfileAt([0, 0], %)
|> angledLine({ angle = 30, length = 2 * e() ^ 2 }, %)
|> yLine(endAbsolute = 0)
|> close()

File diff suppressed because one or more lines are too long

94
docs/kcl/fillet.md Normal file

File diff suppressed because one or more lines are too long

46
docs/kcl/floor.md Normal file

File diff suppressed because one or more lines are too long

View File

@ -4,12 +4,8 @@ excerpt: "Converts a number from centimeters to the current default unit."
layout: manual
---
**WARNING:** This function is deprecated.
Converts a number from centimeters to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42cm`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in centimeters.
For example, if the current file uses inches, `fromCm(1)` will return `0.393701`. If the current file uses millimeters, `fromCm(1)` will return `10`. If the current file uses centimeters, `fromCm(1)` will return `1`.

View File

@ -4,12 +4,8 @@ excerpt: "Converts a number from feet to the current default unit."
layout: manual
---
**WARNING:** This function is deprecated.
Converts a number from feet to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42ft`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in feet.
For example, if the current file uses inches, `fromFt(1)` will return `12`. If the current file uses millimeters, `fromFt(1)` will return `304.8`. If the current file uses feet, `fromFt(1)` will return `1`.

View File

@ -4,12 +4,8 @@ excerpt: "Converts a number from inches to the current default unit."
layout: manual
---
**WARNING:** This function is deprecated.
Converts a number from inches to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42inch`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in inches.
For example, if the current file uses inches, `fromInches(1)` will return `1`. If the current file uses millimeters, `fromInches(1)` will return `25.4`.

View File

@ -4,12 +4,8 @@ excerpt: "Converts a number from meters to the current default unit."
layout: manual
---
**WARNING:** This function is deprecated.
Converts a number from meters to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42m`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in meters.
For example, if the current file uses inches, `fromM(1)` will return `39.3701`. If the current file uses millimeters, `fromM(1)` will return `1000`. If the current file uses meters, `fromM(1)` will return `1`.

View File

@ -4,12 +4,8 @@ excerpt: "Converts a number from mm to the current default unit."
layout: manual
---
**WARNING:** This function is deprecated.
Converts a number from mm to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42mm`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in millimeters.
For example, if the current file uses inches, `fromMm(1)` will return `1/25.4`. If the current file uses millimeters, `fromMm(1)` will return `1`.

View File

@ -4,12 +4,8 @@ excerpt: "Converts a number from yards to the current default unit."
layout: manual
---
**WARNING:** This function is deprecated.
Converts a number from yards to the current default unit.
*DEPRECATED* prefer using explicit numeric suffixes (e.g., `42yd`) or the `to...` conversion functions.
No matter what units the current file uses, this function will always return a number equivalent to the input in yards.
For example, if the current file uses inches, `fromYd(1)` will return `36`. If the current file uses millimeters, `fromYd(1)` will return `914.4`. If the current file uses yards, `fromYd(1)` will return `1`.

View File

@ -8,8 +8,8 @@ Get the shared edge between two faces.
```kcl
getCommon[Edge](/docs/kcl/types/Edge)(faces: [[TagIdentifier](/docs/kcl/types#tag-identifier)]): Uuid
```js
getCommonEdge(faces: [TagIdentifier]): Uuid
```
@ -26,13 +26,13 @@ getCommon[Edge](/docs/kcl/types/Edge)(faces: [[TagIdentifier](/docs/kcl/types#ta
### Examples
```kcl
```js
// Get an edge shared between two faces, created after a chamfer.
scale = 20
part001 = startSketchOn(XY)
|> startProfile(at = [0, 0])
|> startProfileAt([0, 0], %)
|> line(end = [0, scale])
|> line(end = [scale, 0])
|> line(end = [0, -scale])

View File

@ -8,8 +8,8 @@ Get the next adjacent edge to the edge given.
```kcl
getNextAdjacent[Edge](/docs/kcl/types/Edge)(edge: [TagIdentifier](/docs/kcl/types#tag-identifier)): Uuid
```js
getNextAdjacentEdge(tag: TagIdentifier): Uuid
```
@ -17,7 +17,7 @@ getNextAdjacent[Edge](/docs/kcl/types/Edge)(edge: [TagIdentifier](/docs/kcl/type
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `edge` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The tag of the edge you want to find the next adjacent edge of. | Yes |
| [`tag`](/docs/kcl/types/tag) | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | | Yes |
### Returns
@ -26,14 +26,14 @@ getNextAdjacent[Edge](/docs/kcl/types/Edge)(edge: [TagIdentifier](/docs/kcl/type
### Examples
```kcl
```js
exampleSketch = startSketchOn(XZ)
|> startProfile(at = [0, 0])
|> startProfileAt([0, 0], %)
|> line(end = [10, 0])
|> angledLine(angle = 60, length = 10)
|> angledLine(angle = 120, length = 10)
|> angledLine({ angle = 60, length = 10 }, %)
|> angledLine({ angle = 120, length = 10 }, %)
|> line(end = [-10, 0])
|> angledLine(angle = 240, length = 10, tag = $referenceEdge)
|> angledLine({ angle = 240, length = 10 }, %, $referenceEdge)
|> close()
example = extrude(exampleSketch, length = 5)

View File

@ -8,8 +8,8 @@ Get the opposite edge to the edge given.
```kcl
getOpposite[Edge](/docs/kcl/types/Edge)(edge: [TagIdentifier](/docs/kcl/types#tag-identifier)): Uuid
```js
getOppositeEdge(tag: TagIdentifier): Uuid
```
@ -17,7 +17,7 @@ getOpposite[Edge](/docs/kcl/types/Edge)(edge: [TagIdentifier](/docs/kcl/types#ta
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `edge` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The tag of the edge you want to find the opposite edge of. | Yes |
| [`tag`](/docs/kcl/types/tag) | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | | Yes |
### Returns
@ -26,14 +26,14 @@ getOpposite[Edge](/docs/kcl/types/Edge)(edge: [TagIdentifier](/docs/kcl/types#ta
### Examples
```kcl
```js
exampleSketch = startSketchOn(XZ)
|> startProfile(at = [0, 0])
|> startProfileAt([0, 0], %)
|> line(end = [10, 0])
|> angledLine(angle = 60, length = 10)
|> angledLine(angle = 120, length = 10)
|> angledLine({ angle = 60, length = 10 }, %)
|> angledLine({ angle = 120, length = 10 }, %)
|> line(end = [-10, 0])
|> angledLine(angle = 240, length = 10, tag = $referenceEdge)
|> angledLine({ angle = 240, length = 10 }, %, $referenceEdge)
|> close()
example = extrude(exampleSketch, length = 5)

View File

@ -8,8 +8,8 @@ Get the previous adjacent edge to the edge given.
```kcl
getPreviousAdjacent[Edge](/docs/kcl/types/Edge)(edge: [TagIdentifier](/docs/kcl/types#tag-identifier)): Uuid
```js
getPreviousAdjacentEdge(tag: TagIdentifier): Uuid
```
@ -17,7 +17,7 @@ getPreviousAdjacent[Edge](/docs/kcl/types/Edge)(edge: [TagIdentifier](/docs/kcl/
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `edge` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The tag of the edge you want to find the previous adjacent edge of. | Yes |
| [`tag`](/docs/kcl/types/tag) | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | | Yes |
### Returns
@ -26,14 +26,14 @@ getPreviousAdjacent[Edge](/docs/kcl/types/Edge)(edge: [TagIdentifier](/docs/kcl/
### Examples
```kcl
```js
exampleSketch = startSketchOn(XZ)
|> startProfile(at = [0, 0])
|> startProfileAt([0, 0], %)
|> line(end = [10, 0])
|> angledLine(angle = 60, length = 10)
|> angledLine(angle = 120, length = 10)
|> angledLine({ angle = 60, length = 10 }, %)
|> angledLine({ angle = 120, length = 10 }, %)
|> line(end = [-10, 0])
|> angledLine(angle = 240, length = 10, tag = $referenceEdge)
|> angledLine({ angle = 240, length = 10 }, %, $referenceEdge)
|> close()
example = extrude(exampleSketch, length = 5)

67
docs/kcl/hole.md Normal file

File diff suppressed because one or more lines are too long

85
docs/kcl/hollow.md Normal file

File diff suppressed because one or more lines are too long

80
docs/kcl/import.md Normal file

File diff suppressed because one or more lines are too long

View File

@ -22,115 +22,6 @@ layout: manual
* [`string`](kcl/types/string)
* [`tag`](kcl/types/tag)
* **std**
* [`END`](kcl/consts/std-END)
* [`START`](kcl/consts/std-START)
* [`X`](kcl/consts/std-X)
* [`XY`](kcl/consts/std-XY)
* [`XZ`](kcl/consts/std-XZ)
* [`Y`](kcl/consts/std-Y)
* [`YZ`](kcl/consts/std-YZ)
* [`Z`](kcl/consts/std-Z)
* [`angledLine`](kcl/angledLine)
* [`angledLineThatIntersects`](kcl/angledLineThatIntersects)
* [`appearance`](kcl/appearance)
* [`arc`](kcl/arc)
* [`assert`](kcl/assert)
* [`assertIs`](kcl/assertIs)
* [`bezierCurve`](kcl/bezierCurve)
* [`circleThreePoint`](kcl/circleThreePoint)
* [`clone`](kcl/clone)
* [`close`](kcl/close)
* [`extrude`](kcl/extrude)
* [`getCommonEdge`](kcl/getCommonEdge)
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
* [`getOppositeEdge`](kcl/getOppositeEdge)
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
* [`helix`](kcl/std-helix)
* [`intersect`](kcl/intersect)
* [`involuteCircular`](kcl/involuteCircular)
* [`lastSegX`](kcl/lastSegX)
* [`lastSegY`](kcl/lastSegY)
* [`legAngX`](kcl/legAngX)
* [`legAngY`](kcl/legAngY)
* [`legLen`](kcl/legLen)
* [`line`](kcl/line)
* [`loft`](kcl/loft)
* [`map`](kcl/map)
* [`offsetPlane`](kcl/std-offsetPlane)
* [`patternCircular2d`](kcl/patternCircular2d)
* [`patternCircular3d`](kcl/patternCircular3d)
* [`patternLinear2d`](kcl/patternLinear2d)
* [`patternLinear3d`](kcl/patternLinear3d)
* [`patternTransform`](kcl/patternTransform)
* [`patternTransform2d`](kcl/patternTransform2d)
* [`polygon`](kcl/polygon)
* [`pop`](kcl/pop)
* [`profileStart`](kcl/profileStart)
* [`profileStartX`](kcl/profileStartX)
* [`profileStartY`](kcl/profileStartY)
* [`push`](kcl/push)
* [`reduce`](kcl/reduce)
* [`revolve`](kcl/std-revolve)
* [`rotate`](kcl/rotate)
* [`scale`](kcl/scale)
* [`segAng`](kcl/segAng)
* [`segEnd`](kcl/segEnd)
* [`segEndX`](kcl/segEndX)
* [`segEndY`](kcl/segEndY)
* [`segLen`](kcl/segLen)
* [`segStart`](kcl/segStart)
* [`segStartX`](kcl/segStartX)
* [`segStartY`](kcl/segStartY)
* [`startProfile`](kcl/startProfile)
* [`startSketchOn`](kcl/startSketchOn)
* [`subtract`](kcl/subtract)
* [`subtract2d`](kcl/subtract2d)
* [`sweep`](kcl/sweep)
* [`tangentToEnd`](kcl/tangentToEnd)
* [`tangentialArc`](kcl/tangentialArc)
* [`translate`](kcl/translate)
* [`union`](kcl/union)
* [`xLine`](kcl/xLine)
* [`yLine`](kcl/yLine)
* **std::math**
* [`E`](kcl/consts/std-math-E)
* [`PI`](kcl/consts/std-math-PI)
* [`TAU`](kcl/consts/std-math-TAU)
* [`abs`](kcl/std-math-abs)
* [`acos`](kcl/std-math-acos)
* [`asin`](kcl/std-math-asin)
* [`atan`](kcl/std-math-atan)
* [`atan2`](kcl/std-math-atan2)
* [`ceil`](kcl/std-math-ceil)
* [`cos`](kcl/std-math-cos)
* [`floor`](kcl/std-math-floor)
* [`ln`](kcl/std-math-ln)
* [`log`](kcl/std-math-log)
* [`log10`](kcl/std-math-log10)
* [`log2`](kcl/std-math-log2)
* [`max`](kcl/std-math-max)
* [`min`](kcl/std-math-min)
* [`polar`](kcl/std-math-polar)
* [`pow`](kcl/std-math-pow)
* [`rem`](kcl/std-math-rem)
* [`round`](kcl/std-math-round)
* [`sin`](kcl/std-math-sin)
* [`sqrt`](kcl/std-math-sqrt)
* [`tan`](kcl/std-math-tan)
* **std::sketch**
* [`circle`](kcl/std-sketch-circle)
* [`mirror2d`](kcl/std-sketch-mirror2d)
* **std::solid**
* [`chamfer`](kcl/std-solid-chamfer)
* [`fillet`](kcl/std-solid-fillet)
* [`hollow`](kcl/std-solid-hollow)
* [`shell`](kcl/std-solid-shell)
* **std::turns**
* [`turns::HALF_TURN`](kcl/consts/std-turns-HALF_TURN)
* [`turns::QUARTER_TURN`](kcl/consts/std-turns-QUARTER_TURN)
* [`turns::THREE_QUARTER_TURN`](kcl/consts/std-turns-THREE_QUARTER_TURN)
* [`turns::ZERO`](kcl/consts/std-turns-ZERO)
* **std::types**
* [`Axis2d`](kcl/types/Axis2d)
* [`Axis3d`](kcl/types/Axis3d)
* [`Edge`](kcl/types/Edge)
@ -141,12 +32,124 @@ layout: manual
* [`Point3d`](kcl/types/Point3d)
* [`Sketch`](kcl/types/Sketch)
* [`Solid`](kcl/types/Solid)
* **std::units**
* [`units::toCentimeters`](kcl/std-units-toCentimeters)
* [`units::toDegrees`](kcl/std-units-toDegrees)
* [`units::toFeet`](kcl/std-units-toFeet)
* [`units::toInches`](kcl/std-units-toInches)
* [`units::toMeters`](kcl/std-units-toMeters)
* [`units::toMillimeters`](kcl/std-units-toMillimeters)
* [`units::toRadians`](kcl/std-units-toRadians)
* [`units::toYards`](kcl/std-units-toYards)
* [`X`](kcl/consts/std-X)
* [`XY`](kcl/consts/std-XY)
* [`XZ`](kcl/consts/std-XZ)
* [`Y`](kcl/consts/std-Y)
* [`YZ`](kcl/consts/std-YZ)
* [`Z`](kcl/consts/std-Z)
* [`abs`](kcl/abs)
* [`acos`](kcl/acos)
* [`angleToMatchLengthX`](kcl/angleToMatchLengthX)
* [`angleToMatchLengthY`](kcl/angleToMatchLengthY)
* [`angledLine`](kcl/angledLine)
* [`angledLineOfXLength`](kcl/angledLineOfXLength)
* [`angledLineOfYLength`](kcl/angledLineOfYLength)
* [`angledLineThatIntersects`](kcl/angledLineThatIntersects)
* [`angledLineToX`](kcl/angledLineToX)
* [`angledLineToY`](kcl/angledLineToY)
* [`appearance`](kcl/appearance)
* [`arc`](kcl/arc)
* [`arcTo`](kcl/arcTo)
* [`asin`](kcl/asin)
* [`assert`](kcl/assert)
* [`assertEqual`](kcl/assertEqual)
* [`assertGreaterThan`](kcl/assertGreaterThan)
* [`assertGreaterThanOrEq`](kcl/assertGreaterThanOrEq)
* [`assertLessThan`](kcl/assertLessThan)
* [`assertLessThanOrEq`](kcl/assertLessThanOrEq)
* [`atan`](kcl/atan)
* [`atan2`](kcl/atan2)
* [`bezierCurve`](kcl/bezierCurve)
* [`ceil`](kcl/ceil)
* [`chamfer`](kcl/chamfer)
* [`circleThreePoint`](kcl/circleThreePoint)
* [`close`](kcl/close)
* [`extrude`](kcl/extrude)
* [`fillet`](kcl/fillet)
* [`floor`](kcl/floor)
* [`fromCm`](kcl/fromCm)
* [`fromFt`](kcl/fromFt)
* [`fromInches`](kcl/fromInches)
* [`fromM`](kcl/fromM)
* [`fromMm`](kcl/fromMm)
* [`fromYd`](kcl/fromYd)
* [`getCommonEdge`](kcl/getCommonEdge)
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
* [`getOppositeEdge`](kcl/getOppositeEdge)
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
* [`helix`](kcl/std-helix)
* [`hole`](kcl/hole)
* [`hollow`](kcl/hollow)
* [`lastSegX`](kcl/lastSegX)
* [`lastSegY`](kcl/lastSegY)
* [`legAngX`](kcl/legAngX)
* [`legAngY`](kcl/legAngY)
* [`legLen`](kcl/legLen)
* [`line`](kcl/line)
* [`ln`](kcl/ln)
* [`loft`](kcl/loft)
* [`log`](kcl/log)
* [`log10`](kcl/log10)
* [`log2`](kcl/log2)
* [`map`](kcl/map)
* [`max`](kcl/max)
* [`min`](kcl/min)
* [`offsetPlane`](kcl/offsetPlane)
* [`patternCircular2d`](kcl/patternCircular2d)
* [`patternCircular3d`](kcl/patternCircular3d)
* [`patternLinear2d`](kcl/patternLinear2d)
* [`patternLinear3d`](kcl/patternLinear3d)
* [`patternTransform`](kcl/patternTransform)
* [`patternTransform2d`](kcl/patternTransform2d)
* [`polygon`](kcl/polygon)
* [`pop`](kcl/pop)
* [`pow`](kcl/pow)
* [`profileStart`](kcl/profileStart)
* [`profileStartX`](kcl/profileStartX)
* [`profileStartY`](kcl/profileStartY)
* [`push`](kcl/push)
* [`reduce`](kcl/reduce)
* [`rem`](kcl/rem)
* [`revolve`](kcl/std-revolve)
* [`rotate`](kcl/rotate)
* [`round`](kcl/round)
* [`scale`](kcl/scale)
* [`segAng`](kcl/segAng)
* [`segEnd`](kcl/segEnd)
* [`segEndX`](kcl/segEndX)
* [`segEndY`](kcl/segEndY)
* [`segLen`](kcl/segLen)
* [`segStart`](kcl/segStart)
* [`segStartX`](kcl/segStartX)
* [`segStartY`](kcl/segStartY)
* [`shell`](kcl/shell)
* [`sqrt`](kcl/sqrt)
* [`startProfileAt`](kcl/startProfileAt)
* [`startSketchOn`](kcl/startSketchOn)
* [`sweep`](kcl/sweep)
* [`tangentToEnd`](kcl/tangentToEnd)
* [`tangentialArc`](kcl/tangentialArc)
* [`tangentialArcTo`](kcl/tangentialArcTo)
* [`tangentialArcToRelative`](kcl/tangentialArcToRelative)
* [`toDegrees`](kcl/toDegrees)
* [`toRadians`](kcl/toRadians)
* [`translate`](kcl/translate)
* [`xLine`](kcl/xLine)
* [`yLine`](kcl/yLine)
* **std::math**
* [`E`](kcl/consts/std-math-E)
* [`PI`](kcl/consts/std-math-PI)
* [`TAU`](kcl/consts/std-math-TAU)
* [`cos`](kcl/std-math-cos)
* [`polar`](kcl/std-math-polar)
* [`sin`](kcl/std-math-sin)
* [`tan`](kcl/std-math-tan)
* **std::sketch**
* [`circle`](kcl/std-sketch-circle)
* [`mirror2d`](kcl/std-sketch-mirror2d)
* **std::turns**
* [`turns::HALF_TURN`](kcl/consts/std-turns-HALF_TURN)
* [`turns::QUARTER_TURN`](kcl/consts/std-turns-QUARTER_TURN)
* [`turns::THREE_QUARTER_TURN`](kcl/consts/std-turns-THREE_QUARTER_TURN)
* [`turns::ZERO`](kcl/consts/std-turns-ZERO)

View File

@ -34,7 +34,7 @@ int(num: number): number
```js
n = int(ceil(5 / 2))
assert(n, isEqualTo = 3, error = "5/2 = 2.5, rounded up makes 3")
assertEqual(n, 3, 0.0001, "5/2 = 2.5, rounded up makes 3")
// Draw n cylinders.
startSketchOn(XZ)
|> circle(center = [0, 0], radius = 2)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -21,6 +21,4 @@ once fixed in engine will just start working here with no language changes.
- **Chamfers**: Chamfers cannot intersect, you will get an error. Only simple
chamfer cases work currently.
- **Appearance**: Changing the appearance on a loft does not work.
- **CSG Booleans**: Coplanar (bodies that share a plane) unions, subtractions, and intersections are not currently supported.
- **Appearance**: Changing the appearance on a loft does not work.

View File

@ -8,8 +8,8 @@ Extract the 'x' axis value of the last line segment in the provided 2-d sketch.
```kcl
lastSegX(sketch: [Sketch](/docs/kcl/types/Sketch)): [number](/docs/kcl/types/number)
```js
lastSegX(sketch: Sketch): number
```
@ -26,9 +26,9 @@ lastSegX(sketch: [Sketch](/docs/kcl/types/Sketch)): [number](/docs/kcl/types/num
### Examples
```kcl
```js
exampleSketch = startSketchOn(XZ)
|> startProfile(at = [0, 0])
|> startProfileAt([0, 0], %)
|> line(end = [5, 0])
|> line(end = [20, 5])
|> line(end = [lastSegX(%), 0])

View File

@ -8,8 +8,8 @@ Extract the 'y' axis value of the last line segment in the provided 2-d sketch.
```kcl
lastSegY(sketch: [Sketch](/docs/kcl/types/Sketch)): [number](/docs/kcl/types/number)
```js
lastSegY(sketch: Sketch): number
```
@ -26,9 +26,9 @@ lastSegY(sketch: [Sketch](/docs/kcl/types/Sketch)): [number](/docs/kcl/types/num
### Examples
```kcl
```js
exampleSketch = startSketchOn(XZ)
|> startProfile(at = [0, 0])
|> startProfileAt([0, 0], %)
|> line(end = [5, 0])
|> line(end = [20, 5])
|> line(end = [0, lastSegY(%)])

View File

@ -8,11 +8,11 @@ Compute the angle of the given leg for x.
```kcl
```js
legAngX(
hypotenuse: [number](/docs/kcl/types/number),
leg: [number](/docs/kcl/types/number),
): [number](/docs/kcl/types/number)
hypotenuse: number,
leg: number,
): number
```
### Tags
@ -24,8 +24,8 @@ legAngX(
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `hypotenuse` | [`number`](/docs/kcl/types/number) | The length of the triangle's hypotenuse | Yes |
| `leg` | [`number`](/docs/kcl/types/number) | The length of one of the triangle's legs (i.e. non-hypotenuse side) | Yes |
| `hypotenuse` | [`number`](/docs/kcl/types/number) | | Yes |
| `leg` | [`number`](/docs/kcl/types/number) | | Yes |
### Returns
@ -34,8 +34,8 @@ legAngX(
### Examples
```kcl
legAngX(hypotenuse = 5, leg = 3)
```js
legAngX(5, 3)
```

View File

@ -8,11 +8,11 @@ Compute the angle of the given leg for y.
```kcl
```js
legAngY(
hypotenuse: [number](/docs/kcl/types/number),
leg: [number](/docs/kcl/types/number),
): [number](/docs/kcl/types/number)
hypotenuse: number,
leg: number,
): number
```
### Tags
@ -24,8 +24,8 @@ legAngY(
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `hypotenuse` | [`number`](/docs/kcl/types/number) | The length of the triangle's hypotenuse | Yes |
| `leg` | [`number`](/docs/kcl/types/number) | The length of one of the triangle's legs (i.e. non-hypotenuse side) | Yes |
| `hypotenuse` | [`number`](/docs/kcl/types/number) | | Yes |
| `leg` | [`number`](/docs/kcl/types/number) | | Yes |
### Returns
@ -34,8 +34,8 @@ legAngY(
### Examples
```kcl
legAngY(hypotenuse = 5, leg = 3)
```js
legAngY(5, 3)
```

View File

@ -8,11 +8,11 @@ Compute the length of the given leg.
```kcl
```js
legLen(
hypotenuse: [number](/docs/kcl/types/number),
leg: [number](/docs/kcl/types/number),
): [number](/docs/kcl/types/number)
hypotenuse: number,
leg: number,
): number
```
### Tags
@ -24,8 +24,8 @@ legLen(
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `hypotenuse` | [`number`](/docs/kcl/types/number) | The length of the triangle's hypotenuse | Yes |
| `leg` | [`number`](/docs/kcl/types/number) | The length of one of the triangle's legs (i.e. non-hypotenuse side) | Yes |
| `hypotenuse` | [`number`](/docs/kcl/types/number) | | Yes |
| `leg` | [`number`](/docs/kcl/types/number) | | Yes |
### Returns
@ -34,8 +34,8 @@ legLen(
### Examples
```kcl
legLen(hypotenuse = 5, leg = 3)
```js
legLen(5, 3)
```

View File

@ -8,13 +8,13 @@ Extend the current sketch with a new straight line.
```kcl
```js
line(
sketch: [Sketch](/docs/kcl/types/Sketch),
endAbsolute?: [[[number](/docs/kcl/types/number)]](/docs/kcl/types/[number](/docs/kcl/types/number)),
end?: [[[number](/docs/kcl/types/number)]](/docs/kcl/types/[number](/docs/kcl/types/number)),
tag?: [TagDeclarator](/docs/kcl/types#tag-declaration),
): [Sketch](/docs/kcl/types/Sketch)
sketch: Sketch,
endAbsolute?: [number],
end?: [number],
tag?: TagDeclarator,
): Sketch
```
@ -25,7 +25,7 @@ line(
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | Which sketch should this path be added to? | Yes |
| `endAbsolute` | [`[number]`](/docs/kcl/types/number) | Which absolute point should this line go to? Incompatible with `end`. | No |
| `end` | [`[number]`](/docs/kcl/types/number) | How far away (along the X and Y axes) should this line go? Incompatible with `endAbsolute`. | No |
| `tag` | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Create a new tag which refers to this line | No |
| [`tag`](/docs/kcl/types/tag) | [`TagDeclarator`](/docs/kcl/types#tag-declaration) | Create a new tag which refers to this line | No |
### Returns
@ -34,10 +34,10 @@ line(
### Examples
```kcl
```js
triangle = startSketchOn(XZ)
|> startProfile(at = [0, 0])
// The END argument means it ends at exactly [10, 0].
|> startProfileAt([0, 0], %)
// The 'end' argument means it ends at exactly [10, 0].
// This is an absolute measurement, it is NOT relative to
// the start of the sketch.
|> line(endAbsolute = [10, 0])
@ -47,7 +47,7 @@ triangle = startSketchOn(XZ)
|> extrude(length = 5)
box = startSketchOn(XZ)
|> startProfile(at = [10, 10])
|> startProfileAt([10, 10], %)
// The 'to' argument means move the pen this much.
// So, [10, 0] is a relative distance away from the current point.
|> line(end = [10, 0])

46
docs/kcl/ln.md Normal file

File diff suppressed because one or more lines are too long

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