Compare commits

...

29 Commits

Author SHA1 Message Date
7c935741e4 Update snapshots from the upload artifact run 2025-02-05 16:36:14 -05:00
87e299e0bb Merge branch 'main' into pierremtb/make-snapshot-bot-upload-instead-of-commit 2025-02-05 16:02:18 -05:00
465e71c12f WIP 2025-02-05 15:54:47 -05:00
df86c93a04 Restrict snapshot bot to non-main branches and to the snapshot dir (#5266)
* Disable snapshot commit bot on main

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Restrict snapshot bot git add dir

* Clean up sanps

* Other git add .

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Clean up after bot (bad bot)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Remove -a from -am

* Clean up

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-02-05 15:44:36 -05:00
824669a1c2 WIP 2025-02-05 15:41:08 -05:00
ba8f8a1722 WIP 2025-02-05 15:30:41 -05:00
f4a4e6c5be Upload only the changes 2025-02-05 15:18:51 -05:00
0d148e80aa Clean up 2025-02-05 15:01:03 -05:00
3300993ac8 Separate snapshot from flow tests 2025-02-05 14:53:54 -05:00
033eaed32e Make snapshot bot upload the changes instead of commit 2025-02-05 14:33:05 -05:00
8aabac0be7 Update types.md with keyword args data (#5270)
A few issues:

- There was no description of how `|>` works
- Need to explain our keyword arguments implementation
- It was using old syntax for `angledLine` which now takes an object as its first parameter, not an array
2025-02-05 13:03:28 -06:00
138728a95d Move Helix button to a section with offset plane (3d 'construction' elements) (#5235)
* Move Helix button to a section with offset plane (3d 'construction' elements)
Fixes #5234

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Trigger CI

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Trigger CI

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-02-05 09:59:58 -05:00
9a92e7d642 Faster local playwright electron test scripts (#5242) 2025-02-05 09:51:23 -05:00
efedc8de58 Point snapshot bot tokens to the right ones (#5265)
Add prefix to secrets for create-github-app-token
2025-02-05 09:46:46 -05:00
f7ee248a26 Fix to use more accurate types with custom isArray() and add lint (#5261)
* Fix to use more accurate types with custom isArray()

* Add lint against Array.isArray()
2025-02-05 09:01:45 -05:00
336f4f27ba Release new kcl-lib and derive-docs (#5259)
* Release new kcl-lib and derive-docs

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-02-05 08:53:50 -05:00
e1f128d64a Refactor execution module (#5162)
* cargo update, etc

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Refactor execution/mod.rs (code motion)

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Refactor caching out of ExecutorContext plus some tidying up

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Move caching logic to inside execution

Signed-off-by: Nick Cameron <nrc@ncameron.org>

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-02-05 17:53:49 +13:00
f858a611f1 Bump vitest to 1.6.1 and 2.1.9 (#5257)
* Bump vitest to 1.6.1 and 2.1.9

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* bad bot

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierre@zoo.dev>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
2025-02-05 02:56:48 +00:00
6ce270c0d0 Fix artifact graph to survive when there's an execution error (#5246)
This also changes artifact graph errors to point to the KCL that the
command originated from.
2025-02-05 01:34:56 +00:00
30ac0e4f48 Add a different class for highlights in dark mode (#5244)
That was dumb easy, we should remember how easy this is when we come
back through and make better redesign choices.
2025-02-05 00:42:57 +00:00
8f90c352fe Add 'Clone' feature to file tree (#5232)
* Add 'Clone' file / folder feature to file tree

* E2E Test: clone file in file tree

* Don't stat if there's no target
2025-02-04 18:13:59 -05:00
bc6f0fceca Allow snapshot bot to trigger CI (#5253)
* WIP: allow snapshot bot to trigger CI

* To revert: change toolbars button order to trigger snapshots

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Revert "To revert: change toolbars button order to trigger snapshots"

This reverts commit d6e2550921.

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Clean up identical snaps

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* bad bot

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-02-04 17:33:34 -05:00
5c830a4ed4 Add skipWin to a large set of playwright tests (#5254)
* WIP: disable more tests

* Fix lint and add 2 windows skips

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Two more skips on win

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* More skips

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* More skips

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* More skips

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* One more skip

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* More skips

* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)

* Replace many win32 skips with tag @skipWin

* More clean upp

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-02-04 16:49:49 -05:00
8a9d50226f Disable text-to-cad tests post kw arg merge (#5252) 2025-02-04 12:21:03 -05:00
8397405998 KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.

PR: https://github.com/KittyCAD/modeling-app/pull/4826

# Changes to KCL stdlib

- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`

Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:

```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```

Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as

```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```

Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main

The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:

```
 line\(([^=]*), %\)
 line(end = $1)

 line\((.*), %, (.*)\)
 line(end = $1, tag = $2)

 lineTo\((.*), %\)
 line(endAbsolute = $1)

 lineTo\((.*), %, (.*)\)
 line(endAbsolute = $1, tag = $2)

 extrude\((.*), %\)
 extrude(length = $1)

extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)

 close\(%, (.*)\)
 close(tag = $1)
```

# Selected notes from commits before I squash them all

* Fix test 'yRelative to horizontal distance'

Fixes:
 - Make a lineTo helper
 - Fix pathToNode to go through the labeled arg .arg property

* Fix test by changing lookups into transformMap

Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.

So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.

* Stop asserting on exact source ranges

When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.

* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.

* Fixed another queryAst test

There were 2 problems:
 - Test was looking for the old style of `line` call to choose an offset
   for pathToNode
 - Test assumed that the `tag` param was always the third one, but in
   a kwarg call, you have to look it up by label

* Fix test: traverse was not handling CallExpressionKw

* Fix another test, addTagKw

addTag helper was not aware of kw args.

* Convert close from positional to kwargs

If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.

But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.

The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.

* Fix the last test

Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.

Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.

* Fix many bad regex find-replaces

I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.

Fixes this.

* Fixes test 'Basic sketch › code pane closed at start'

Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.

This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.

* Fix: Optional args given with wrong type were silently ignored

Optional args don't have to be given. But if the user gives them, they
should be the right type.

Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.

Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
f5b8298735 Revert "Turn off snapshot commit bot" (#5198)
* Turn off snapshot commit bot

Our team is actively investigating a recent spike in flaky WebRTC
connection behavior, which has impacted the web-only Playwright snapshot
tests particularly hard. The bot squashes other GH Actions in it's wake,
so I think we should turn it off so we can see the other E2E tests more
clearly

* Revert "Turn off snapshot commit bot"

This reverts commit ab80bdb08a.

* Revert "Turn off snapshot commit bot"

This reverts commit ab80bdb08a.

---------

Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: Pierre Jacquier <pierre@zoo.dev>
2025-02-04 06:16:09 -05:00
max
25ad603502 Refactor Edge Treatment Module to Break Cyclic Dependency (#5243)
* break cycle

* yarn fmt
2025-02-03 19:14:53 -05:00
86349375d0 Don't run thumbnail hook in browser (#5229)
* Don't run thumbnail hook in browser

* fmt
2025-02-03 21:09:47 +00:00
56d861f2cc Wait for ICE gathering completion before requesting video track + create file e2e test fix (#5193)
* Test main e2e

* Create projects separately in home page tests

I think creating them in Promise.all was introducing nondeterminism and
making tests flaky.

* Query the homepage projects in an order-insensitive way

* Wait for ICE candidate gathering to complete before requesting video track

* Update src/lang/std/engineConnection.ts

Co-authored-by: Jonathan Tran <jonnytran@gmail.com>

* Fix create file e2e failure

* Yarn fmt

* Fix typo: s/that/this

yarn tsc was failing with this error:

```
src/lang/std/engineConnection.ts:1285:7 - error TS2304: Cannot find name 'that'.

1285       that.triggeredStart = false
           ~~~~
```

* Fix up revolve tests

* Turn off 3 flaky Windows tests

* Fix tags

---------

Co-authored-by: Adam Chalmers <adam.chalmers@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-03 13:41:23 -05:00
616 changed files with 147511 additions and 143356 deletions

View File

@ -29,6 +29,13 @@
{ {
"name": "isNaN", "name": "isNaN",
"message": "Use Number.isNaN() instead." "message": "Use Number.isNaN() instead."
},
],
"no-restricted-syntax": [
"error",
{
"selector": "CallExpression[callee.object.name='Array'][callee.property.name='isArray']",
"message": "Use isArray() in lib/utils.ts instead of Array.isArray()."
} }
], ],
"semi": [ "semi": [

View File

@ -1,4 +1,4 @@
name: E2E Tests name: E2E Flow Tests
on: on:
push: push:
branches: [ main ] branches: [ main ]
@ -33,7 +33,7 @@ jobs:
rust: rust:
- 'src/wasm-lib/**' - 'src/wasm-lib/**'
electron: flow-tests:
timeout-minutes: 60 timeout-minutes: 60
name: playwright:electron:${{ matrix.os }} ${{ matrix.shardIndex }} ${{ matrix.shardTotal }} name: playwright:electron:${{ matrix.os }} ${{ matrix.shardIndex }} ${{ matrix.shardTotal }}
strategy: strategy:
@ -47,23 +47,29 @@ jobs:
needs: check-rust-changes needs: check-rust-changes
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'
cache: 'yarn' cache: 'yarn'
- uses: KittyCAD/action-install-cli@main - uses: KittyCAD/action-install-cli@main
- name: Install dependencies - name: Install dependencies
shell: bash shell: bash
run: yarn run: yarn
- name: Cache Playwright Browsers - name: Cache Playwright Browsers
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: | path: |
~/.cache/ms-playwright/ ~/.cache/ms-playwright/
key: ${{ runner.os }}-playwright-${{ hashFiles('yarn.lock') }} key: ${{ runner.os }}-playwright-${{ hashFiles('yarn.lock') }}
- name: Install Playwright Browsers - name: Install Playwright Browsers
shell: bash shell: bash
run: yarn playwright install --with-deps run: yarn playwright install --with-deps
- name: Download Wasm Cache - name: Download Wasm Cache
id: download-wasm id: download-wasm
if: needs.check-rust-changes.outputs.rust-changed == 'false' if: needs.check-rust-changes.outputs.rust-changed == 'false'
@ -75,29 +81,35 @@ jobs:
workflow: build-and-store-wasm.yml workflow: build-and-store-wasm.yml
branch: main branch: main
path: src/wasm-lib/pkg path: src/wasm-lib/pkg
- name: copy wasm blob - name: copy wasm blob
if: needs.check-rust-changes.outputs.rust-changed == 'false' if: needs.check-rust-changes.outputs.rust-changed == 'false'
shell: bash shell: bash
run: cp src/wasm-lib/pkg/wasm_lib_bg.wasm public run: cp src/wasm-lib/pkg/wasm_lib_bg.wasm public
continue-on-error: true continue-on-error: true
- name: Setup Rust - name: Setup Rust
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
- name: Cache Wasm (because rust diff) - name: Cache Wasm (because rust diff)
if: needs.check-rust-changes.outputs.rust-changed == 'true' if: needs.check-rust-changes.outputs.rust-changed == 'true'
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
with: with:
workspaces: './src/wasm-lib' workspaces: './src/wasm-lib'
- name: OR Cache Wasm (because wasm cache failed) - name: OR Cache Wasm (because wasm cache failed)
if: steps.download-wasm.outcome == 'failure' if: steps.download-wasm.outcome == 'failure'
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
with: with:
workspaces: './src/wasm-lib' workspaces: './src/wasm-lib'
- name: install good sed - name: install good sed
if: ${{ startsWith(matrix.os, 'macos') }} if: ${{ startsWith(matrix.os, 'macos') }}
shell: bash shell: bash
run: | run: |
brew install gnu-sed brew install gnu-sed
echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH echo "/opt/homebrew/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
- name: Install vector - name: Install vector
shell: bash shell: bash
# TODO: figure out what to do with this, it's failing # TODO: figure out what to do with this, it's failing
@ -115,81 +127,33 @@ jobs:
sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml
cat /tmp/vector.toml cat /tmp/vector.toml
${HOME}/.vector/bin/vector --config /tmp/vector.toml & ${HOME}/.vector/bin/vector --config /tmp/vector.toml &
- name: Build Wasm (because rust diff) - name: Build Wasm (because rust diff)
if: needs.check-rust-changes.outputs.rust-changed == 'true' if: needs.check-rust-changes.outputs.rust-changed == 'true'
shell: bash shell: bash
run: yarn build:wasm run: yarn build:wasm
- name: OR Build Wasm (because wasm cache failed) - name: OR Build Wasm (because wasm cache failed)
if: steps.download-wasm.outcome == 'failure' if: steps.download-wasm.outcome == 'failure'
shell: bash shell: bash
run: yarn build:wasm run: yarn build:wasm
- name: build web - name: build web
shell: bash shell: bash
run: yarn tronb:vite:dev run: yarn tronb:vite:dev
# - name: Run ubuntu/chrome snapshots
# if: ${{ matrix.os == 'namespace-profile-ubuntu-8-cores' && matrix.shardIndex == 1 }}
# shell: bash
# # TODO: break this in its own job, for now it's not slowing down the overall execution as ubuntu is the quickest,
# # but we could do better. This forces a large 1/1 shard of all 20 snapshot tests that runs in about 3 minutes.
# run: |
# PLATFORM=web yarn playwright test --config=playwright.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=1/1
# env:
# CI: true
# NODE_ENV: development
# VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
# VITE_KC_SKIP_AUTH: true
# token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
# snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() && (success() || failure()) }}
with:
name: playwright-report-${{ matrix.os }}-snapshot-${{ matrix.shardIndex }}-${{ github.sha }}
path: playwright-report/
include-hidden-files: true
retention-days: 30
overwrite: true
- name: Clean up test-results - name: Clean up test-results
if: ${{ !cancelled() && (success() || failure()) }} if: ${{ !cancelled() && (success() || failure()) }}
continue-on-error: true continue-on-error: true
run: rm -r test-results run: rm -r test-results
- name: check for changes
if: ${{ matrix.os == 'namespace-profile-ubuntu-8-cores' && matrix.shardIndex == 1 }}
shell: bash
id: git-check
run: |
git add .
if git status | 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: steps.git-check.outputs.modified == 'true'
# shell: bash
# run: |
# git add .
# 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 }}
# git commit -am "A snapshot a day keeps the bugs away! 📷🐛 (OS: ${{matrix.os}})" || true
# git push
# git push origin ${{ github.head_ref }}
# only upload artifacts if there's actually changes
- uses: actions/upload-artifact@v4
if: steps.git-check.outputs.modified == 'true'
with:
name: playwright-report-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
path: playwright-report/
include-hidden-files: true
retention-days: 30
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
if: ${{ !cancelled() && (success() || failure()) }} if: ${{ !cancelled() && (success() || failure()) }}
continue-on-error: true continue-on-error: true
with: with:
name: test-results-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }} name: test-results-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
path: test-results/ path: test-results/
- name: Run playwright/electron flow (with retries) - name: Run playwright/electron flow (with retries)
id: retry id: retry
if: ${{ !cancelled() && (success() || failure()) }} if: ${{ !cancelled() && (success() || failure()) }}
@ -203,6 +167,7 @@ jobs:
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }} VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
VITE_KC_SKIP_AUTH: true VITE_KC_SKIP_AUTH: true
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }} token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
if: always() if: always()
with: with:
@ -211,6 +176,7 @@ jobs:
include-hidden-files: true include-hidden-files: true
retention-days: 30 retention-days: 30
overwrite: true overwrite: true
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
if: always() if: always()
with: with:
@ -219,4 +185,3 @@ jobs:
include-hidden-files: true include-hidden-files: true
retention-days: 30 retention-days: 30
overwrite: true overwrite: true

145
.github/workflows/e2e-snapshot-tests.yml vendored Normal file
View File

@ -0,0 +1,145 @@
name: E2E Snapshot Tests
on:
push:
branches: [ main ]
pull_request:
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:
check-rust-changes:
runs-on: ubuntu-latest
outputs:
rust-changed: ${{ steps.filter.outputs.rust }}
steps:
- uses: actions/checkout@v4
- id: filter
name: Check for Rust changes
uses: dorny/paths-filter@v3
with:
filters: |
rust:
- 'src/wasm-lib/**'
snapshot-tests:
runs-on: ubuntu-22.04
needs: check-rust-changes
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies
run: yarn
- name: Cache Playwright Browsers
uses: actions/cache@v4
with:
path: |
~/.cache/ms-playwright/
key: ${{ runner.os }}-playwright-${{ hashFiles('yarn.lock') }}
- name: Install Playwright Browsers
run: yarn playwright install --with-deps
- name: Download Wasm Cache
id: download-wasm
if: needs.check-rust-changes.outputs.rust-changed == 'false'
uses: dawidd6/action-download-artifact@v7
continue-on-error: true
with:
github_token: ${{secrets.GITHUB_TOKEN}}
name: wasm-bundle
workflow: build-and-store-wasm.yml
branch: main
path: src/wasm-lib/pkg
- name: copy wasm blob
if: needs.check-rust-changes.outputs.rust-changed == 'false'
run: cp src/wasm-lib/pkg/wasm_lib_bg.wasm public
continue-on-error: true
- name: Setup Rust
if: needs.check-rust-changes.outputs.rust-changed == 'true'
uses: dtolnay/rust-toolchain@stable
- name: Cache Wasm (because rust diff)
if: needs.check-rust-changes.outputs.rust-changed == 'true'
uses: Swatinem/rust-cache@v2
with:
workspaces: './src/wasm-lib'
- name: OR Cache Wasm (because wasm cache failed)
if: steps.download-wasm.outcome == 'failure'
uses: Swatinem/rust-cache@v2
with:
workspaces: './src/wasm-lib'
- name: Build Wasm (because rust diff)
if: needs.check-rust-changes.outputs.rust-changed == 'true'
run: yarn build:wasm
- name: OR Build Wasm (because wasm cache failed)
if: steps.download-wasm.outcome == 'failure'
run: yarn build:wasm
- name: build web
run: yarn tronb:vite:dev
- name: Run chrome snapshots
run: |
PLATFORM=web yarn playwright test --config=playwright.config.ts --retries="3" --update-snapshots --grep=@snapshot
env:
CI: true
NODE_ENV: development
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
VITE_KC_SKIP_AUTH: true
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
- name: check for changes
id: git-check
run: |
{
echo 'changes<<EOF'
git diff --name-only e2e/playwright/snapshot-tests.spec.ts-snapshots
echo EOF
} >> "$GITHUB_OUTPUT"
# only upload artifacts if there's actually changes
- name: Upload changes, if any
if: steps.git-check.outputs.changes != ''
uses: actions/upload-artifact@v4
with:
name: playwright-snapshots-${{ runner.os }}-${{ github.sha }}
path: ${{ steps.git-check.outputs.changes }}
- name: Upload report, if any
uses: actions/upload-artifact@v4
if: steps.git-check.outputs.changes != ''
with:
name: playwright-report-${{ runner.os }}-${{ github.sha }}
path: playwright-report/
include-hidden-files: true
retention-days: 30
- name: Fail the run if we have snapshot updates
if: steps.git-check.outputs.changes != ''
run: exit 1
# TODO: check if we could comment on the PR as well

1
.gitignore vendored
View File

@ -25,6 +25,7 @@ yarn-error.log*
.idea .idea
.vscode .vscode
.helix
src/wasm-lib/.idea src/wasm-lib/.idea
src/wasm-lib/.vscode src/wasm-lib/.vscode

2
.helix/config.toml Normal file
View File

@ -0,0 +1,2 @@
[editor]
auto-format = true

10
.helix/languages.toml Normal file
View File

@ -0,0 +1,10 @@
[language-server.eslint]
args = ["--stdio"]
command = "vscode-eslint-language-server"
[[language]]
name = "typescript"
auto-format = true
formatter = { command = "node_modules/.bin/prettier", args = ["--parser", "typescript"] }
language-servers = [ { name = "eslint", only-features = [ "diagnostics" ] }, "typescript-language-server" ]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

View File

@ -31,12 +31,12 @@ appearance(data: AppearanceData, solid_set: SolidSet) -> SolidSet
// Add color to an extruded solid. // Add color to an extruded solid.
exampleSketch = startSketchOn("XZ") exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> lineTo([10, 0], %) |> line(endAbsolute = [10, 0])
|> lineTo([0, 10], %) |> line(endAbsolute = [0, 10])
|> lineTo([-10, 0], %) |> line(endAbsolute = [-10, 0])
|> close(%) |> close()
example = extrude(5, exampleSketch) example = extrude(exampleSketch, length = 5)
|> appearance({ |> appearance({
color = '#ff0000', color = '#ff0000',
metalness = 50, metalness = 50,
@ -65,11 +65,11 @@ sketch001 = startSketchOn('XY')
fn cube(center) { fn cube(center) {
return startSketchOn('XY') return startSketchOn('XY')
|> startProfileAt([center[0] - 10, center[1] - 10], %) |> startProfileAt([center[0] - 10, center[1] - 10], %)
|> lineTo([center[0] + 10, center[1] - 10], %) |> line(endAbsolute = [center[0] + 10, center[1] - 10])
|> lineTo([center[0] + 10, center[1] + 10], %) |> line(endAbsolute = [center[0] + 10, center[1] + 10])
|> lineTo([center[0] - 10, center[1] + 10], %) |> line(endAbsolute = [center[0] - 10, center[1] + 10])
|> close(%) |> close()
|> extrude(10, %) |> extrude(length = 10)
} }
example0 = cube([0, 0]) example0 = cube([0, 0])
@ -95,11 +95,11 @@ appearance({
// This example shows setting the appearance _after_ the shell. // This example shows setting the appearance _after_ the shell.
firstSketch = startSketchOn('XY') firstSketch = startSketchOn('XY')
|> startProfileAt([-12, 12], %) |> startProfileAt([-12, 12], %)
|> line([24, 0], %) |> line(end = [24, 0])
|> line([0, -24], %) |> line(end = [0, -24])
|> line([-24, 0], %) |> line(end = [-24, 0])
|> close(%) |> close()
|> extrude(6, %) |> extrude(length = 6)
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) shell({ faces = ['end'], thickness = 0.25 }, firstSketch)
|> appearance({ |> appearance({
@ -116,11 +116,11 @@ shell({ faces = ['end'], thickness = 0.25 }, firstSketch)
// This example shows setting the appearance _before_ the shell. // This example shows setting the appearance _before_ the shell.
firstSketch = startSketchOn('XY') firstSketch = startSketchOn('XY')
|> startProfileAt([-12, 12], %) |> startProfileAt([-12, 12], %)
|> line([24, 0], %) |> line(end = [24, 0])
|> line([0, -24], %) |> line(end = [0, -24])
|> line([-24, 0], %) |> line(end = [-24, 0])
|> close(%) |> close()
|> extrude(6, %) |> extrude(length = 6)
|> appearance({ |> appearance({
color = '#ff0000', color = '#ff0000',
metalness = 90, metalness = 90,
@ -137,12 +137,12 @@ shell({ faces = ['end'], thickness = 0.25 }, firstSketch)
// This example shows _before_ the pattern. // This example shows _before_ the pattern.
exampleSketch = startSketchOn('XZ') exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line([0, 2], %) |> line(end = [0, 2])
|> line([3, 1], %) |> line(end = [3, 1])
|> line([0, -4], %) |> line(end = [0, -4])
|> close(%) |> close()
example = extrude(1, exampleSketch) example = extrude(exampleSketch, length = 1)
|> appearance({ |> appearance({
color = '#ff0000', color = '#ff0000',
metalness = 90, metalness = 90,
@ -162,12 +162,12 @@ example = extrude(1, exampleSketch)
// This example shows _after_ the pattern. // This example shows _after_ the pattern.
exampleSketch = startSketchOn('XZ') exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line([0, 2], %) |> line(end = [0, 2])
|> line([3, 1], %) |> line(end = [3, 1])
|> line([0, -4], %) |> line(end = [0, -4])
|> close(%) |> close()
example = extrude(1, exampleSketch) example = extrude(exampleSketch, length = 1)
|> patternLinear3d({ |> patternLinear3d({
axis = [1, 0, 1], axis = [1, 0, 1],
instances = 7, instances = 7,
@ -186,10 +186,10 @@ example = extrude(1, exampleSketch)
// Color the result of a 2D pattern that was extruded. // Color the result of a 2D pattern that was extruded.
exampleSketch = startSketchOn('XZ') exampleSketch = startSketchOn('XZ')
|> startProfileAt([.5, 25], %) |> startProfileAt([.5, 25], %)
|> line([0, 5], %) |> line(end = [0, 5])
|> line([-1, 0], %) |> line(end = [-1, 0])
|> line([0, -5], %) |> line(end = [0, -5])
|> close(%) |> close()
|> patternCircular2d({ |> patternCircular2d({
center = [0, 0], center = [0, 0],
instances = 13, instances = 13,
@ -197,7 +197,7 @@ exampleSketch = startSketchOn('XZ')
rotateDuplicates = true rotateDuplicates = true
}, %) }, %)
example = extrude(1, exampleSketch) example = extrude(exampleSketch, length = 1)
|> appearance({ |> appearance({
color = '#ff0000', color = '#ff0000',
metalness = 90, metalness = 90,
@ -214,11 +214,11 @@ example = extrude(1, exampleSketch)
// Create a path for the sweep. // Create a path for the sweep.
sweepPath = startSketchOn('XZ') sweepPath = startSketchOn('XZ')
|> startProfileAt([0.05, 0.05], %) |> startProfileAt([0.05, 0.05], %)
|> line([0, 7], %) |> line(end = [0, 7])
|> tangentialArc({ offset = 90, radius = 5 }, %) |> tangentialArc({ offset = 90, radius = 5 }, %)
|> line([-3, 0], %) |> line(end = [-3, 0])
|> tangentialArc({ offset = -90, radius = 5 }, %) |> tangentialArc({ offset = -90, radius = 5 }, %)
|> line([0, 7], %) |> line(end = [0, 7])
pipeHole = startSketchOn('XY') pipeHole = startSketchOn('XY')
|> circle({ center = [0, 0], radius = 1.5 }, %) |> circle({ center = [0, 0], radius = 1.5 }, %)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -36,12 +36,12 @@ filletRadius = 2
mountingPlateSketch = startSketchOn("XY") mountingPlateSketch = startSketchOn("XY")
|> startProfileAt([-width / 2, -length / 2], %) |> startProfileAt([-width / 2, -length / 2], %)
|> lineTo([width / 2, -length / 2], %, $edge1) |> line(endAbsolute = [width / 2, -length / 2], tag = $edge1)
|> lineTo([width / 2, length / 2], %, $edge2) |> line(endAbsolute = [width / 2, length / 2], tag = $edge2)
|> lineTo([-width / 2, length / 2], %, $edge3) |> line(endAbsolute = [-width / 2, length / 2], tag = $edge3)
|> close(%, $edge4) |> close(tag = $edge4)
mountingPlate = extrude(thickness, mountingPlateSketch) mountingPlate = extrude(mountingPlateSketch, length = thickness)
|> fillet({ |> fillet({
radius = filletRadius, radius = filletRadius,
tags = [ tags = [
@ -63,12 +63,12 @@ filletRadius = 1
mountingPlateSketch = startSketchOn("XY") mountingPlateSketch = startSketchOn("XY")
|> startProfileAt([-width / 2, -length / 2], %) |> startProfileAt([-width / 2, -length / 2], %)
|> lineTo([width / 2, -length / 2], %, $edge1) |> line(endAbsolute = [width / 2, -length / 2], tag = $edge1)
|> lineTo([width / 2, length / 2], %, $edge2) |> line(endAbsolute = [width / 2, length / 2], tag = $edge2)
|> lineTo([-width / 2, length / 2], %, $edge3) |> line(endAbsolute = [-width / 2, length / 2], tag = $edge3)
|> close(%, $edge4) |> close(tag = $edge4)
mountingPlate = extrude(thickness, mountingPlateSketch) mountingPlate = extrude(mountingPlateSketch, length = thickness)
|> fillet({ |> fillet({
radius = filletRadius, radius = filletRadius,
tolerance = 0.000001, tolerance = 0.000001,

File diff suppressed because one or more lines are too long

View File

@ -29,14 +29,14 @@ getNextAdjacentEdge(tag: TagIdentifier) -> Uuid
```js ```js
exampleSketch = startSketchOn('XZ') exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line([10, 0], %) |> line(end = [10, 0])
|> angledLine({ angle = 60, length = 10 }, %) |> angledLine({ angle = 60, length = 10 }, %)
|> angledLine({ angle = 120, length = 10 }, %) |> angledLine({ angle = 120, length = 10 }, %)
|> line([-10, 0], %) |> line(end = [-10, 0])
|> angledLine({ angle = 240, length = 10 }, %, $referenceEdge) |> angledLine({ angle = 240, length = 10 }, %, $referenceEdge)
|> close(%) |> close()
example = extrude(5, exampleSketch) example = extrude(exampleSketch, length = 5)
|> fillet({ |> fillet({
radius = 3, radius = 3,
tags = [getNextAdjacentEdge(referenceEdge)] tags = [getNextAdjacentEdge(referenceEdge)]

View File

@ -29,14 +29,14 @@ getOppositeEdge(tag: TagIdentifier) -> Uuid
```js ```js
exampleSketch = startSketchOn('XZ') exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line([10, 0], %) |> line(end = [10, 0])
|> angledLine({ angle = 60, length = 10 }, %) |> angledLine({ angle = 60, length = 10 }, %)
|> angledLine({ angle = 120, length = 10 }, %) |> angledLine({ angle = 120, length = 10 }, %)
|> line([-10, 0], %) |> line(end = [-10, 0])
|> angledLine({ angle = 240, length = 10 }, %, $referenceEdge) |> angledLine({ angle = 240, length = 10 }, %, $referenceEdge)
|> close(%) |> close()
example = extrude(5, exampleSketch) example = extrude(exampleSketch, length = 5)
|> fillet({ |> fillet({
radius = 3, radius = 3,
tags = [getOppositeEdge(referenceEdge)] tags = [getOppositeEdge(referenceEdge)]

View File

@ -29,14 +29,14 @@ getPreviousAdjacentEdge(tag: TagIdentifier) -> Uuid
```js ```js
exampleSketch = startSketchOn('XZ') exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line([10, 0], %) |> line(end = [10, 0])
|> angledLine({ angle = 60, length = 10 }, %) |> angledLine({ angle = 60, length = 10 }, %)
|> angledLine({ angle = 120, length = 10 }, %) |> angledLine({ angle = 120, length = 10 }, %)
|> line([-10, 0], %) |> line(end = [-10, 0])
|> angledLine({ angle = 240, length = 10 }, %, $referenceEdge) |> angledLine({ angle = 240, length = 10 }, %, $referenceEdge)
|> close(%) |> close()
example = extrude(5, exampleSketch) example = extrude(exampleSketch, length = 5)
|> fillet({ |> fillet({
radius = 3, radius = 3,
tags = [getPreviousAdjacentEdge(referenceEdge)] tags = [getPreviousAdjacentEdge(referenceEdge)]

View File

@ -49,7 +49,7 @@ springSketch = startSketchOn('YZ')
// Create a helix around an edge. // Create a helix around an edge.
helper001 = startSketchOn('XZ') helper001 = startSketchOn('XZ')
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line([0, 10], %, $edge001) |> line(end = [0, 10], tag = $edge001)
helixPath = helix({ helixPath = helix({
angleStart = 0, angleStart = 0,

View File

@ -30,7 +30,7 @@ helixRevolutions(data: HelixRevolutionsData, solid: Solid) -> Solid
```js ```js
part001 = startSketchOn('XY') part001 = startSketchOn('XY')
|> circle({ center = [5, 5], radius = 10 }, %) |> circle({ center = [5, 5], radius = 10 }, %)
|> extrude(10, %) |> extrude(length = 10)
|> helixRevolutions({ |> helixRevolutions({
angleStart = 0, angleStart = 0,
ccw = true, ccw = true,

File diff suppressed because one or more lines are too long

View File

@ -31,11 +31,11 @@ hollow(thickness: number, solid: Solid) -> Solid
// Hollow a basic sketch. // Hollow a basic sketch.
firstSketch = startSketchOn('XY') firstSketch = startSketchOn('XY')
|> startProfileAt([-12, 12], %) |> startProfileAt([-12, 12], %)
|> line([24, 0], %) |> line(end = [24, 0])
|> line([0, -24], %) |> line(end = [0, -24])
|> line([-24, 0], %) |> line(end = [-24, 0])
|> close(%) |> close()
|> extrude(6, %) |> extrude(length = 6)
|> hollow(0.25, %) |> hollow(0.25, %)
``` ```
@ -45,11 +45,11 @@ firstSketch = startSketchOn('XY')
// Hollow a basic sketch. // Hollow a basic sketch.
firstSketch = startSketchOn('-XZ') firstSketch = startSketchOn('-XZ')
|> startProfileAt([-12, 12], %) |> startProfileAt([-12, 12], %)
|> line([24, 0], %) |> line(end = [24, 0])
|> line([0, -24], %) |> line(end = [0, -24])
|> line([-24, 0], %) |> line(end = [-24, 0])
|> close(%) |> close()
|> extrude(6, %) |> extrude(length = 6)
|> hollow(0.5, %) |> hollow(0.5, %)
``` ```
@ -60,25 +60,25 @@ firstSketch = startSketchOn('-XZ')
size = 100 size = 100
case = startSketchOn('-XZ') case = startSketchOn('-XZ')
|> startProfileAt([-size, -size], %) |> startProfileAt([-size, -size], %)
|> line([2 * size, 0], %) |> line(end = [2 * size, 0])
|> line([0, 2 * size], %) |> line(end = [0, 2 * size])
|> tangentialArcTo([-size, size], %) |> tangentialArcTo([-size, size], %)
|> close(%) |> close()
|> extrude(65, %) |> extrude(length = 65)
thing1 = startSketchOn(case, 'end') thing1 = startSketchOn(case, 'end')
|> circle({ |> circle({
center = [-size / 2, -size / 2], center = [-size / 2, -size / 2],
radius = 25 radius = 25
}, %) }, %)
|> extrude(50, %) |> extrude(length = 50)
thing2 = startSketchOn(case, 'end') thing2 = startSketchOn(case, 'end')
|> circle({ |> circle({
center = [size / 2, -size / 2], center = [size / 2, -size / 2],
radius = 25 radius = 25
}, %) }, %)
|> extrude(50, %) |> extrude(length = 50)
hollow(0.5, case) hollow(0.5, case)
``` ```

View File

@ -58,7 +58,6 @@ layout: manual
* [`legAngY`](kcl/legAngY) * [`legAngY`](kcl/legAngY)
* [`legLen`](kcl/legLen) * [`legLen`](kcl/legLen)
* [`line`](kcl/line) * [`line`](kcl/line)
* [`lineTo`](kcl/lineTo)
* [`ln`](kcl/ln) * [`ln`](kcl/ln)
* [`loft`](kcl/loft) * [`loft`](kcl/loft)
* [`log`](kcl/log) * [`log`](kcl/log)

View File

@ -38,7 +38,7 @@ assertEqual(n, 3, 0.0001, "5/2 = 2.5, rounded up makes 3")
// Draw n cylinders. // Draw n cylinders.
startSketchOn('XZ') startSketchOn('XZ')
|> circle({ center = [0, 0], radius = 2 }, %) |> circle({ center = [0, 0], radius = 2 }, %)
|> extrude(5, %) |> extrude(length = 5)
|> patternTransform(n, fn(id) { |> patternTransform(n, fn(id) {
return { translate = [4 * id, 0, 0] } return { translate = [4 * id, 0, 0] }
}, %) }, %)

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

View File

@ -34,18 +34,18 @@ loft(sketches: [Sketch], v_degree: NonZeroU32, bez_approximate_rational: bool, b
// Loft a square and a triangle. // Loft a square and a triangle.
squareSketch = startSketchOn('XY') squareSketch = startSketchOn('XY')
|> startProfileAt([-100, 200], %) |> startProfileAt([-100, 200], %)
|> line([200, 0], %) |> line(end = [200, 0])
|> line([0, -200], %) |> line(end = [0, -200])
|> line([-200, 0], %) |> line(end = [-200, 0])
|> lineTo([profileStartX(%), profileStartY(%)], %) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close(%) |> close()
triangleSketch = startSketchOn(offsetPlane('XY', 75)) triangleSketch = startSketchOn(offsetPlane('XY', 75))
|> startProfileAt([0, 125], %) |> startProfileAt([0, 125], %)
|> line([-15, -30], %) |> line(end = [-15, -30])
|> line([30, 0], %) |> line(end = [30, 0])
|> lineTo([profileStartX(%), profileStartY(%)], %) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close(%) |> close()
loft([squareSketch, triangleSketch]) loft([squareSketch, triangleSketch])
``` ```
@ -56,11 +56,11 @@ loft([squareSketch, triangleSketch])
// Loft a square, a circle, and another circle. // Loft a square, a circle, and another circle.
squareSketch = startSketchOn('XY') squareSketch = startSketchOn('XY')
|> startProfileAt([-100, 200], %) |> startProfileAt([-100, 200], %)
|> line([200, 0], %) |> line(end = [200, 0])
|> line([0, -200], %) |> line(end = [0, -200])
|> line([-200, 0], %) |> line(end = [-200, 0])
|> lineTo([profileStartX(%), profileStartY(%)], %) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close(%) |> close()
circleSketch0 = startSketchOn(offsetPlane('XY', 75)) circleSketch0 = startSketchOn(offsetPlane('XY', 75))
|> circle({ center = [0, 100], radius = 50 }, %) |> circle({ center = [0, 100], radius = 50 }, %)
@ -81,11 +81,11 @@ loft([
// Loft a square, a circle, and another circle with options. // Loft a square, a circle, and another circle with options.
squareSketch = startSketchOn('XY') squareSketch = startSketchOn('XY')
|> startProfileAt([-100, 200], %) |> startProfileAt([-100, 200], %)
|> line([200, 0], %) |> line(end = [200, 0])
|> line([0, -200], %) |> line(end = [0, -200])
|> line([-200, 0], %) |> line(end = [-200, 0])
|> lineTo([profileStartX(%), profileStartY(%)], %) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close(%) |> close()
circleSketch0 = startSketchOn(offsetPlane('XY', 75)) circleSketch0 = startSketchOn(offsetPlane('XY', 75))
|> circle({ center = [0, 100], radius = 50 }, %) |> circle({ center = [0, 100], radius = 50 }, %)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -31,7 +31,7 @@ patternCircular3d(data: CircularPattern3dData, solid_set: SolidSet) -> [Solid]
exampleSketch = startSketchOn('XZ') exampleSketch = startSketchOn('XZ')
|> circle({ center = [0, 0], radius = 1 }, %) |> circle({ center = [0, 0], radius = 1 }, %)
example = extrude(-5, exampleSketch) example = extrude(exampleSketch, length = -5)
|> patternCircular3d({ |> patternCircular3d({
axis = [1, -1, 0], axis = [1, -1, 0],
center = [10, -20, 0], center = [10, -20, 0],

File diff suppressed because one or more lines are too long

View File

@ -30,12 +30,12 @@ patternLinear3d(data: LinearPattern3dData, solid_set: SolidSet) -> [Solid]
```js ```js
exampleSketch = startSketchOn('XZ') exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line([0, 2], %) |> line(end = [0, 2])
|> line([3, 1], %) |> line(end = [3, 1])
|> line([0, -4], %) |> line(end = [0, -4])
|> close(%) |> close()
example = extrude(1, exampleSketch) example = extrude(exampleSketch, length = 1)
|> patternLinear3d({ |> patternLinear3d({
axis = [1, 0, 1], axis = [1, 0, 1],
instances = 7, instances = 7,

View File

@ -63,7 +63,7 @@ fn transform(id) {
// Sketch 4 cylinders. // Sketch 4 cylinders.
sketch001 = startSketchOn('XZ') sketch001 = startSketchOn('XZ')
|> circle({ center = [0, 0], radius = 2 }, %) |> circle({ center = [0, 0], radius = 2 }, %)
|> extrude(5, %) |> extrude(length = 5)
|> patternTransform(4, transform, %) |> patternTransform(4, transform, %)
``` ```
@ -79,7 +79,7 @@ fn transform(id) {
sketch001 = startSketchOn('XZ') sketch001 = startSketchOn('XZ')
|> circle({ center = [0, 0], radius = 2 }, %) |> circle({ center = [0, 0], radius = 2 }, %)
|> extrude(5, %) |> extrude(length = 5)
|> patternTransform(4, transform, %) |> patternTransform(4, transform, %)
``` ```
@ -97,12 +97,12 @@ fn cube(length, center) {
return startSketchOn('XY') return startSketchOn('XY')
|> startProfileAt(p0, %) |> startProfileAt(p0, %)
|> lineTo(p1, %) |> line(endAbsolute = p1)
|> lineTo(p2, %) |> line(endAbsolute = p2)
|> lineTo(p3, %) |> line(endAbsolute = p3)
|> lineTo(p0, %) |> line(endAbsolute = p0)
|> close(%) |> close()
|> extrude(length, %) |> extrude(length = length)
} }
width = 20 width = 20
@ -135,12 +135,12 @@ fn cube(length, center) {
return startSketchOn('XY') return startSketchOn('XY')
|> startProfileAt(p0, %) |> startProfileAt(p0, %)
|> lineTo(p1, %) |> line(endAbsolute = p1)
|> lineTo(p2, %) |> line(endAbsolute = p2)
|> lineTo(p3, %) |> line(endAbsolute = p3)
|> lineTo(p0, %) |> line(endAbsolute = p0)
|> close(%) |> close()
|> extrude(length, %) |> extrude(length = length)
} }
width = 20 width = 20
@ -179,7 +179,7 @@ fn layer() {
return startSketchOn("XY") return startSketchOn("XY")
// or some other plane idk // or some other plane idk
|> circle({ center = [0, 0], radius = 1 }, %, $tag1) |> circle({ center = [0, 0], radius = 1 }, %, $tag1)
|> extrude(h, %) |> extrude(length = h)
} }
// The vase is 100 layers tall. // The vase is 100 layers tall.
// The 100 layers are replica of each other, with a slight transformation applied to each. // The 100 layers are replica of each other, with a slight transformation applied to each.
@ -205,7 +205,7 @@ startSketchOn('XY')
center = [0, 0], center = [0, 0],
inscribed = false inscribed = false
}, %) }, %)
|> extrude(4, %) |> extrude(length = 4)
|> patternTransform(3, transform, %) |> patternTransform(3, transform, %)
``` ```

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -30,12 +30,12 @@ segEnd(tag: TagIdentifier) -> [number]
w = 15 w = 15
cube = startSketchOn('XY') cube = startSketchOn('XY')
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line([w, 0], %, $line1) |> line(end = [w, 0], tag = $line1)
|> line([0, w], %, $line2) |> line(end = [0, w], tag = $line2)
|> line([-w, 0], %, $line3) |> line(end = [-w, 0], tag = $line3)
|> line([0, -w], %, $line4) |> line(end = [0, -w], tag = $line4)
|> close(%) |> close()
|> extrude(5, %) |> extrude(length = 5)
fn cylinder(radius, tag) { fn cylinder(radius, tag) {
return startSketchOn('XY') return startSketchOn('XY')
@ -44,7 +44,7 @@ fn cylinder(radius, tag) {
radius = radius, radius = radius,
center = segEnd(tag) center = segEnd(tag)
}, %) }, %)
|> extrude(radius, %) |> extrude(length = radius)
} }
cylinder(1, line1) cylinder(1, line1)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -30,12 +30,12 @@ segStart(tag: TagIdentifier) -> [number]
w = 15 w = 15
cube = startSketchOn('XY') cube = startSketchOn('XY')
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line([w, 0], %, $line1) |> line(end = [w, 0], tag = $line1)
|> line([0, w], %, $line2) |> line(end = [0, w], tag = $line2)
|> line([-w, 0], %, $line3) |> line(end = [-w, 0], tag = $line3)
|> line([0, -w], %, $line4) |> line(end = [0, -w], tag = $line4)
|> close(%) |> close()
|> extrude(5, %) |> extrude(length = 5)
fn cylinder(radius, tag) { fn cylinder(radius, tag) {
return startSketchOn('XY') return startSketchOn('XY')
@ -44,7 +44,7 @@ fn cylinder(radius, tag) {
radius = radius, radius = radius,
center = segStart(tag) center = segStart(tag)
}, %) }, %)
|> extrude(radius, %) |> extrude(length = radius)
} }
cylinder(1, line1) cylinder(1, line1)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -31,11 +31,11 @@ shell(data: ShellData, solid_set: SolidSet) -> SolidSet
// Remove the end face for the extrusion. // Remove the end face for the extrusion.
firstSketch = startSketchOn('XY') firstSketch = startSketchOn('XY')
|> startProfileAt([-12, 12], %) |> startProfileAt([-12, 12], %)
|> line([24, 0], %) |> line(end = [24, 0])
|> line([0, -24], %) |> line(end = [0, -24])
|> line([-24, 0], %) |> line(end = [-24, 0])
|> close(%) |> close()
|> extrude(6, %) |> extrude(length = 6)
// Remove the end face for the extrusion. // Remove the end face for the extrusion.
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) shell({ faces = ['end'], thickness = 0.25 }, firstSketch)
@ -47,11 +47,11 @@ shell({ faces = ['end'], thickness = 0.25 }, firstSketch)
// Remove the start face for the extrusion. // Remove the start face for the extrusion.
firstSketch = startSketchOn('-XZ') firstSketch = startSketchOn('-XZ')
|> startProfileAt([-12, 12], %) |> startProfileAt([-12, 12], %)
|> line([24, 0], %) |> line(end = [24, 0])
|> line([0, -24], %) |> line(end = [0, -24])
|> line([-24, 0], %) |> line(end = [-24, 0])
|> close(%) |> close()
|> extrude(6, %) |> extrude(length = 6)
// Remove the start face for the extrusion. // Remove the start face for the extrusion.
shell({ faces = ['start'], thickness = 0.25 }, firstSketch) shell({ faces = ['start'], thickness = 0.25 }, firstSketch)
@ -63,11 +63,11 @@ shell({ faces = ['start'], thickness = 0.25 }, firstSketch)
// Remove a tagged face and the end face for the extrusion. // Remove a tagged face and the end face for the extrusion.
firstSketch = startSketchOn('XY') firstSketch = startSketchOn('XY')
|> startProfileAt([-12, 12], %) |> startProfileAt([-12, 12], %)
|> line([24, 0], %) |> line(end = [24, 0])
|> line([0, -24], %) |> line(end = [0, -24])
|> line([-24, 0], %, $myTag) |> line(end = [-24, 0], tag = $myTag)
|> close(%) |> close()
|> extrude(6, %) |> extrude(length = 6)
// Remove a tagged face for the extrusion. // Remove a tagged face for the extrusion.
shell({ faces = [myTag], thickness = 0.25 }, firstSketch) shell({ faces = [myTag], thickness = 0.25 }, firstSketch)
@ -79,11 +79,11 @@ shell({ faces = [myTag], thickness = 0.25 }, firstSketch)
// Remove multiple faces at once. // Remove multiple faces at once.
firstSketch = startSketchOn('XY') firstSketch = startSketchOn('XY')
|> startProfileAt([-12, 12], %) |> startProfileAt([-12, 12], %)
|> line([24, 0], %) |> line(end = [24, 0])
|> line([0, -24], %) |> line(end = [0, -24])
|> line([-24, 0], %, $myTag) |> line(end = [-24, 0], tag = $myTag)
|> close(%) |> close()
|> extrude(6, %) |> extrude(length = 6)
// Remove a tagged face and the end face for the extrusion. // Remove a tagged face and the end face for the extrusion.
shell({ shell({
@ -99,25 +99,25 @@ shell({
size = 100 size = 100
case = startSketchOn('-XZ') case = startSketchOn('-XZ')
|> startProfileAt([-size, -size], %) |> startProfileAt([-size, -size], %)
|> line([2 * size, 0], %) |> line(end = [2 * size, 0])
|> line([0, 2 * size], %) |> line(end = [0, 2 * size])
|> tangentialArcTo([-size, size], %) |> tangentialArcTo([-size, size], %)
|> close(%) |> close()
|> extrude(65, %) |> extrude(length = 65)
thing1 = startSketchOn(case, 'end') thing1 = startSketchOn(case, 'end')
|> circle({ |> circle({
center = [-size / 2, -size / 2], center = [-size / 2, -size / 2],
radius = 25 radius = 25
}, %) }, %)
|> extrude(50, %) |> extrude(length = 50)
thing2 = startSketchOn(case, 'end') thing2 = startSketchOn(case, 'end')
|> circle({ |> circle({
center = [size / 2, -size / 2], center = [size / 2, -size / 2],
radius = 25 radius = 25
}, %) }, %)
|> extrude(50, %) |> extrude(length = 50)
// We put "case" in the shell function to shell the entire object. // We put "case" in the shell function to shell the entire object.
shell({ faces = ['start'], thickness = 5 }, case) shell({ faces = ['start'], thickness = 5 }, case)
@ -130,25 +130,25 @@ shell({ faces = ['start'], thickness = 5 }, case)
size = 100 size = 100
case = startSketchOn('XY') case = startSketchOn('XY')
|> startProfileAt([-size, -size], %) |> startProfileAt([-size, -size], %)
|> line([2 * size, 0], %) |> line(end = [2 * size, 0])
|> line([0, 2 * size], %) |> line(end = [0, 2 * size])
|> tangentialArcTo([-size, size], %) |> tangentialArcTo([-size, size], %)
|> close(%) |> close()
|> extrude(65, %) |> extrude(length = 65)
thing1 = startSketchOn(case, 'end') thing1 = startSketchOn(case, 'end')
|> circle({ |> circle({
center = [-size / 2, -size / 2], center = [-size / 2, -size / 2],
radius = 25 radius = 25
}, %) }, %)
|> extrude(50, %) |> extrude(length = 50)
thing2 = startSketchOn(case, 'end') thing2 = startSketchOn(case, 'end')
|> circle({ |> circle({
center = [size / 2, -size / 2], center = [size / 2, -size / 2],
radius = 25 radius = 25
}, %) }, %)
|> extrude(50, %) |> extrude(length = 50)
// We put "thing1" in the shell function to shell the end face of the object. // We put "thing1" in the shell function to shell the end face of the object.
shell({ faces = ['end'], thickness = 5 }, thing1) shell({ faces = ['end'], thickness = 5 }, thing1)
@ -164,25 +164,25 @@ shell({ faces = ['end'], thickness = 5 }, thing1)
size = 100 size = 100
case = startSketchOn('XY') case = startSketchOn('XY')
|> startProfileAt([-size, -size], %) |> startProfileAt([-size, -size], %)
|> line([2 * size, 0], %) |> line(end = [2 * size, 0])
|> line([0, 2 * size], %) |> line(end = [0, 2 * size])
|> tangentialArcTo([-size, size], %) |> tangentialArcTo([-size, size], %)
|> close(%) |> close()
|> extrude(65, %) |> extrude(length = 65)
thing1 = startSketchOn(case, 'end') thing1 = startSketchOn(case, 'end')
|> circle({ |> circle({
center = [-size / 2, -size / 2], center = [-size / 2, -size / 2],
radius = 25 radius = 25
}, %) }, %)
|> extrude(50, %) |> extrude(length = 50)
thing2 = startSketchOn(case, 'end') thing2 = startSketchOn(case, 'end')
|> circle({ |> circle({
center = [size / 2, -size / 2], center = [size / 2, -size / 2],
radius = 25 radius = 25
}, %) }, %)
|> extrude(50, %) |> extrude(length = 50)
// We put "thing1" and "thing2" in the shell function to shell the end face of the object. // We put "thing1" and "thing2" in the shell function to shell the end face of the object.
shell({ faces = ['end'], thickness = 5 }, [thing1, thing2]) shell({ faces = ['end'], thickness = 5 }, [thing1, thing2])

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

View File

@ -34,11 +34,11 @@ sweep(data: SweepData, sketch: Sketch) -> Solid
// Create a path for the sweep. // Create a path for the sweep.
sweepPath = startSketchOn('XZ') sweepPath = startSketchOn('XZ')
|> startProfileAt([0.05, 0.05], %) |> startProfileAt([0.05, 0.05], %)
|> line([0, 7], %) |> line(end = [0, 7])
|> tangentialArc({ offset = 90, radius = 5 }, %) |> tangentialArc({ offset = 90, radius = 5 }, %)
|> line([-3, 0], %) |> line(end = [-3, 0])
|> tangentialArc({ offset = -90, radius = 5 }, %) |> tangentialArc({ offset = -90, radius = 5 }, %)
|> line([0, 7], %) |> line(end = [0, 7])
// Create a hole for the pipe. // Create a hole for the pipe.
pipeHole = startSketchOn('XY') pipeHole = startSketchOn('XY')

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -47,21 +47,6 @@ myObj = { a = 0, b = "thing" }
We support two different ways of getting properties from objects, you can call We support two different ways of getting properties from objects, you can call
`myObj.a` or `myObj["a"]` both work. `myObj.a` or `myObj["a"]` both work.
## Functions
We also have support for defining your own functions. Functions can take in any
type of argument. Below is an example of the syntax:
```
fn myFn(x) {
return x
}
```
As you can see above `myFn` just returns whatever it is given.
## Binary expressions ## Binary expressions
You can also do math! Let's show an example below: You can also do math! Let's show an example below:
@ -76,6 +61,120 @@ You can nest expressions in parenthesis as well:
myMathExpression = 3 + (1 * 2 / (3 - 7)) myMathExpression = 3 + (1 * 2 / (3 - 7))
``` ```
## Functions
We also have support for defining your own functions. Functions can take in any
type of argument. Below is an example of the syntax:
```
fn myFn(x) {
return x
}
```
As you can see above `myFn` just returns whatever it is given.
KCL's early drafts used positional arguments, but we now use keyword arguments. If you declare a
function like this:
```
fn add(left, right) {
return left + right
}
```
You can call it like this:
```
total = add(left = 1, right = 2)
```
Functions can also declare one *unlabeled* arg. If you do want to declare an unlabeled arg, it must
be the first arg declared.
```
// The @ indicates an argument can be used without a label.
// Note that only the first argument can use @.
fn increment(@x) {
return x + 1
}
fn add(@x, delta) {
return x + delta
}
two = increment(1)
three = add(1, delta = 2)
```
## Pipelines
It can be hard to read repeated function calls, because of all the nested brackets.
```
i = 1
x = h(g(f(i)))
```
You can make this easier to read by breaking it into many declarations, but that is a bit annoying.
```
i = 1
x0 = f(i)
x1 = g(x0)
x = h(x1)
```
Instead, you can use the pipeline operator (`|>`) to simplify this.
Basically, `x |> f(%)` is a shorthand for `f(x)`. The left-hand side of the `|>` gets put into
the `%` in the right-hand side.
So, this means `x |> f(%) |> g(%)` is shorthand for `g(f(x))`. The code example above, with its
somewhat-clunky `x0` and `x1` constants could be rewritten as
```
i = 1
x = i
|> f(%)
|> g(%)
|> h(%)
```
This helps keep your code neat and avoid unnecessary declarations.
## Pipelines and keyword arguments
Say you have a long pipeline of sketch functions, like this:
```
startSketch()
|> line(%, end = [3, 4])
|> line(%, end = [10, 10])
|> line(%, end = [-13, -14])
|> close(%)
```
In this example, each function call outputs a sketch, and it gets put into the next function call via
the `%`, into the first (unlabeled) argument.
If a function call uses an unlabeled first parameter, it will default to `%` if it's not given. This
means that `|> line(%, end = [3, 4])` and `|> line(end = [3, 4])` are equivalent! So the above
could be rewritten as
```
startSketch()
|> line(end = [3, 4])
|> line(end = [10, 10])
|> line(end = [-13, -14])
|> close()
```
Note that we are still in the process of migrating KCL's standard library to use keyword arguments. So some
functions are still unfortunately using positional arguments. We're moving them over, so keep checking back.
Some functions like `angledLine`, `startProfileAt` etc are still using the old positional argument syntax.
Check the docs page for each function and look at its examples to see.
## Tags ## Tags
Tags are used to give a name (tag) to a specific path. Tags are used to give a name (tag) to a specific path.
@ -88,17 +187,17 @@ way:
``` ```
startSketchOn('XZ') startSketchOn('XZ')
|> startProfileAt(origin, %) |> startProfileAt(origin, %)
|> angledLine([0, 191.26], %, $rectangleSegmentA001) |> angledLine({angle = 0, length = 191.26}, %, $rectangleSegmentA001)
|> angledLine([ |> angledLine({
segAng(rectangleSegmentA001) - 90, angle = segAng(rectangleSegmentA001) - 90,
196.99 length = 196.99,
], %, $rectangleSegmentB001) }, %, $rectangleSegmentB001)
|> angledLine([ |> angledLine({
segAng(rectangleSegmentA001), angle = segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001) length = -segLen(rectangleSegmentA001),
], %, $rectangleSegmentC001) }, %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close(%) |> close()
``` ```
### Tag Identifier ### Tag Identifier
@ -121,17 +220,17 @@ However if the code was written like this:
fn rect(origin) { fn rect(origin) {
return startSketchOn('XZ') return startSketchOn('XZ')
|> startProfileAt(origin, %) |> startProfileAt(origin, %)
|> angledLine([0, 191.26], %, $rectangleSegmentA001) |> angledLine({angle = 0, length = 191.26}, %, $rectangleSegmentA001)
|> angledLine([ |> angledLine({
segAng(rectangleSegmentA001) - 90, angle = segAng(rectangleSegmentA001) - 90,
196.99 length = 196.99
], %, $rectangleSegmentB001) }, %, $rectangleSegmentB001)
|> angledLine([ |> angledLine({
segAng(rectangleSegmentA001), angle = segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001) length = -segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001) }, %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close(%) |> close()
} }
rect([0, 0]) rect([0, 0])
@ -149,17 +248,17 @@ For example the following code works.
fn rect(origin) { fn rect(origin) {
return startSketchOn('XZ') return startSketchOn('XZ')
|> startProfileAt(origin, %) |> startProfileAt(origin, %)
|> angledLine([0, 191.26], %, $rectangleSegmentA001) |> angledLine({angle = 0, length = 191.26}, %, $rectangleSegmentA001)
|> angledLine([ |> angledLine({
segAng(rectangleSegmentA001) - 90, angle = segAng(rectangleSegmentA001) - 90,
196.99 length = 196.99
], %, $rectangleSegmentB001) }, %, $rectangleSegmentB001)
|> angledLine([ |> angledLine({
segAng(rectangleSegmentA001), angle = segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001) length = -segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001) }, %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close(%) |> close()
} }
rect([0, 0]) rect([0, 0])

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

View File

@ -144,7 +144,7 @@ async function doBasicSketch(
|> xLine(-segLen(seg01), %)`) |> xLine(-segLen(seg01), %)`)
} }
test.describe('Basic sketch', () => { test.describe('Basic sketch', { tag: ['@skipWin'] }, () => {
test.fixme('code pane open at start', async ({ page, homePage }) => { test.fixme('code pane open at start', async ({ page, homePage }) => {
await doBasicSketch(page, homePage, ['code']) await doBasicSketch(page, homePage, ['code'])
}) })

View File

@ -4,7 +4,10 @@ import { getUtils } from './test-utils'
import { EngineCommand } from 'lang/std/artifactGraph' import { EngineCommand } from 'lang/std/artifactGraph'
import { uuidv4 } from 'lib/utils' import { uuidv4 } from 'lib/utils'
test.describe('Can create sketches on all planes and their back sides', () => { test.describe(
'Can create sketches on all planes and their back sides',
{ tag: ['@skipWin'] },
() => {
const sketchOnPlaneAndBackSideTest = async ( const sketchOnPlaneAndBackSideTest = async (
page: Page, page: Page,
homePage: HomePageFixture, homePage: HomePageFixture,
@ -88,11 +91,17 @@ test.describe('Can create sketches on all planes and their back sides', () => {
}) })
test('YZ', async ({ page, homePage }) => { test('YZ', async ({ page, homePage }) => {
await sketchOnPlaneAndBackSideTest(page, homePage, 'YZ', { x: 700, y: 250 }) // green plane await sketchOnPlaneAndBackSideTest(page, homePage, 'YZ', {
x: 700,
y: 250,
}) // green plane
}) })
test('XZ', async ({ page, homePage }) => { test('XZ', async ({ page, homePage }) => {
await sketchOnPlaneAndBackSideTest(page, homePage, '-XZ', { x: 700, y: 80 }) // blue plane await sketchOnPlaneAndBackSideTest(page, homePage, '-XZ', {
x: 700,
y: 80,
}) // blue plane
}) })
test('-XY', async ({ page, homePage }) => { test('-XY', async ({ page, homePage }) => {
@ -110,6 +119,10 @@ test.describe('Can create sketches on all planes and their back sides', () => {
}) })
test('-XZ', async ({ page, homePage }) => { test('-XZ', async ({ page, homePage }) => {
await sketchOnPlaneAndBackSideTest(page, homePage, 'XZ', { x: 700, y: 427 }) // back of blue plane await sketchOnPlaneAndBackSideTest(page, homePage, 'XZ', {
x: 700,
y: 427,
}) // back of blue plane
}) })
}) }
)

View File

@ -6,7 +6,7 @@ import { bracket } from 'lib/exampleKcl'
import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates' import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates'
import fsp from 'fs/promises' import fsp from 'fs/promises'
test.describe('Code pane and errors', () => { test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
test('Typing KCL errors induces a badge on the code pane button', async ({ test('Typing KCL errors induces a badge on the code pane button', async ({
page, page,
homePage, homePage,
@ -20,11 +20,11 @@ test.describe('Code pane and errors', () => {
`// Extruded Triangle `// Extruded Triangle
sketch001 = startSketchOn('XZ') sketch001 = startSketchOn('XZ')
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line([10, 0], %) |> line(end = [10, 0])
|> line([-5, 10], %) |> line(end = [-5, 10])
|> lineTo([profileStartX(%), profileStartY(%)], %) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close(%) |> close()
extrude001 = extrude(5, sketch001)` extrude001 = extrude(sketch001, length = 5)`
) )
}) })
@ -79,8 +79,10 @@ test.describe('Code pane and errors', () => {
// Delete a character to break the KCL // Delete a character to break the KCL
await editor.openPane() await editor.openPane()
await editor.scrollToText('thickness, bracketLeg1Sketch)') await editor.scrollToText('bracketLeg1Sketch, length = thickness)')
await page.getByText('extrude(thickness, bracketLeg1Sketch)').click() await page
.getByText('extrude(bracketLeg1Sketch, length = thickness)')
.click()
await page.keyboard.press('Backspace') await page.keyboard.press('Backspace')
// Ensure that a badge appears on the button // Ensure that a badge appears on the button
@ -107,7 +109,7 @@ test.describe('Code pane and errors', () => {
await editor.openPane() await editor.openPane()
// Go to our problematic code again (missing closing paren!) // Go to our problematic code again (missing closing paren!)
await editor.scrollToText('extrude(thickness, bracketLeg1Sketch') await editor.scrollToText('extrude(bracketLeg1Sketch, length = thickness')
// Ensure that a badge appears on the button // Ensure that a badge appears on the button
await expect(codePaneButtonHolder).toContainText('notification') await expect(codePaneButtonHolder).toContainText('notification')

View File

@ -4,7 +4,7 @@ import { executorInputPath, getUtils } from './test-utils'
import { KCL_DEFAULT_LENGTH } from 'lib/constants' import { KCL_DEFAULT_LENGTH } from 'lib/constants'
import path from 'path' import path from 'path'
test.describe('Command bar tests', () => { test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
test('Extrude from command bar selects extrude line after', async ({ test('Extrude from command bar selects extrude line after', async ({
page, page,
homePage, homePage,
@ -14,10 +14,10 @@ test.describe('Command bar tests', () => {
'persistCode', 'persistCode',
`sketch001 = startSketchOn('XY') `sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %) |> startProfileAt([-10, -10], %)
|> line([20, 0], %) |> line(end = [20, 0])
|> line([0, 20], %) |> line(end = [0, 20])
|> xLine(-20, %) |> xLine(-20, %)
|> close(%) |> close()
` `
) )
}) })
@ -32,7 +32,7 @@ test.describe('Command bar tests', () => {
await u.closeDebugPanel() await u.closeDebugPanel()
// Click the line of code for xLine. // Click the line of code for xLine.
await page.getByText(`close(%)`).click() // TODO remove this and reinstate // await topHorzSegmentClick() await page.getByText(`close()`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
await page.waitForTimeout(100) await page.waitForTimeout(100)
await page.getByRole('button', { name: 'Extrude' }).click() await page.getByRole('button', { name: 'Extrude' }).click()
@ -42,7 +42,47 @@ test.describe('Command bar tests', () => {
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
await page.waitForTimeout(200) await page.waitForTimeout(200)
await expect(page.locator('.cm-activeLine')).toHaveText( await expect(page.locator('.cm-activeLine')).toHaveText(
`extrude001 = extrude(${KCL_DEFAULT_LENGTH}, sketch001)` `extrude001 = extrude(sketch001, length = ${KCL_DEFAULT_LENGTH})`
)
})
// TODO: fix this test after the electron migration
test.fixme('Fillet from command bar', async ({ page, homePage }) => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`sketch001 = startSketchOn('XY')
|> startProfileAt([-5, -5], %)
|> line(end = [0, 10])
|> line(end = [10, 0])
|> line(end = [0, -10])
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
extrude001 = extrude(sketch001, length = -10)`
)
})
const u = await getUtils(page)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
const selectSegment = () => page.getByText(`line(end = [0, -10])`).click()
await selectSegment()
await page.waitForTimeout(100)
await page.getByRole('button', { name: 'Fillet' }).click()
await page.waitForTimeout(100)
await page.keyboard.press('Enter') // skip selection
await page.waitForTimeout(100)
await page.keyboard.press('Enter') // accept default radius
await page.waitForTimeout(100)
await page.keyboard.press('Enter') // submit
await page.waitForTimeout(100)
await expect(page.locator('.cm-activeLine')).toContainText(
`fillet({ radius = ${KCL_DEFAULT_LENGTH}, tags = [seg01] }, %)`
) )
}) })
@ -127,10 +167,10 @@ test.describe('Command bar tests', () => {
await expect(commandLevelArgButton).toHaveText('level: project') await expect(commandLevelArgButton).toHaveText('level: project')
}) })
test('Command bar keybinding works from code editor and can change a setting', async ({ test(
page, 'Command bar keybinding works from code editor and can change a setting',
homePage, { tag: ['@skipWin'] },
}) => { async ({ page, homePage }) => {
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
@ -162,10 +202,9 @@ test.describe('Command bar tests', () => {
await page.keyboard.press('ArrowDown') await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown') await page.keyboard.press('ArrowDown')
await page.keyboard.press('ArrowDown') await page.keyboard.press('ArrowDown')
await expect(page.getByRole('option', { name: 'system' })).toHaveAttribute( await expect(
'data-headlessui-state', page.getByRole('option', { name: 'system' })
'active' ).toHaveAttribute('data-headlessui-state', 'active')
)
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Check the toast appeared // Check the toast appeared
@ -174,7 +213,8 @@ test.describe('Command bar tests', () => {
).toBeVisible() ).toBeVisible()
// Check that the theme changed // Check that the theme changed
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`) await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
}) }
)
test('Can extrude from the command bar', async ({ page, homePage }) => { test('Can extrude from the command bar', async ({ page, homePage }) => {
await page.addInitScript(async () => { await page.addInitScript(async () => {
@ -183,10 +223,10 @@ test.describe('Command bar tests', () => {
`distance = sqrt(20) `distance = sqrt(20)
sketch001 = startSketchOn('XZ') sketch001 = startSketchOn('XZ')
|> startProfileAt([-6.95, 10.98], %) |> startProfileAt([-6.95, 10.98], %)
|> line([25.1, 0.41], %) |> line(end = [25.1, 0.41])
|> line([0.73, -20.93], %) |> line(end = [0.73, -20.93])
|> line([-23.44, 0.52], %) |> line(end = [-23.44, 0.52])
|> close(%) |> close()
` `
) )
}) })
@ -251,7 +291,7 @@ test.describe('Command bar tests', () => {
await u.waitForCmdReceive('extrude') await u.waitForCmdReceive('extrude')
await expect(page.locator('.cm-content')).toContainText( await expect(page.locator('.cm-content')).toContainText(
'extrude001 = extrude(distance001, sketch001)' 'extrude001 = extrude(sketch001, length = distance001)'
) )
}) })

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