Compare commits

..

2 Commits

Author SHA1 Message Date
a74771c50d Change to only check formatting, not do it 2025-05-11 14:17:53 -04:00
cb330b99f0 Change biome fmt to be pre-push 2025-05-11 14:14:58 -04:00
928 changed files with 541829 additions and 284118 deletions

View File

@ -1,41 +1,13 @@
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
if [ -z "${TAB_API_URL:-}" ] || [ -z "${TAB_API_KEY:-}" ]; then BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME:-}}"
exit 0 COMMIT="${CI_COMMIT_SHA:-${GITHUB_SHA:-}}"
fi
project="https://github.com/KittyCAD/modeling-app" curl --request POST \
branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME:-}}"
commit="${CI_COMMIT_SHA:-${GITHUB_SHA:-}}"
echo "Uploading batch results"
curl --silent --request POST \
--header "X-API-Key: ${TAB_API_KEY}" \ --header "X-API-Key: ${TAB_API_KEY}" \
--form "project=${project}" \ --form "project=https://github.com/KittyCAD/modeling-app" \
--form "branch=${branch}" \ --form "branch=${BRANCH}" \
--form "commit=${commit}" \ --form "commit=${COMMIT}" \
--form "tests=@test-results/junit.xml" \ --form "tests=@test-results/junit.xml" \
--form "CI_COMMIT_SHA=${CI_COMMIT_SHA:-}" \
--form "CI_PR_NUMBER=${CI_PR_NUMBER:-}" \
--form "GITHUB_BASE_REF=${GITHUB_BASE_REF:-}" \
--form "GITHUB_EVENT_NAME=${GITHUB_EVENT_NAME:-}" \
--form "GITHUB_HEAD_REF=${GITHUB_HEAD_REF:-}" \
--form "GITHUB_REF_NAME=${GITHUB_REF_NAME:-}" \
--form "GITHUB_REF=${GITHUB_REF:-}" \
--form "GITHUB_SHA=${GITHUB_SHA:-}" \
--form "GITHUB_WORKFLOW=${GITHUB_WORKFLOW:-}" \
--form "RUNNER_ARCH=${RUNNER_ARCH:-}" \
${TAB_API_URL}/api/results/bulk ${TAB_API_URL}/api/results/bulk
echo
echo "Sharing updated report"
curl --silent --request POST \
--header "Content-Type: application/json" \
--header "X-API-Key: ${TAB_API_KEY}" \
--data "{
\"project\": \"${project}\",
\"branch\": \"${branch}\",
\"commit\": \"${commit}\"
}" \
${TAB_API_URL}/api/share

View File

@ -7,13 +7,14 @@ on:
- main - main
tags: tags:
- 'v[0-9]+.[0-9]+.[0-9]+' - 'v[0-9]+.[0-9]+.[0-9]+'
- 'nightly-v[0-9]+.[0-9]+.[0-9]+'
env: env:
IS_RELEASE: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'v') }} IS_RELEASE: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'v') }}
IS_NIGHTLY: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} IS_NIGHTLY: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'nightly-v') }}
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
@ -94,9 +95,7 @@ jobs:
- name: Set nightly version, product name, release notes, and icons - name: Set nightly version, product name, release notes, and icons
if: ${{ env.IS_NIGHTLY == 'true' }} if: ${{ env.IS_NIGHTLY == 'true' }}
run: | run: |
COMMIT=$(git rev-parse --short HEAD) export VERSION=${GITHUB_REF_NAME#nightly-v}
DATE=$(date +'%-y.%-m.%-d')
export VERSION=$DATE-main.$COMMIT
npm run files:set-version npm run files:set-version
npm run files:flip-to-nightly npm run files:flip-to-nightly
@ -123,6 +122,18 @@ jobs:
- id: export_notes - id: export_notes
run: echo "notes=`cat release-notes.md`" >> "$GITHUB_OUTPUT" run: echo "notes=`cat release-notes.md`" >> "$GITHUB_OUTPUT"
- name: Prepare electron-builder.yml file for updater test
if: ${{ env.IS_RELEASE == 'true' }}
run: |
yq -i '.publish[0].url = "https://dl.zoo.dev/releases/modeling-app/updater-test"' electron-builder.yml
- uses: actions/upload-artifact@v4
if: ${{ env.IS_RELEASE == 'true' }}
with:
name: prepared-files-updater-test
path: |
electron-builder.yml
build-apps: build-apps:
needs: [prepare-files] needs: [prepare-files]
@ -247,13 +258,55 @@ jobs:
# TODO: add the 'Build for Mac TestFlight (nightly)' stage back # TODO: add the 'Build for Mac TestFlight (nightly)' stage back
# The steps below are for updater-test builds, only on release
- uses: actions/download-artifact@v4
if: ${{ env.IS_RELEASE == 'true' }}
name: prepared-files-updater-test
- name: Copy updated electron-builder.yml file for updater test
if: ${{ env.IS_RELEASE == 'true' }}
run: |
ls -R prepared-files-updater-test
cp prepared-files-updater-test/electron-builder.yml electron-builder.yml
- name: Build the app (updater-test)
if: ${{ env.IS_RELEASE == 'true' }}
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
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
- uses: actions/upload-artifact@v4
if: ${{ env.IS_RELEASE == 'true' }}
with:
name: updater-test-arm64-${{ matrix.platform }}
path: |
out/*-arm64-win.exe
out/*-arm64-mac.dmg
out/*-arm64-linux.AppImage
- uses: actions/upload-artifact@v4
if: ${{ env.IS_RELEASE == 'true' }}
with:
name: updater-test-x64-${{ matrix.platform }}
path: |
out/*-x64-win.exe
out/*-x64-mac.dmg
out/*-x86_64-linux.AppImage
upload-apps-release: upload-apps-release:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
permissions: permissions:
contents: write contents: write
# Equivalent to IS_RELEASE || IS_NIGHTLY (but we can't access those env vars here) if: ${{ github.ref_type == 'tag' }}
if: ${{ (github.ref_type == 'tag' && startsWith(github.ref_name, 'v')) || (github.event_name == 'push' && github.ref == 'refs/heads/main') }}
env: env:
VERSION_NO_V: ${{ needs.prepare-files.outputs.version }} VERSION_NO_V: ${{ needs.prepare-files.outputs.version }}
VERSION: ${{ format('v{0}', needs.prepare-files.outputs.version) }} VERSION: ${{ format('v{0}', needs.prepare-files.outputs.version) }}
@ -359,6 +412,17 @@ jobs:
- name: List artifacts - name: List artifacts
run: "ls -R out" run: "ls -R out"
- name: Set more complete nightly release notes
if: ${{ env.IS_NIGHTLY == 'true' }}
run: |
# Note: preferred going this way instead of a full clone in the checkout step,
# see https://github.com/actions/checkout/issues/1471
git fetch --prune --unshallow --tags
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
- name: Authenticate to Google Cloud - name: Authenticate to Google Cloud
if: ${{ env.IS_NIGHTLY == 'true' }} if: ${{ env.IS_NIGHTLY == 'true' }}
uses: 'google-github-actions/auth@v2.1.8' uses: 'google-github-actions/auth@v2.1.8'

View File

@ -1,56 +0,0 @@
name: Build WASM
on:
workflow_call:
permissions:
contents: read
jobs:
npm-build-wasm:
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- name: Install dependencies
run: npm install
- 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
- uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
with:
tool: wasm-pack
- name: Use Rust cache
uses: Swatinem/rust-cache@v2
with:
workspaces: './rust'
- name: Build Wasm
shell: bash
run: npm run build:wasm
- uses: actions/upload-artifact@v4
with:
name: prepared-wasm
path: |
rust/kcl-wasm-lib/pkg/kcl_wasm_lib*
- uses: actions/upload-artifact@v4
with:
name: prepared-ts-rs-bindings
path: |
rust/kcl-lib/bindings/*

View File

@ -155,7 +155,7 @@ jobs:
shell: bash shell: bash
run: | run: |
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./ [ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
- name: Install Rust - name: Install rust
uses: actions-rust-lang/setup-rust-toolchain@v1 uses: actions-rust-lang/setup-rust-toolchain@v1
with: with:
cache: false # Configured below. cache: false # Configured below.
@ -188,56 +188,6 @@ jobs:
env: env:
TAB_API_URL: ${{ secrets.TAB_API_URL }} TAB_API_URL: ${{ secrets.TAB_API_URL }}
TAB_API_KEY: ${{ secrets.TAB_API_KEY }} TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
run-internal-kcl-samples:
name: cargo test (internal-kcl-samples)
runs-on:
- runs-on=${{ github.run_id }}
- runner=32cpu-linux-x64
- extras=s3-cache
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 internal KCL samples
run: git clone --depth=1 https://x-access-token:${{ secrets.GH_PAT_KCL_SAMPLES_INTERNAL }}@github.com/KittyCAD/kcl-samples-internal public/kcl-samples/internal
- name: Run tests
shell: bash
run: |-
cd rust/kcl-lib
cargo nextest run \
--retries=10 --no-fail-fast --features artifact-graph --profile=ci \
internal \
2>&1 | tee /tmp/github-actions.log
env:
TWENTY_TWENTY: overwrite
INSTA_UPDATE: always
EXPECTORATE: overwrite
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN_DEV}}
ZOO_HOST: https://api.dev.zoo.dev
MODELING_APP_INTERNAL_SAMPLES_SECRET: ${{secrets.MODELING_APP_INTERNAL_SAMPLES_SECRET}}
run-wasm-tests: run-wasm-tests:
name: Run wasm tests name: Run wasm tests
strategy: strategy:

47
.github/workflows/check-exampleKcl.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: Check Onboarding KCL
on:
pull_request:
types: [opened, synchronize]
paths:
- 'src/lib/exampleKcl.ts'
- 'public/kcl-samples/bracket/main.kcl'
permissions:
contents: read
issues: write
pull-requests: write
jobs:
comment:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Comment on PR
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 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,
issue_number
});
const commentExists = comments.some(comment => comment.body === message);
if (!commentExists) {
// Post a comment on the PR
await github.rest.issues.createComment({
owner,
repo,
issue_number,
body: message,
});
}

View File

@ -18,13 +18,73 @@ permissions:
jobs: jobs:
prepare-wasm: conditions:
# separate job on Ubuntu to build or fetch the wasm blob once on the fastest runner runs-on: ubuntu-latest
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64 outputs:
significant: ${{ steps.path-changes.outputs.significant }}
should-run: ${{ steps.should-run.outputs.should-run }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Fetch the base branch
if: ${{ github.event_name == 'pull_request' }}
run: git fetch origin ${{ github.base_ref }} --depth=1
- name: Check for path changes
id: path-changes
shell: bash
run: |
set -euo pipefail
# Manual runs or push should run all tests.
if [[ ${{ github.event_name }} != 'pull_request' ]]; then
echo "significant=true" >> $GITHUB_OUTPUT
exit 0
fi
changed_files=$(git diff --name-only origin/${{ github.base_ref }})
echo "$changed_files"
if grep -Evq '^README.md|^public/kcl-samples/|^rust/kcl-lib/tests/kcl_samples/' <<< "$changed_files" ; then
echo "significant=true" >> $GITHUB_OUTPUT
else
echo "significant=false" >> $GITHUB_OUTPUT
fi
- name: Should run
id: should-run
shell: bash
run: |
set -euo pipefail
# We should run when this is a scheduled run or if there are
# significant changes in the diff.
if [[ ${{ github.event_name }} == 'schedule' || ${{ steps.path-changes.outputs.significant }} == 'true' ]]; then
echo "should-run=true" >> $GITHUB_OUTPUT
else
echo "should-run=false" >> $GITHUB_OUTPUT
fi
- name: Display conditions
shell: bash
run: |
# For debugging purposes
set -euo pipefail
echo "GITHUB_REF: $GITHUB_REF"
echo "GITHUB_HEAD_REF: $GITHUB_HEAD_REF"
echo "GITHUB_BASE_REF: $GITHUB_BASE_REF"
echo "significant: ${{ steps.path-changes.outputs.significant }}"
echo "should-run: ${{ steps.should-run.outputs.should-run }}"
prepare-wasm:
# separate job on Ubuntu to build or fetch the wasm blob once on the fastest runner
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
needs: conditions
steps:
- uses: actions/checkout@v4
if: needs.conditions.outputs.should-run == 'true'
- id: filter - id: filter
if: needs.conditions.outputs.should-run == 'true'
name: Check for Rust changes name: Check for Rust changes
uses: dorny/paths-filter@v3 uses: dorny/paths-filter@v3
with: with:
@ -33,16 +93,18 @@ jobs:
- 'rust/**' - 'rust/**'
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
if: needs.conditions.outputs.should-run == 'true'
with: with:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'
cache: 'npm' cache: 'npm'
- name: Install dependencies - name: Install dependencies
if: needs.conditions.outputs.should-run == 'true'
run: npm install run: npm install
- name: Download Wasm cache - name: Download Wasm Cache
id: download-wasm id: download-wasm
if: ${{ github.event_name != 'schedule' && steps.filter.outputs.rust == 'false' }} if: ${{ needs.conditions.outputs.should-run == 'true' && github.event_name != 'schedule' && steps.filter.outputs.rust == 'false' }}
uses: dawidd6/action-download-artifact@v7 uses: dawidd6/action-download-artifact@v7
continue-on-error: true continue-on-error: true
with: with:
@ -52,8 +114,9 @@ jobs:
branch: main branch: main
path: rust/kcl-wasm-lib/pkg path: rust/kcl-wasm-lib/pkg
- name: Build Wasm condition - name: Build WASM condition
id: wasm id: wasm
if: needs.conditions.outputs.should-run == 'true'
run: | run: |
set -euox pipefail set -euox pipefail
# Build wasm if this is a scheduled run, there are Rust changes, or # Build wasm if this is a scheduled run, there are Rust changes, or
@ -65,34 +128,35 @@ jobs:
fi fi
- name: Use correct Rust toolchain - name: Use correct Rust toolchain
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }} if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
shell: bash shell: bash
run: | run: |
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./ [ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
- name: Install Rust - name: Install rust
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }} if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
uses: actions-rust-lang/setup-rust-toolchain@v1 uses: actions-rust-lang/setup-rust-toolchain@v1
with: with:
cache: false # Configured below. cache: false # Configured below.
- uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc - uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }} if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
with: with:
tool: wasm-pack tool: wasm-pack
- name: Use Rust cache - name: Rust Cache
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }} if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
with: with:
workspaces: './rust' workspaces: './rust'
- name: Build Wasm - name: Build Wasm
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }} if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
shell: bash shell: bash
run: npm run build:wasm run: npm run build:wasm
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
if: needs.conditions.outputs.should-run == 'true'
with: with:
name: prepared-wasm name: prepared-wasm
path: | path: |
@ -101,9 +165,10 @@ jobs:
snapshots: snapshots:
name: playwright:snapshots:ubuntu name: playwright:snapshots:ubuntu
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64 runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
needs: [prepare-wasm] needs: [conditions, prepare-wasm]
steps: steps:
- uses: actions/create-github-app-token@v1 - uses: actions/create-github-app-token@v1
if: needs.conditions.outputs.should-run == 'true'
id: app-token id: app-token
with: with:
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }} app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
@ -111,13 +176,16 @@ jobs:
owner: ${{ github.repository_owner }} owner: ${{ github.repository_owner }}
- uses: actions/checkout@v4 - uses: actions/checkout@v4
if: needs.conditions.outputs.should-run == 'true'
with: with:
token: ${{ steps.app-token.outputs.token }} token: ${{ steps.app-token.outputs.token }}
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
if: needs.conditions.outputs.should-run == 'true'
name: prepared-wasm name: prepared-wasm
- name: Copy prepared Wasm - name: Copy prepared wasm
if: needs.conditions.outputs.should-run == 'true'
run: | run: |
ls -R prepared-wasm ls -R prepared-wasm
cp prepared-wasm/kcl_wasm_lib_bg.wasm public cp prepared-wasm/kcl_wasm_lib_bg.wasm public
@ -125,25 +193,34 @@ jobs:
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
if: needs.conditions.outputs.should-run == 'true'
with: with:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'
cache: 'npm' cache: 'npm'
- name: Install dependencies - name: Install dependencies
id: deps-install id: deps-install
if: needs.conditions.outputs.should-run == 'true'
run: npm install run: npm install
- name: Cache browsers - name: Cache Playwright Browsers
if: needs.conditions.outputs.should-run == 'true'
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: | path: |
~/.cache/ms-playwright/ ~/.cache/ms-playwright/
key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }} key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }}
- name: Install browsers - name: Install Playwright Browsers
if: needs.conditions.outputs.should-run == 'true'
run: npm run playwright install --with-deps run: npm run playwright install --with-deps
- name: Capture snapshots - name: build web
if: needs.conditions.outputs.should-run == 'true'
run: npm run tronb:vite:dev
- name: Run ubuntu/chrome snapshots
if: needs.conditions.outputs.should-run == 'true'
uses: nick-fields/retry@v3.0.2 uses: nick-fields/retry@v3.0.2
with: with:
shell: bash shell: bash
@ -159,7 +236,7 @@ jobs:
TARGET: web TARGET: web
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
if: ${{ !cancelled() && (success() || failure()) }} if: ${{ needs.conditions.outputs.should-run == 'true' && !cancelled() && (success() || failure()) }}
with: with:
name: playwright-report-ubuntu-snapshot-${{ github.sha }} name: playwright-report-ubuntu-snapshot-${{ github.sha }}
path: playwright-report/ path: playwright-report/
@ -167,8 +244,8 @@ jobs:
retention-days: 30 retention-days: 30
overwrite: true overwrite: true
- name: Check diff - name: Check for changes
if: ${{ github.ref != 'refs/heads/main' }} if: ${{ needs.conditions.outputs.should-run == 'true' && github.ref != 'refs/heads/main' }}
shell: bash shell: bash
id: git-check id: git-check
run: | run: |
@ -178,8 +255,9 @@ jobs:
else echo "modified=false" >> $GITHUB_OUTPUT else echo "modified=false" >> $GITHUB_OUTPUT
fi fi
- name: Commit changes - name: Commit changes, if any
if: ${{ steps.git-check.outputs.modified == 'true' }} # TODO: find a more reliable way to detect visual changes
if: ${{ false && needs.conditions.outputs.should-run == 'true' && steps.git-check.outputs.modified == 'true' }}
shell: bash shell: bash
run: | run: |
git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots
@ -189,12 +267,12 @@ jobs:
git fetch origin git fetch origin
echo ${{ github.head_ref }} echo ${{ github.head_ref }}
git checkout ${{ github.head_ref }} git checkout ${{ github.head_ref }}
git commit --message "Update snapshots" || true git commit -m "A snapshot a day keeps the bugs away! 📷🐛" || true
git push git push
git push origin ${{ github.head_ref }} git push origin ${{ github.head_ref }}
electron: electron:
needs: [prepare-wasm] needs: [conditions, prepare-wasm]
timeout-minutes: 60 timeout-minutes: 60
env: env:
OS_NAME: ${{ contains(matrix.os, 'ubuntu') && 'ubuntu' || (contains(matrix.os, 'windows') && 'windows' || 'macos') }} OS_NAME: ${{ contains(matrix.os, 'ubuntu') && 'ubuntu' || (contains(matrix.os, 'windows') && 'windows' || 'macos') }}
@ -230,24 +308,21 @@ jobs:
shardTotal: 8 shardTotal: 8
- os: namespace-profile-macos-8-cores - os: namespace-profile-macos-8-cores
shardIndex: 1 shardIndex: 1
shardTotal: 2 shardTotal: 1
- os: namespace-profile-macos-8-cores
shardIndex: 2
shardTotal: 2
- os: windows-latest-8-cores - os: windows-latest-8-cores
shardIndex: 1 shardIndex: 1
shardTotal: 2 shardTotal: 1
- os: windows-latest-8-cores
shardIndex: 2
shardTotal: 2
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
if: needs.conditions.outputs.should-run == 'true'
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
if: needs.conditions.outputs.should-run == 'true'
name: prepared-wasm name: prepared-wasm
- name: Copy prepared wasm - name: Copy prepared wasm
if: needs.conditions.outputs.should-run == 'true'
run: | run: |
ls -R prepared-wasm ls -R prepared-wasm
cp prepared-wasm/kcl_wasm_lib_bg.wasm public cp prepared-wasm/kcl_wasm_lib_bg.wasm public
@ -255,15 +330,18 @@ jobs:
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
if: needs.conditions.outputs.should-run == 'true'
with: with:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'
cache: 'npm' cache: 'npm'
- name: Install dependencies - name: Install dependencies
id: deps-install id: deps-install
if: needs.conditions.outputs.should-run == 'true'
run: npm install run: npm install
- name: Cache Playwright Browsers - name: Cache Playwright Browsers
if: needs.conditions.outputs.should-run == 'true'
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: | path: |
@ -271,20 +349,22 @@ jobs:
key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }} key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }}
- name: Install Playwright Browsers - name: Install Playwright Browsers
if: needs.conditions.outputs.should-run == 'true'
run: npm run playwright install --with-deps run: npm run playwright install --with-deps
- name: Build web - name: Build web
if: needs.conditions.outputs.should-run == 'true'
run: npm run tronb:vite:dev run: npm run tronb:vite:dev
- name: Start Vector - name: Start Vector
if: ${{ !contains(matrix.os, 'windows') }} if: ${{ needs.conditions.outputs.should-run == 'true' && !contains(matrix.os, 'windows') }}
run: .github/ci-cd-scripts/start-vector-${{ env.OS_NAME }}.sh run: .github/ci-cd-scripts/start-vector-${{ env.OS_NAME }}.sh
env: env:
GH_ACTIONS_AXIOM_TOKEN: ${{ secrets.GH_ACTIONS_AXIOM_TOKEN }} GH_ACTIONS_AXIOM_TOKEN: ${{ secrets.GH_ACTIONS_AXIOM_TOKEN }}
OS_NAME: ${{ env.OS_NAME }} OS_NAME: ${{ env.OS_NAME }}
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
if: ${{ !cancelled() && (success() || failure()) }} if: ${{ needs.conditions.outputs.should-run == 'true' && !cancelled() && (success() || failure()) }}
continue-on-error: true continue-on-error: true
with: with:
name: test-results-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }} name: test-results-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
@ -292,7 +372,7 @@ jobs:
- name: Run playwright/electron flow (with retries) - name: Run playwright/electron flow (with retries)
id: retry id: retry
if: ${{ !cancelled() && steps.deps-install.outcome == 'success' }} if: ${{ needs.conditions.outputs.should-run == 'true' && !cancelled() && steps.deps-install.outcome == 'success' }}
uses: nick-fields/retry@v3.0.2 uses: nick-fields/retry@v3.0.2
with: with:
shell: bash shell: bash
@ -309,7 +389,7 @@ jobs:
TARGET: desktop TARGET: desktop
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
if: always() if: ${{ needs.conditions.outputs.should-run == 'true' && always() }}
with: with:
name: test-results-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }} name: test-results-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
path: test-results/ path: test-results/
@ -318,7 +398,7 @@ jobs:
overwrite: true overwrite: true
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
if: always() if: ${{ needs.conditions.outputs.should-run == 'true' && always() }}
with: with:
name: playwright-report-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }} name: playwright-report-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
path: playwright-report/ path: playwright-report/

View File

@ -21,11 +21,14 @@ on:
- '**.rs' - '**.rs'
- .github/workflows/kcl-language-server.yml - .github/workflows/kcl-language-server.yml
workflow_dispatch: workflow_dispatch:
permissions: permissions:
contents: read contents: read
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true cancel-in-progress: true
env: env:
CARGO_INCREMENTAL: 0 CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10 CARGO_NET_RETRY: 10
@ -35,9 +38,10 @@ env:
MACOSX_DEPLOYMENT_TARGET: 10.15 MACOSX_DEPLOYMENT_TARGET: 10.15
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER: arm-linux-gnueabihf-gcc CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER: arm-linux-gnueabihf-gcc
jobs: jobs:
test: test:
name: kcl-language-server (vscode tests) name: vscode tests
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@ -73,20 +77,22 @@ jobs:
include: include:
- os: windows-latest - os: windows-latest
target: x86_64-pc-windows-msvc target: x86_64-pc-windows-msvc
code-target: win32-x64 code-target:
#- os: windows-latest win32-x64
#target: i686-pc-windows-msvc #- os: windows-latest
#code-target: #target: i686-pc-windows-msvc
#win32-ia32 #code-target:
#- os: windows-latest #win32-ia32
#target: aarch64-pc-windows-msvc #- os: windows-latest
#code-target: win32-arm64 #target: aarch64-pc-windows-msvc
#code-target: win32-arm64
- os: ubuntu-latest - os: ubuntu-latest
target: x86_64-unknown-linux-gnu target: x86_64-unknown-linux-gnu
code-target: linux-x64 code-target:
#- os: ubuntu-latest linux-x64
#target: aarch64-unknown-linux-musl #- os: ubuntu-latest
#code-target: linux-arm64 #target: aarch64-unknown-linux-musl
#code-target: linux-arm64
- os: ubuntu-latest - os: ubuntu-latest
target: aarch64-unknown-linux-gnu target: aarch64-unknown-linux-gnu
code-target: linux-arm64 code-target: linux-arm64
@ -99,33 +105,41 @@ jobs:
- os: macos-latest - os: macos-latest
target: aarch64-apple-darwin target: aarch64-apple-darwin
code-target: darwin-arm64 code-target: darwin-arm64
name: kcl-language-server build-release (${{ matrix.target }})
name: build-release (${{ matrix.target }})
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
container: ${{ matrix.container }} container: ${{ matrix.container }}
env: env:
RA_TARGET: ${{ matrix.target }} RA_TARGET: ${{ matrix.target }}
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: ${{ env.FETCH_DEPTH }} fetch-depth: ${{ env.FETCH_DEPTH }}
- name: Use correct Rust toolchain - name: Use correct Rust toolchain
shell: bash shell: bash
run: | run: |
rm rust/rust-toolchain.toml rm rust/rust-toolchain.toml
- name: Install rust - name: Install rust
uses: actions-rust-lang/setup-rust-toolchain@v1 uses: actions-rust-lang/setup-rust-toolchain@v1
with: with:
cache: rust cache: rust
components: rust-src components: rust-src
target: ${{ matrix.target }} target: ${{ matrix.target }}
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
- name: Update apt repositories - name: Update apt repositories
if: matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'arm-unknown-linux-gnueabihf' || matrix.os == 'ubuntu-latest' if: matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'arm-unknown-linux-gnueabihf' || matrix.os == 'ubuntu-latest'
run: sudo apt-get update run: sudo apt-get update
- if: ${{ matrix.os == 'ubuntu-latest' }} - if: ${{ matrix.os == 'ubuntu-latest' }}
name: Install deps name: Install deps
shell: bash shell: bash
@ -150,53 +164,64 @@ jobs:
zlib1g-dev zlib1g-dev
cargo install cross cargo install cross
- name: Install AArch64 target toolchain - name: Install AArch64 target toolchain
if: matrix.target == 'aarch64-unknown-linux-gnu' if: matrix.target == 'aarch64-unknown-linux-gnu'
run: sudo apt-get install gcc-aarch64-linux-gnu run: sudo apt-get install gcc-aarch64-linux-gnu
- name: Install ARM target toolchain - name: Install ARM target toolchain
if: matrix.target == 'arm-unknown-linux-gnueabihf' if: matrix.target == 'arm-unknown-linux-gnueabihf'
run: sudo apt-get install gcc-arm-linux-gnueabihf run: sudo apt-get install gcc-arm-linux-gnueabihf
- name: build - name: build
run: | run: |
cd rust cd rust
cargo kcl-language-server-release build --client-patch-version ${{ github.run_number }} cargo kcl-language-server-release build --client-patch-version ${{ github.run_number }}
- name: Install dependencies - name: Install dependencies
run: | run: |
cd rust/kcl-language-server cd rust/kcl-language-server
# npm will symlink which will cause issues w tarballing later # npm will symlink which will cause issues w tarballing later
yarn install yarn install
- name: Package Extension (release) - name: Package Extension (release)
if: startsWith(github.event.ref, 'refs/tags/') if: startsWith(github.event.ref, 'refs/tags/')
run: | run: |
cd rust/kcl-language-server 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 --yarn -o "../build/kcl-language-server-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }}
- name: Package Extension (nightly) - name: Package Extension (nightly)
if: startsWith(github.event.ref, 'refs/tags/') == false if: startsWith(github.event.ref, 'refs/tags/') == false
run: | run: |
cd rust/kcl-language-server 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 --yarn -o "../build/kcl-language-server-${{ matrix.code-target }}.vsix" --target ${{ matrix.code-target }} --pre-release
- name: remove server - name: remove server
if: matrix.target == 'x86_64-unknown-linux-gnu' if: matrix.target == 'x86_64-unknown-linux-gnu'
run: | run: |
cd rust/kcl-language-server cd rust/kcl-language-server
rm -rf server rm -rf server
- name: Package Extension (no server, release) - name: Package Extension (no server, release)
if: matrix.target == 'x86_64-unknown-linux-gnu' && startsWith(github.event.ref, 'refs/tags/') if: matrix.target == 'x86_64-unknown-linux-gnu' && startsWith(github.event.ref, 'refs/tags/')
run: | run: |
cd rust/kcl-language-server cd rust/kcl-language-server
npx vsce package --yarn -o ../build/kcl-language-server-no-server.vsix npx vsce package --yarn -o ../build/kcl-language-server-no-server.vsix
- name: Package Extension (no server, nightly) - name: Package Extension (no server, nightly)
if: matrix.target == 'x86_64-unknown-linux-gnu' && startsWith(github.event.ref, 'refs/tags/') == false if: matrix.target == 'x86_64-unknown-linux-gnu' && startsWith(github.event.ref, 'refs/tags/') == false
run: | run: |
cd rust/kcl-language-server cd rust/kcl-language-server
npx vsce package --yarn -o ../build/kcl-language-server-no-server.vsix --pre-release npx vsce package --yarn -o ../build/kcl-language-server-no-server.vsix --pre-release
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: release-${{ matrix.target }} name: release-${{ matrix.target }}
path: ./rust/build path: ./rust/build
build-release-x86_64-unknown-linux-musl: build-release-x86_64-unknown-linux-musl:
name: kcl-language-server build-release (x86_64-unknown-linux-musl) name: build-release (x86_64-unknown-linux-musl)
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
RA_TARGET: x86_64-unknown-linux-musl RA_TARGET: x86_64-unknown-linux-musl
@ -206,6 +231,7 @@ jobs:
image: alpine:latest image: alpine:latest
volumes: volumes:
- /usr/local/cargo/registry:/usr/local/cargo/registry - /usr/local/cargo/registry:/usr/local/cargo/registry
steps: steps:
- name: Install dependencies - name: Install dependencies
run: | run: |
@ -219,46 +245,55 @@ jobs:
nodejs \ nodejs \
npm \ npm \
yarn yarn
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: ${{ env.FETCH_DEPTH }} fetch-depth: ${{ env.FETCH_DEPTH }}
- name: Use correct Rust toolchain - name: Use correct Rust toolchain
shell: bash shell: bash
run: | run: |
rm rust/rust-toolchain.toml rm rust/rust-toolchain.toml
- name: Install rust - name: Install rust
uses: actions-rust-lang/setup-rust-toolchain@v1 uses: actions-rust-lang/setup-rust-toolchain@v1
with: with:
cache: rust cache: rust
components: rust-src components: rust-src
target: ${{ matrix.target }} target: ${{ matrix.target }}
- name: build - name: build
run: | run: |
cd rust cd rust
cargo kcl-language-server-release build --client-patch-version ${{ github.run_number }} cargo kcl-language-server-release build --client-patch-version ${{ github.run_number }}
- name: Install dependencies - name: Install dependencies
run: | run: |
cd rust/kcl-language-server cd rust/kcl-language-server
# npm will symlink which will cause issues w tarballing later # npm will symlink which will cause issues w tarballing later
yarn install yarn install
- name: Package Extension (release) - name: Package Extension (release)
if: startsWith(github.event.ref, 'refs/tags/') if: startsWith(github.event.ref, 'refs/tags/')
run: | run: |
cd rust/kcl-language-server cd rust/kcl-language-server
npx vsce package --yarn -o "../build/kcl-language-server-alpine-x64.vsix" --target alpine-x64 npx vsce package --yarn -o "../build/kcl-language-server-alpine-x64.vsix" --target alpine-x64
- name: Package Extension (release) - name: Package Extension (release)
if: startsWith(github.event.ref, 'refs/tags/') == false if: startsWith(github.event.ref, 'refs/tags/') == false
run: | run: |
cd rust/kcl-language-server 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 --yarn -o "../build/kcl-language-server-alpine-x64.vsix" --target alpine-x64 --pre-release
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: release-x86_64-unknown-linux-musl name: release-x86_64-unknown-linux-musl
path: ./rust/build path: ./rust/build
publish: publish:
name: kcl-language-server (publish) name: publish
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: ["build-release", "build-release-x86_64-unknown-linux-musl"] needs: ["build-release", "build-release-x86_64-unknown-linux-musl"]
if: startsWith(github.event.ref, 'refs/tags') if: startsWith(github.event.ref, 'refs/tags')
@ -266,17 +301,22 @@ jobs:
contents: write contents: write
steps: steps:
- run: echo "TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - run: echo "TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- run: 'echo "TAG: $TAG"' - run: 'echo "TAG: $TAG"'
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: ${{ env.FETCH_DEPTH }} fetch-depth: ${{ env.FETCH_DEPTH }}
- name: Install Nodejs - name: Install Nodejs
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
- run: echo "HEAD_SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV - run: echo "HEAD_SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV
- run: 'echo "HEAD_SHA: $HEAD_SHA"' - run: 'echo "HEAD_SHA: $HEAD_SHA"'
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
with: with:
name: release-aarch64-apple-darwin name: release-aarch64-apple-darwin
@ -304,29 +344,33 @@ jobs:
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
with: with:
name: release-x86_64-pc-windows-msvc name: release-x86_64-pc-windows-msvc
path: rust/build path:
#- uses: actions/download-artifact@v4 rust/build
#with: #- uses: actions/download-artifact@v4
#name: release-i686-pc-windows-msvc #with:
#path: #name: release-i686-pc-windows-msvc
#build #path:
#- uses: actions/download-artifact@v4 #build
#with: #- uses: actions/download-artifact@v4
#name: release-aarch64-pc-windows-msvc #with:
#path: rust/build #name: release-aarch64-pc-windows-msvc
#path: rust/build
- run: ls -al ./rust/build - run: ls -al ./rust/build
- name: Publish Release - name: Publish Release
uses: ./.github/actions/github-release uses: ./.github/actions/github-release
with: with:
files: "rust/build/*" files: "rust/build/*"
name: ${{ env.TAG }} name: ${{ env.TAG }}
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
- name: move files to dir for upload - name: move files to dir for upload
shell: bash shell: bash
run: | run: |
cd rust cd rust
mkdir -p releases/language-server/${{ env.TAG }} mkdir -p releases/language-server/${{ env.TAG }}
cp -r build/* releases/language-server/${{ env.TAG }} cp -r build/* releases/language-server/${{ env.TAG }}
- name: "Authenticate to Google Cloud" - name: "Authenticate to Google Cloud"
uses: "google-github-actions/auth@v2.1.8" uses: "google-github-actions/auth@v2.1.8"
with: with:
@ -341,12 +385,15 @@ jobs:
with: with:
path: rust/releases path: rust/releases
destination: dl.kittycad.io destination: dl.kittycad.io
- run: rm rust/build/kcl-language-server-no-server.vsix - run: rm rust/build/kcl-language-server-no-server.vsix
- name: Publish Extension (Code Marketplace, release) - name: Publish Extension (Code Marketplace, release)
# token from https://dev.azure.com/kcl-language-server/ # token from https://dev.azure.com/kcl-language-server/
run: | run: |
cd rust/kcl-language-server cd rust/kcl-language-server
npx vsce publish --pat ${{ secrets.VSCE_PAT }} --packagePath ../build/kcl-language-server-*.vsix npx vsce publish --pat ${{ secrets.VSCE_PAT }} --packagePath ../build/kcl-language-server-*.vsix
- name: Publish Extension (OpenVSX, release) - name: Publish Extension (OpenVSX, release)
run: | run: |
cd rust/kcl-language-server cd rust/kcl-language-server

View File

@ -4,6 +4,7 @@
# maturin generate-ci github # maturin generate-ci github
# #
name: kcl-python-bindings name: kcl-python-bindings
on: on:
push: push:
branches: branches:
@ -26,14 +27,16 @@ on:
- '**.rs' - '**.rs'
- .github/workflows/kcl-python-bindings.yml - .github/workflows/kcl-python-bindings.yml
workflow_dispatch: workflow_dispatch:
permissions: permissions:
contents: read contents: read
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
linux-x86_64: linux-x86_64:
name: kcl-python-bindings (linux-x86_64)
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -55,8 +58,8 @@ jobs:
with: with:
name: wheels-linux-x86_64 name: wheels-linux-x86_64
path: rust/kcl-python-bindings/dist path: rust/kcl-python-bindings/dist
windows: windows:
name: kcl-python-bindings (windows)
runs-on: windows-16-cores runs-on: windows-16-cores
strategy: strategy:
matrix: matrix:
@ -81,8 +84,8 @@ jobs:
with: with:
name: wheels-windows-${{ matrix.target }} name: wheels-windows-${{ matrix.target }}
path: rust/kcl-python-bindings/dist path: rust/kcl-python-bindings/dist
macos: macos:
name: kcl-python-bindings (macos)
runs-on: macos-latest runs-on: macos-latest
strategy: strategy:
matrix: matrix:
@ -107,8 +110,8 @@ jobs:
with: with:
name: wheels-macos-${{ matrix.target }} name: wheels-macos-${{ matrix.target }}
path: rust/kcl-python-bindings/dist path: rust/kcl-python-bindings/dist
test: test:
name: kcl-python-bindings (test)
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -124,8 +127,8 @@ jobs:
env: env:
KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }} KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
ZOO_HOST: https://api.dev.zoo.dev ZOO_HOST: https://api.dev.zoo.dev
sdist: sdist:
name: kcl-python-bindings (sdist)
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -133,10 +136,10 @@ jobs:
uses: astral-sh/setup-uv@v5 uses: astral-sh/setup-uv@v5
- name: Install codespell - name: Install codespell
run: | run: |
uv venv .venv uv venv .venv
echo "VIRTUAL_ENV=.venv" >> $GITHUB_ENV echo "VIRTUAL_ENV=.venv" >> $GITHUB_ENV
echo "$PWD/.venv/bin" >> $GITHUB_PATH echo "$PWD/.venv/bin" >> $GITHUB_PATH
uv pip install pip --upgrade uv pip install pip --upgrade
- name: Build sdist - name: Build sdist
uses: PyO3/maturin-action@v1 uses: PyO3/maturin-action@v1
with: with:
@ -148,6 +151,7 @@ jobs:
with: with:
name: wheels-sdist name: wheels-sdist
path: rust/kcl-python-bindings/dist path: rust/kcl-python-bindings/dist
release: release:
name: Release name: Release
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -164,11 +168,11 @@ jobs:
uses: astral-sh/setup-uv@v5 uses: astral-sh/setup-uv@v5
- name: do uv things - name: do uv things
run: | run: |
cd rust/kcl-python-bindings cd rust/kcl-python-bindings
uv venv .venv uv venv .venv
echo "VIRTUAL_ENV=.venv" >> $GITHUB_ENV echo "VIRTUAL_ENV=.venv" >> $GITHUB_ENV
echo "$PWD/.venv/bin" >> $GITHUB_PATH echo "$PWD/.venv/bin" >> $GITHUB_PATH
uv pip install pip --upgrade uv pip install pip --upgrade
- name: Publish to PyPI - name: Publish to PyPI
uses: PyO3/maturin-action@v1 uses: PyO3/maturin-action@v1
env: env:

View File

@ -28,7 +28,53 @@ jobs:
- run: npm run fmt:check - run: npm run fmt:check
npm-build-wasm: npm-build-wasm:
uses: ./.github/workflows/build-wasm.yml # Build the wasm blob once on the fastest runner.
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- name: Install dependencies
run: npm install
- 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.
- uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
with:
tool: wasm-pack
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: './rust'
- name: Build Wasm
shell: bash
run: npm run build:wasm
- uses: actions/upload-artifact@v4
with:
name: prepared-wasm
path: |
rust/kcl-wasm-lib/pkg/kcl_wasm_lib*
- uses: actions/upload-artifact@v4
with:
name: prepared-ts-rs-bindings
path: |
rust/kcl-lib/bindings/*
npm-tsc: npm-tsc:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -127,3 +173,116 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Run codespell - name: Run codespell
uses: crate-ci/typos@v1.32.0 uses: crate-ci/typos@v1.32.0
npm-unit-test-kcl-samples:
runs-on: ubuntu-latest
needs: npm-build-wasm
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- run: npm install
- uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
with:
tool: wasm-pack
- name: Download all artifacts
uses: actions/download-artifact@v4
- name: Copy prepared wasm
run: |
ls -R prepared-wasm
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
mkdir rust/kcl-wasm-lib/pkg
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
- name: Copy prepared ts-rs bindings
run: |
ls -R prepared-ts-rs-bindings
mkdir rust/kcl-lib/bindings
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
- run: npm run 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
- name: run unit tests for kcl samples
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
run: npm run test:unit:kcl-samples
env:
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
npm-unit-test:
runs-on: ubuntu-latest
needs: npm-build-wasm
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- run: npm install
- uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
with:
tool: wasm-pack
- name: Download all artifacts
uses: actions/download-artifact@v4
- name: Copy prepared wasm
run: |
ls -R prepared-wasm
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
mkdir rust/kcl-wasm-lib/pkg
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
- name: Copy prepared ts-rs bindings
run: |
ls -R prepared-ts-rs-bindings
mkdir rust/kcl-lib/bindings
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
- run: npm run 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
- name: Run unit tests
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
run: xvfb-run -a npm run test:unit
env:
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
- name: Check for changes
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
id: git-check
run: |
git add src/lang/std/artifactMapGraphs
if git status src/lang/std/artifactMapGraphs | grep -q "Changes to be committed"
then echo "modified=true" >> $GITHUB_OUTPUT
else echo "modified=false" >> $GITHUB_OUTPUT
fi
- name: Commit changes, if any
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' && steps.git-check.outputs.modified == 'true' }}
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git remote set-url origin https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
git fetch origin
echo ${{ github.head_ref }}
git checkout ${{ github.head_ref }}
# TODO when webkit works on ubuntu remove the os part of the commit message
git commit -am "Look at this (photo)Graph *in the voice of Nickelback*" || true
git push
git push origin ${{ github.head_ref }}

39
.github/workflows/tag-nightly.yml vendored Normal file
View File

@ -0,0 +1,39 @@
name: tag-nightly
permissions:
contents: write
on:
schedule:
- cron: '0 4 * * *'
# Daily at 04:00 AM UTC
# Will checkout the last commit from the default branch (main as of 2023-10-04)
jobs:
tag-nightly:
runs-on: ubuntu-22.04
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 }}
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- run: npm install
- name: Push tag
run: |
VERSION_NO_V=$(date +'%-y.%-m.%-d')
TAG="nightly-v$VERSION_NO_V"
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git tag $TAG
git push origin tag $TAG

View File

@ -1,124 +0,0 @@
name: Unit Tests
on:
pull_request:
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: write
pull-requests: write
actions: read
jobs:
npm-build-wasm:
uses: ./.github/workflows/build-wasm.yml
npm-test-unit:
runs-on: ubuntu-latest
needs: npm-build-wasm
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- run: npm install
- uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
with:
tool: wasm-pack
- name: Download all artifacts
uses: actions/download-artifact@v4
- name: Copy prepared wasm
run: |
ls -R prepared-wasm
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
mkdir rust/kcl-wasm-lib/pkg
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
- name: Copy prepared ts-rs bindings
run: |
ls -R prepared-ts-rs-bindings
mkdir rust/kcl-lib/bindings
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
- run: npm run 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
- name: Run unit tests
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
run: xvfb-run -a npm run test:unit
env:
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
- name: Check for changes
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
id: git-check
run: |
git add src/lang/std/artifactMapGraphs
if git status src/lang/std/artifactMapGraphs | grep -q "Changes to be committed"
then echo "modified=true" >> $GITHUB_OUTPUT
else echo "modified=false" >> $GITHUB_OUTPUT
fi
- name: Commit changes, if any
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' && steps.git-check.outputs.modified == 'true' }}
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git remote set-url origin https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
git fetch origin
echo ${{ github.head_ref }}
git checkout ${{ github.head_ref }}
# TODO when webkit works on ubuntu remove the os part of the commit message
git commit -am "Look at this (photo)Graph *in the voice of Nickelback*" || true
git push
git push origin ${{ github.head_ref }}
npm-test-unit-components:
runs-on: ubuntu-latest
needs: npm-build-wasm
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- run: npm install
- uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
with:
tool: wasm-pack
- name: Download all artifacts
uses: actions/download-artifact@v4
- name: Copy prepared wasm
run: |
ls -R prepared-wasm
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
mkdir rust/kcl-wasm-lib/pkg
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
- name: Copy prepared ts-rs bindings
run: |
ls -R prepared-ts-rs-bindings
mkdir rust/kcl-lib/bindings
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
- name: Run component tests
run: npm run test:unit:components

2
.gitignore vendored
View File

@ -58,8 +58,6 @@ trace.zip
/public/kcl-samples/.github /public/kcl-samples/.github
/public/kcl-samples/screenshots/main.kcl /public/kcl-samples/screenshots/main.kcl
/public/kcl-samples/step/main.kcl /public/kcl-samples/step/main.kcl
/public/kcl-samples/internal
/rust/kcl-lib/tests/kcl_samples/internal
/test-results/ /test-results/
/playwright-report/ /playwright-report/
/blob-report/ /blob-report/

View File

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

1
.husky/pre-push Executable file
View File

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

View File

@ -130,7 +130,7 @@ git tag $VERSION
git push origin --tags git push origin --tags
``` ```
This will trigger the `build-apps` workflow, set the version, build & sign the apps, and generate release files. 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)). The workflow should be listed right away [in this list](https://github.com/KittyCAD/modeling-app/actions/workflows/build-apps.yml?query=event%3Apush)).
@ -142,10 +142,13 @@ The release builds can be found under the `out-{arch}-{platform}` zip files, at
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. 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.
A prompt should show up asking for a downgrade to the last release version. Running through that at the end of testing ##### Updater-test builds
and making sure the current release candidate has the ability to be updated to what electron-updater points to is critical,
but what is actually being downloaded and installed isn't. 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.
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.
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) # Windows (PowerShell)

View File

@ -23,7 +23,6 @@ endif
install: node_modules/.package-lock.json $(CARGO) $(WASM_PACK) ## Install dependencies install: node_modules/.package-lock.json $(CARGO) $(WASM_PACK) ## Install dependencies
node_modules/.package-lock.json: package.json package-lock.json node_modules/.package-lock.json: package.json package-lock.json
npm prune
npm install npm install
$(CARGO): $(CARGO):
@ -44,15 +43,15 @@ endif
# BUILD # BUILD
CARGO_SOURCES := rust/.cargo/config.toml $(wildcard rust/Cargo.*) $(wildcard rust/**/Cargo.*) CARGO_SOURCES := rust/.cargo/config.toml $(wildcard rust/Cargo.*) $(wildcard rust/**/Cargo.*)
KCL_SOURCES := $(wildcard public/kcl-samples/**/*.kcl)
RUST_SOURCES := $(wildcard rust/**/*.rs) RUST_SOURCES := $(wildcard rust/**/*.rs)
REACT_SOURCES := $(wildcard src/*.tsx) $(wildcard src/**/*.tsx) REACT_SOURCES := $(wildcard src/*.tsx) $(wildcard src/**/*.tsx)
TYPESCRIPT_SOURCES := tsconfig.* $(wildcard src/*.ts) $(wildcard src/**/*.ts) TYPESCRIPT_SOURCES := tsconfig.* $(wildcard src/*.ts) $(wildcard src/**/*.ts)
VITE_SOURCES := $(wildcard vite.*) $(wildcard vite/**/*.tsx) VITE_SOURCES := $(wildcard vite.*) $(wildcard vite/**/*.tsx)
.PHONY: build .PHONY: build
build: install public/kcl_wasm_lib_bg.wasm public/kcl-samples/manifest.json .vite/build/main.js build: install public/kcl_wasm_lib_bg.wasm .vite/build/main.js
public/kcl_wasm_lib_bg.wasm: $(CARGO_SOURCES) $(RUST_SOURCES) public/kcl_wasm_lib_bg.wasm: $(CARGO_SOURCES) $(RUST_SOURCES)
ifdef WINDOWS ifdef WINDOWS
@ -61,9 +60,6 @@ else
npm run build:wasm:dev npm run build:wasm:dev
endif endif
public/kcl-samples/manifest.json: $(KCL_SOURCES)
cd rust/kcl-lib && EXPECTORATE=overwrite cargo test generate_manifest
.vite/build/main.js: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES) .vite/build/main.js: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
npm run tronb:vite:dev npm run tronb:vite:dev
@ -111,7 +107,6 @@ test: test-unit test-e2e
.PHONY: test-unit .PHONY: test-unit
test-unit: install ## Run the unit tests test-unit: install ## Run the unit tests
npm run test:rust npm run test:rust
npm run test:unit:components
@ curl -fs localhost:3000 >/dev/null || ( echo "Error: localhost:3000 not available, 'make run-web' first" && exit 1 ) @ curl -fs localhost:3000 >/dev/null || ( echo "Error: localhost:3000 not available, 'make run-web' first" && exit 1 )
npm run test:unit npm run test:unit

View File

@ -34,7 +34,7 @@ The 3D view in Design Studio is just a video stream from our hosted geometry eng
- WebSockets (via [KittyCAD TS client](https://github.com/KittyCAD/kittycad.ts)) - WebSockets (via [KittyCAD TS client](https://github.com/KittyCAD/kittycad.ts))
- Code Editor - Code Editor
- [CodeMirror](https://codemirror.net/) - [CodeMirror](https://codemirror.net/)
- [Custom WASM LSP Server](https://github.com/KittyCAD/modeling-app/tree/main/rust/kcl-lib/src/lsp/kcl) - Custom WASM LSP Server
- Modeling - Modeling
- [KittyCAD TypeScript client](https://github.com/KittyCAD/kittycad.ts) - [KittyCAD TypeScript client](https://github.com/KittyCAD/kittycad.ts)

View File

@ -21,7 +21,7 @@ extend-exclude = [
] ]
[default.extend-words] [default.extend-words]
metalness = "metalness" # appearance API metalness = "metalness" # appearance API
Hom = "Hom" # short for homogenous Hom = "Hom" # short for homogenous
typ = "typ" # used to declare a variable named 'type' which is a reserved keyword in Rust typ = "typ" # used to declare a variable named 'type' which is a reserved keyword in Rust
ue = "ue" # short for UnaryExpression ue = "ue" # short for UnaryExpression
@ -29,7 +29,6 @@ THRE = "THRE" # Weird bug that wrongly detects THREEjs as a typo
nwo = "nwo" # don't know what this is about tbh nwo = "nwo" # don't know what this is about tbh
"ot" = "ot" # some abbreviation, idk what "ot" = "ot" # some abbreviation, idk what
"oe" = "oe" # some abbreviation, idk what "oe" = "oe" # some abbreviation, idk what
"colinear" = "colinear" # some engine shit, kidding
[default] [default]
extend-ignore-identifiers-re = [ extend-ignore-identifiers-re = [

View File

@ -1,32 +0,0 @@
---
title: "Arrays and ranges"
excerpt: "Documentation of the KCL language for the Zoo Design Studio."
layout: manual
---
Arrays are sequences of values.
Arrays can be written out as *array literals* using a sequence of expressions surrounded by square brackets, e.g., `['hello', 'world']` is an array of strings, `[x, x + 1, x + 2]` is an array of numbers (assuming `x` is a number), `[]` is an empty array, and `['hello', 42, true]` is a mixed array.
A value in an array can be accessed by indexing using square brackets where the index is a number, for example, `arr[0]`, `arr[42]`, `arr[i]` (where `arr` is an array and `i` is a (whole) number).
There are some useful functions for working with arrays in the standard library, see [std::array](/docs/kcl-std/modules/std-array) for details.
## Array types
Arrays have their own types: `[T]` where `T` is the type of the elements of the array, for example, `[string]` means an array of `string`s and `[any]` means an array of any values.
Array types can also include length information: `[T; n]` denotes an array of length `n` (where `n` is a number literal) and `[T; 1+]` denotes an array whose length is at least one (i.e., a non-empty array). E.g., `[string; 1+]` and `[number(mm); 3]` are valid array types.
## Ranges
Ranges are a succinct way to create an array of sequential numbers. The syntax is `[start .. end]` where `start` and `end` evaluate to whole numbers (integers). Ranges are inclusive of the start and end. The end must be greater than the start. Examples:
```kcl,norun
[0..3] // [0, 1, 2, 3]
[3..10] // [3, 4, 5, 6, 7, 8, 9, 10]
x = 2
[x..x+1] // [2, 3]
```
The units of the start and end numbers must be the same and the result inherits those units.

View File

@ -1,99 +0,0 @@
---
title: "Importing geometry from other CAD systems"
excerpt: "Documentation of the KCL language for the Zoo Design Studio."
layout: manual
---
`import` can also be used to import files from other CAD systems. The format of the statement is the
same as for KCL files. You can only import the whole file, not items from it. E.g.,
```norun
import "tests/inputs/cube.obj"
// Use `cube` just like a KCL object.
```
```kcl
import "tests/inputs/cube.sldprt" as cube
// Use `cube` just like a KCL object.
```
For formats lacking unit data (such as STL, OBJ, or PLY files), the default
unit of measurement is millimeters. Alternatively you may specify the unit
by using an attribute. Likewise, you can also specify a coordinate system. E.g.,
```kcl
@(lengthUnit = ft, coords = opengl)
import "tests/inputs/cube.obj"
```
When importing a GLTF file, the bin file will be imported as well.
Import paths are relative to the current project directory. Imports currently only work when
using the native Design Studio, not in the browser.
### Supported values
File formats: `fbx`, `gltf`/`glb`, `obj`+, `ply`+, `sldprt`, `step`/`stp`, `stl`+. (Those marked with a
'+' support customising the length unit and coordinate system).
Length units: `mm` (the default), `cm`, `m`, `inch`, `ft`, `yd`.
Coordinate systems:
- `zoo` (the default), forward: -Y, up: +Z, handedness: right
- `opengl`, forward: +Z, up: +Y, handedness: right
- `vulkan`, forward: +Z, up: -Y, handedness: left
---
## Performance deepdive for foreignfile imports
Parallelized foreignfile imports now let you overlap file reads, initialization,
and rendering. To maximize throughput, you need to understand the three distinct
stages—reading, initializing (background render start), and invocation (blocking)
—and structure your code to defer blocking operations until the end.
### Foreign import execution stages
1. **Import (Read / Initialization) Stage**
```kcl
import "tests/inputs/cube.step" as cube
```
- Reads the file from disk and makes its API available.
- Starts engine rendering but **does not block** your script.
- This kickstarts the render pipeline while you keep executing other code.
2. **Invocation (Blocking) Stage**
```kcl
import "tests/inputs/cube.step" as cube
cube
|> translate(z=10) // ← blocks here only
```
- Any method call (e.g., `translate`, `scale`, `rotate`) waits for the background render to finish before applying transformations.
### Best practices
#### 1. Defer blocking calls
```kcl
import "tests/inputs/cube.step" as cube // 1) Read / Background render starts
// --- perform other operations and calculations here ---
cube
|> translate(z=10) // 2) Blocks only here
```
#### 2. Split heavy work into separate modules
Place computationally expensive or IOheavy work into its own module so it can render in parallel while `main.kcl` continues.
#### Future improvements
Upcoming releases will autoanalyse dependencies and only block when truly necessary. Until then, explicit deferral will give you the best performance.

View File

@ -5,7 +5,7 @@ layout: manual
--- ---
This is a reference for KCL. If you are learning KCL, you may prefer the [guide]() which explains This is a reference for KCL. If you are learning KCL, you may prefer the [guide]() which explains
things in a more tutorial fashion. See also our documentation of the [standard library](/docs/kcl-std). things in a more tutorial fashion.
## Topics ## Topics
@ -14,9 +14,7 @@ things in a more tutorial fashion. See also our documentation of the [standard l
* [Values and types](/docs/kcl-lang/types) * [Values and types](/docs/kcl-lang/types)
* [Numeric types and units](/docs/kcl-lang/numeric) * [Numeric types and units](/docs/kcl-lang/numeric)
* [Functions](/docs/kcl-lang/functions) * [Functions](/docs/kcl-lang/functions)
* [Arrays and ranges](/docs/kcl-lang/arrays) * [Projects, modules, and imports](/docs/kcl-lang/modules)
* [Projects and modules](/docs/kcl-lang/modules)
* [Attributes](/docs/kcl-lang/attributes) * [Attributes](/docs/kcl-lang/attributes)
* [Importing geometry from other CAD systems](/docs/kcl-lang/foreign-imports)
* [Settings](/docs/kcl-lang/settings) * [Settings](/docs/kcl-lang/settings)
* [Known Issues](/docs/kcl-lang/known-issues) * [Known Issues](/docs/kcl-lang/known-issues)

View File

@ -1,5 +1,5 @@
--- ---
title: "Projects and modules" title: "Modules"
excerpt: "Documentation of the KCL language for the Zoo Design Studio." excerpt: "Documentation of the KCL language for the Zoo Design Studio."
layout: manual layout: manual
--- ---
@ -264,3 +264,102 @@ cube
clone(cube) clone(cube)
|> translate(x=20) |> translate(x=20)
``` ```
---
## Importing files from other CAD systems
`import` can also be used to import files from other CAD systems. The format of the statement is the
same as for KCL files. You can only import the whole file, not items from it. E.g.,
```norun
import "tests/inputs/cube.obj"
// Use `cube` just like a KCL object.
```
```kcl
import "tests/inputs/cube.sldprt" as cube
// Use `cube` just like a KCL object.
```
For formats lacking unit data (such as STL, OBJ, or PLY files), the default
unit of measurement is millimeters. Alternatively you may specify the unit
by using an attribute. Likewise, you can also specify a coordinate system. E.g.,
```kcl
@(lengthUnit = ft, coords = opengl)
import "tests/inputs/cube.obj"
```
When importing a GLTF file, the bin file will be imported as well.
Import paths are relative to the current project directory. Imports currently only work when
using the native Design Studio, not in the browser.
### Supported values
File formats: `fbx`, `gltf`/`glb`, `obj`+, `ply`+, `sldprt`, `step`/`stp`, `stl`+. (Those marked with a
'+' support customising the length unit and coordinate system).
Length units: `mm` (the default), `cm`, `m`, `inch`, `ft`, `yd`.
Coordinate systems:
- `zoo` (the default), forward: -Y, up: +Z, handedness: right
- `opengl`, forward: +Z, up: +Y, handedness: right
- `vulkan`, forward: +Z, up: -Y, handedness: left
---
## Performance deepdive for foreignfile imports
Parallelized foreignfile imports now let you overlap file reads, initialization,
and rendering. To maximize throughput, you need to understand the three distinct
stages—reading, initializing (background render start), and invocation (blocking)
—and structure your code to defer blocking operations until the end.
### Foreign import execution stages
1. **Import (Read / Initialization) Stage**
```kcl
import "tests/inputs/cube.step" as cube
```
- Reads the file from disk and makes its API available.
- Starts engine rendering but **does not block** your script.
- This kickstarts the render pipeline while you keep executing other code.
2. **Invocation (Blocking) Stage**
```kcl
import "tests/inputs/cube.step" as cube
cube
|> translate(z=10) // ← blocks here only
```
- Any method call (e.g., `translate`, `scale`, `rotate`) waits for the background render to finish before applying transformations.
### Best practices
#### 1. Defer blocking calls
```kcl
import "tests/inputs/cube.step" as cube // 1) Read / Background render starts
// --- perform other operations and calculations here ---
cube
|> translate(z=10) // 2) Blocks only here
```
#### 2. Split heavy work into separate modules
Place computationally expensive or IOheavy work into its own module so it can render in parallel while `main.kcl` continues.
#### Future improvements
Upcoming releases will autoanalyse dependencies and only block when truly necessary. Until then, explicit deferral will give you the best performance.

View File

@ -28,8 +28,6 @@ Any of the suffixes described above can be used meaning that values with that ty
You can also use `number(Length)`, `number(Angle)`, or `number(Count)`. These types mean a number with any length, angle, or unitless (count) units, respectively (note that `number(_)` and `number(Count)` are equivalent since there is only one kind of unitless-ness). You can also use `number(Length)`, `number(Angle)`, or `number(Count)`. These types mean a number with any length, angle, or unitless (count) units, respectively (note that `number(_)` and `number(Count)` are equivalent since there is only one kind of unitless-ness).
Using just `number` means accepting any kind of number, even where the units are unknown by KCL.
## Function calls ## Function calls

View File

@ -19,6 +19,18 @@ myBool = false
Currently you cannot redeclare a constant. Currently you cannot redeclare a constant.
## Arrays
An array is defined with `[]` braces. What is inside the brackets can
be of any type. For example, the following is completely valid:
```
myArray = ["thing", 2, false]
```
If you want to get a value from an array you can use the index like so:
`myArray[0]`.
## Objects ## Objects
@ -28,8 +40,8 @@ An object is defined with `{}` braces. Here is an example object:
myObj = { a = 0, b = "thing" } myObj = { a = 0, b = "thing" }
``` ```
To get the property of an object, you can call `myObj.a`, which in the above We support two different ways of getting properties from objects, you can call
example returns 0. `myObj.a` or `myObj["a"]` both work.
## `ImportedGeometry` ## `ImportedGeometry`

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -11,13 +11,7 @@ The value of `pi`, Archimedes constant (π).
PI: number(_?) = 3.14159265358979323846264338327950288_? PI: number(_?) = 3.14159265358979323846264338327950288_?
``` ```
`PI` is a number and is technically a ratio, so you might expect it to have type `number(_)`.
However, `PI` is nearly always used for converting between different units - usually degrees to or
from radians. Therefore, `PI` is treated a bit specially by KCL and always has unknown units. This
means that if you use `PI`, you will need to give KCL some extra information about the units of numbers.
Usually you should use type ascription on the result of calculations, e.g., `(2 * PI): number(rad)`.
You might prefer to use `units::toRadians` or `units::toDegrees` to convert between angles with
different units.
### Examples ### Examples

View File

@ -1,16 +0,0 @@
---
title: "sweep::SKETCH_PLANE"
subtitle: "Constant in std::sweep"
excerpt: "Local/relative to a position centered within the plane being sketched on"
layout: manual
---
Local/relative to a position centered within the plane being sketched on
```kcl
sweep::SKETCH_PLANE: string = 'sketchPlane'
```

View File

@ -1,16 +0,0 @@
---
title: "sweep::TRAJECTORY"
subtitle: "Constant in std::sweep"
excerpt: "Local/relative to the trajectory curve"
layout: manual
---
Local/relative to the trajectory curve
```kcl
sweep::TRAJECTORY: string = 'trajectoryCurve'
```

View File

@ -26,7 +26,7 @@ You can provide more than one sketch to extrude, and they will all be extruded i
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `sketches` | [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) | Which sketch or sketches should be extruded | Yes | | `sketches` | [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) | Which sketch or sketches should be extruded | Yes |
| `length` | [`number`](/docs/kcl-std/types/std-types-number) | How far to extrude the given sketches | Yes | | `length` | [`number`](/docs/kcl-std/types/std-types-number) | How far to extrude the given sketches | Yes |
| `symmetric` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the extrusion will happen symmetrically around the sketch. Otherwise, the extrusion will happen on only one side of the sketch. | No | | `symmetric` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the extrusion will happen symmetrically around the sketch. Otherwise, the | No |
| `bidirectionalLength` | [`number`](/docs/kcl-std/types/std-types-number) | If specified, will also extrude in the opposite direction to 'distance' to the specified distance. If 'symmetric' is true, this value is ignored. | No | | `bidirectionalLength` | [`number`](/docs/kcl-std/types/std-types-number) | If specified, will also extrude in the opposite direction to 'distance' to the specified distance. If 'symmetric' is true, this value is ignored. | No |
| `tagStart` | [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator) | A named tag for the face at the start of the extrusion, i.e. the original sketch | No | | `tagStart` | [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator) | A named tag for the face at the start of the extrusion, i.e. the original sketch | No |
| `tagEnd` | [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator) | A named tag for the face at the end of the extrusion, i.e. the new face created by extruding the original sketch | No | | `tagEnd` | [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator) | A named tag for the face at the end of the extrusion, i.e. the new face created by extruding the original sketch | No |

File diff suppressed because one or more lines are too long

View File

@ -1,38 +0,0 @@
---
title: "legAngX"
subtitle: "Function in std::math"
excerpt: "Compute the angle of the given leg for x."
layout: manual
---
Compute the angle of the given leg for x.
```kcl
legAngX(
hypotenuse: number(Length),
leg: number(Length),
): number(deg)
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `hypotenuse` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The length of the triangle's hypotenuse. | Yes |
| `leg` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The length of one of the triangle's legs (i.e. non-hypotenuse side). | Yes |
### Returns
[`number(deg)`](/docs/kcl-std/types/std-types-number) - A number.
### Examples
```kcl
legAngX(hypotenuse = 5, leg = 3)
```

View File

@ -1,38 +0,0 @@
---
title: "legAngY"
subtitle: "Function in std::math"
excerpt: "Compute the angle of the given leg for y."
layout: manual
---
Compute the angle of the given leg for y.
```kcl
legAngY(
hypotenuse: number(Length),
leg: number(Length),
): number(deg)
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `hypotenuse` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The length of the triangle's hypotenuse. | Yes |
| `leg` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The length of one of the triangle's legs (i.e. non-hypotenuse side). | Yes |
### Returns
[`number(deg)`](/docs/kcl-std/types/std-types-number) - A number.
### Examples
```kcl
legAngY(hypotenuse = 5, leg = 3)
```

View File

@ -1,38 +0,0 @@
---
title: "legLen"
subtitle: "Function in std::math"
excerpt: "Compute the length of the given leg."
layout: manual
---
Compute the length of the given leg.
```kcl
legLen(
hypotenuse: number(Length),
leg: number(Length),
): number(deg)
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `hypotenuse` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The length of the triangle's hypotenuse. | Yes |
| `leg` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The length of one of the triangle's legs (i.e. non-hypotenuse side). | Yes |
### Returns
[`number(deg)`](/docs/kcl-std/types/std-types-number) - A number.
### Examples
```kcl
legLen(hypotenuse = 5, leg = 3)
```

View File

@ -8,7 +8,7 @@ layout: manual
Convert a number to centimeters from its current units. Convert a number to centimeters from its current units.
```kcl ```kcl
units::toCentimeters(@num: number(Length)): number(cm) units::toCentimeters(@num: number(cm)): number(cm)
``` ```
@ -17,7 +17,7 @@ units::toCentimeters(@num: number(Length)): number(cm)
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes | | `num` | [`number(cm)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
### Returns ### Returns

View File

@ -8,7 +8,7 @@ layout: manual
Converts a number to degrees from its current units. Converts a number to degrees from its current units.
```kcl ```kcl
units::toDegrees(@num: number(Angle)): number(deg) units::toDegrees(@num: number(deg)): number(deg)
``` ```
@ -17,7 +17,7 @@ units::toDegrees(@num: number(Angle)): number(deg)
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `num` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | A number. | Yes | | `num` | [`number(deg)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
### Returns ### Returns

View File

@ -8,7 +8,7 @@ layout: manual
Convert a number to feet from its current units. Convert a number to feet from its current units.
```kcl ```kcl
units::toFeet(@num: number(Length)): number(ft) units::toFeet(@num: number(ft)): number(ft)
``` ```
@ -17,7 +17,7 @@ units::toFeet(@num: number(Length)): number(ft)
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes | | `num` | [`number(ft)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
### Returns ### Returns

View File

@ -8,7 +8,7 @@ layout: manual
Convert a number to inches from its current units. Convert a number to inches from its current units.
```kcl ```kcl
units::toInches(@num: number(Length)): number(in) units::toInches(@num: number(in)): number(in)
``` ```
@ -17,7 +17,7 @@ units::toInches(@num: number(Length)): number(in)
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes | | `num` | [`number(in)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
### Returns ### Returns

View File

@ -8,7 +8,7 @@ layout: manual
Convert a number to meters from its current units. Convert a number to meters from its current units.
```kcl ```kcl
units::toMeters(@num: number(Length)): number(m) units::toMeters(@num: number(m)): number(m)
``` ```
@ -17,7 +17,7 @@ units::toMeters(@num: number(Length)): number(m)
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes | | `num` | [`number(m)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
### Returns ### Returns

View File

@ -8,7 +8,7 @@ layout: manual
Convert a number to millimeters from its current units. Convert a number to millimeters from its current units.
```kcl ```kcl
units::toMillimeters(@num: number(Length)): number(mm) units::toMillimeters(@num: number(mm)): number(mm)
``` ```
@ -17,7 +17,7 @@ units::toMillimeters(@num: number(Length)): number(mm)
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes | | `num` | [`number(mm)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
### Returns ### Returns

View File

@ -8,7 +8,7 @@ layout: manual
Converts a number to radians from its current units. Converts a number to radians from its current units.
```kcl ```kcl
units::toRadians(@num: number(Angle)): number(rad) units::toRadians(@num: number(rad)): number(rad)
``` ```
@ -17,7 +17,7 @@ units::toRadians(@num: number(Angle)): number(rad)
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `num` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | A number. | Yes | | `num` | [`number(rad)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
### Returns ### Returns

View File

@ -8,7 +8,7 @@ layout: manual
Converts a number to yards from its current units. Converts a number to yards from its current units.
```kcl ```kcl
units::toYards(@num: number(Length)): number(yd) units::toYards(@num: number(yd)): number(yd)
``` ```
@ -17,7 +17,7 @@ units::toYards(@num: number(Length)): number(yd)
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes | | `num` | [`number(yd)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
### Returns ### Returns

View File

@ -12,15 +12,15 @@ layout: manual
* [`appearance`](/docs/kcl-std/appearance) * [`appearance`](/docs/kcl-std/appearance)
* [`assert`](/docs/kcl-std/assert) * [`assert`](/docs/kcl-std/assert)
* [`assertIs`](/docs/kcl-std/assertIs) * [`assertIs`](/docs/kcl-std/assertIs)
* [`clone`](/docs/kcl-std/functions/std-clone) * [`clone`](/docs/kcl-std/clone)
* [`helix`](/docs/kcl-std/functions/std-helix) * [`helix`](/docs/kcl-std/functions/std-helix)
* [`offsetPlane`](/docs/kcl-std/functions/std-offsetPlane) * [`offsetPlane`](/docs/kcl-std/functions/std-offsetPlane)
* [`patternLinear2d`](/docs/kcl-std/patternLinear2d) * [`patternLinear2d`](/docs/kcl-std/patternLinear2d)
* [**std::array**](/docs/kcl-std/modules/std-array) * [**std::array**](/docs/kcl-std/modules/std-array)
* [`map`](/docs/kcl-std/functions/std-array-map) * [`map`](/docs/kcl-std/map)
* [`pop`](/docs/kcl-std/functions/std-array-pop) * [`pop`](/docs/kcl-std/pop)
* [`push`](/docs/kcl-std/functions/std-array-push) * [`push`](/docs/kcl-std/push)
* [`reduce`](/docs/kcl-std/functions/std-array-reduce) * [`reduce`](/docs/kcl-std/reduce)
* [**std::math**](/docs/kcl-std/modules/std-math) * [**std::math**](/docs/kcl-std/modules/std-math)
* [`abs`](/docs/kcl-std/functions/std-math-abs) * [`abs`](/docs/kcl-std/functions/std-math-abs)
* [`acos`](/docs/kcl-std/functions/std-math-acos) * [`acos`](/docs/kcl-std/functions/std-math-acos)
@ -30,9 +30,9 @@ layout: manual
* [`ceil`](/docs/kcl-std/functions/std-math-ceil) * [`ceil`](/docs/kcl-std/functions/std-math-ceil)
* [`cos`](/docs/kcl-std/functions/std-math-cos) * [`cos`](/docs/kcl-std/functions/std-math-cos)
* [`floor`](/docs/kcl-std/functions/std-math-floor) * [`floor`](/docs/kcl-std/functions/std-math-floor)
* [`legAngX`](/docs/kcl-std/functions/std-math-legAngX) * [`legAngX`](/docs/kcl-std/legAngX)
* [`legAngY`](/docs/kcl-std/functions/std-math-legAngY) * [`legAngY`](/docs/kcl-std/legAngY)
* [`legLen`](/docs/kcl-std/functions/std-math-legLen) * [`legLen`](/docs/kcl-std/legLen)
* [`ln`](/docs/kcl-std/functions/std-math-ln) * [`ln`](/docs/kcl-std/functions/std-math-ln)
* [`log`](/docs/kcl-std/functions/std-math-log) * [`log`](/docs/kcl-std/functions/std-math-log)
* [`log10`](/docs/kcl-std/functions/std-math-log10) * [`log10`](/docs/kcl-std/functions/std-math-log10)
@ -128,9 +128,6 @@ layout: manual
* [`E`](/docs/kcl-std/consts/std-math-E) * [`E`](/docs/kcl-std/consts/std-math-E)
* [`PI`](/docs/kcl-std/consts/std-math-PI) * [`PI`](/docs/kcl-std/consts/std-math-PI)
* [`TAU`](/docs/kcl-std/consts/std-math-TAU) * [`TAU`](/docs/kcl-std/consts/std-math-TAU)
* [**std::sweep**](/docs/kcl-std/modules/std-sweep)
* [`sweep::SKETCH_PLANE`](/docs/kcl-std/consts/std-sweep-SKETCH_PLANE)
* [`sweep::TRAJECTORY`](/docs/kcl-std/consts/std-sweep-TRAJECTORY)
* [**std::turns**](/docs/kcl-std/modules/std-turns) * [**std::turns**](/docs/kcl-std/modules/std-turns)
* [`turns::HALF_TURN`](/docs/kcl-std/consts/std-turns-HALF_TURN) * [`turns::HALF_TURN`](/docs/kcl-std/consts/std-turns-HALF_TURN)
* [`turns::QUARTER_TURN`](/docs/kcl-std/consts/std-turns-QUARTER_TURN) * [`turns::QUARTER_TURN`](/docs/kcl-std/consts/std-turns-QUARTER_TURN)
@ -143,13 +140,12 @@ See also the [types overview](/docs/kcl-lang/types)
* [**Primitive types**](/docs/kcl-lang/types) * [**Primitive types**](/docs/kcl-lang/types)
* [`End`](/docs/kcl-lang/types#End) * [`End`](/docs/kcl-lang/types#End)
* [`ImportedGeometry`](/docs/kcl-std/types/std-types-ImportedGeometry) * [`ImportedGeometry`](/docs/kcl-lang/types#ImportedGeometry)
* [`Start`](/docs/kcl-lang/types#Start) * [`Start`](/docs/kcl-lang/types#Start)
* [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator) * [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator)
* [`TagIdentifier`](/docs/kcl-lang/types#TagIdentifier) * [`TagIdentifier`](/docs/kcl-lang/types#TagIdentifier)
* [`any`](/docs/kcl-std/types/std-types-any) * [`any`](/docs/kcl-std/types/std-types-any)
* [`bool`](/docs/kcl-std/types/std-types-bool) * [`bool`](/docs/kcl-std/types/std-types-bool)
* [`fn`](/docs/kcl-std/types/std-types-fn)
* [`number`](/docs/kcl-std/types/std-types-number) * [`number`](/docs/kcl-std/types/std-types-number)
* [`string`](/docs/kcl-std/types/std-types-string) * [`string`](/docs/kcl-std/types/std-types-string)
* [`tag`](/docs/kcl-std/types/std-types-tag) * [`tag`](/docs/kcl-std/types/std-types-tag)

38
docs/kcl-std/legAngX.md Normal file
View File

@ -0,0 +1,38 @@
---
title: "legAngX"
subtitle: "Function in std::math"
excerpt: "Compute the angle of the given leg for x."
layout: manual
---
Compute the angle of the given leg for x.
```kcl
legAngX(
hypotenuse: number,
leg: number,
): number
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `hypotenuse` | [`number`](/docs/kcl-std/types/std-types-number) | The length of the triangle's hypotenuse | Yes |
| `leg` | [`number`](/docs/kcl-std/types/std-types-number) | The length of one of the triangle's legs (i.e. non-hypotenuse side) | Yes |
### Returns
[`number`](/docs/kcl-std/types/std-types-number) - A number.
### Examples
```kcl
legAngX(hypotenuse = 5, leg = 3)
```

38
docs/kcl-std/legAngY.md Normal file
View File

@ -0,0 +1,38 @@
---
title: "legAngY"
subtitle: "Function in std::math"
excerpt: "Compute the angle of the given leg for y."
layout: manual
---
Compute the angle of the given leg for y.
```kcl
legAngY(
hypotenuse: number,
leg: number,
): number
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `hypotenuse` | [`number`](/docs/kcl-std/types/std-types-number) | The length of the triangle's hypotenuse | Yes |
| `leg` | [`number`](/docs/kcl-std/types/std-types-number) | The length of one of the triangle's legs (i.e. non-hypotenuse side) | Yes |
### Returns
[`number`](/docs/kcl-std/types/std-types-number) - A number.
### Examples
```kcl
legAngY(hypotenuse = 5, leg = 3)
```

38
docs/kcl-std/legLen.md Normal file
View File

@ -0,0 +1,38 @@
---
title: "legLen"
subtitle: "Function in std::math"
excerpt: "Compute the length of the given leg."
layout: manual
---
Compute the length of the given leg.
```kcl
legLen(
hypotenuse: number,
leg: number,
): number
```
### Arguments
| Name | Type | Description | Required |
|----------|------|-------------|----------|
| `hypotenuse` | [`number`](/docs/kcl-std/types/std-types-number) | The length of the triangle's hypotenuse | Yes |
| `leg` | [`number`](/docs/kcl-std/types/std-types-number) | The length of one of the triangle's legs (i.e. non-hypotenuse side) | Yes |
### Returns
[`number`](/docs/kcl-std/types/std-types-number) - A number.
### Examples
```kcl
legLen(hypotenuse = 5, leg = 3)
```

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -12,8 +12,8 @@ Functions for manipulating arrays of values.
## Functions and constants ## Functions and constants
* [`map`](/docs/kcl-std/functions/std-array-map) * [`map`](/docs/kcl-std/map)
* [`pop`](/docs/kcl-std/functions/std-array-pop) * [`pop`](/docs/kcl-std/pop)
* [`push`](/docs/kcl-std/functions/std-array-push) * [`push`](/docs/kcl-std/push)
* [`reduce`](/docs/kcl-std/functions/std-array-reduce) * [`reduce`](/docs/kcl-std/reduce)

View File

@ -23,9 +23,9 @@ Functions for mathematical operations and some useful constants.
* [`ceil`](/docs/kcl-std/functions/std-math-ceil) * [`ceil`](/docs/kcl-std/functions/std-math-ceil)
* [`cos`](/docs/kcl-std/functions/std-math-cos) * [`cos`](/docs/kcl-std/functions/std-math-cos)
* [`floor`](/docs/kcl-std/functions/std-math-floor) * [`floor`](/docs/kcl-std/functions/std-math-floor)
* [`legAngX`](/docs/kcl-std/functions/std-math-legAngX) * [`legAngX`](/docs/kcl-std/legAngX)
* [`legAngY`](/docs/kcl-std/functions/std-math-legAngY) * [`legAngY`](/docs/kcl-std/legAngY)
* [`legLen`](/docs/kcl-std/functions/std-math-legLen) * [`legLen`](/docs/kcl-std/legLen)
* [`ln`](/docs/kcl-std/functions/std-math-ln) * [`ln`](/docs/kcl-std/functions/std-math-ln)
* [`log`](/docs/kcl-std/functions/std-math-log) * [`log`](/docs/kcl-std/functions/std-math-log)
* [`log10`](/docs/kcl-std/functions/std-math-log10) * [`log10`](/docs/kcl-std/functions/std-math-log10)

View File

@ -1,17 +0,0 @@
---
title: "sweep"
subtitle: "Module in std"
excerpt: ""
layout: manual
---
## Functions and constants
* [`sweep::SKETCH_PLANE`](/docs/kcl-std/consts/std-sweep-SKETCH_PLANE)
* [`sweep::TRAJECTORY`](/docs/kcl-std/consts/std-sweep-TRAJECTORY)

View File

@ -18,7 +18,6 @@ Types can (optionally) be used to describe a function's arguments and returned v
* [`Edge`](/docs/kcl-std/types/std-types-Edge) * [`Edge`](/docs/kcl-std/types/std-types-Edge)
* [`Face`](/docs/kcl-std/types/std-types-Face) * [`Face`](/docs/kcl-std/types/std-types-Face)
* [`Helix`](/docs/kcl-std/types/std-types-Helix) * [`Helix`](/docs/kcl-std/types/std-types-Helix)
* [`ImportedGeometry`](/docs/kcl-std/types/std-types-ImportedGeometry)
* [`Plane`](/docs/kcl-std/types/std-types-Plane) * [`Plane`](/docs/kcl-std/types/std-types-Plane)
* [`Point2d`](/docs/kcl-std/types/std-types-Point2d) * [`Point2d`](/docs/kcl-std/types/std-types-Point2d)
* [`Point3d`](/docs/kcl-std/types/std-types-Point3d) * [`Point3d`](/docs/kcl-std/types/std-types-Point3d)
@ -26,7 +25,6 @@ Types can (optionally) be used to describe a function's arguments and returned v
* [`Solid`](/docs/kcl-std/types/std-types-Solid) * [`Solid`](/docs/kcl-std/types/std-types-Solid)
* [`any`](/docs/kcl-std/types/std-types-any) * [`any`](/docs/kcl-std/types/std-types-any)
* [`bool`](/docs/kcl-std/types/std-types-bool) * [`bool`](/docs/kcl-std/types/std-types-bool)
* [`fn`](/docs/kcl-std/types/std-types-fn)
* [`number`](/docs/kcl-std/types/std-types-number) * [`number`](/docs/kcl-std/types/std-types-number)
* [`string`](/docs/kcl-std/types/std-types-string) * [`string`](/docs/kcl-std/types/std-types-string)
* [`tag`](/docs/kcl-std/types/std-types-tag) * [`tag`](/docs/kcl-std/types/std-types-tag)

View File

@ -11,15 +11,12 @@ Contains frequently used constants, functions for interacting with the KittyCAD
The standard library is organised into modules (listed below), but most things are always available in KCL programs. The standard library is organised into modules (listed below), but most things are always available in KCL programs.
You might also want the [KCL language reference](/docs/kcl-lang) or the [KCL guide]().
## Modules ## Modules
* [`array`](/docs/kcl-std/modules/std-array) * [`array`](/docs/kcl-std/modules/std-array)
* [`math`](/docs/kcl-std/modules/std-math) * [`math`](/docs/kcl-std/modules/std-math)
* [`sketch`](/docs/kcl-std/modules/std-sketch) * [`sketch`](/docs/kcl-std/modules/std-sketch)
* [`solid`](/docs/kcl-std/modules/std-solid) * [`solid`](/docs/kcl-std/modules/std-solid)
* [`sweep::sweep`](/docs/kcl-std/modules/std-sweep)
* [`transform`](/docs/kcl-std/modules/std-transform) * [`transform`](/docs/kcl-std/modules/std-transform)
* [`turns::turns`](/docs/kcl-std/modules/std-turns) * [`turns::turns`](/docs/kcl-std/modules/std-turns)
* [`types`](/docs/kcl-std/modules/std-types) * [`types`](/docs/kcl-std/modules/std-types)
@ -38,7 +35,7 @@ You might also want the [KCL language reference](/docs/kcl-lang) or the [KCL gui
* [`appearance`](/docs/kcl-std/appearance) * [`appearance`](/docs/kcl-std/appearance)
* [`assert`](/docs/kcl-std/assert) * [`assert`](/docs/kcl-std/assert)
* [`assertIs`](/docs/kcl-std/assertIs) * [`assertIs`](/docs/kcl-std/assertIs)
* [`clone`](/docs/kcl-std/functions/std-clone) * [`clone`](/docs/kcl-std/clone)
* [`helix`](/docs/kcl-std/functions/std-helix) * [`helix`](/docs/kcl-std/functions/std-helix)
* [`offsetPlane`](/docs/kcl-std/functions/std-offsetPlane) * [`offsetPlane`](/docs/kcl-std/functions/std-offsetPlane)
* [`patternLinear2d`](/docs/kcl-std/patternLinear2d) * [`patternLinear2d`](/docs/kcl-std/patternLinear2d)

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 it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,13 +0,0 @@
---
title: "ImportedGeometry"
subtitle: "Type in std::types"
excerpt: "Represents geometry which is defined using some other CAD system and imported into KCL."
layout: manual
---
Represents geometry which is defined using some other CAD system and imported into KCL.

View File

@ -7,25 +7,8 @@ layout: manual
An abstract plane. An abstract plane.
A plane has a position and orientation in space defined by its origin and axes. A plane is abstract A plane has a position and orientation in space defined by its origin and axes. A plane can be used
in the sense that it is not part of the objects being drawn. A plane can be used to sketch on. to sketch on.
A plane can be created in several ways:
- you can use one of the default planes, e.g., `XY`.
- you can use `offsetPlane` to create a new plane offset from an existing one, e.g., `offsetPlane(XY, offset = 150)`.
- you can use negation to create a plane from an existing one which is identical but has an opposite normal
e.g., `-XY`.
- you can define an entirely custom plane, e.g.,
```js
myXY = {
origin = { x = 0, y = 0, z = 0 },
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 1, z = 0 },
}
```
Any object with appropriate `origin`, `xAxis`, and `yAxis` fields can be used as a plane.

View File

@ -1,13 +0,0 @@
---
title: "fn"
subtitle: "Type in std::types"
excerpt: "The type of any function in KCL."
layout: manual
---
The type of any function in KCL.

View File

@ -134,6 +134,8 @@ extrude001 = extrude(sketch001, length = 5)`
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await page.waitForTimeout(1000)
// Ensure badge is present // Ensure badge is present
const codePaneButtonHolder = page.locator('#code-button-holder') const codePaneButtonHolder = page.locator('#code-button-holder')
await expect(codePaneButtonHolder).toContainText('notification', { await expect(codePaneButtonHolder).toContainText('notification', {
@ -156,14 +158,10 @@ extrude001 = extrude(sketch001, length = 5)`
await expect( await expect(
page page
.getByText( .getByText(
'Solid3D revolve failed: sketch profile must lie entirely on one side of the revolution axis' 'Modeling command failed: [ApiError { error_code: InternalEngine, message: "Solid3D revolve failed: sketch profile must lie entirely on one side of the revolution axis" }]'
) )
.first() .first()
).toBeVisible() ).toBeVisible()
// Make sure ApiError is not on the page.
// This ensures we didn't nest the json
await expect(page.getByText('ApiError')).not.toBeVisible()
}) })
test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({ test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({
@ -181,7 +179,7 @@ extrude001 = extrude(sketch001, length = 5)`
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
// await scene.settled(cmdBar) await scene.settled(cmdBar)
// Ensure badge is present // Ensure badge is present
const codePaneButtonHolder = page.locator('#code-button-holder') const codePaneButtonHolder = page.locator('#code-button-holder')

View File

@ -58,6 +58,12 @@ test(
await expect(submitButton).toBeVisible() await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Look out for the toast message
const exportingToastMessage = page.getByText(`Exporting...`)
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
await expect(exportingToastMessage).toBeVisible()
await expect(alreadyExportingToastMessage).not.toBeVisible()
// Expect it to succeed // Expect it to succeed
const errorToastMessage = page.getByText(`Error while exporting`) const errorToastMessage = page.getByText(`Error while exporting`)
const engineErrorToastMessage = page.getByText(`Nothing to export`) const engineErrorToastMessage = page.getByText(`Nothing to export`)
@ -65,9 +71,8 @@ test(
await expect(engineErrorToastMessage).not.toBeVisible() await expect(engineErrorToastMessage).not.toBeVisible()
const successToastMessage = page.getByText(`Exported successfully`) const successToastMessage = page.getByText(`Exported successfully`)
await page.waitForTimeout(1_000) await expect(successToastMessage).toBeVisible()
const count = await successToastMessage.count() await expect(exportingToastMessage).not.toBeVisible()
await expect(count).toBeGreaterThanOrEqual(1)
// Check for the exported file // Check for the exported file
const firstFileFullPath = path.resolve( const firstFileFullPath = path.resolve(
@ -136,9 +141,7 @@ test(
await expect(engineErrorToastMessage).not.toBeVisible() await expect(engineErrorToastMessage).not.toBeVisible()
const successToastMessage = page.getByText(`Exported successfully`) const successToastMessage = page.getByText(`Exported successfully`)
await page.waitForTimeout(1_000) await expect(successToastMessage).toBeVisible()
const count = await successToastMessage.count()
await expect(count).toBeGreaterThanOrEqual(1)
await expect(exportingToastMessage).not.toBeVisible() await expect(exportingToastMessage).not.toBeVisible()
// Check for the exported file= // Check for the exported file=

View File

@ -1533,6 +1533,7 @@ sketch001 = startSketchOn(XZ)
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.connectionEstablished() await scene.connectionEstablished()
await scene.settled(cmdBar)
await scene.expectPixelColor( await scene.expectPixelColor(
TEST_COLORS.DARK_MODE_BKGD, TEST_COLORS.DARK_MODE_BKGD,

View File

@ -155,12 +155,6 @@ export class CmdBarFixture {
} }
} }
closeCmdBar = async () => {
const cmdBarCloseBtn = this.page.getByTestId('command-bar-close-button')
await cmdBarCloseBtn.click()
await expect(this.cmdBarElement).not.toBeVisible()
}
get cmdSearchInput() { get cmdSearchInput() {
return this.page.getByTestId('cmd-bar-search') return this.page.getByTestId('cmd-bar-search')
} }
@ -304,27 +298,4 @@ export class CmdBarFixture {
`Monitoring text-to-cad API requests. Output will be saved to: ${outputPath}` `Monitoring text-to-cad API requests. Output will be saved to: ${outputPath}`
) )
} }
async toBeOpened() {
// Check that the command bar is opened
await expect(this.cmdBarElement).toBeVisible({ timeout: 10_000 })
}
async expectArgValue(value: string) {
// Check the placeholder project name exists
const actualArgument = await this.cmdBarElement
.getByTestId('cmd-bar-arg-value')
.inputValue()
const expectedArgument = value
expect(actualArgument).toBe(expectedArgument)
}
async expectCommandName(value: string) {
// Check the placeholder project name exists
const actual = await this.cmdBarElement
.getByTestId('command-name')
.textContent()
const expected = value
expect(actual).toBe(expected)
}
} }

View File

@ -24,7 +24,6 @@ export class HomePageFixture {
projectTextName!: Locator projectTextName!: Locator
sortByDateBtn!: Locator sortByDateBtn!: Locator
sortByNameBtn!: Locator sortByNameBtn!: Locator
appHeader!: Locator
tutorialBtn!: Locator tutorialBtn!: Locator
constructor(page: Page) { constructor(page: Page) {
@ -45,7 +44,6 @@ export class HomePageFixture {
this.sortByDateBtn = this.page.getByTestId('home-sort-by-modified') this.sortByDateBtn = this.page.getByTestId('home-sort-by-modified')
this.sortByNameBtn = this.page.getByTestId('home-sort-by-name') this.sortByNameBtn = this.page.getByTestId('home-sort-by-name')
this.appHeader = this.page.getByTestId('app-header')
this.tutorialBtn = this.page.getByTestId('home-tutorial-button') this.tutorialBtn = this.page.getByTestId('home-tutorial-button')
} }
@ -127,11 +125,4 @@ export class HomePageFixture {
await this.createAndGoToProject(name) await this.createAndGoToProject(name)
} }
isNativeFileMenuCreated = async () => {
await expect(this.appHeader).toHaveAttribute(
'data-native-file-menu',
'true'
)
}
} }

View File

@ -46,7 +46,6 @@ export class SceneFixture {
public networkToggleConnected!: Locator public networkToggleConnected!: Locator
public engineConnectionsSpinner!: Locator public engineConnectionsSpinner!: Locator
public startEditSketchBtn!: Locator public startEditSketchBtn!: Locator
public appHeader!: Locator
constructor(page: Page) { constructor(page: Page) {
this.page = page this.page = page
@ -58,7 +57,6 @@ export class SceneFixture {
this.startEditSketchBtn = page this.startEditSketchBtn = page
.getByRole('button', { name: 'Start Sketch' }) .getByRole('button', { name: 'Start Sketch' })
.or(page.getByRole('button', { name: 'Edit Sketch' })) .or(page.getByRole('button', { name: 'Edit Sketch' }))
this.appHeader = this.page.getByTestId('app-header')
} }
private _serialiseScene = async (): Promise<SceneSerialised> => { private _serialiseScene = async (): Promise<SceneSerialised> => {
const camera = await this.getCameraInfo() const camera = await this.getCameraInfo()
@ -282,13 +280,6 @@ export class SceneFixture {
await expect(buttonToTest).toBeVisible() await expect(buttonToTest).toBeVisible()
await buttonToTest.click() await buttonToTest.click()
} }
isNativeFileMenuCreated = async () => {
await expect(this.appHeader).toHaveAttribute(
'data-native-file-menu',
'true'
)
}
} }
function isColourArray( function isColourArray(

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@ test.describe('Onboarding tests', () => {
homePage, homePage,
toolbar, toolbar,
editor, editor,
scene,
tronApp, tronApp,
}) => { }) => {
if (!tronApp) { if (!tronApp) {
@ -61,6 +62,7 @@ test.describe('Onboarding tests', () => {
await editor.expectEditor.toContain('@settings(defaultLengthUnit = in)', { await editor.expectEditor.toContain('@settings(defaultLengthUnit = in)', {
shouldNormalise: true, shouldNormalise: true,
}) })
await scene.connectionEstablished()
}) })
await test.step('Go home and verify we still see the tutorial button, then begin it.', async () => { await test.step('Go home and verify we still see the tutorial button, then begin it.', async () => {
@ -130,7 +132,9 @@ test.describe('Onboarding tests', () => {
}) })
await test.step('Dismiss the onboarding', async () => { await test.step('Dismiss the onboarding', async () => {
await postDismissToast.waitFor({ state: 'hidden' })
await page.keyboard.press('Escape') await page.keyboard.press('Escape')
await expect(postDismissToast).toBeVisible()
await expect(page.getByTestId('onboarding-content')).not.toBeVisible() await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
await expect.poll(() => page.url()).not.toContain('/onboarding') await expect.poll(() => page.url()).not.toContain('/onboarding')
}) })
@ -158,10 +162,13 @@ test.describe('Onboarding tests', () => {
await test.step('Gets to the onboarding start', async () => { await test.step('Gets to the onboarding start', async () => {
await expect(toolbar.projectName).toContainText('tutorial-project') await expect(toolbar.projectName).toContainText('tutorial-project')
await expect(tutorialWelcomeHeading).toBeVisible() await expect(tutorialWelcomeHeading).toBeVisible()
await scene.connectionEstablished()
}) })
await test.step('Dismiss the onboarding', async () => { await test.step('Dismiss the onboarding', async () => {
await postDismissToast.waitFor({ state: 'hidden' })
await page.keyboard.press('Escape') await page.keyboard.press('Escape')
await expect(postDismissToast).toBeVisible()
await expect(page.getByTestId('onboarding-content')).not.toBeVisible() await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
await expect.poll(() => page.url()).not.toContain('/onboarding') await expect.poll(() => page.url()).not.toContain('/onboarding')
}) })

View File

@ -1931,37 +1931,50 @@ sketch002 = startSketchOn(XZ)
}) })
}) })
test(`Sweep point-and-click helix`, async ({ test(`Sweep point-and-click failing validation`, async ({
context, context,
page, page,
homePage, homePage,
scene, scene,
editor,
toolbar, toolbar,
cmdBar, cmdBar,
}) => { }) => {
const circleCode = `circle(sketch001, center = [0, -1], radius = .1)` const initialCode = `@settings(defaultLengthUnit = in)
const initialCode = `helix001 = helix( sketch001 = startSketchOn(YZ)
axis = X, |> circle(
radius = 1, center = [0, 0],
length = 10, radius = 500
revolutions = 10, )
angleStart = 0, sketch002 = startSketchOn(XZ)
ccw = false, |> startProfile(at = [0, 0])
) |> xLine(length = -500)
sketch001 = startSketchOn(XZ) |> line(endAbsolute = [-2000, 500])
profile001 = ${circleCode}` `
const sweepDeclaration = 'sweep001 = sweep(profile001, path = helix001)'
await context.addInitScript((initialCode) => { await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode) localStorage.setItem('persistCode', initialCode)
}, initialCode) }, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.settled(cmdBar) await scene.settled(cmdBar)
await test.step(`Add sweep through the command bar flow`, async () => { // One dumb hardcoded screen pixel value
await toolbar.openPane('feature-tree') const testPoint = { x: 700, y: 250 }
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
const [clickOnSketch2] = scene.makeMouseHelpers(
testPoint.x - 50,
testPoint.y
)
await test.step(`Look for sketch001`, async () => {
await toolbar.closePane('code')
await scene.expectPixelColor([53, 53, 53], testPoint, 15)
})
await test.step(`Go through the command bar flow and fail validation with a toast`, async () => {
await toolbar.sweepButton.click() await toolbar.sweepButton.click()
await expect
.poll(() => page.getByText('Please select one').count())
.toBe(1)
await cmdBar.expectState({ await cmdBar.expectState({
commandName: 'Sweep', commandName: 'Sweep',
currentArgKey: 'sketches', currentArgKey: 'sketches',
@ -1974,8 +1987,7 @@ profile001 = ${circleCode}`
highlightedHeaderArg: 'sketches', highlightedHeaderArg: 'sketches',
stage: 'arguments', stage: 'arguments',
}) })
await editor.scrollToText(circleCode) await clickOnSketch1()
await page.getByText(circleCode).click()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.expectState({ await cmdBar.expectState({
commandName: 'Sweep', commandName: 'Sweep',
@ -1989,39 +2001,11 @@ profile001 = ${circleCode}`
highlightedHeaderArg: 'path', highlightedHeaderArg: 'path',
stage: 'arguments', stage: 'arguments',
}) })
const helix = await toolbar.getFeatureTreeOperation('Helix', 0) await clickOnSketch2()
await helix.click()
await cmdBar.expectState({
commandName: 'Sweep',
currentArgKey: 'path',
currentArgValue: '',
headerArguments: {
Sectional: '',
Sketches: '1 face',
Path: '',
},
highlightedHeaderArg: 'path',
stage: 'arguments',
})
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.expectState({ await expect(
commandName: 'Sweep', page.getByText('Unable to sweep with the current selection. Reason:')
headerArguments: { ).toBeVisible()
Sketches: '1 face',
Path: '1 helix',
Sectional: '',
},
stage: 'review',
})
await cmdBar.progressCmdBar()
await editor.expectEditor.toContain(sweepDeclaration)
})
await test.step('Delete sweep via feature tree selection', async () => {
const sweep = await toolbar.getFeatureTreeOperation('Sweep', 0)
await sweep.click()
await page.keyboard.press('Delete')
await editor.expectEditor.not.toContain(sweepDeclaration)
}) })
}) })
@ -3643,6 +3627,67 @@ profile001 = startProfile(sketch001, at = [-20, 20])
}) })
}) })
test(`Shell dry-run validation rejects sweeps`, async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
}) => {
const initialCode = `sketch001 = startSketchOn(YZ)
|> circle(
center = [0, 0],
radius = 500
)
sketch002 = startSketchOn(XZ)
|> startProfile(at = [0, 0])
|> xLine(length = -2000)
sweep001 = sweep(sketch001, path = sketch002)
`
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value
const testPoint = { x: 500, y: 250 }
const [clickOnSweep] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
await test.step(`Confirm sweep exists`, async () => {
await toolbar.closePane('code')
await scene.expectPixelColor([231, 231, 231], testPoint, 15)
})
await test.step(`Go through the Shell flow and fail validation with a toast`, async () => {
await toolbar.shellButton.click()
await expect
.poll(() => page.getByText('Please select one').count())
.toBe(1)
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'selection',
currentArgValue: '',
headerArguments: {
Selection: '',
Thickness: '',
},
highlightedHeaderArg: 'selection',
commandName: 'Shell',
})
await clickOnSweep()
await page.waitForTimeout(500)
await cmdBar.progressCmdBar()
await expect(
page.getByText('Unable to shell with the current selection. Reason:')
).toBeVisible()
await page.waitForTimeout(1000)
})
})
test.describe('Revolve point and click workflows', () => { test.describe('Revolve point and click workflows', () => {
test('Base case workflow, auto spam continue in command bar', async ({ test('Base case workflow, auto spam continue in command bar', async ({
context, context,
@ -4898,34 +4943,4 @@ path001 = startProfile(sketch001, at = [0, 0])
) )
}) })
}) })
test(`Point and click codemods can't run on KCL errors`, async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
}) => {
const badCode = `sketch001 = startSketchOn(XZ)
profile001 = circle(sketch001, center = [0, 0], radius = 1)
extrude001 = extrude(profile001 length = 1)`
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, badCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.connectionEstablished()
await test.step(`Start Sketch is disabled`, async () => {
await expect(toolbar.startSketchBtn).not.toBeEnabled()
await editor.expectEditor.toContain(badCode, { shouldNormalise: true })
})
await test.step(`Helix is disabled`, async () => {
await expect(toolbar.helixButton).not.toBeEnabled()
await editor.expectEditor.toContain(badCode, { shouldNormalise: true })
})
})
}) })

View File

@ -1818,6 +1818,7 @@ test(
'extrude-custom-plane.kcl', 'extrude-custom-plane.kcl',
'extrude-inside-fn-with-tags.kcl', 'extrude-inside-fn-with-tags.kcl',
'fillet-and-shell.kcl', 'fillet-and-shell.kcl',
'fillet_duplicate_tags.kcl',
'focusrite_scarlett_mounting_bracket.kcl', 'focusrite_scarlett_mounting_bracket.kcl',
'function_sketch.kcl', 'function_sketch.kcl',
'function_sketch_with_position.kcl', 'function_sketch_with_position.kcl',
@ -1825,6 +1826,7 @@ test(
'helix_defaults.kcl', 'helix_defaults.kcl',
'helix_defaults_negative_extrude.kcl', 'helix_defaults_negative_extrude.kcl',
'helix_with_length.kcl', 'helix_with_length.kcl',
'i_shape.kcl',
'kittycad_svg.kcl', 'kittycad_svg.kcl',
'lego.kcl', 'lego.kcl',
'lsystem.kcl', 'lsystem.kcl',

View File

@ -99,8 +99,6 @@ test.describe('edit with AI example snapshots', () => {
await test.step('fire off edit prompt', async () => { await test.step('fire off edit prompt', async () => {
await cmdBar.captureTextToCadRequestSnapshot(test.info()) await cmdBar.captureTextToCadRequestSnapshot(test.info())
await cmdBar.openCmdBar('promptToEdit') await cmdBar.openCmdBar('promptToEdit')
await page.waitForTimeout(100)
await cmdBar.progressCmdBar()
// being specific about the color with a hex means asserting pixel color is more stable // being specific about the color with a hex means asserting pixel color is more stable
await page await page
.getByTestId('cmd-bar-arg-value') .getByTestId('cmd-bar-arg-value')

View File

@ -88,8 +88,6 @@ test.describe('Prompt-to-edit tests', () => {
await test.step('fire off edit prompt', async () => { await test.step('fire off edit prompt', async () => {
await cmdBar.openCmdBar('promptToEdit') await cmdBar.openCmdBar('promptToEdit')
await page.waitForTimeout(100)
await cmdBar.progressCmdBar()
// being specific about the color with a hex means asserting pixel color is more stable // being specific about the color with a hex means asserting pixel color is more stable
await page await page
.getByTestId('cmd-bar-arg-value') .getByTestId('cmd-bar-arg-value')
@ -167,8 +165,6 @@ test.describe('Prompt-to-edit tests', () => {
await test.step('fire of bad prompt', async () => { await test.step('fire of bad prompt', async () => {
await cmdBar.openCmdBar('promptToEdit') await cmdBar.openCmdBar('promptToEdit')
await page.waitForTimeout(100)
await cmdBar.progressCmdBar()
await page await page
.getByTestId('cmd-bar-arg-value') .getByTestId('cmd-bar-arg-value')
.fill('ansheusha asnthuatshoeuhtaoetuhthaeu laughs in dvorak') .fill('ansheusha asnthuatshoeuhtaoetuhthaeu laughs in dvorak')

View File

@ -19,12 +19,11 @@ test.describe('Regression tests', () => {
context, context,
page, page,
homePage, homePage,
scene,
}) => { }) => {
// because the model has `line([0,0]..` it is valid code, but the model is invalid // because the model has `line([0,0]..` it is valid code, but the model is invalid
// regression test for https://github.com/KittyCAD/modeling-app/issues/3251 // regression test for https://github.com/KittyCAD/modeling-app/issues/3251
// Since the bad model also found as issue with the artifact graph, which in tern blocked the editor diognostics // Since the bad model also found as issue with the artifact graph, which in tern blocked the editor diognostics
// const u = await getUtils(page) const u = await getUtils(page)
await context.addInitScript(async () => { await context.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -41,8 +40,7 @@ test.describe('Regression tests', () => {
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.connectionEstablished() await u.waitForPageLoad()
// await u.waitForPageLoad()
// error in guter // error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible() await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
@ -53,11 +51,8 @@ test.describe('Regression tests', () => {
// the close doesn't work // the close doesn't work
// when https://github.com/KittyCAD/modeling-app/issues/3268 is closed // when https://github.com/KittyCAD/modeling-app/issues/3268 is closed
// this test will need updating // this test will need updating
const crypticErrorText = `Cannot close a path that is non-planar or with duplicate vertices. const crypticErrorText = `ApiError`
Internal engine error on request`
await expect(page.getByText(crypticErrorText).first()).toBeVisible() await expect(page.getByText(crypticErrorText).first()).toBeVisible()
// Ensure we didn't nest the json.
await expect(page.getByText('ApiError')).not.toBeVisible()
}) })
test('user should not have to press down twice in cmdbar', async ({ test('user should not have to press down twice in cmdbar', async ({
page, page,
@ -190,8 +185,8 @@ extrude001 = extrude(sketch001, length = 50)
page.locator('.pretty-json-container >> text=myVar:"67') page.locator('.pretty-json-container >> text=myVar:"67')
).toBeVisible() ).toBeVisible()
}) })
test('ProgramMemory can be serialised', async ({ page, homePage, scene }) => { test('ProgramMemory can be serialised', async ({ page, homePage }) => {
// const u = await getUtils(page) const u = await getUtils(page)
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -216,12 +211,11 @@ extrude001 = extrude(sketch001, length = 50)
// Listen for all console events and push the message text to an array // Listen for all console events and push the message text to an array
page.on('console', (message) => messages.push(message.text())) page.on('console', (message) => messages.push(message.text()))
await homePage.goToModelingScene() await homePage.goToModelingScene()
// await u.waitForPageLoad() await u.waitForPageLoad()
await scene.connectionEstablished()
// wait for execution done // wait for execution done
// await u.openDebugPanel() await u.openDebugPanel()
// await u.expectCmdLog('[data-message-type="execution-done"]') await u.expectCmdLog('[data-message-type="execution-done"]')
const forbiddenMessages = ['cannot serialize tagged newtype variant'] const forbiddenMessages = ['cannot serialize tagged newtype variant']
forbiddenMessages.forEach((forbiddenMessage) => { forbiddenMessages.forEach((forbiddenMessage) => {
@ -235,7 +229,6 @@ extrude001 = extrude(sketch001, length = 50)
context, context,
page, page,
homePage, homePage,
scene,
}) => { }) => {
const u = await getUtils(page) const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio // const PUR = 400 / 37.5 //pixeltoUnitRatio
@ -254,10 +247,11 @@ extrude001 = extrude(sketch001, length = 50)
shell(exampleSketch, faces = ['end'], thickness = 0.25)` shell(exampleSketch, faces = ['end'], thickness = 0.25)`
) )
}) })
await homePage.goToModelingScene()
await scene.connectionEstablished()
await expect(async () => { await expect(async () => {
await homePage.goToModelingScene()
await u.waitForPageLoad()
// error in guter // error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible({ await expect(page.locator('.cm-lint-marker-error')).toBeVisible({
timeout: 1_000, timeout: 1_000,
@ -335,10 +329,10 @@ extrude002 = extrude(profile002, length = 150)
) )
const websocketPromise = page.waitForEvent('websocket') const websocketPromise = page.waitForEvent('websocket')
await toolbar.closePane('code')
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await toolbar.closePane('code')
const websocket = await websocketPromise const websocket = await websocketPromise
await scene.connectionEstablished() await scene.connectionEstablished()
@ -458,10 +452,12 @@ extrude002 = extrude(profile002, length = 150)
// Click the stl. // Click the stl.
await expect(stlOption).toBeVisible() await expect(stlOption).toBeVisible()
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Click the checkbox // Click the checkbox
await expect(submitButton).toBeVisible() await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Find the toast. // Find the toast.
@ -469,13 +465,11 @@ extrude002 = extrude(profile002, length = 150)
await expect(exportingToastMessage).toBeVisible() await expect(exportingToastMessage).toBeVisible()
// Expect it to succeed. // Expect it to succeed.
await expect(exportingToastMessage).not.toBeVisible() await expect(exportingToastMessage).not.toBeVisible({ timeout: 15_000 })
await expect(engineErrorToastMessage).not.toBeVisible() await expect(engineErrorToastMessage).not.toBeVisible()
const successToastMessage = page.getByText(`Exported successfully`) const successToastMessage = page.getByText(`Exported successfully`)
await page.waitForTimeout(1_000) await expect(successToastMessage).toBeVisible()
const count = await successToastMessage.count()
await expect(count).toBeGreaterThanOrEqual(1)
} }
) )
// We updated this test such that you can have multiple exports going at once. // We updated this test such that you can have multiple exports going at once.
@ -551,15 +545,14 @@ extrude002 = extrude(profile002, length = 150)
expect(alreadyExportingToastMessage).not.toBeVisible(), expect(alreadyExportingToastMessage).not.toBeVisible(),
]) ])
const count = await successToastMessage.count() await expect(successToastMessage).toHaveCount(2)
await expect(count).toBeGreaterThanOrEqual(2)
}) })
}) })
test( test(
`Network health indicator only appears in modeling view`, `Network health indicator only appears in modeling view`,
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }) => { async ({ context, page }, testInfo) => {
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'bracket') const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
@ -568,7 +561,9 @@ extrude002 = extrude(profile002, length = 150)
path.join(bracketDir, 'main.kcl') path.join(bracketDir, 'main.kcl')
) )
}) })
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
const u = await getUtils(page)
// Locators // Locators
const projectsHeading = page.getByRole('heading', { const projectsHeading = page.getByRole('heading', {
@ -588,8 +583,10 @@ extrude002 = extrude(profile002, length = 150)
}) })
await test.step('Check the modeling view', async () => { await test.step('Check the modeling view', async () => {
await expect(projectsHeading).not.toBeVisible()
await expect(networkHealthIndicator).toBeVisible() await expect(networkHealthIndicator).toBeVisible()
await expect(networkHealthIndicator).toContainText('Problem')
await u.waitForPageLoad()
await expect(networkHealthIndicator).toContainText('Connected')
}) })
} }
) )
@ -844,40 +841,6 @@ washer = extrude(washerSketch, length = thicknessMax)`
await editor.expectEditor.toContain('@settings(defaultLengthUnit = yd)') await editor.expectEditor.toContain('@settings(defaultLengthUnit = yd)')
}) })
}) })
test('Exiting existing sketch without editing should not delete it', async ({
page,
editor,
homePage,
context,
toolbar,
scene,
cmdBar,
}) => {
await context.folderSetupFn(async (dir) => {
const testDir = path.join(dir, 'test')
await fsp.mkdir(testDir, { recursive: true })
await fsp.writeFile(
path.join(testDir, 'main.kcl'),
`s1 = startSketchOn(XY)
|> startProfile(at = [0, 25])
|> xLine(endAbsolute = -15 + 1.5)
s2 = startSketchOn(XY)
|> startProfile(at = [25, 0])
|> yLine(endAbsolute = -15 + 1.5)`,
'utf-8'
)
})
await homePage.openProject('test')
await scene.settled(cmdBar)
await toolbar.waitForFeatureTreeToBeBuilt()
await toolbar.editSketch(1)
await page.waitForTimeout(1000) // Just hang out for a second
await toolbar.exitSketch()
await editor.expectEditor.toContain('s2 = startSketchOn(XY)')
})
}) })
async function clickExportButton(page: Page) { async function clickExportButton(page: Page) {

View File

@ -1365,18 +1365,18 @@ solid001 = subtract([extrude001], tools = [extrude002])
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
`fn in2mm(@inches) { `fn in2mm = (inches) => {
return inches * 25.4 return inches * 25.4
} }
railTop = in2mm(.748) const railTop = in2mm(.748)
railSide = in2mm(.024) const railSide = in2mm(.024)
railBaseWidth = in2mm(.612) const railBaseWidth = in2mm(.612)
railWideWidth = in2mm(.835) const railWideWidth = in2mm(.835)
railBaseLength = in2mm(.200) const railBaseLength = in2mm(.200)
railClampable = in2mm(.200) const railClampable = in2mm(.200)
rail = startSketchOn(XZ) const rail = startSketchOn(XZ)
|> startProfile(at = [-railTop / 2, railClampable + railBaseLength]) |> startProfile(at = [-railTop / 2, railClampable + railBaseLength])
|> line(endAbsolute = [ |> line(endAbsolute = [
railTop / 2, railTop / 2,
@ -3496,69 +3496,6 @@ profile001 = startProfile(sketch001, at = [-102.72, 237.44])
).toBeVisible() ).toBeVisible()
}) })
// Ensure feature tree is not showing previous file's content when switching to a file with KCL errors.
test('Feature tree shows correct sketch count per file', async ({
context,
homePage,
scene,
toolbar,
cmdBar,
page,
}) => {
const u = await getUtils(page)
// Setup project with files.
const GOOD_KCL = `sketch001 = startSketchOn(XZ)
profile001 = startProfile(sketch001, at = [220.81, 253.8])
|> line(end = [132.84, -151.31])
|> line(end = [25.51, 167.15])
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
sketch002 = startSketchOn(XZ)
profile002 = startProfile(sketch002, at = [158.35, -70.82])
|> line(end = [73.9, -152.19])
|> line(end = [85.33, 135.48])
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()`
const ERROR_KCL = `sketch001 = startSketchOn(XZ)
profile001 = startProfile(sketch001, at = [127.56, 179.02])
|> line(end = [132.84, -112.6])
|> line(end = [85.33, 234.01])
|> line(enfd = [-137.23, -54.55])`
await context.folderSetupFn(async (dir) => {
const projectDir = path.join(dir, 'multi-file-sketch-test')
await fs.mkdir(projectDir, { recursive: true })
await Promise.all([
fs.writeFile(path.join(projectDir, 'good.kcl'), GOOD_KCL, 'utf-8'),
fs.writeFile(path.join(projectDir, 'error.kcl'), ERROR_KCL, 'utf-8'),
])
})
await page.setBodyDimensions({ width: 1000, height: 800 })
await homePage.openProject('multi-file-sketch-test')
await scene.connectionEstablished()
await u.closeDebugPanel()
await toolbar.openFeatureTreePane()
await toolbar.openPane('files')
await toolbar.openFile('good.kcl')
await expect(
toolbar.featureTreePane.getByRole('button', { name: 'Sketch' })
).toHaveCount(2)
await toolbar.openFile('error.kcl')
await expect(
toolbar.featureTreePane.getByRole('button', { name: 'Sketch' })
).toHaveCount(0)
})
test('adding a syntax error, recovers after fixing', async ({ test('adding a syntax error, recovers after fixing', async ({
page, page,
homePage, homePage,

View File

@ -1,3 +1,4 @@
import { KCL_DEFAULT_LENGTH } from '@src/lib/constants'
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture' import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture' import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates' import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates'
@ -8,7 +9,6 @@ import {
settingsToToml, settingsToToml,
} from '@e2e/playwright/test-utils' } from '@e2e/playwright/test-utils'
import { expect, test } from '@e2e/playwright/zoo-test' import { expect, test } from '@e2e/playwright/zoo-test'
import { KCL_DEFAULT_LENGTH } from '@src/lib/constants'
test.beforeEach(async ({ page, context }) => { test.beforeEach(async ({ page, context }) => {
// Make the user avatar image always 404 // Make the user avatar image always 404
@ -766,7 +766,7 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => {
}) })
}) })
test('theme persists', async ({ page, context, homePage }) => { test('theme persists', async ({ page, context }) => {
const u = await getUtils(page) const u = await getUtils(page)
await context.addInitScript(async () => { await context.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
@ -784,7 +784,7 @@ test('theme persists', async ({ page, context, homePage }) => {
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
await homePage.goToModelingScene() await u.waitForAuthSkipAppStart()
await page.waitForTimeout(500) await page.waitForTimeout(500)
// await page.getByRole('link', { name: 'Settings Settings (tooltip)' }).click() // await page.getByRole('link', { name: 'Settings Settings (tooltip)' }).click()
@ -812,7 +812,7 @@ test('theme persists', async ({ page, context, homePage }) => {
// Disconnect and reconnect to check the theme persists through a reload // Disconnect and reconnect to check the theme persists through a reload
// Expect the network to be down // Expect the network to be down
await expect(networkToggle).toContainText('Problem') await expect(networkToggle).toContainText('Offline')
// simulate network up // simulate network up
await u.emulateNetworkConditions({ await u.emulateNetworkConditions({
@ -873,50 +873,6 @@ sweepSketch = startSketchOn(XY)
mask: lowerRightMasks(page), mask: lowerRightMasks(page),
}) })
}) })
test('code color goober works with single quotes', async ({
page,
context,
scene,
cmdBar,
}) => {
const u = await getUtils(page)
await context.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`// Create a pipe using a sweep.
// Create a path for the sweep.
sweepPath = startSketchOn(XZ)
|> startProfile(at = [0.05, 0.05])
|> line(end = [0, 7])
|> tangentialArc(angle = 90, radius = 5)
|> line(end = [-3, 0])
|> tangentialArc(angle = -90, radius = 5)
|> line(end = [0, 7])
sweepSketch = startSketchOn(XY)
|> startProfile(at = [2, 0])
|> arc(angleStart = 0, angleEnd = 360, radius = 2)
|> sweep(path = sweepPath)
|> appearance(
color = '#bb00ff',
metalness = 90,
roughness = 90
)
`
)
})
await page.setViewportSize({ width: 1200, height: 1000 })
await u.waitForAuthSkipAppStart()
await scene.settled(cmdBar)
await expect(page, 'expect small color widget').toHaveScreenshot({
maxDiffPixels: 100,
mask: lowerRightMasks(page),
})
})
test('code color goober opening window', async ({ test('code color goober opening window', async ({
page, page,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

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