Compare commits

..

1 Commits

Author SHA1 Message Date
e2d1475b1f Update exampleKcl.ts 2024-04-26 08:13:50 -04:00
92 changed files with 701 additions and 2169 deletions

View File

@ -3,4 +3,3 @@ VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
VITE_KC_SKIP_AUTH=false
VITE_KC_CONNECTION_TIMEOUT_MS=5000
VITE_KC_DEV_TOKEN="your token from dev.zoo.dev should go in .env.development.local"

View File

@ -130,9 +130,7 @@ jobs:
matrix:
os: [macos-14, ubuntu-latest, windows-latest]
env:
# Specific Apple Universal target for macos
TAURI_ARGS_MACOS: ${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }}
# Only build executable on linux (no appimage or deb)
TAURI_ARGS_UBUNTU: ${{ matrix.os == 'ubuntu-latest' && '--bundles' || '' }}
steps:
- uses: actions/checkout@v4
@ -239,9 +237,9 @@ jobs:
includeDebug: true
args: "${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}"
- name: Build for Mac TestFlight (nightly)
if: ${{ github.event_name == 'schedule' && matrix.os == 'macos-14' }}
shell: bash
- name: Mac App Store
if: ${{ env.BUILD_RELEASE == 'true' && matrix.os == 'macos-14' }}
run: |
unset APPLE_SIGNING_IDENTITY
unset APPLE_CERTIFICATE
@ -250,40 +248,45 @@ jobs:
profile="src-tauri/entitlements/Mac_App_Distribution.provisionprofile"
mkdir -p src-tauri/entitlements
echo -n "${APPLE_STORE_PROVISIONING_PROFILE}" | base64 --decode -o "${profile}"
echo "${APPLE_STORE_PROVISIONING_PROFILE}" | base64 --decode > "${profile}"
echo -n "${APPLE_STORE_DISTRIBUTION_CERT}" | base64 --decode -o "dist.cer"
echo -n "${APPLE_STORE_INSTALLER_CERT}" | base64 --decode -o "installer.cer"
echo "${APPLE_STORE_DISTRIBUTION_CERT}" | base64 --decode > "dist.cer"
echo "${APPLE_STORE_INSTALLER_CERT}" | base64 --decode > "installer.cer"
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
KEYCHAIN_PASSWORD="password"
# load the certificates into the keychain
# Create a custom keychain
security create-keychain -p gh_actions refine-build.keychain
# create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# Make the custom keychain default, so xcodebuild will use it for signing
security default-keychain -s refine-build.keychain
# import certificate to keychain
security import "dist.cer" -P "$APPLE_STORE_P12_PASSWORD" -k $KEYCHAIN_PATH -f pkcs12 -t cert -A
security import "installer.cer" -P "$APPLE_STORE_P12_PASSWORD" -k $KEYCHAIN_PATH -f pkcs12 -t cert -A
# Unlock the keychain
security unlock-keychain -p gh_actions refine-build.keychain
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
# Set keychain timeout to 1 hour for long builds
security set-keychain-settings -t 3600 -l ~/Library/Keychains/refine-build.keychain
# Add certificates to keychain and allow codesign to access them
security import "dist.cer" -k ~/Library/Keychains/refine-build.keychain -T /usr/bin/codesign
security import "installer.cer" -k ~/Library/Keychains/refine-build.keychain -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k gh_actions refine-build.keychain
target="universal-apple-darwin"
# Turn off the default target
# We don't want to install the updater for the apple store build
sed -i.bu "s/default =/# default =/" src-tauri/Cargo.toml
rm src-tauri/Cargo.toml.bu
git diff src-tauri/Cargo.toml
sed -i "s/default =/# default =/" src-tauri/Cargo.toml
yarn tauri build --target "${target}" --verbose
yarn tauri build --target "${target}" --verbose --config src-tauri/tauri.app-store.conf.json
ls -l src-tauri/target/${target}
ls -l src-tauri/target
ls -l src-tauri/target/${target}/release/bundle/macos
ls -l src-tauri/entitlements
app_path="src-tauri/target/${target}/release/bundle/macos/Zoo Modeling App.app"
build_name="src-tauri/target/${target}/release/bundle/macos/Zoo Modeling App.pkg"
cp_dir="src-tauri/target/${target}/release/bundle/macos/Zoo Modeling App.app/Contents/embedded.provisionprofile"
entitlements="src-tauri/entitlements/app-store.entitlements"
entitlements="src-tauri/entitlements/Zoo Modeling App.entitlements"
cp "${profile}" "${cp_dir}"
@ -293,38 +296,20 @@ jobs:
# Undo the changes to the Cargo.toml
git checkout src-tauri/Cargo.toml
env:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_STORE_PROVISIONING_PROFILE: ${{ secrets.APPLE_STORE_PROVISIONING_PROFILE }}
APPLE_STORE_DISTRIBUTION_CERT: ${{ secrets.APPLE_STORE_DISTRIBUTION_CERT }}
APPLE_STORE_INSTALLER_CERT: ${{ secrets.APPLE_STORE_INSTALLER_CERT }}
APPLE_STORE_P12_PASSWORD: ${{ secrets.APPLE_STORE_P12_PASSWORD }}
- name: 'Upload to Mac TestFlight (nightly)'
- name: 'Upload app to TestFlight'
uses: apple-actions/upload-testflight-build@v1
if: ${{ github.event_name == 'schedule' && matrix.os == 'macos-14' }}
if: ${{ env.BUILD_RELEASE == 'true' && matrix.os == 'macos-14' }}
with:
app-path: 'src-tauri/target/universal-apple-darwin/release/bundle/macos/Zoo Modeling App.pkg'
issuer-id: ${{ secrets.APPLE_STORE_ISSUER_ID }}
api-key-id: ${{ secrets.APPLE_STORE_API_KEY_ID }}
api-private-key: ${{ secrets.APPLE_STORE_API_PRIVATE_KEY }}
app-type: osx
- name: Clean up after Mac TestFlight (nightly)
if: ${{ github.event_name == 'schedule' && matrix.os == 'macos-14' }}
shell: bash
run: |
git status
# remove our target builds because we want to make sure the later build
# includes the updater, and that anything we changed with the target
# does not persist
rm -rf src-tauri/target
# Lets get rid of the info.plist for the normal mac builds since its
# being sketchy.
rm src-tauri/Info.plist
# We do this after the apple store because the apple store build is
# specific and we want to overwrite it with the this new build after and
@ -353,10 +338,11 @@ jobs:
with:
path: "${{ env.PREFIX }}/${{ env.MODE }}/bundle/*/*"
# TODO: re-enable linux e2e tests when possible
- name: Run e2e tests (linux only)
if: ${{ matrix.os == 'ubuntu-latest' && github.event_name != 'release' && github.event_name != 'schedule' }}
if: false
run: |
cargo install tauri-driver --force
cargo install tauri-driver
source .env.${{ env.BUILD_RELEASE == 'true' && 'production' || 'development' }}
export VITE_KC_API_BASE_URL
xvfb-run yarn test:e2e:tauri

View File

@ -17,11 +17,11 @@ jobs:
name: Read Cut release PR info and create release
with:
script: |
const { owner, repo } = context.repo
const { owner, repo, sha } = context.repo
const pulls = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner,
repo,
commit_sha: context.sha,
commit_sha: sha,
})
const { title, body } = pulls.data[0]
const version = title.split('Cut release ')[1]

View File

@ -72,13 +72,7 @@ finally, to run the web app only, run:
yarn start
```
If you're not an KittyCAD employee you won't be able to access the dev environment, you should copy everything from `.env.production` to `.env.development` to make it point to production instead, then when you navigate to `localhost:3000` the easiest way to sign in is to paste `localStorage.setItem('TOKEN_PERSIST_KEY', "your-token-from-https://zoo.dev/account/api-tokens")` replacing the with a real token from https://zoo.dev/account/api-tokens ofcourse, then navigate to localhost:3000 again. Note that navigating to localhost:3000/signin removes your token so you will need to set the token again.
### Development environment variables
The Copilot LSP plugin in the editor requires a Zoo API token to run. In production, we authenticate this with a token via cookie in the browser and device auth token in the desktop environment, but this token is inaccessible in the dev browser version because the cookie is considered "cross-site" (from `localhost` to `dev.zoo.dev`). There is an optional environment variable called `VITE_KC_DEV_TOKEN` that you can populate with a dev token in a `.env.development.local` file to not check it into Git, which will use that token instead of other methods for the LSP service.
### Developing in Chrome
## Developing in Chrome
Chrome is in the process of rolling out a new default which
[blocks Third-Party Cookies](https://developer.chrome.com/en/docs/privacy-sandbox/third-party-cookie-phase-out/).

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
import { test, expect } from '@playwright/test'
import { makeTemplate, getUtils } from './test-utils'
import { getUtils } from './test-utils'
import waitOn from 'wait-on'
import { roundOff } from 'lib/utils'
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
@ -8,8 +8,7 @@ import {
TEST_SETTINGS,
TEST_SETTINGS_KEY,
TEST_SETTINGS_CORRUPTED,
TEST_SETTINGS_ONBOARDING_EXPORT,
TEST_SETTINGS_ONBOARDING_START,
TEST_SETTINGS_ONBOARDING,
} from './storageStates'
import * as TOML from '@iarna/toml'
@ -279,7 +278,7 @@ test('if you write invalid kcl you get inlined errors', async ({ page }) => {
const bottomAng = 25
*/
await page.click('.cm-content')
await page.keyboard.type('$ error')
await page.keyboard.type('# error')
// press arrows to clear autocomplete
await page.keyboard.press('ArrowLeft')
@ -296,10 +295,10 @@ test('if you write invalid kcl you get inlined errors', async ({ page }) => {
// error text on hover
await page.hover('.cm-lint-marker-error')
await expect(page.getByText("found unknown token '$'")).toBeVisible()
await expect(page.getByText("found unknown token '#'")).toBeVisible()
// select the line that's causing the error and delete it
await page.getByText('$ error').click()
await page.getByText('# error').click()
await page.keyboard.press('End')
await page.keyboard.down('Shift')
await page.keyboard.press('Home')
@ -528,10 +527,6 @@ test.describe('Can create sketches on all planes and their back sides', () => {
})
test('Auto complete works', async ({ page }) => {
test.skip(
true,
'CORS issue stopping the kcl lsp from working, enable again later'
)
const u = getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
@ -685,45 +680,6 @@ test('Project settings can be set and override user settings', async ({
await expect(page.locator('select[name="app-theme"]')).toHaveValue('light')
})
test('Click through each onboarding step', async ({ page }) => {
const u = getUtils(page)
// Override beforeEach test setup
await page.addInitScript(
async ({ settingsKey, settings }) => {
// Give no initial code, so that the onboarding start is shown immediately
localStorage.setItem('persistCode', '')
localStorage.setItem(settingsKey, settings)
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING_START }),
}
)
await page.setViewportSize({ width: 1200, height: 1080 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
// Test that the onboarding pane loaded
await expect(page.getByText('Welcome to Modeling App! This')).toBeVisible()
const nextButton = page.getByTestId('onboarding-next')
while ((await nextButton.innerText()) !== 'Finish') {
await expect(nextButton).toBeVisible()
await nextButton.click()
}
// Finish the onboarding
await expect(nextButton).toBeVisible()
await nextButton.click()
// Test that the onboarding pane is gone
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
await expect(page.url()).not.toContain('onboarding')
})
test('Onboarding redirects and code updating', async ({ page }) => {
const u = getUtils(page)
@ -736,7 +692,7 @@ test('Onboarding redirects and code updating', async ({ page }) => {
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING_EXPORT }),
settings: TOML.stringify({ settings: TEST_SETTINGS_ONBOARDING }),
}
)
@ -1001,13 +957,13 @@ test.describe('Command bar tests', () => {
localStorage.setItem(
'persistCode',
`const distance = sqrt(20)
const part001 = startSketchOn('-XZ')
|> startProfileAt([-6.95, 10.98], %)
|> line([25.1, 0.41], %)
|> line([0.73, -20.93], %)
|> line([-23.44, 0.52], %)
|> close(%)
`
const part001 = startSketchOn('-XZ')
|> startProfileAt([-6.95, 4.98], %)
|> line([25.1, 0.41], %)
|> line([0.73, -14.93], %)
|> line([-23.44, 0.52], %)
|> close(%)
`
)
})
@ -1024,6 +980,7 @@ test.describe('Command bar tests', () => {
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await u.clearCommandLogs()
await page.getByText('|> line([0.73, -14.93], %)').click()
await page.getByRole('button', { name: 'Extrude' }).isEnabled()
let cmdSearchBar = page.getByPlaceholder('Search commands')
@ -1033,12 +990,6 @@ test.describe('Command bar tests', () => {
// Search for extrude command and choose it
await page.getByRole('option', { name: 'Extrude' }).click()
// Assert that we're on the selection step
await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled()
// Select a face
await page.mouse.move(700, 200)
await page.mouse.click(700, 200)
// Assert that we're on the distance step
await expect(page.getByRole('button', { name: 'distance' })).toBeDisabled()
@ -1072,9 +1023,9 @@ test.describe('Command bar tests', () => {
`const distance = sqrt(20)
const distance001 = 5 + 7
const part001 = startSketchOn('-XZ')
|> startProfileAt([-6.95, 10.98], %)
|> startProfileAt([-6.95, 4.98], %)
|> line([25.1, 0.41], %)
|> line([0.73, -20.93], %)
|> line([0.73, -14.93], %)
|> line([-23.44, 0.52], %)
|> close(%)
|> extrude(distance001, %)`.replace(/(\r\n|\n|\r)/gm, '') // remove newlines
@ -1399,7 +1350,7 @@ test('Deselecting line tool should mean nothing happens on click', async ({
`const part001 = startSketchOn('-XZ')`
)
await page.waitForTimeout(600)
await page.waitForTimeout(300)
let previousCodeContent = await page.locator('.cm-content').innerText()
@ -1698,13 +1649,14 @@ test('Sketch on face', async ({ page }) => {
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
previousCodeContent = await page.locator('.cm-content').innerText()
const result = makeTemplate`const part002 = startSketchOn(part001, 'seg01')
|> startProfileAt([-12.83, 6.7], %)
|> line([${[2.28, 2.35]}, -${0.07}], %)
|> line([-3.05, -1.47], %)
|> close(%)`
await expect(page.locator('.cm-content')).toHaveText(result.regExp)
await expect(page.locator('.cm-content'))
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|> startProfileAt([-12.83, 6.7], %)
|> line([${process?.env?.CI ? 2.28 : 2.28}, -${
process?.env?.CI ? 0.07 : 0.07
}], %)
|> line([-3.05, -1.47], %)
|> close(%)`)
// exit sketch
await u.openAndClearDebugPanel()
@ -1723,9 +1675,15 @@ test('Sketch on face', async ({ page }) => {
await expect(page.getByText('Confirm Extrude')).toBeVisible()
await page.keyboard.press('Enter')
const result2 = result.genNext`
|> extrude(${[5, 5]} + 7, %)`
await expect(page.locator('.cm-content')).toHaveText(result2.regExp)
await expect(page.locator('.cm-content'))
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|> startProfileAt([-12.83, 6.7], %)
|> line([${process?.env?.CI ? 2.28 : 2.28}, -${
process?.env?.CI ? 0.07 : 0.07
}], %)
|> line([-3.05, -1.47], %)
|> close(%)
|> extrude(5 + 7, %)`)
})
test('Can code mod a line length', async ({ page }) => {

View File

@ -507,7 +507,7 @@ test('Draft rectangles should look right', async ({ page, context }) => {
`const part001 = startSketchOn('-XZ')`
)
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
await u.closeDebugPanel()
const startXPx = 600
@ -597,15 +597,12 @@ test.describe('Client side scene scale should match engine scale', () => {
// exit sketch
await u.openAndClearDebugPanel()
await u.doAndWaitForImageDiff(
() => page.getByRole('button', { name: 'Exit Sketch' }).click(),
200
)
await page.getByRole('button', { name: 'Exit Sketch' }).click()
// wait for execution done
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
await page.waitForTimeout(300)
await page.waitForTimeout(200)
// second screen shot should look almost identical, i.e. scale should be the same.
await expect(page).toHaveScreenshot({
@ -699,15 +696,12 @@ test.describe('Client side scene scale should match engine scale', () => {
// exit sketch
await u.openAndClearDebugPanel()
await u.doAndWaitForImageDiff(
() => page.getByRole('button', { name: 'Exit Sketch' }).click(),
200
)
await page.getByRole('button', { name: 'Exit Sketch' }).click()
// wait for execution done
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.clearAndCloseDebugPanel()
await page.waitForTimeout(300)
await page.waitForTimeout(200)
// second screen shot should look almost identical, i.e. scale should be the same.
await expect(page).toHaveScreenshot({

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -22,16 +22,11 @@ export const TEST_SETTINGS = {
},
} satisfies Partial<SaveSettingsPayload>
export const TEST_SETTINGS_ONBOARDING_EXPORT = {
export const TEST_SETTINGS_ONBOARDING = {
...TEST_SETTINGS,
app: { ...TEST_SETTINGS.app, onboardingStatus: '/export' },
} satisfies Partial<SaveSettingsPayload>
export const TEST_SETTINGS_ONBOARDING_START = {
...TEST_SETTINGS,
app: { ...TEST_SETTINGS.app, onboardingStatus: '' },
} satisfies Partial<SaveSettingsPayload>
export const TEST_SETTINGS_CORRUPTED = {
app: {
theme: Themes.Dark,

View File

@ -182,76 +182,3 @@ export function getUtils(page: Page) {
}),
}
}
type TemplateOptions = Array<number | Array<number>>
type makeTemplateReturn = {
regExp: RegExp
genNext: (
templateParts: TemplateStringsArray,
...options: TemplateOptions
) => makeTemplateReturn
}
const escapeRegExp = (string: string) => {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // $& means the whole matched string
}
const _makeTemplate = (
templateParts: TemplateStringsArray,
...options: TemplateOptions
) => {
const length = Math.max(...options.map((a) => (Array.isArray(a) ? a[0] : 0)))
let reExpTemplate = ''
for (let i = 0; i < length; i++) {
const currentStr = templateParts.map((str, index) => {
const currentOptions = options[index]
return (
escapeRegExp(str) +
String(
Array.isArray(currentOptions)
? currentOptions[i]
: typeof currentOptions === 'number'
? currentOptions
: ''
)
)
})
reExpTemplate += '|' + currentStr.join('')
}
return new RegExp(reExpTemplate)
}
/**
* Tool for making templates to match code snippets in the editor with some fudge factor,
* as there's some level of non-determinism.
*
* Usage is as such:
* ```typescript
* const result = makeTemplate`const myVar = aFunc(${[1, 2, 3]})`
* await expect(page.locator('.cm-content')).toHaveText(result.regExp)
* ```
* Where the value `1`, `2` or `3` are all valid and should make the test pass.
*
* The function also has a `genNext` function that allows you to chain multiple templates
* together without having to repeat previous parts of the template.
* ```typescript
* const result2 = result.genNext`const myVar2 = aFunc(${[4, 5, 6]})`
* ```
*/
export const makeTemplate: (
templateParts: TemplateStringsArray,
...values: TemplateOptions
) => makeTemplateReturn = (templateParts, ...options) => {
return {
regExp: _makeTemplate(templateParts, ...options),
genNext: (
nextTemplateParts: TemplateStringsArray,
...nextOptions: TemplateOptions
) =>
makeTemplate(
[...templateParts, ...nextTemplateParts] as any as TemplateStringsArray,
[...options, ...nextOptions] as any
),
}
}

View File

@ -2,7 +2,7 @@ import { browser, $, expect } from '@wdio/globals'
import fs from 'fs/promises'
const documentsDir = `${process.env.HOME}/Documents`
const userSettingsDir = `${process.env.HOME}/.config/dev.zoo.modeling-app`
const userSettingsFile = `${process.env.HOME}/.config/dev.zoo.modeling-app/user.toml`
const defaultProjectDir = `${documentsDir}/zoo-modeling-app-projects`
const newProjectDir = `${documentsDir}/a-different-directory`
const userCodeDir = '/tmp/kittycad_user_code'
@ -29,10 +29,8 @@ describe('ZMA (Tauri, Linux)', () => {
// Clean up filesystem from previous tests
await new Promise((resolve) => setTimeout(resolve, 100))
await fs.rm(defaultProjectDir, { force: true, recursive: true })
await fs.rm(newProjectDir, { force: true, recursive: true })
await fs.rm(userCodeDir, { force: true })
await fs.rm(userSettingsDir, { force: true, recursive: true })
await fs.mkdir(defaultProjectDir, { recursive: true })
await fs.rm(userSettingsFile, { force: true })
await fs.mkdir(newProjectDir, { recursive: true })
const signInButton = await $('[data-testid="sign-in-button"]')
@ -72,7 +70,6 @@ describe('ZMA (Tauri, Linux)', () => {
console.log(cr.status)
// Now should be signed in
await new Promise((resolve) => setTimeout(resolve, 10000))
const newFileButton = await $('[data-testid="home-new-file"]')
expect(await newFileButton.getText()).toEqual('New project')
})
@ -120,8 +117,8 @@ describe('ZMA (Tauri, Linux)', () => {
it('opens the new file and expects a loading stream', async () => {
const projectLink = await $('[data-testid="project-link"]')
await click(projectLink)
const errorText = await $('[data-testid="unexpected-error"]')
expect(await errorText.getText()).toContain('unexpected error')
const loadingText = await $('[data-testid="loading-stream"]')
expect(await loadingText.getText()).toContain('Loading stream...')
await browser.execute('window.location.href = "tauri://localhost/home"')
})

View File

@ -15,7 +15,7 @@
<script
defer
data-domain="app.zoo.dev"
src="https://plausible.corp.zoo.dev/js/script.tagged-events.js"
src="https://plausible.corp.zoo.dev/js/script.js"
></script>
<title>Zoo Modeling App</title>
</head>

View File

@ -1,6 +1,6 @@
{
"name": "untitled-app",
"version": "0.20.2",
"version": "0.19.4",
"private": true,
"dependencies": {
"@codemirror/autocomplete": "^6.16.0",
@ -10,7 +10,7 @@
"@fortawesome/react-fontawesome": "^0.2.0",
"@headlessui/react": "^1.7.19",
"@headlessui/tailwindcss": "^0.2.0",
"@kittycad/lib": "^0.0.60",
"@kittycad/lib": "^0.0.58",
"@lezer/javascript": "^1.4.9",
"@open-rpc/client-js": "^1.8.1",
"@react-hook/resize-observer": "^1.2.6",
@ -123,7 +123,6 @@
"@tauri-apps/cli": "^2.0.0-beta.13",
"@types/crypto-js": "^4.2.2",
"@types/debounce-promise": "^3.1.9",
"@types/mocha": "^10.0.6",
"@types/pixelmatch": "^5.2.6",
"@types/pngjs": "^6.0.4",
"@types/react-modal": "^3.16.3",

View File

@ -27,7 +27,7 @@ export default defineConfig({
baseURL: 'http://localhost:3000',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'retain-on-failure',
trace: 'on-first-retry',
},
/* Configure projects for major browsers */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 867 KiB

After

Width:  |  Height:  |  Size: 142 KiB

326
src-tauri/Cargo.lock generated
View File

@ -38,17 +38,6 @@ dependencies = [
"cpufeatures",
]
[[package]]
name = "ahash"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
dependencies = [
"getrandom 0.2.14",
"once_cell",
"version_check",
]
[[package]]
name = "ahash"
version = "0.8.11"
@ -92,24 +81,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_log-sys"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937"
[[package]]
name = "android_logger"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c494134f746c14dc653a35a4ea5aca24ac368529da5370ecf41fe0341c35772f"
dependencies = [
"android_log-sys",
"env_logger",
"log",
"once_cell",
]
[[package]]
name = "android_system_properties"
version = "0.1.5"
@ -183,7 +154,6 @@ dependencies = [
"anyhow",
"kcl-lib",
"kittycad",
"log",
"oauth2",
"serde_json",
"tauri",
@ -193,14 +163,12 @@ dependencies = [
"tauri-plugin-dialog",
"tauri-plugin-fs",
"tauri-plugin-http",
"tauri-plugin-log",
"tauri-plugin-os",
"tauri-plugin-process",
"tauri-plugin-shell",
"tauri-plugin-updater",
"tokio",
"toml 0.8.12",
"url",
]
[[package]]
@ -212,12 +180,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "ashpd"
version = "0.8.1"
@ -328,9 +290,9 @@ dependencies = [
[[package]]
name = "async-recursion"
version = "1.1.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5"
dependencies = [
"proc-macro2",
"quote",
@ -453,9 +415,9 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "base64"
version = "0.22.1"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51"
[[package]]
name = "base64ct"
@ -544,30 +506,6 @@ dependencies = [
"tracing",
]
[[package]]
name = "borsh"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbe5b10e214954177fb1dc9fbd20a1a2608fe99e6c832033bdc7cea287a20d77"
dependencies = [
"borsh-derive",
"cfg_aliases 0.1.1",
]
[[package]]
name = "borsh-derive"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7a8646f94ab393e43e8b35a2558b1624bed28b97ee09c5d15456e3c9463f46d"
dependencies = [
"once_cell",
"proc-macro-crate 3.1.0",
"proc-macro2",
"quote",
"syn 2.0.60",
"syn_derive",
]
[[package]]
name = "brotli"
version = "3.5.0"
@ -595,7 +533,7 @@ version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d43b38e074cc0de2957f10947e376a1d88b9c4dbab340b590800cc1b2e066b2"
dependencies = [
"ahash 0.8.11",
"ahash",
"base64 0.13.1",
"bitvec",
"chrono",
@ -617,39 +555,6 @@ version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "byte-unit"
version = "5.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ac19bdf0b2665407c39d82dbc937e951e7e2001609f0fb32edd0af45a2d63e"
dependencies = [
"rust_decimal",
"serde",
"utf8-width",
]
[[package]]
name = "bytecheck"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2"
dependencies = [
"bytecheck_derive",
"ptr_meta",
"simdutf8",
]
[[package]]
name = "bytecheck_derive"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "bytemuck"
version = "1.15.0"
@ -1388,16 +1293,6 @@ dependencies = [
"syn 2.0.60",
]
[[package]]
name = "env_logger"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
dependencies = [
"log",
"regex",
]
[[package]]
name = "equivalent"
version = "1.0.1"
@ -1471,15 +1366,6 @@ dependencies = [
"simd-adler32",
]
[[package]]
name = "fern"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee"
dependencies = [
"log",
]
[[package]]
name = "field-offset"
version = "0.3.6"
@ -2077,9 +1963,6 @@ name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash 0.7.8",
]
[[package]]
name = "hashbrown"
@ -2536,13 +2419,13 @@ dependencies = [
[[package]]
name = "kcl-lib"
version = "0.1.54"
version = "0.1.53"
dependencies = [
"anyhow",
"approx",
"async-recursion",
"async-trait",
"base64 0.22.1",
"base64 0.22.0",
"bson",
"chrono",
"clap",
@ -2595,9 +2478,9 @@ dependencies = [
[[package]]
name = "kittycad"
version = "0.3.1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6e12eb45fd9a28c8e99dbdef54556246b39acee14e4aa6f0fc43636caa62d9"
checksum = "ddc922f0da3abc22661bf49423c9bfcc02ce6ae92dae007ede6990874789545b"
dependencies = [
"anyhow",
"async-trait",
@ -2774,7 +2657,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
dependencies = [
"serde",
"value-bag",
]
[[package]]
@ -3087,15 +2969,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "num_threads"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
dependencies = [
"libc",
]
[[package]]
name = "oauth2"
version = "4.4.2"
@ -3745,26 +3618,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "ptr_meta"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1"
dependencies = [
"ptr_meta_derive",
]
[[package]]
name = "ptr_meta_derive"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "quick-xml"
version = "0.31.0"
@ -3982,15 +3835,6 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "rend"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c"
dependencies = [
"bytecheck",
]
[[package]]
name = "reqwest"
version = "0.11.27"
@ -4041,7 +3885,7 @@ version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10"
dependencies = [
"base64 0.22.1",
"base64 0.22.0",
"bytes",
"encoding_rs",
"futures-core",
@ -4199,35 +4043,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "rkyv"
version = "0.7.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0"
dependencies = [
"bitvec",
"bytecheck",
"bytes",
"hashbrown 0.12.3",
"ptr_meta",
"rend",
"rkyv_derive",
"seahash",
"tinyvec",
"uuid",
]
[[package]]
name = "rkyv_derive"
version = "0.7.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "ropey"
version = "1.6.1"
@ -4238,22 +4053,6 @@ dependencies = [
"str_indices",
]
[[package]]
name = "rust_decimal"
version = "1.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1790d1c4c0ca81211399e0e0af16333276f375209e71a37b67698a373db5b47a"
dependencies = [
"arrayvec",
"borsh",
"bytes",
"num-traits",
"rand 0.8.5",
"rkyv",
"serde",
"serde_json",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
@ -4336,7 +4135,7 @@ version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
dependencies = [
"base64 0.22.1",
"base64 0.22.0",
"rustls-pki-types",
]
@ -4399,9 +4198,9 @@ dependencies = [
[[package]]
name = "schemars"
version = "0.8.17"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f55c82c700538496bdc329bb4918a81f87cc8888811bd123cf325a0f2f8d309"
checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29"
dependencies = [
"bigdecimal",
"bytes",
@ -4417,14 +4216,14 @@ dependencies = [
[[package]]
name = "schemars_derive"
version = "0.8.17"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83263746fe5e32097f06356968a077f96089739c927a61450efa069905eec108"
checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 2.0.60",
"syn 1.0.109",
]
[[package]]
@ -4449,12 +4248,6 @@ dependencies = [
"untrusted",
]
[[package]]
name = "seahash"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]]
name = "security-framework"
version = "2.10.0"
@ -4509,9 +4302,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.200"
version = "1.0.198"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f"
checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
dependencies = [
"serde_derive",
]
@ -4527,9 +4320,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.200"
version = "1.0.198"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb"
checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
dependencies = [
"proc-macro2",
"quote",
@ -4538,13 +4331,13 @@ dependencies = [
[[package]]
name = "serde_derive_internals"
version = "0.29.0"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
"syn 1.0.109",
]
[[package]]
@ -4743,12 +4536,6 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "simdutf8"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
[[package]]
name = "siphasher"
version = "0.3.11"
@ -5006,18 +4793,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn_derive"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.60",
]
[[package]]
name = "sync_wrapper"
version = "0.1.2"
@ -5162,9 +4937,9 @@ dependencies = [
[[package]]
name = "tauri"
version = "2.0.0-beta.17"
version = "2.0.0-beta.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fedd5490eddf117253945f0baedafded43474c971cba546a818f527d5c26266"
checksum = "4d411ebb670bbe5cf948f6c24978632937329748b499de1619ab55ad31512652"
dependencies = [
"anyhow",
"bytes",
@ -5176,7 +4951,7 @@ dependencies = [
"getrandom 0.2.14",
"glob",
"gtk",
"heck 0.5.0",
"heck 0.4.1",
"http 1.1.0",
"jni",
"libc",
@ -5237,7 +5012,7 @@ version = "2.0.0-beta.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b383f341efb803852b0235a2f330ca90c4c113f422dd6d646b888685b372cace"
dependencies = [
"base64 0.22.1",
"base64 0.22.0",
"brotli",
"ico",
"json-patch",
@ -5377,27 +5152,6 @@ dependencies = [
"urlpattern",
]
[[package]]
name = "tauri-plugin-log"
version = "2.0.0-beta.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97718db0d981b03b7b1257c22f699ff46639220c5acb4510ac9696437afc93f8"
dependencies = [
"android_logger",
"byte-unit",
"cocoa",
"fern",
"log",
"objc",
"serde",
"serde_json",
"serde_repr",
"swift-rs",
"tauri",
"tauri-plugin",
"time",
]
[[package]]
name = "tauri-plugin-os"
version = "2.0.0-beta.3"
@ -5452,7 +5206,7 @@ version = "2.0.0-beta.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f34be6851c7e84ca99b3bddd57e033d55d8bfca1dd153d6e8d18e9f1fb95469"
dependencies = [
"base64 0.22.1",
"base64 0.22.0",
"dirs-next",
"flate2",
"futures-util",
@ -5476,9 +5230,9 @@ dependencies = [
[[package]]
name = "tauri-runtime"
version = "2.0.0-beta.14"
version = "2.0.0-beta.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "148b6e6aff8e63fe5d4ae1d50159d50cfc0b4309abdeca64833c887c6b5631ef"
checksum = "c7439729d0107c9797764919c39c4a4cc3af64306faaa48271da50d8eb4c0283"
dependencies = [
"dpi",
"gtk",
@ -5495,9 +5249,9 @@ dependencies = [
[[package]]
name = "tauri-runtime-wry"
version = "2.0.0-beta.14"
version = "2.0.0-beta.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "398d065c6e0fbf3c4304583759b6e153bc1e0daeb033bede6834ebe4df371fc3"
checksum = "0c38dcfa7f8c2b2e344c7401972e0ddaaec4fa655666788d94b1852d6c4a7fe8"
dependencies = [
"cocoa",
"gtk",
@ -5638,9 +5392,7 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
"deranged",
"itoa 1.0.11",
"libc",
"num-conv",
"num_threads",
"powerfmt",
"serde",
"time-core",
@ -6210,12 +5962,6 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "utf8-width"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3"
[[package]]
name = "utf8parse"
version = "0.2.1"
@ -6270,12 +6016,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "value-bag"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101"
[[package]]
name = "version-compare"
version = "0.2.0"
@ -6922,7 +6662,7 @@ version = "0.39.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e180ac2740d6cb4d5cec0abf63eacbea90f1b7e5e3803043b13c1c84c4b7884"
dependencies = [
"base64 0.22.1",
"base64 0.22.0",
"block",
"cocoa",
"core-graphics",

View File

@ -17,7 +17,6 @@ tauri-build = { version = "2.0.0-beta.13", features = [] }
anyhow = "1"
kcl-lib = { version = "0.1.53", path = "../src/wasm-lib/kcl" }
kittycad = "0.3.0"
log = "0.4.21"
oauth2 = "4.4.2"
serde_json = "1.0"
tauri = { version = "2.0.0-beta.15", features = [ "devtools", "unstable"] }
@ -26,14 +25,12 @@ tauri-plugin-deep-link = { version = "2.0.0-beta.3" }
tauri-plugin-dialog = { version = "2.0.0-beta.6" }
tauri-plugin-fs = { version = "2.0.0-beta.6" }
tauri-plugin-http = { version = "2.0.0-beta.6" }
tauri-plugin-log = { version = "2.0.0-beta.4" }
tauri-plugin-os = { version = "2.0.0-beta.2" }
tauri-plugin-process = { version = "2.0.0-beta.2" }
tauri-plugin-shell = { version = "2.0.0-beta.2" }
tauri-plugin-updater = { version = "2.0.0-beta.4" }
tokio = { version = "1.37.0", features = ["time", "fs", "process"] }
toml = "0.8.2"
url = "2.5.0"
[features]
default = ["updater"]

View File

@ -2,24 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>NSDesktopFolderUsageDescription</key>
<string>Zoo Modeling App accesses the Desktop to load and save your project files and/or exported files here</string>
<key>NSDocumentsFolderUsageDescription</key>
<string>Zoo Modeling App accesses the Documents folder to load and save your project files and/or exported files here</string>
<key>NSDownloadsFolderUsageDescription</key>
<string>Zoo Modeling App accesses the Downloads folder to load and save your project files and/or exported files here</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>DTXcode</key>
<string>1501</string>
<key>DTXcodeBuild</key>
<string>15A507</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
@ -32,178 +14,14 @@
</array>
</dict>
</array>
<key>LSFileQuarantineEnabled</key>
<false/>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>dev.zoo.kcl</string>
</array>
<key>CFBundleTypeName</key>
<string>KCL</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>dev.zoo.toml</string>
</array>
<key>CFBundleTypeName</key>
<string>TOML</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>dev.zoo.gltf</string>
</array>
<key>CFBundleTypeName</key>
<string>glTF</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>dev.zoo.glb</string>
</array>
<key>CFBundleTypeName</key>
<string>glb</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>dev.zoo.step</string>
</array>
<key>CFBundleTypeName</key>
<string>STEP</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>dev.zoo.fbx</string>
</array>
<key>CFBundleTypeName</key>
<string>FBX</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>dev.zoo.sldprt</string>
</array>
<key>CFBundleTypeName</key>
<string>Solidworks Part</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>public.geometry-definition-format</string>
</array>
<key>CFBundleTypeName</key>
<string>OBJ</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>public.polygon-file-format</string>
</array>
<key>CFBundleTypeName</key>
<string>PLY</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>public.standard-tesselated-geometry-format</string>
</array>
<key>CFBundleTypeName</key>
<string>STL</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>public.folder</string>
</array>
<key>CFBundleTypeName</key>
<string>Folders</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
</dict>
</array>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>dev.zoo.kcl</string>
<key>UTTypeReferenceURL</key>
<string>https://zoo.dev/docs/kcl</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.source-code</string>
<string>public.data</string>
<string>public.text</string>
<string>public.plain-text</string>
<string>public.3d-content</string>
<string>public.script</string>
</array>
<key>UTTypeDescription</key>
<string>KCL (KittyCAD Language) document</string>
@ -219,158 +37,6 @@
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>dev.zoo.gltf</string>
<key>UTTypeReferenceURL</key>
<string>https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.text</string>
<string>public.plain-text</string>
<string>public.3d-content</string>
<string>public.json</string>
</array>
<key>UTTypeDescription</key>
<string>Graphics Library Transmission Format (glTF)</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>gltf</string>
</array>
<key>public.mime-type</key>
<array>
<string>model/gltf+json</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>dev.zoo.glb</string>
<key>UTTypeReferenceURL</key>
<string>https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.3d-content</string>
</array>
<key>UTTypeDescription</key>
<string>Graphics Library Transmission Format (glTF) binary</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>glb</string>
</array>
<key>public.mime-type</key>
<array>
<string>model/gltf-binary</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>dev.zoo.step</string>
<key>UTTypeReferenceURL</key>
<string>https://www.loc.gov/preservation/digital/formats/fdd/fdd000448.shtml</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.3d-content</string>
<string>public.text</string>
<string>public.plain-text</string>
</array>
<key>UTTypeDescription</key>
<string>STEP-file, ISO 10303-21</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>step</string>
<string>stp</string>
</array>
<key>public.mime-type</key>
<array>
<string>model/step</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>dev.zoo.sldprt</string>
<key>UTTypeReferenceURL</key>
<string>https://docs.fileformat.com/cad/sldprt/</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.3d-content</string>
</array>
<key>UTTypeDescription</key>
<string>Solidworks Part</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>sldprt</string>
</array>
<key>public.mime-type</key>
<array>
<string>model/vnd.solidworks.sldprt</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>dev.zoo.fbx</string>
<key>UTTypeReferenceURL</key>
<string>https://en.wikipedia.org/wiki/FBX</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.3d-content</string>
</array>
<key>UTTypeDescription</key>
<string>Autodesk Filmbox (FBX) format</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>fbx</string>
<string>fbxb</string>
</array>
<key>public.mime-type</key>
<array>
<string>model/vnd.autodesk.fbx</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>dev.zoo.toml</string>
<key>UTTypeReferenceURL</key>
<string>https://toml.io/en/</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.text</string>
<string>public.plain-text</string>
</array>
<key>UTTypeDescription</key>
<string>Tom's Obvious Minimal Language</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>kcl</string>
</array>
<key>public.mime-type</key>
<array>
<string>text/toml</string>
</array>
</dict>
</dict>
</array>
</dict>
</plist>

View File

@ -9,7 +9,6 @@
"permissions": [
"cli:default",
"deep-link:default",
"log:default",
"path:default",
"event:default",
"window:default",

View File

@ -4,10 +4,10 @@
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>

View File

@ -12,7 +12,7 @@ use anyhow::Result;
use kcl_lib::settings::types::{
file::{FileEntry, Project, ProjectRoute, ProjectState},
project::ProjectConfiguration,
Configuration,
Configuration, DEFAULT_PROJECT_KCL_FILE,
};
use oauth2::TokenResponse;
use tauri::{ipc::InvokeError, Manager};
@ -226,7 +226,7 @@ async fn read_dir_recursive(path: &str) -> Result<FileEntry, InvokeError> {
/// The string returned from this method is the access token.
#[tauri::command]
async fn login(app: tauri::AppHandle, host: &str) -> Result<String, InvokeError> {
log::debug!("Logging in...");
println!("Logging in...");
// Do an OAuth 2.0 Device Authorization Grant dance to get a token.
let device_auth_url = oauth2::DeviceAuthorizationUrl::new(format!("{host}/oauth2/device/auth"))
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
@ -265,7 +265,7 @@ async fn login(app: tauri::AppHandle, host: &str) -> Result<String, InvokeError>
// and bypass the shell::open call as it fails on GitHub Actions.
let e2e_tauri_enabled = env::var("E2E_TAURI_ENABLED").is_ok();
if e2e_tauri_enabled {
log::warn!("E2E_TAURI_ENABLED is set, won't open {} externally", auth_uri.secret());
println!("E2E_TAURI_ENABLED is set, won't open {} externally", auth_uri.secret());
tokio::fs::write("/tmp/kittycad_user_code", details.user_code().secret())
.await
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
@ -308,7 +308,7 @@ async fn get_user(token: &str, hostname: &str) -> Result<kittycad::types::User,
baseurl = format!("http://{host}")
}
}
log::debug!("Getting user info...");
println!("Getting user info...");
// use kittycad library to fetch the user info from /user/me
let mut client = kittycad::Client::new(token);
@ -334,7 +334,7 @@ fn show_in_folder(path: &str) -> Result<(), InvokeError> {
#[cfg(not(unix))]
{
Command::new("explorer")
.args(["/select,", path]) // The comma after select is not a typo
.args(["/select,", &path]) // The comma after select is not a typo
.spawn()
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
}
@ -342,7 +342,7 @@ fn show_in_folder(path: &str) -> Result<(), InvokeError> {
#[cfg(unix)]
{
Command::new("open")
.args(["-R", path])
.args(["-R", &path])
.spawn()
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
}
@ -350,33 +350,6 @@ fn show_in_folder(path: &str) -> Result<(), InvokeError> {
Ok(())
}
#[allow(dead_code)]
fn open_url_sync(app: &tauri::AppHandle, url: &url::Url) {
log::debug!("Opening URL: {:?}", url);
let cloned_url = url.clone();
let runner: tauri::async_runtime::JoinHandle<Result<ProjectState>> = tauri::async_runtime::spawn(async move {
let url_str = cloned_url.path().to_string();
log::debug!("Opening URL path : {}", url_str);
let path = Path::new(url_str.as_str());
ProjectState::new_from_path(path.to_path_buf()).await
});
// Block on the handle.
match tauri::async_runtime::block_on(runner) {
Ok(Ok(store)) => {
// Create a state object to hold the project.
app.manage(state::Store::new(store));
}
Err(e) => {
log::warn!("Error opening URL:{} {:?}", url, e);
}
Ok(Err(e)) => {
log::warn!("Error opening URL:{} {:?}", url, e);
}
}
}
fn main() -> Result<()> {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
@ -402,15 +375,6 @@ fn main() -> Result<()> {
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_http::init())
.plugin(
tauri_plugin_log::Builder::new()
.targets([
tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::Stdout),
tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::LogDir { file_name: None }),
])
.level(log::LevelFilter::Debug)
.build(),
)
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_shell::init())
@ -446,7 +410,6 @@ fn main() -> Result<()> {
if let Some(source_arg) = matches.args.get("source") {
// We don't do an else here because this can be null.
if let Some(value) = source_arg.value.as_str() {
log::info!("Got path in cli argument: {}", value);
source_path = Some(Path::new(value).to_path_buf());
}
}
@ -456,10 +419,6 @@ fn main() -> Result<()> {
}
}
if verbose {
log::debug!("Verbose mode enabled.");
}
// If we have a source path to open, make sure it exists.
let Some(source_path) = source_path else {
// The user didn't provide a source path to open.
@ -477,7 +436,74 @@ fn main() -> Result<()> {
}
let runner: tauri::async_runtime::JoinHandle<Result<ProjectState>> =
tauri::async_runtime::spawn(async move { ProjectState::new_from_path(source_path).await });
tauri::async_runtime::spawn(async move {
// Fix for "." path, which is the current directory.
let source_path = if source_path == Path::new(".") {
std::env::current_dir()
.map_err(|e| anyhow::anyhow!("Error getting the current directory: {:?}", e))?
} else {
source_path
};
// If the path does not start with a slash, it is a relative path.
// We need to convert it to an absolute path.
let source_path = if source_path.is_relative() {
std::env::current_dir()
.map_err(|e| anyhow::anyhow!("Error getting the current directory: {:?}", e))?
.join(source_path)
} else {
source_path
};
// If the path is a directory, let's assume it is a project directory.
if source_path.is_dir() {
// Load the details about the project from the path.
let project = Project::from_path(&source_path).await.map_err(|e| {
anyhow::anyhow!("Error loading project from path {}: {:?}", source_path.display(), e)
})?;
if verbose {
println!("Project loaded from path: {}", source_path.display());
}
// Create the default file in the project.
// Write the initial project file.
let project_file = source_path.join(DEFAULT_PROJECT_KCL_FILE);
tokio::fs::write(&project_file, vec![]).await?;
return Ok(ProjectState {
project,
current_file: Some(project_file.display().to_string()),
});
}
// We were given a file path, not a directory.
// Let's get the parent directory of the file.
let parent = source_path.parent().ok_or_else(|| {
anyhow::anyhow!(
"Error getting the parent directory of the file: {}",
source_path.display()
)
})?;
// Load the details about the project from the parent directory.
let project = Project::from_path(&parent).await.map_err(|e| {
anyhow::anyhow!("Error loading project from path {}: {:?}", source_path.display(), e)
})?;
if verbose {
println!(
"Project loaded from path: {}, current file: {}",
parent.display(),
source_path.display()
);
}
Ok(ProjectState {
project,
current_file: Some(source_path.display().to_string()),
})
});
// Block on the handle.
let store = tauri::async_runtime::block_on(runner)??;
@ -486,30 +512,13 @@ fn main() -> Result<()> {
app.manage(state::Store::new(store));
// Listen on the deep links.
app.listen("deep-link://new-url", |event| {
log::info!("got deep-link url: {:?}", event);
// TODO: open_url_sync(app.handle(), event.url);
app.listen("deep-link://new-url", |url| {
dbg!(url);
});
Ok(())
})
.build(tauri::generate_context!())?
.run(
#[allow(unused_variables)]
|app, event| {
#[cfg(any(target_os = "macos", target_os = "ios"))]
if let tauri::RunEvent::Opened { urls } = event {
log::info!("Opened URLs: {:?}", urls);
// Handle the first URL.
// TODO: do we want to handle more than one URL?
// Under what conditions would we even have more than one?
if let Some(url) = urls.first() {
open_url_sync(app, url);
}
}
},
);
.run(tauri::generate_context!())?;
Ok(())
}

View File

@ -1,8 +0,0 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"bundle": {
"macOS": {
"entitlements": "entitlements/app-store.entitlements"
}
}
}

View File

@ -37,7 +37,9 @@
}
},
"longDescription": "",
"macOS": {},
"macOS": {
"entitlements": "entitlements/Zoo Modeling App.entitlements"
},
"resources": [],
"shortDescription": "",
"targets": "all"
@ -54,8 +56,6 @@
},
{
"name": "source",
"description": "The file or directory to open",
"required": false,
"index": 1,
"takesValue": true
}
@ -64,9 +64,7 @@
},
"deep-link": {
"domains": [
{
"host": "app.zoo.dev"
}
{ "host": "app.zoo.dev" }
]
},
"shell": {
@ -74,5 +72,5 @@
}
},
"productName": "Zoo Modeling App",
"version": "0.20.2"
"version": "0.19.4"
}

View File

@ -5,45 +5,7 @@
"certificateThumbprint": "F4C9A52FF7BC26EE5E054946F6B11DEEA94C748D",
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.digicert.com"
},
"fileAssociations": [
{
"ext": ["kcl"],
"mimeType": "text/vnd.zoo.kcl"
},
{
"ext": ["obj"],
"mimeType": "model/obj"
},
{
"ext": ["gltf"],
"mimeType": "model/gltf+json"
},
{
"ext": ["glb"],
"mimeType": "model/gltf+binary"
},
{
"ext": ["fbx", "fbxb"],
"mimeType": "model/fbx"
},
{
"ext": ["stl"],
"mimeType": "model/stl"
},
{
"ext": ["ply"],
"mimeType": "model/ply"
},
{
"ext": ["step", "stp"],
"mimeType": "model/step"
},
{
"ext": ["sldprt"],
"mimeType": "model/sldprt"
}
]
}
},
"plugins": {
"updater": {

View File

@ -964,7 +964,7 @@ export class SceneEntities {
if (!draftInfo)
// don't want to mod the user's code yet as they have't committed to the change yet
// plus this would be the truncated ast being recast, it would be wrong
codeManager.updateCodeEditor(code)
codeManager.updateCodeStateEditor(code)
const { programMemory } = await executeAst({
ast: truncatedAst,
useFakeExecutor: true,

View File

@ -1,13 +1,12 @@
import { Toolbar } from '../Toolbar'
import UserSidebarMenu from 'components/UserSidebarMenu'
import UserSidebarMenu from './UserSidebarMenu'
import { type IndexLoaderData } from 'lib/types'
import ProjectSidebarMenu from './ProjectSidebarMenu'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import styles from './AppHeader.module.css'
import { useCommandsContext } from 'hooks/useCommandsContext'
import { ActionButton } from 'components/ActionButton'
import { ActionButton } from './ActionButton'
import usePlatform from 'hooks/usePlatform'
import { RefreshButton } from 'components/RefreshButton'
interface AppHeaderProps extends React.PropsWithChildren {
showToolbar?: boolean
@ -61,12 +60,7 @@ export const AppHeader = ({
</div>
<div className="flex items-center gap-1 py-1 ml-auto">
{/* If there are children, show them, otherwise show User menu */}
{children || (
<>
<RefreshButton />
<UserSidebarMenu user={user} />
</>
)}
{children || <UserSidebarMenu user={user} />}
</div>
</header>
)

View File

@ -7,7 +7,6 @@ import {
getSelectionType,
getSelectionTypeDisplayText,
} from 'lib/selections'
import { kclManager } from 'lib/singletons'
import { modelingMachine } from 'machines/modelingMachine'
import { useCallback, useEffect, useRef, useState } from 'react'
import { useHotkeys } from 'react-hotkeys-hook'
@ -51,14 +50,6 @@ function CommandBarSelectionInput({
inputRef.current?.focus()
}, [selection, inputRef])
// Exit engine's edit mode when this input step is active,
// and re-enter it when it's not.
// In future the engine's edit mode will go away and this will be handled differently.
useEffect(() => {
kclManager.exitEditMode()
return () => kclManager.enterEditMode()
}, [])
// Fast-forward through this arg if it's marked as skippable
// and we have a valid selection already
useEffect(() => {

View File

@ -41,16 +41,6 @@ const CustomIconMap = {
/>
</svg>
),
arrowRotateRight: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M15.5 7.59684L15.5 8.09684L15 8.09684L10.7931 8.09684L10.7931 7.09684L13.769 7.09684C13.3052 6.54751 12.7147 6.11526 12.0452 5.83941C11.2133 5.49662 10.2977 5.41109 9.41668 5.59387C8.53566 5.77666 7.72967 6.21935 7.10277 6.8648C6.47588 7.51025 6.05687 8.32881 5.89986 9.21478C5.74284 10.1008 5.85503 11.0134 6.22194 11.835C6.58884 12.6566 7.19361 13.3493 7.95816 13.8237C8.7227 14.2981 9.61192 14.5325 10.511 14.4964C11.41 14.4604 12.2776 14.1557 13.0018 13.6216L13.5953 14.4264C12.7103 15.0792 11.6499 15.4516 10.551 15.4956C9.45216 15.5397 8.36535 15.2533 7.4309 14.6734C6.49646 14.0936 5.75729 13.2469 5.30885 12.2428C4.86041 11.2386 4.7233 10.1231 4.9152 9.04027C5.10711 7.95742 5.61923 6.95696 6.38543 6.16808C7.15164 5.3792 8.13674 4.83812 9.21354 4.61472C10.2903 4.39132 11.4094 4.49586 12.4262 4.91483C13.2286 5.24545 13.9382 5.7599 14.5 6.41286L14.5 3.38998L15.5 3.38998L15.5 7.59684Z"
fill="currentColor"
/>
</svg>
),
arrowUp: (
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path

View File

@ -16,7 +16,7 @@ export const ErrorPage = () => {
return (
<div className="flex flex-col items-center justify-center h-screen">
<section className="max-w-full xl:max-w-4xl mx-auto">
<h1 className="text-4xl mb-8 font-bold" data-testid="unexpected-error">
<h1 className="text-4xl mb-8 font-bold">
An unexpected error occurred
</h1>
{isRouteErrorResponse(error) && (
@ -26,12 +26,7 @@ export const ErrorPage = () => {
)}
<div className="flex justify-between gap-2 mt-6">
{isTauri() && (
<ActionButton
Element="link"
to={'/'}
icon={{ icon: faHome }}
data-testid="unexpected-error-home"
>
<ActionButton Element="link" to={'/'} icon={{ icon: faHome }}>
Go Home
</ActionButton>
)}

View File

@ -85,7 +85,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
},
},
} = useSettingsAuthContext()
const token = auth?.context.token
const token = auth?.context?.token
const navigate = useNavigate()
const { overallState } = useNetworkStatus()
const isNetworkOkay = overallState === NetworkHealthState.Ok

View File

@ -24,7 +24,6 @@ export const ModelingPaneHeader = ({
export const ModelingPane = ({
title,
id,
children,
className,
Menu,
@ -44,7 +43,6 @@ export const ModelingPane = ({
<section
{...props}
data-testid={detailsTestId}
id={id}
className={
pointerEventsCssClass + styles.panel + ' group ' + (className || '')
}

View File

@ -2,7 +2,7 @@ import ReactCodeMirror from '@uiw/react-codemirror'
import { TEST } from 'env'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { Themes, getSystemTheme } from 'lib/theme'
import { useEffect, useMemo, useRef } from 'react'
import { useEffect, useMemo } from 'react'
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search'
import { lineHighlightField } from 'editor/highlightextension'
import { roundOff } from 'lib/utils'
@ -190,15 +190,13 @@ export const KclEditorPane = () => {
return extensions
}, [kclLSP, copilotLSP, textWrapping.current, cursorBlinking.current])
const initialCode = useRef(codeManager.code)
return (
<div
id="code-mirror-override"
className={'absolute inset-0 ' + (cursorBlinking.current ? 'blink' : '')}
>
<ReactCodeMirror
value={initialCode.current}
value={codeManager.code}
extensions={editorExtensions}
theme={theme}
onCreateEditor={(_editorView) =>

View File

@ -165,11 +165,7 @@ function ModelingSidebarSection({
<Tab.Panel key="none" />
{filteredPanes.map((pane) => (
<Tab.Panel key={pane.id} className="h-full">
<ModelingPane
id={`${pane.id}-pane`}
title={pane.title}
Menu={pane.Menu}
>
<ModelingPane title={pane.title} Menu={pane.Menu}>
{pane.Content instanceof Function ? (
<pane.Content />
) : (

View File

@ -1,37 +0,0 @@
import { CustomIcon } from './CustomIcon'
import Tooltip from './Tooltip'
export function RefreshButton() {
async function refresh() {
if (window && 'plausible' in window) {
const p = window.plausible as (
event: string,
options?: { props: Record<string, string> }
) => Promise<void>
// Send a refresh event to Plausible so we can track how often users get stuck
await p('Refresh', {
props: {
method: 'UI button',
// TODO: add more coredump data here
},
})
}
// Window may not be available in some environments
window?.location.reload()
}
return (
<button
onClick={refresh}
className="p-1 m-0 bg-chalkboard-10/80 dark:bg-chalkboard-100/50 hover:bg-chalkboard-10 dark:hover:bg-chalkboard-100 rounded-full border border-solid border-chalkboard-10 dark:border-chalkboard-100"
>
<CustomIcon name="arrowRotateRight" className="w-5 h-5" />
<Tooltip position="bottom-right">
<span>Refresh and report</span>
<br />
<span className="text-xs">Send us data on how you got stuck</span>
</Tooltip>
</button>
)
}

View File

@ -1,152 +0,0 @@
import { Toggle } from 'components/Toggle/Toggle'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { Setting } from 'lib/settings/initialSettings'
import {
SetEventTypes,
SettingsLevel,
WildcardSetEvent,
} from 'lib/settings/settingsTypes'
import { getSettingInputType } from 'lib/settings/settingsUtils'
import { useMemo } from 'react'
import { Event } from 'xstate'
interface SettingsFieldInputProps {
// We don't need the fancy types here,
// it doesn't help us with autocomplete or anything
category: string
settingName: string
settingsLevel: SettingsLevel
setting: Setting<unknown>
}
export function SettingsFieldInput({
category,
settingName,
settingsLevel,
setting,
}: SettingsFieldInputProps) {
const {
settings: { context, send },
} = useSettingsAuthContext()
const options = useMemo(() => {
return setting.commandConfig &&
'options' in setting.commandConfig &&
setting.commandConfig.options
? setting.commandConfig.options instanceof Array
? setting.commandConfig.options
: setting.commandConfig.options(
{
argumentsToSubmit: {
level: settingsLevel,
},
},
context
)
: []
}, [setting, settingsLevel, context])
const inputType = getSettingInputType(setting)
switch (inputType) {
case 'component':
return (
setting.Component && (
<setting.Component
value={setting[settingsLevel] || setting.getFallback(settingsLevel)}
updateValue={(newValue) => {
send({
type: `set.${category}.${settingName}`,
data: {
level: settingsLevel,
value: newValue,
},
} as unknown as Event<WildcardSetEvent>)
}}
/>
)
)
case 'boolean':
return (
<Toggle
offLabel="Off"
onLabel="On"
onChange={(e) =>
send({
type: `set.${category}.${settingName}`,
data: {
level: settingsLevel,
value: Boolean(e.target.checked),
},
} as SetEventTypes)
}
checked={Boolean(
setting[settingsLevel] !== undefined
? setting[settingsLevel]
: setting.getFallback(settingsLevel)
)}
name={`${category}-${settingName}`}
data-testid={`${category}-${settingName}`}
/>
)
case 'options':
return (
<select
name={`${category}-${settingName}`}
data-testid={`${category}-${settingName}`}
className="p-1 bg-transparent border rounded-sm border-chalkboard-30 w-full"
value={String(
setting[settingsLevel] || setting.getFallback(settingsLevel)
)}
onChange={(e) =>
send({
type: `set.${category}.${settingName}`,
data: {
level: settingsLevel,
value: e.target.value,
},
} as unknown as Event<WildcardSetEvent>)
}
>
{options &&
options.length > 0 &&
options.map((option) => (
<option key={option.name} value={String(option.value)}>
{option.name}
</option>
))}
</select>
)
case 'string':
return (
<input
name={`${category}-${settingName}`}
data-testid={`${category}-${settingName}`}
type="text"
className="p-1 bg-transparent border rounded-sm border-chalkboard-30 w-full"
defaultValue={String(
setting[settingsLevel] || setting.getFallback(settingsLevel)
)}
onBlur={(e) => {
if (
setting[settingsLevel] === undefined
? setting.getFallback(settingsLevel) !== e.target.value
: setting[settingsLevel] !== e.target.value
) {
send({
type: `set.${category}.${settingName}`,
data: {
level: settingsLevel,
value: e.target.value,
},
} as unknown as Event<WildcardSetEvent>)
}
}}
/>
)
}
return (
<p className="text-destroy-70 dark:text-destroy-20">
No component or input type found for setting {settingName} in category{' '}
{category}
</p>
)
}

View File

@ -1,110 +0,0 @@
import { Combobox } from '@headlessui/react'
import { CustomIcon } from 'components/CustomIcon'
import decamelize from 'decamelize'
import Fuse from 'fuse.js'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { Setting } from 'lib/settings/initialSettings'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useHotkeys } from 'react-hotkeys-hook'
import { useNavigate } from 'react-router-dom'
export function SettingsSearchBar() {
const inputRef = useRef<HTMLInputElement>(null)
useHotkeys(
'Ctrl+.',
(e) => {
e.preventDefault()
inputRef.current?.focus()
},
{ enableOnFormTags: true }
)
const navigate = useNavigate()
const [query, setQuery] = useState('')
const { settings } = useSettingsAuthContext()
const settingsAsSearchable = useMemo(
() =>
Object.entries(settings.state.context).flatMap(
([category, categorySettings]) =>
Object.entries(categorySettings).flatMap(([settingName, setting]) => {
const s = setting as Setting
return ['project', 'user']
.filter((l) => s.hideOnLevel !== l)
.map((l) => ({
category: decamelize(category, { separator: ' ' }),
settingName: settingName,
settingNameDisplay: decamelize(settingName, { separator: ' ' }),
setting: s,
level: l,
}))
})
),
[settings.state.context]
)
const [searchResults, setSearchResults] = useState(settingsAsSearchable)
const fuse = new Fuse(settingsAsSearchable, {
keys: ['category', 'settingNameDisplay', 'setting.description'],
includeScore: true,
})
useEffect(() => {
const results = fuse.search(query).map((result) => result.item)
setSearchResults(query.length > 0 ? results : settingsAsSearchable)
}, [query])
function handleSelection({
level,
settingName,
}: {
category: string
settingName: string
setting: Setting<unknown>
level: string
}) {
navigate(`?tab=${level}#${settingName}`)
}
return (
<Combobox onChange={handleSelection}>
<div className="relative group">
<div className="flex items-center gap-2 py-0.5 pr-1 pl-2 rounded border-solid border border-primary/10 dark:border-chalkboard-80 focus-within:border-primary dark:focus-within:border-chalkboard-30">
<Combobox.Input
ref={inputRef}
onChange={(event) => setQuery(event.target.value)}
className="w-full bg-transparent focus:outline-none selection:bg-primary/20 dark:selection:bg-primary/40 dark:focus:outline-none"
placeholder="Search settings (^.)"
autoCapitalize="off"
autoComplete="off"
autoCorrect="off"
spellCheck="false"
autoFocus
/>
<CustomIcon
name="search"
className="w-5 h-5 rounded-sm bg-primary/10 text-primary group-focus-within:bg-primary group-focus-within:text-chalkboard-10"
/>
</div>
<Combobox.Options className="absolute top-full mt-2 right-0 w-80 overflow-y-auto z-50 max-h-96 cursor-pointer bg-chalkboard-10 dark:bg-chalkboard-100 border border-solid border-primary dark:border-chalkboard-30 rounded">
{searchResults?.map((option) => (
<Combobox.Option
key={`${option.category}-${option.settingName}-${option.level}`}
value={option}
className="flex flex-col items-start gap-2 px-4 py-2 ui-active:bg-primary/10 dark:ui-active:bg-chalkboard-90"
>
<p className="flex-grow text-base capitalize m-0 leading-none">
{option.level} ·{' '}
{decamelize(option.category, { separator: ' ' })} ·{' '}
{option.settingNameDisplay}
</p>
{option.setting.description && (
<p className="text-xs leading-tight text-chalkboard-70 dark:text-chalkboard-50">
{option.setting.description}
</p>
)}
</Combobox.Option>
))}
</Combobox.Options>
</div>
</Combobox>
)
}

View File

@ -1,60 +0,0 @@
import { CustomIcon } from 'components/CustomIcon'
import Tooltip from 'components/Tooltip'
import { SettingsLevel } from 'lib/settings/settingsTypes'
interface SettingsSectionProps extends React.HTMLProps<HTMLDivElement> {
title: string
description?: string
className?: string
parentLevel?: SettingsLevel | 'default'
onFallback?: () => void
settingHasChanged?: boolean
headingClassName?: string
}
export function SettingsSection({
title,
id,
description,
className,
children,
parentLevel,
settingHasChanged,
onFallback,
headingClassName = 'text-lg font-normal capitalize tracking-wide',
}: SettingsSectionProps) {
return (
<section
id={id}
className={
'group p-2 pl-0 grid grid-cols-2 gap-6 items-start ' +
className +
(settingHasChanged ? ' border-0 border-l-2 -ml-0.5 border-primary' : '')
}
>
<div className="ml-2">
<div className="flex items-center gap-2">
<h2 className={headingClassName}>{title}</h2>
{onFallback && parentLevel && settingHasChanged && (
<button
onClick={onFallback}
className="hidden group-hover:block group-focus-within:block border-none p-0 hover:bg-warn-10 dark:hover:bg-warn-80 focus:bg-warn-10 dark:focus:bg-warn-80 focus:outline-none"
>
<CustomIcon name="refresh" className="w-4 h-4" />
<span className="sr-only">Roll back {title}</span>
<Tooltip position="right">
Roll back to match {parentLevel}
</Tooltip>
</button>
)}
</div>
{description && (
<p className="mt-2 text-xs text-chalkboard-80 dark:text-chalkboard-30">
{description}
</p>
)}
</div>
<div>{children}</div>
</section>
)
}

View File

@ -1,28 +0,0 @@
import { CustomIcon, CustomIconName } from 'components/CustomIcon'
interface SettingsTabButtonProps {
checked: boolean
icon: CustomIconName
text: string
}
export function SettingsTabButton(props: SettingsTabButtonProps) {
const { checked, icon, text } = props
return (
<div
className={`cursor-pointer select-none flex items-center gap-1 p-1 pr-2 -mb-[1px] border-0 border-b ${
checked
? 'border-primary'
: 'border-chalkboard-20 dark:border-chalkboard-30 hover:bg-primary/20 dark:hover:bg-primary/50'
}`}
>
<CustomIcon
name={icon}
className={
'w-5 h-5 ' + (checked ? 'bg-primary !text-chalkboard-10' : '')
}
/>
<span>{text}</span>
</div>
)
}

View File

@ -1,39 +0,0 @@
import { RadioGroup } from '@headlessui/react'
import { SettingsTabButton } from './SettingsTabButton'
interface SettingsTabButtonProps {
value: string
onChange: (value: string) => void
showProjectTab: boolean
}
export function SettingsTabs({
value,
onChange,
showProjectTab,
}: SettingsTabButtonProps) {
return (
<RadioGroup
value={value}
onChange={onChange}
className="flex justify-start pl-4 pr-5 gap-5 border-0 border-b border-b-chalkboard-20 dark:border-b-chalkboard-90"
>
<RadioGroup.Option value="user">
{({ checked }) => (
<SettingsTabButton checked={checked} icon="person" text="User" />
)}
</RadioGroup.Option>
{showProjectTab && (
<RadioGroup.Option value="project">
{({ checked }) => (
<SettingsTabButton
checked={checked}
icon="folder"
text="This project"
/>
)}
</RadioGroup.Option>
)}
</RadioGroup>
)
}

View File

@ -1,13 +1,7 @@
.toggle {
@apply flex items-center gap-2 w-fit;
@apply text-chalkboard-110;
--toggle-size: 0.75rem;
--toggle-size: 1.25rem;
--padding: 0.25rem;
--border: 1px;
}
:global(.dark) .toggle {
@apply text-chalkboard-10;
}
.toggle:focus-within > span {
@ -19,12 +13,9 @@
}
.toggle > span {
@apply relative rounded border border-chalkboard-70 hover:border-chalkboard-80 cursor-pointer;
border-width: var(--border);
width: calc(
2 * (var(--toggle-size) + var(--padding) * 2 - var(--border) * 2)
);
height: calc(var(--toggle-size) + var(--padding) * 2 - var(--border) * 2);
@apply relative rounded border border-chalkboard-110 hover:border-chalkboard-100 cursor-pointer;
width: calc(2 * (var(--toggle-size) + var(--padding)));
height: calc(var(--toggle-size) + var(--padding));
}
:global(.dark) .toggle > span {
@ -32,26 +23,18 @@
}
.toggle > span::after {
width: var(--toggle-size);
height: var(--toggle-size);
border-radius: calc(var(--toggle-size) / 8);
content: '';
@apply absolute bg-chalkboard-70;
@apply absolute w-4 h-4 rounded-sm bg-chalkboard-110;
top: 50%;
left: 50%;
translate: calc(-100% - var(--padding) + var(--border)) -50%;
translate: calc(-100% - var(--padding)) -50%;
transition: translate 0.08s ease-out;
}
:global(.dark) .toggle > span::after {
@apply bg-chalkboard-50;
@apply bg-chalkboard-10;
}
.toggle input:checked + span::after {
translate: calc(50% - var(--padding) + var(--border)) -50%;
@apply bg-chalkboard-110;
}
:global(.dark) .toggle input:checked + span::after {
@apply bg-chalkboard-10;
translate: calc(50% - var(--padding)) -50%;
}

View File

@ -19,11 +19,7 @@ export const Toggle = ({
}: ToggleProps) => {
return (
<label className={`${styles.toggle} ${className}`}>
<p
className={checked ? 'text-chalkboard-70 dark:text-chalkboard-50' : ''}
>
{offLabel}
</p>
{offLabel}
<input
type="checkbox"
name={name}
@ -32,11 +28,7 @@ export const Toggle = ({
onChange={onChange}
/>
<span></span>
<p
className={!checked ? 'text-chalkboard-70 dark:text-chalkboard-50' : ''}
>
{onLabel}
</p>
{onLabel}
</label>
)
}

View File

@ -189,7 +189,6 @@ export default class EditorManager {
const ignoreEvents: ModelingMachineEvent['type'][] = [
'Equip Line tool',
'Equip tangential arc to',
'Equip rectangle tool',
]
if (!this._modelingEvent) {

View File

@ -167,7 +167,6 @@ export class LanguageServerPlugin implements PluginValue {
if (pos === null) return null
const dom = document.createElement('div')
dom.classList.add('documentation')
dom.style.zIndex = '99999999'
if (this.allowHTMLContent) dom.innerHTML = formatContents(contents)
else dom.textContent = formatContents(contents)
return { pos, end, create: (view) => ({ dom }), above: true }

View File

@ -7,8 +7,5 @@ export const VITE_KC_API_BASE_URL = import.meta.env.VITE_KC_API_BASE_URL
export const VITE_KC_SITE_BASE_URL = import.meta.env.VITE_KC_SITE_BASE_URL
export const VITE_KC_CONNECTION_TIMEOUT_MS = import.meta.env
.VITE_KC_CONNECTION_TIMEOUT_MS
export const VITE_KC_DEV_TOKEN = import.meta.env.VITE_KC_DEV_TOKEN as
| string
| undefined
export const TEST = import.meta.env.TEST
export const DEV = import.meta.env.DEV

View File

@ -17,7 +17,7 @@ import {
ExtrudeGroup,
} from 'lang/wasm'
import { getNodeFromPath } from './queryAst'
import { codeManager, editorManager, sceneInfra } from 'lib/singletons'
import { codeManager, editorManager } from 'lib/singletons'
export class KclManager {
private _ast: Program = {
@ -187,7 +187,6 @@ export class KclManager {
ast,
engineCommandManager: this.engineCommandManager,
})
sceneInfra.modelingSend({ type: 'code edit during sketch' })
enterEditMode(programMemory, this.engineCommandManager)
this.isExecuting = false
// Check the cancellation token for this execution before applying side effects
@ -220,7 +219,7 @@ export class KclManager {
const newCode = recast(ast)
const newAst = this.safeParse(newCode)
if (!newAst) return
codeManager.updateCodeEditor(newCode)
codeManager.updateCodeStateEditor(newCode)
// Write the file to disk.
await codeManager.writeToFile()
await this?.engineCommandManager?.waitForReady
@ -317,7 +316,7 @@ export class KclManager {
if (execute) {
// Call execute on the set ast.
// Update the code state and editor.
codeManager.updateCodeEditor(newCode)
codeManager.updateCodeStateEditor(newCode)
// Write the file to disk.
await codeManager.writeToFile()
await this.executeAst(astWithUpdatedSource)

View File

@ -11,7 +11,8 @@ const PERSIST_CODE_TOKEN = 'persistCode'
export default class CodeManager {
private _code: string = bracket
#updateState: (arg: string) => void = () => {}
private _updateState: (arg: string) => void = () => {}
private _updateEditor: (arg: string) => void = () => {}
private _currentFilePath: string | null = null
constructor() {
@ -45,7 +46,7 @@ export default class CodeManager {
}
registerCallBacks({ setCode }: { setCode: (arg: string) => void }) {
this.#updateState = setCode
this._updateState = setCode
}
updateCurrentFilePath(path: string) {
@ -56,20 +57,18 @@ export default class CodeManager {
updateCodeState(code: string): void {
if (this._code !== code) {
this.code = code
this.#updateState(code)
this._updateState(code)
}
}
// Update the code in the editor.
updateCodeEditor(code: string): void {
const lastCode = this._code
this.code = code
this._updateEditor(code)
if (editorManager.editorView) {
editorManager.editorView.dispatch({
changes: {
from: 0,
to: editorManager.editorView.state.doc.length,
insert: code,
},
changes: { from: 0, to: lastCode.length, insert: code },
})
}
}
@ -78,7 +77,8 @@ export default class CodeManager {
updateCodeStateEditor(code: string): void {
if (this._code !== code) {
this.code = code
this.#updateState(code)
this._updateState(code)
this._updateEditor(code)
}
}

View File

@ -335,9 +335,7 @@ class EngineConnection {
// Information on the connect transaction
const createPeerConnection = () => {
this.pc = new RTCPeerConnection({
bundlePolicy: 'max-bundle',
})
this.pc = new RTCPeerConnection()
// Data channels MUST BE specified before SDP offers because requesting
// them affects what our needs are!
@ -654,9 +652,7 @@ failed cmd type was ${artifactThatFailed?.commandType}`
// No ICE servers can be valid in a local dev. env.
if (ice_servers?.length === 0) {
console.warn('No ICE servers')
this.pc?.setConfiguration({
bundlePolicy: 'max-bundle',
})
this.pc?.setConfiguration({})
} else {
// When we set the Configuration, we want to always force
// iceTransportPolicy to 'relay', since we know the topology
@ -664,7 +660,6 @@ failed cmd type was ${artifactThatFailed?.commandType}`
// talk to the engine in any configuration /other/ than relay
// from a infra POV.
this.pc?.setConfiguration({
bundlePolicy: 'max-bundle',
iceServers: ice_servers,
iceTransportPolicy: 'relay',
})

View File

@ -1,19 +1,17 @@
export const bracket = `// Shelf Bracket
// This is a shelf bracket made out of 6061-T6 aluminum sheet metal. The required thickness is calculated based on a point load of 300 lbs applied to the end of the shelf. There are two brackets holding up the shelf, so the moment experienced is divided by 2. The shelf is 1 foot long from the wall.
// Define our bracket feet lengths
const shelfMountL = 8 // The length of the bracket holding up the shelf is 6 inches
const wallMountL = 6 // the length of the bracket
// Define constants required to calculate the thickness needed to support 300 lbs
const sigmaAllow = 35000 // psi
const width = 6 // inch
const p = 300 // Force on shelf - lbs
const L = 12 // inches
const M = L * p / 2 // Moment experienced at fixed end of bracket
const FOS = 2 // Factor of safety of 2 to be conservative
const distance = 12 // inches
const M = distance * p / 2 // Moment experienced at fixed end of bracket
const FOS = 2 // Factor of safety of 2
const shelfMountL = 8 // The length of the bracket holding up the shelf is 6 inches
const wallMountL = 8 // the length of the bracket
// Calculate the thickness off the bending stress and factor of safety
// Calculate the thickness off the allowable bending stress and factor of safety
const thickness = sqrt(6 * M * FOS / (width * sigmaAllow))
// 0.25 inch fillet radius

View File

@ -14,21 +14,9 @@ interface ModelingAppFile {
const save_ = async (file: ModelingAppFile) => {
try {
if (isTauri()) {
const extension = file.name.split('.').pop() || null
let extensions: string[] = []
if (extension !== null) {
extensions.push(extension)
}
// Open a dialog to save the file.
const filePath = await save({
defaultPath: file.name,
filters: [
{
name: 'model',
extensions: extensions,
},
],
})
if (filePath === null) {
@ -60,7 +48,7 @@ export async function exportSave(data: ArrayBuffer) {
// This converts the ArrayBuffer to a Rust equivalent Vec<u8>.
let uintArray = new Uint8Array(data)
let files: ModelingAppFile[] = deserialize_files(uintArray)
const files: ModelingAppFile[] = deserialize_files(uintArray)
if (files.length > 1) {
let zip = new JSZip()

View File

@ -2,7 +2,7 @@ import { createMachine, assign } from 'xstate'
import { Models } from '@kittycad/lib'
import withBaseURL from '../lib/withBaseURL'
import { isTauri } from 'lib/isTauri'
import { VITE_KC_API_BASE_URL, VITE_KC_DEV_TOKEN } from 'env'
import { VITE_KC_API_BASE_URL } from 'env'
import { getUser as getUserTauri } from 'lib/tauri'
const SKIP_AUTH =
@ -112,25 +112,14 @@ export const authMachine = createMachine<UserContext, Events>(
)
async function getUser(context: UserContext) {
const token =
context.token && context.token !== ''
? context.token
: getCookie(COOKIE_NAME) ||
localStorage?.getItem(TOKEN_PERSIST_KEY) ||
VITE_KC_DEV_TOKEN
const url = withBaseURL('/user')
const headers: { [key: string]: string } = {
'Content-Type': 'application/json',
}
if (!token && isTauri()) throw new Error('No token found')
if (token) headers['Authorization'] = `Bearer ${context.token}`
if (SKIP_AUTH)
return {
user: LOCAL_USER,
token,
}
if (!context.token && isTauri()) throw new Error('No token found')
if (context.token) headers['Authorization'] = `Bearer ${context.token}`
if (SKIP_AUTH) return LOCAL_USER
const userPromise = !isTauri()
? fetch(url, {
@ -147,8 +136,13 @@ async function getUser(context: UserContext) {
if ('error_code' in user) throw new Error(user.message)
return {
user: user as Models['User_type'],
token,
user,
token:
context.token && context.token !== ''
? context.token
: getCookie(COOKIE_NAME) ||
localStorage?.getItem(TOKEN_PERSIST_KEY) ||
'',
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,13 +1,13 @@
import { OnboardingButtons, useDismiss, useNextClick } from '.'
import { onboardingPaths } from 'routes/Onboarding/paths'
import { useStore } from '../../useStore'
import { SettingsSection } from 'routes/Settings'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import {
CameraSystem,
cameraMouseDragGuards,
cameraSystems,
} from 'lib/cameraControls'
import { SettingsSection } from 'components/Settings/SettingsSection'
export default function Units() {
const { buttonDownInStream } = useStore((s) => ({

View File

@ -12,7 +12,7 @@ export default function FutureWork() {
useEffect(() => {
// We do want to update both the state and editor here.
codeManager.updateCodeEditor(bracket)
codeManager.updateCodeStateEditor(bracket)
if (kclManager.engineCommandManager.engineConnection?.isReady()) {
// If the engine is ready, promptly execute the loaded code
kclManager.executeCode(true)

View File

@ -56,7 +56,7 @@ function OnboardingWithNewFile() {
dismiss={dismiss}
next={() => {
// We do want to update both the state and editor here.
codeManager.updateCodeEditor(bracket)
codeManager.updateCodeStateEditor(bracket)
kclManager.executeCode(true)
next()
}}
@ -80,7 +80,7 @@ function OnboardingWithNewFile() {
dismiss={dismiss}
next={() => {
void createAndOpenNewProject()
codeManager.updateCodeEditor(bracket)
codeManager.updateCodeStateEditor(bracket)
dismiss()
}}
nextText="Make a new project"
@ -113,7 +113,7 @@ export default function Introduction() {
const isStarterCode = currentCode === '' || currentCode === bracket
useEffect(() => {
if (codeManager.code === '') codeManager.updateCodeEditor(bracket)
if (codeManager.code === '') codeManager.updateCodeStateEditor(bracket)
}, [])
return isStarterCode ? (

View File

@ -73,15 +73,6 @@ export default function ParametricModeling() {
</em>
.
</p>
<figure className="my-4 w-2/3 mx-auto">
<img
src={`/onboarding-bracket-dimensions${getImageTheme()}.png`}
alt="Bracket Dimensions"
/>
<figcaption className="text-small italic text-center">
Bracket Dimensions
</figcaption>
</figure>
</section>
<OnboardingButtons
currentSlug={onboardingPaths.PARAMETRIC_MODELING}

View File

@ -11,7 +11,7 @@ export default function Sketching() {
useEffect(() => {
// We do want to update both the state and editor here.
codeManager.updateCodeEditor('')
codeManager.updateCodeStateEditor('')
if (kclManager.engineCommandManager.engineConnection?.isReady()) {
// If the engine is ready, promptly execute the loaded code
kclManager.executeCode(true)

View File

@ -1,7 +1,7 @@
import { faArrowRight, faXmark } from '@fortawesome/free-solid-svg-icons'
import { type BaseUnit, baseUnitsUnion } from 'lib/settings/settingsTypes'
import { ActionButton } from 'components/ActionButton'
import { SettingsSection } from 'components/Settings/SettingsSection'
import { SettingsSection } from '../Settings'
import { useDismiss, useNextClick } from '.'
import { onboardingPaths } from 'routes/Onboarding/paths'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'

View File

@ -1,6 +1,11 @@
import { ActionButton } from '../components/ActionButton'
import { SetEventTypes, SettingsLevel } from 'lib/settings/settingsTypes'
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'
import {
SetEventTypes,
SettingsLevel,
WildcardSetEvent,
} from 'lib/settings/settingsTypes'
import { Toggle } from 'components/Toggle/Toggle'
import { useLocation, useNavigate } from 'react-router-dom'
import { useHotkeys } from 'react-hotkeys-hook'
import { paths } from 'lib/paths'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
@ -9,32 +14,27 @@ import { createAndOpenNewProject, getSettingsFolderPaths } from 'lib/tauriFS'
import { sep } from '@tauri-apps/api/path'
import { isTauri } from 'lib/isTauri'
import toast from 'react-hot-toast'
import { Fragment, useEffect, useRef } from 'react'
import React, { Fragment, useMemo, useRef, useState } from 'react'
import { Setting } from 'lib/settings/initialSettings'
import decamelize from 'decamelize'
import { Dialog, Transition } from '@headlessui/react'
import { CustomIcon } from 'components/CustomIcon'
import { Event } from 'xstate'
import { Dialog, RadioGroup, Transition } from '@headlessui/react'
import { CustomIcon, CustomIconName } from 'components/CustomIcon'
import Tooltip from 'components/Tooltip'
import {
getSettingInputType,
shouldHideSetting,
shouldShowSettingInput,
} from 'lib/settings/settingsUtils'
import { getInitialDefaultDir, showInFolder } from 'lib/tauri'
import { SettingsSearchBar } from 'components/Settings/SettingsSearchBar'
import { SettingsTabs } from 'components/Settings/SettingsTabs'
import { SettingsSection } from 'components/Settings/SettingsSection'
import { SettingsFieldInput } from 'components/Settings/SettingsFieldInput'
export const APP_VERSION = import.meta.env.PACKAGE_VERSION || 'unknown'
export const Settings = () => {
const navigate = useNavigate()
const [searchParams, setSearchParams] = useSearchParams()
const close = () => navigate(location.pathname.replace(paths.SETTINGS, ''))
const location = useLocation()
const isFileSettings = location.pathname.includes(paths.FILE)
const searchParamTab =
(searchParams.get('tab') as SettingsLevel) ??
(isFileSettings ? 'project' : 'user')
const projectPath =
isFileSettings && isTauri()
? decodeURI(
@ -44,7 +44,9 @@ export const Settings = () => {
.slice(0, decodeURI(location.pathname).lastIndexOf(sep()))
)
: undefined
const [settingsLevel, setSettingsLevel] = useState<SettingsLevel>(
isFileSettings ? 'project' : 'user'
)
const scrollRef = useRef<HTMLDivElement>(null)
const dotDotSlash = useDotDotSlash()
useHotkeys('esc', () => navigate(dotDotSlash()))
@ -68,20 +70,6 @@ export const Settings = () => {
}
}
// Scroll to the hash on load if it exists
useEffect(() => {
console.log('hash', location.hash)
if (location.hash) {
const element = document.getElementById(location.hash.slice(1))
if (element) {
element.scrollIntoView({ block: 'center', behavior: 'smooth' })
;(
element.querySelector('input, select, textarea') as HTMLInputElement
)?.focus()
}
}
}, [location.hash])
return (
<Transition appear show={true} as={Fragment}>
<Dialog
@ -114,24 +102,42 @@ export const Settings = () => {
<Dialog.Panel className="rounded relative mx-auto bg-chalkboard-10 dark:bg-chalkboard-100 border dark:border-chalkboard-70 max-w-3xl w-full max-h-[66vh] shadow-lg flex flex-col gap-8">
<div className="p-5 pb-0 flex justify-between items-center">
<h1 className="text-2xl font-bold">Settings</h1>
<div className="flex gap-4 items-start">
<SettingsSearchBar />
<button
onClick={close}
className="p-0 m-0 focus:ring-0 focus:outline-none border-none hover:bg-destroy-10 focus:bg-destroy-10 dark:hover:bg-destroy-80/50 dark:focus:bg-destroy-80/50"
data-testid="settings-close-button"
>
<CustomIcon name="close" className="w-5 h-5" />
</button>
</div>
<button
onClick={close}
className="p-0 m-0 focus:ring-0 focus:outline-none border-none hover:bg-destroy-10 focus:bg-destroy-10 dark:hover:bg-destroy-80/50 dark:focus:bg-destroy-80/50"
data-testid="settings-close-button"
>
<CustomIcon name="close" className="w-5 h-5" />
</button>
</div>
<SettingsTabs
value={searchParamTab}
onChange={(v) => setSearchParams((p) => ({ ...p, tab: v }))}
showProjectTab={isFileSettings}
/>
<RadioGroup
value={settingsLevel}
onChange={setSettingsLevel}
className="flex justify-start pl-4 pr-5 gap-5 border-0 border-b border-b-chalkboard-20 dark:border-b-chalkboard-90"
>
<RadioGroup.Option value="user">
{({ checked }) => (
<SettingsTabButton
checked={checked}
icon="person"
text="User"
/>
)}
</RadioGroup.Option>
{isFileSettings && (
<RadioGroup.Option value="project">
{({ checked }) => (
<SettingsTabButton
checked={checked}
icon="folder"
text="This project"
/>
)}
</RadioGroup.Option>
)}
</RadioGroup>
<div
className="flex-1 grid items-stretch pl-4 pr-5 pb-5 gap-2 overflow-hidden"
className="flex-1 grid items-stretch pl-4 pr-5 pb-5 gap-4 overflow-hidden"
style={{ gridTemplateColumns: 'auto 1fr' }}
>
<div className="flex w-32 flex-col gap-3 pr-2 py-1 border-0 border-r border-r-chalkboard-20 dark:border-r-chalkboard-90">
@ -140,7 +146,7 @@ export const Settings = () => {
// Filter out categories that don't have any non-hidden settings
Object.values(categorySettings).some(
(setting: Setting) =>
!shouldHideSetting(setting, searchParamTab)
!shouldHideSetting(setting, settingsLevel)
)
)
.map(([category]) => (
@ -150,7 +156,7 @@ export const Settings = () => {
scrollRef.current
?.querySelector(`#category-${category}`)
?.scrollIntoView({
block: 'center',
block: 'nearest',
behavior: 'smooth',
})
}
@ -164,7 +170,7 @@ export const Settings = () => {
scrollRef.current
?.querySelector(`#settings-resets`)
?.scrollIntoView({
block: 'center',
block: 'nearest',
behavior: 'smooth',
})
}
@ -177,7 +183,7 @@ export const Settings = () => {
scrollRef.current
?.querySelector(`#settings-about`)
?.scrollIntoView({
block: 'center',
block: 'nearest',
behavior: 'smooth',
})
}
@ -187,19 +193,19 @@ export const Settings = () => {
</button>
</div>
<div className="relative overflow-y-auto">
<div ref={scrollRef} className="flex flex-col gap-4 px-2">
<div ref={scrollRef} className="flex flex-col gap-6 px-2">
{Object.entries(context)
.filter(([_, categorySettings]) =>
// Filter out categories that don't have any non-hidden settings
Object.values(categorySettings).some(
(setting) => !shouldHideSetting(setting, searchParamTab)
(setting) => !shouldHideSetting(setting, settingsLevel)
)
)
.map(([category, categorySettings]) => (
<Fragment key={category}>
<h2
id={`category-${category}`}
className="text-xl mt-6 first-of-type:mt-0 capitalize font-bold"
className="text-2xl mt-6 first-of-type:mt-0 capitalize font-bold"
>
{decamelize(category, { separator: ' ' })}
</h2>
@ -208,50 +214,44 @@ export const Settings = () => {
// Filter out settings that don't have a Component or inputType
// or are hidden on the current level or the current platform
(item: [string, Setting<unknown>]) =>
shouldShowSettingInput(item[1], searchParamTab)
shouldShowSettingInput(item[1], settingsLevel)
)
.map(([settingName, s]) => {
const setting = s as Setting
const parentValue =
setting[setting.getParentLevel(searchParamTab)]
setting[setting.getParentLevel(settingsLevel)]
return (
<SettingsSection
title={decamelize(settingName, {
separator: ' ',
})}
id={settingName}
className={
location.hash === `#${settingName}`
? 'bg-primary/10 dark:bg-chalkboard-90'
: ''
}
key={`${category}-${settingName}-${searchParamTab}`}
key={`${category}-${settingName}-${settingsLevel}`}
description={setting.description}
settingHasChanged={
setting[searchParamTab] !== undefined &&
setting[searchParamTab] !==
setting.getFallback(searchParamTab)
setting[settingsLevel] !== undefined &&
setting[settingsLevel] !==
setting.getFallback(settingsLevel)
}
parentLevel={setting.getParentLevel(
searchParamTab
settingsLevel
)}
onFallback={() =>
send({
type: `set.${category}.${settingName}`,
data: {
level: searchParamTab,
level: settingsLevel,
value:
parentValue !== undefined
? parentValue
: setting.getFallback(searchParamTab),
: setting.getFallback(settingsLevel),
},
} as SetEventTypes)
}
>
<SettingsFieldInput
<GeneratedSetting
category={category}
settingName={settingName}
settingsLevel={searchParamTab}
settingsLevel={settingsLevel}
setting={setting}
/>
</SettingsSection>
@ -298,7 +298,7 @@ export const Settings = () => {
? decodeURIComponent(projectPath)
: undefined
)
showInFolder(paths[searchParamTab])
showInFolder(paths[settingsLevel])
}}
icon={{
icon: 'folder',
@ -368,3 +368,226 @@ export const Settings = () => {
</Transition>
)
}
interface SettingsSectionProps extends React.PropsWithChildren {
title: string
description?: string
className?: string
parentLevel?: SettingsLevel | 'default'
onFallback?: () => void
settingHasChanged?: boolean
headingClassName?: string
}
export function SettingsSection({
title,
description,
className,
children,
parentLevel,
settingHasChanged,
onFallback,
headingClassName = 'text-base font-normal capitalize tracking-wide',
}: SettingsSectionProps) {
return (
<section
className={
'group grid grid-cols-2 gap-6 items-start ' +
className +
(settingHasChanged ? ' border-0 border-l-2 -ml-0.5 border-primary' : '')
}
>
<div className="ml-2">
<div className="flex items-center gap-2">
<h2 className={headingClassName}>{title}</h2>
{onFallback && parentLevel && settingHasChanged && (
<button
onClick={onFallback}
className="hidden group-hover:block group-focus-within:block border-none p-0 hover:bg-warn-10 dark:hover:bg-warn-80 focus:bg-warn-10 dark:focus:bg-warn-80 focus:outline-none"
>
<CustomIcon name="refresh" className="w-4 h-4" />
<span className="sr-only">Roll back {title}</span>
<Tooltip position="right">
Roll back to match {parentLevel}
</Tooltip>
</button>
)}
</div>
{description && (
<p className="mt-2 text-xs text-chalkboard-80 dark:text-chalkboard-30">
{description}
</p>
)}
</div>
<div>{children}</div>
</section>
)
}
interface GeneratedSettingProps {
// We don't need the fancy types here,
// it doesn't help us with autocomplete or anything
category: string
settingName: string
settingsLevel: SettingsLevel
setting: Setting<unknown>
}
function GeneratedSetting({
category,
settingName,
settingsLevel,
setting,
}: GeneratedSettingProps) {
const {
settings: { context, send },
} = useSettingsAuthContext()
const options = useMemo(() => {
return setting.commandConfig &&
'options' in setting.commandConfig &&
setting.commandConfig.options
? setting.commandConfig.options instanceof Array
? setting.commandConfig.options
: setting.commandConfig.options(
{
argumentsToSubmit: {
level: settingsLevel,
},
},
context
)
: []
}, [setting, settingsLevel, context])
const inputType = getSettingInputType(setting)
switch (inputType) {
case 'component':
return (
setting.Component && (
<setting.Component
value={setting[settingsLevel] || setting.getFallback(settingsLevel)}
updateValue={(newValue) => {
send({
type: `set.${category}.${settingName}`,
data: {
level: settingsLevel,
value: newValue,
},
} as unknown as Event<WildcardSetEvent>)
}}
/>
)
)
case 'boolean':
return (
<Toggle
offLabel="Off"
onLabel="On"
onChange={(e) =>
send({
type: `set.${category}.${settingName}`,
data: {
level: settingsLevel,
value: Boolean(e.target.checked),
},
} as SetEventTypes)
}
checked={Boolean(
setting[settingsLevel] !== undefined
? setting[settingsLevel]
: setting.getFallback(settingsLevel)
)}
name={`${category}-${settingName}`}
data-testid={`${category}-${settingName}`}
/>
)
case 'options':
return (
<select
name={`${category}-${settingName}`}
data-testid={`${category}-${settingName}`}
className="p-1 bg-transparent border rounded-sm border-chalkboard-30 w-full"
value={String(
setting[settingsLevel] || setting.getFallback(settingsLevel)
)}
onChange={(e) =>
send({
type: `set.${category}.${settingName}`,
data: {
level: settingsLevel,
value: e.target.value,
},
} as unknown as Event<WildcardSetEvent>)
}
>
{options &&
options.length > 0 &&
options.map((option) => (
<option key={option.name} value={String(option.value)}>
{option.name}
</option>
))}
</select>
)
case 'string':
return (
<input
name={`${category}-${settingName}`}
data-testid={`${category}-${settingName}`}
type="text"
className="p-1 bg-transparent border rounded-sm border-chalkboard-30 w-full"
defaultValue={String(
setting[settingsLevel] || setting.getFallback(settingsLevel)
)}
onBlur={(e) => {
if (
setting[settingsLevel] === undefined
? setting.getFallback(settingsLevel) !== e.target.value
: setting[settingsLevel] !== e.target.value
) {
send({
type: `set.${category}.${settingName}`,
data: {
level: settingsLevel,
value: e.target.value,
},
} as unknown as Event<WildcardSetEvent>)
}
}}
/>
)
}
return (
<p className="text-destroy-70 dark:text-destroy-20">
No component or input type found for setting {settingName} in category{' '}
{category}
</p>
)
}
interface SettingsTabButtonProps {
checked: boolean
icon: CustomIconName
text: string
}
function SettingsTabButton(props: SettingsTabButtonProps) {
const { checked, icon, text } = props
return (
<div
className={`cursor-pointer select-none flex items-center gap-1 p-1 pr-2 -mb-[1px] border-0 border-b ${
checked
? 'border-primary'
: 'border-chalkboard-20 dark:border-chalkboard-30 hover:bg-primary/20 dark:hover:bg-primary/50'
}`}
>
<CustomIcon
name={icon}
className={
'w-5 h-5 ' + (checked ? 'bg-primary !text-chalkboard-10' : '')
}
/>
<span>{text}</span>
</div>
)
}

181
src/wasm-lib/Cargo.lock generated
View File

@ -155,9 +155,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.83"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
dependencies = [
"backtrace",
]
@ -246,7 +246,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -257,7 +257,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -280,7 +280,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -324,9 +324,9 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "base64"
version = "0.22.1"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51"
[[package]]
name = "base64ct"
@ -596,7 +596,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -846,7 +846,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -870,7 +870,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim 0.10.0",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -881,7 +881,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
dependencies = [
"darling_core",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -922,7 +922,7 @@ checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
"synstructure 0.13.1",
]
@ -976,7 +976,7 @@ dependencies = [
"rustfmt-wrapper",
"serde",
"serde_tokenstream",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -988,7 +988,7 @@ dependencies = [
"diesel_table_macro_syntax",
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -997,7 +997,7 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5"
dependencies = [
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -1026,7 +1026,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -1108,7 +1108,7 @@ checksum = "03cdc46ec28bd728e67540c528013c6a10eb69a02eb31078a1bda695438cbfb8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -1325,7 +1325,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -1436,7 +1436,7 @@ dependencies = [
"inflections",
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -1808,16 +1808,16 @@ dependencies = [
[[package]]
name = "interceptor"
version = "0.11.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b12e186d2a4c21225df6beb8ae5d81817c928da12e7ce78d0953fc74d88b590"
checksum = "5927883184e6a819b22d5e4f5f7bc7ca134fde9b2026fbddd8d95249746ba21e"
dependencies = [
"async-trait",
"bytes",
"log",
"rand 0.8.5",
"rtcp",
"rtp",
"rtp 0.9.0",
"thiserror",
"tokio",
"waitgroup",
@ -1895,13 +1895,13 @@ dependencies = [
[[package]]
name = "kcl-lib"
version = "0.1.54"
version = "0.1.53"
dependencies = [
"anyhow",
"approx 0.5.1",
"async-recursion",
"async-trait",
"base64 0.22.1",
"base64 0.22.0",
"bson",
"chrono",
"clap",
@ -1959,14 +1959,14 @@ dependencies = [
"pretty_assertions",
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
name = "kittycad"
version = "0.3.1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6e12eb45fd9a28c8e99dbdef54556246b39acee14e4aa6f0fc43636caa62d9"
checksum = "ddc922f0da3abc22661bf49423c9bfcc02ce6ae92dae007ede6990874789545b"
dependencies = [
"anyhow",
"async-trait",
@ -2003,9 +2003,9 @@ dependencies = [
[[package]]
name = "kittycad-execution-plan"
version = "0.1.6"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0936396491f132c163e9411a3dd699e5b2daa49c19436691c5153f7ad2d4953d"
checksum = "ae99665cd699f8800da8ea4b01889c0c9c61619d2a9dc62d1d5028f1b21110bd"
dependencies = [
"bytes",
"gltf-json",
@ -2032,7 +2032,7 @@ checksum = "0611fc9b9786175da21d895ffa0f65039e19c9111e94a41b7af999e3b95f045f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -2048,9 +2048,9 @@ dependencies = [
[[package]]
name = "kittycad-modeling-cmds"
version = "0.2.23"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c1f8bdab7f09b4f4a954dafe0694e96f6f2b73733cf7cddccc42ee493e2c1e9"
checksum = "e326955e8f315590a1926c17ff6a6082d3013f472c881aba56d73bfa170cf5b3"
dependencies = [
"anyhow",
"chrono",
@ -2083,7 +2083,7 @@ checksum = "385775cc9d5bf25579f3029824ca1a6e7ab1b7c338e972ec8e8fcefff801f353"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -2457,7 +2457,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -2614,7 +2614,7 @@ dependencies = [
"regex",
"regex-syntax 0.7.5",
"structmeta 0.2.0",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -2628,7 +2628,7 @@ dependencies = [
"regex",
"regex-syntax 0.8.2",
"structmeta 0.3.0",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -2694,7 +2694,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -3239,6 +3239,19 @@ dependencies = [
"webrtc-util",
]
[[package]]
name = "rtp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e60482acbe8afb31edf6b1413103b7bca7a65004c423b3c3993749a083994fbe"
dependencies = [
"bytes",
"rand 0.8.5",
"serde",
"thiserror",
"webrtc-util",
]
[[package]]
name = "rtp"
version = "0.10.0"
@ -3419,9 +3432,9 @@ dependencies = [
[[package]]
name = "schemars"
version = "0.8.17"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f55c82c700538496bdc329bb4918a81f87cc8888811bd123cf325a0f2f8d309"
checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29"
dependencies = [
"bigdecimal",
"bytes",
@ -3436,14 +3449,14 @@ dependencies = [
[[package]]
name = "schemars_derive"
version = "0.8.17"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83263746fe5e32097f06356968a077f96089739c927a61450efa069905eec108"
checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 2.0.61",
"syn 1.0.109",
]
[[package]]
@ -3519,9 +3532,9 @@ checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
[[package]]
name = "serde"
version = "1.0.200"
version = "1.0.198"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f"
checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
dependencies = [
"serde_derive",
]
@ -3537,24 +3550,24 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.200"
version = "1.0.198"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb"
checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
name = "serde_derive_internals"
version = "0.29.0"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 1.0.109",
]
[[package]]
@ -3577,7 +3590,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -3598,7 +3611,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -3773,7 +3786,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive 0.2.0",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -3785,7 +3798,7 @@ dependencies = [
"proc-macro2",
"quote",
"structmeta-derive 0.3.0",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -3796,7 +3809,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -3807,7 +3820,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -3879,9 +3892,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.61"
version = "2.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9"
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
dependencies = [
"proc-macro2",
"quote",
@ -3914,7 +3927,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -4000,22 +4013,22 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.60"
version = "1.0.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.60"
version = "1.0.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -4122,7 +4135,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -4285,7 +4298,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -4313,7 +4326,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -4390,7 +4403,7 @@ dependencies = [
"Inflector",
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
"termcolor",
]
@ -4586,7 +4599,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -4662,7 +4675,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
"wasm-bindgen-shared",
]
@ -4697,7 +4710,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -4782,9 +4795,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]]
name = "webrtc"
version = "0.10.1"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbdf025f0fa62f4bf252b2fb0cff0a04d3eac2021c440096649e62f4e48553d"
checksum = "d91e7cf018f7185552bf6a5dd839f4ed9827aea33b746763c9a215f84a0d0b34"
dependencies = [
"arc-swap",
"async-trait",
@ -4797,9 +4810,9 @@ dependencies = [
"rand 0.8.5",
"rcgen",
"regex",
"ring 0.17.8",
"ring 0.16.20",
"rtcp",
"rtp",
"rtp 0.9.0",
"rustls 0.21.11",
"sdp",
"serde",
@ -4839,9 +4852,9 @@ dependencies = [
[[package]]
name = "webrtc-dtls"
version = "0.9.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "188ce061a2371bdf4df54b136c89a6df243ed0ef6b03431b4bd18482cd718dfe"
checksum = "32b140b953f986e97828aa33ec6318186b05d862bee689efbc57af04a243e832"
dependencies = [
"aes",
"aes-gcm",
@ -4859,7 +4872,7 @@ dependencies = [
"rand 0.8.5",
"rand_core 0.6.4",
"rcgen",
"ring 0.17.8",
"ring 0.16.20",
"rustls 0.21.11",
"sec1",
"serde",
@ -4919,7 +4932,7 @@ dependencies = [
"byteorder",
"bytes",
"rand 0.8.5",
"rtp",
"rtp 0.10.0",
"thiserror",
]
@ -4942,9 +4955,9 @@ dependencies = [
[[package]]
name = "webrtc-srtp"
version = "0.12.0"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "383b0f0f73ee6cce396bdbc4d54ec661861a59eae9fc988914c1a8d82c5ac272"
checksum = "c1db1f36c1c81e4b1e531c0b9678ba0c93809e196ce62122d87259bb71c03b9f"
dependencies = [
"aead",
"aes",
@ -4955,7 +4968,7 @@ dependencies = [
"hmac",
"log",
"rtcp",
"rtp",
"rtp 0.9.0",
"sha1",
"subtle",
"thiserror",
@ -5269,7 +5282,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]
@ -5289,7 +5302,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.61",
"syn 2.0.60",
]
[[package]]

View File

@ -62,11 +62,11 @@ members = [
]
[workspace.dependencies]
kittycad = { version = "0.3.1", default-features = false, features = ["js", "requests"] }
kittycad-execution-plan = "0.1.6"
kittycad = { version = "0.3.0", default-features = false, features = ["js", "requests"] }
kittycad-execution-plan = "0.1.5"
kittycad-execution-plan-macros = "0.1.9"
kittycad-execution-plan-traits = "0.1.14"
kittycad-modeling-cmds = "0.2.23"
kittycad-modeling-cmds = "0.2.21"
kittycad-modeling-session = "0.1.4"
[[test]]

View File

@ -18,12 +18,12 @@ once_cell = "1.19.0"
proc-macro2 = "1"
quote = "1"
regex = "1.10"
serde = { version = "1.0.200", features = ["derive"] }
serde = { version = "1.0.198", features = ["derive"] }
serde_tokenstream = "0.2"
syn = { version = "2.0.61", features = ["full"] }
syn = { version = "2.0.60", features = ["full"] }
[dev-dependencies]
anyhow = "1.0.83"
anyhow = "1.0.82"
expectorate = "1.1.0"
pretty_assertions = "1.4.0"
rustfmt-wrapper = "0.2.1"

View File

@ -14,7 +14,7 @@ kittycad-execution-plan-traits = { workspace = true }
kittycad-execution-plan-macros = { workspace = true }
kittycad-modeling-cmds = { workspace = true }
kittycad-modeling-session = { workspace = true }
thiserror = "1.0.60"
thiserror = "1.0.59"
tokio = { version = "1.37.0", features = ["macros", "rt"] }
twenty-twenty = "0.7.0"
uuid = "1.8"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 78 KiB

View File

@ -642,15 +642,14 @@ impl Callable for StartSketchAt {
// Next, enter sketch mode.
stack_api_call(
&mut instructions,
ModelingCmdEndpoint::EnableSketchMode,
ModelingCmdEndpoint::SketchModeEnable,
None,
Uuid::new_v4().into(),
[
Some(axes.z).into_parts(),
vec![false.into()], // adjust camera
vec![false.into()], // animated
vec![false.into()], // ortho mode
vec![plane_id.into()], // entity id (plane in this case)
vec![false.into()], // animated
vec![false.into()], // ortho mode
vec![plane_id.into()],
],
);

View File

@ -15,7 +15,7 @@ databake = "0.1.7"
kcl-lib = { path = "../kcl" }
proc-macro2 = "1"
quote = "1"
syn = { version = "2.0.61", features = ["full"] }
syn = { version = "2.0.60", features = ["full"] }
[dev-dependencies]
pretty_assertions = "1.4.0"

View File

@ -1,7 +1,7 @@
[package]
name = "kcl-lib"
description = "KittyCAD Language implementation and tools"
version = "0.1.54"
version = "0.1.53"
edition = "2021"
license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app"
@ -11,10 +11,10 @@ keywords = ["kcl", "KittyCAD", "CAD"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = { version = "1.0.83", features = ["backtrace"] }
anyhow = { version = "1.0.82", features = ["backtrace"] }
async-recursion = "1.1.1"
async-trait = "0.1.80"
base64 = "0.22.1"
base64 = "0.22.0"
chrono = "0.4.38"
clap = { version = "4.5.4", default-features = false, optional = true }
dashmap = "5.5.3"
@ -32,11 +32,11 @@ mime_guess = "2.0.4"
parse-display = "0.9.0"
reqwest = { version = "0.11.26", default-features = false, features = ["stream", "rustls-tls"] }
ropey = "1.6.1"
schemars = { version = "0.8.17", features = ["impl_json_schema", "url", "uuid1"] }
serde = { version = "1.0.200", features = ["derive"] }
schemars = { version = "0.8.16", features = ["impl_json_schema", "url", "uuid1"] }
serde = { version = "1.0.198", features = ["derive"] }
serde_json = "1.0.116"
sha2 = "0.10.8"
thiserror = "1.0.60"
thiserror = "1.0.59"
toml = "0.8.12"
ts-rs = { version = "7.1.1", features = ["uuid-impl", "url-impl", "chrono-impl"] }
url = { version = "2.5.0", features = ["serde"] }
@ -73,7 +73,7 @@ debug = true
debug = true # Flamegraphs of benchmarks require accurate debug symbols
[dev-dependencies]
base64 = "0.22.1"
base64 = "0.22.0"
convert_case = "0.6.0"
criterion = "0.5.1"
expectorate = "1.1.0"

View File

@ -36,28 +36,6 @@ pub struct Program {
}
impl Program {
pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> {
// Check if we are in the non code meta.
if let Some(meta) = self.get_non_code_meta_for_position(pos) {
for node in &meta.start {
if node.contains(pos) {
// We only care about the shebang.
if let NonCodeValue::Shebang { value: _ } = &node.value {
let source_range: SourceRange = node.into();
return Some(Hover::Comment {
value: r#"The `#!` at the start of a script, known as a shebang, specifies the path to the interpreter that should execute the script. This line is not necessary for your `kcl` to run in the modeling-app. You can safely delete it. If you wish to learn more about what you _can_ do with a shebang, read this doc: [zoo.dev/docs/faq/shebang](https://zoo.dev/docs/faq/shebang)."#.to_string(),
range: source_range.to_lsp_range(code),
});
}
}
}
}
let value = self.get_value_for_position(pos)?;
value.get_hover_value_for_position(pos, code)
}
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
let indentation = options.get_indentation(indentation_level);
let result = self
@ -836,18 +814,6 @@ pub struct NonCodeNode {
pub value: NonCodeValue,
}
impl From<NonCodeNode> for SourceRange {
fn from(value: NonCodeNode) -> Self {
Self([value.start, value.end])
}
}
impl From<&NonCodeNode> for SourceRange {
fn from(value: &NonCodeNode) -> Self {
Self([value.start, value.end])
}
}
impl NonCodeNode {
pub fn contains(&self, pos: usize) -> bool {
self.start <= pos && pos <= self.end
@ -855,7 +821,6 @@ impl NonCodeNode {
pub fn value(&self) -> String {
match &self.value {
NonCodeValue::Shebang { value } => value.clone(),
NonCodeValue::InlineComment { value, style: _ } => value.clone(),
NonCodeValue::BlockComment { value, style: _ } => value.clone(),
NonCodeValue::NewLineBlockComment { value, style: _ } => value.clone(),
@ -865,7 +830,6 @@ impl NonCodeNode {
pub fn format(&self, indentation: &str) -> String {
match &self.value {
NonCodeValue::Shebang { value } => format!("{}\n\n", value),
NonCodeValue::InlineComment {
value,
style: CommentStyle::Line,
@ -918,15 +882,6 @@ pub enum CommentStyle {
#[ts(export)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum NonCodeValue {
/// A shebang.
/// This is a special type of comment that is at the top of the file.
/// It looks like this:
/// ```python,no_run
/// #!/usr/bin/env python
/// ```
Shebang {
value: String,
},
/// An inline comment.
/// Here are examples:
/// `1 + 1 // This is an inline comment`.
@ -3021,10 +2976,6 @@ pub enum Hover {
parameter_index: u32,
range: LspRange,
},
Comment {
value: String,
range: LspRange,
},
}
/// Format options.
@ -3322,117 +3273,6 @@ fn ghi = (x) => {
assert_eq!(recasted, r#""#);
}
#[test]
fn test_recast_shebang_only() {
let some_program_string = r#"#!/usr/local/env zoo kcl"#;
let tokens = crate::token::lexer(some_program_string).unwrap();
let parser = crate::parser::Parser::new(tokens);
let result = parser.ast();
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
r#"syntax: KclErrorDetails { source_ranges: [SourceRange([21, 24])], message: "Unexpected end of file. The compiler expected a function body items (functions are made up of variable declarations, expressions, and return statements, each of those is a possible body item" }"#
);
}
#[test]
fn test_recast_shebang() {
let some_program_string = r#"#!/usr/local/env zoo kcl
const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)
"#;
let tokens = crate::token::lexer(some_program_string).unwrap();
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let recasted = program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"#!/usr/local/env zoo kcl
const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)
"#
);
}
#[test]
fn test_recast_shebang_new_lines() {
let some_program_string = r#"#!/usr/local/env zoo kcl
const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)
"#;
let tokens = crate::token::lexer(some_program_string).unwrap();
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let recasted = program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"#!/usr/local/env zoo kcl
const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)
"#
);
}
#[test]
fn test_recast_shebang_with_comments() {
let some_program_string = r#"#!/usr/local/env zoo kcl
// Yo yo my comments.
const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)
"#;
let tokens = crate::token::lexer(some_program_string).unwrap();
let parser = crate::parser::Parser::new(tokens);
let program = parser.ast().unwrap();
let recasted = program.recast(&Default::default(), 0);
assert_eq!(
recasted,
r#"#!/usr/local/env zoo kcl
// Yo yo my comments.
const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)
"#
);
}
#[test]
fn test_recast_large_file() {
let some_program_string = r#"// define constants

View File

@ -67,7 +67,7 @@ impl ProgramMemory {
/// Add to the program memory.
pub fn add(&mut self, key: &str, value: MemoryItem, source_range: SourceRange) -> Result<(), KclError> {
if self.root.contains_key(key) {
if self.root.get(key).is_some() {
return Err(KclError::ValueAlreadyDefined(KclErrorDetails {
message: format!("Cannot redefine {}", key),
source_ranges: vec![source_range],

View File

@ -795,7 +795,11 @@ impl LanguageServer for Backend {
return Ok(None);
};
let Some(hover) = ast.get_hover_value_for_position(pos, current_code) else {
let Some(value) = ast.get_value_for_position(pos) else {
return Ok(None);
};
let Some(hover) = value.get_hover_value_for_position(pos, current_code) else {
return Ok(None);
};
@ -832,13 +836,6 @@ impl LanguageServer for Backend {
}))
}
crate::ast::types::Hover::Signature { .. } => Ok(None),
crate::ast::types::Hover::Comment { value, range } => Ok(Some(Hover {
contents: HoverContents::Markup(MarkupContent {
kind: MarkupKind::Markdown,
value,
}),
range: Some(range),
})),
}
}
@ -947,9 +944,6 @@ impl LanguageServer for Backend {
Ok(Some(signature.clone()))
}
crate::ast::types::Hover::Comment { value: _, range: _ } => {
return Ok(None);
}
}
}

View File

@ -742,7 +742,7 @@ async fn test_kcl_lsp_create_zip() {
assert_eq!(files.len(), 11);
let util_path = format!("{}/util.rs", string_path).replace("file://", "");
assert!(files.contains_key(&util_path));
assert!(files.get(&util_path).is_some());
assert_eq!(files.get("/test.kcl"), Some(&4));
}
@ -884,53 +884,6 @@ async fn test_kcl_lsp_on_hover() {
}
}
#[tokio::test(flavor = "multi_thread")]
async fn test_kcl_lsp_on_hover_shebang() {
let server = kcl_lsp_server(false).await.unwrap();
// Send open file.
server
.did_open(tower_lsp::lsp_types::DidOpenTextDocumentParams {
text_document: tower_lsp::lsp_types::TextDocumentItem {
uri: "file:///test.kcl".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: r#"#!/usr/bin/env zoo kcl view
startSketchOn()"#
.to_string(),
},
})
.await;
server.wait_on_handle().await;
// Send hover request.
let hover = server
.hover(tower_lsp::lsp_types::HoverParams {
text_document_position_params: tower_lsp::lsp_types::TextDocumentPositionParams {
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
},
position: tower_lsp::lsp_types::Position { line: 0, character: 2 },
},
work_done_progress_params: Default::default(),
})
.await
.unwrap();
// Check the hover.
if let Some(hover) = hover {
assert_eq!(
hover.contents,
tower_lsp::lsp_types::HoverContents::Markup(tower_lsp::lsp_types::MarkupContent {
kind: tower_lsp::lsp_types::MarkupKind::Markdown,
value: "The `#!` at the start of a script, known as a shebang, specifies the path to the interpreter that should execute the script. This line is not necessary for your `kcl` to run in the modeling-app. You can safely delete it. If you wish to learn more about what you _can_ do with a shebang, read this doc: [zoo.dev/docs/faq/shebang](https://zoo.dev/docs/faq/shebang).".to_string()
})
);
} else {
panic!("Expected hover");
}
}
#[tokio::test(flavor = "multi_thread")]
async fn test_kcl_lsp_signature_help() {
let server = kcl_lsp_server(false).await.unwrap();

View File

@ -5,7 +5,7 @@ use winnow::{
dispatch,
error::{ErrMode, StrContext, StrContextValue},
prelude::*,
token::{any, one_of, take_till},
token::{any, one_of},
};
use crate::{
@ -39,13 +39,7 @@ fn expected(what: &'static str) -> StrContext {
}
fn program(i: TokenSlice) -> PResult<Program> {
let shebang = opt(shebang).parse_next(i)?;
let mut out = function_body.parse_next(i)?;
// Add the shebang to the non-code meta.
if let Some(shebang) = shebang {
out.non_code_meta.start.insert(0, shebang);
}
// Match original parser behaviour, for now.
// Once this is merged and stable, consider changing this as I think it's more accurate
// without the -1.
@ -392,39 +386,6 @@ fn whitespace(i: TokenSlice) -> PResult<Vec<Token>> {
.parse_next(i)
}
/// A shebang is a line at the start of a file that starts with `#!`.
/// If the shebang is present it takes up the whole line.
fn shebang(i: TokenSlice) -> PResult<NonCodeNode> {
// Parse the hash and the bang.
hash.parse_next(i)?;
bang.parse_next(i)?;
// Get the rest of the line.
// Parse everything until the next newline.
let tokens = take_till(0.., |token: Token| token.value.contains('\n')).parse_next(i)?;
let value = tokens.iter().map(|t| t.value.as_str()).collect::<String>();
if tokens.is_empty() {
return Err(ErrMode::Cut(
KclError::Syntax(KclErrorDetails {
source_ranges: vec![],
message: "expected a shebang value after #!".to_owned(),
})
.into(),
));
}
// Strip all the whitespace after the shebang.
opt(whitespace).parse_next(i)?;
Ok(NonCodeNode {
start: 0,
end: tokens.last().unwrap().end,
value: NonCodeValue::Shebang {
value: format!("#!{}", value),
},
})
}
/// Parse the = operator.
fn equals(i: TokenSlice) -> PResult<Token> {
one_of((TokenType::Operator, "="))
@ -640,7 +601,6 @@ fn noncode_just_after_code(i: TokenSlice) -> PResult<NonCodeNode> {
// There's an empty line between the body item and the comment,
// This means the comment is a NewLineBlockComment!
let value = match nc.value {
NonCodeValue::Shebang { value } => NonCodeValue::Shebang { value },
// Change block comments to inline, as discussed above
NonCodeValue::BlockComment { value, style } => NonCodeValue::NewLineBlockComment { value, style },
// Other variants don't need to change.
@ -660,7 +620,6 @@ fn noncode_just_after_code(i: TokenSlice) -> PResult<NonCodeNode> {
// There's no newline between the body item and comment,
// so if this is a comment, it must be inline with code.
let value = match nc.value {
NonCodeValue::Shebang { value } => NonCodeValue::Shebang { value },
// Change block comments to inline, as discussed above
NonCodeValue::BlockComment { value, style } => NonCodeValue::InlineComment { value, style },
// Other variants don't need to change.
@ -1245,16 +1204,6 @@ fn comma(i: TokenSlice) -> PResult<()> {
Ok(())
}
fn hash(i: TokenSlice) -> PResult<()> {
TokenType::Hash.parse_from(i)?;
Ok(())
}
fn bang(i: TokenSlice) -> PResult<()> {
TokenType::Bang.parse_from(i)?;
Ok(())
}
fn period(i: TokenSlice) -> PResult<()> {
TokenType::Period.parse_from(i)?;
Ok(())
@ -2382,7 +2331,7 @@ const secondExtrude = startSketchOn('XY')
let err = parser.ast().unwrap_err();
assert_eq!(
err.to_string(),
r#"syntax: KclErrorDetails { source_ranges: [SourceRange([0, 1])], message: "Unexpected token" }"#
r#"lexical: KclErrorDetails { source_ranges: [SourceRange([1, 2])], message: "found unknown token '!'" }"#
);
}
@ -2449,7 +2398,7 @@ z(-[["#,
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
r#"syntax: KclErrorDetails { source_ranges: [SourceRange([3, 4])], message: "Unexpected token" }"#
r#"lexical: KclErrorDetails { source_ranges: [SourceRange([6, 7])], message: "found unknown token '#'" }"#
);
}
@ -2461,7 +2410,7 @@ z(-[["#,
assert!(result.is_err());
assert_eq!(
result.err().unwrap().to_string(),
r#"syntax: KclErrorDetails { source_ranges: [SourceRange([2, 3])], message: "Unexpected token" }"#
r#"lexical: KclErrorDetails { source_ranges: [SourceRange([25, 26]), SourceRange([26, 27])], message: "found unknown tokens [#, #]" }"#
);
}

View File

@ -1,13 +1,11 @@
//! Types for interacting with files in projects.
use std::path::{Path, PathBuf};
use anyhow::Result;
use parse_display::{Display, FromStr};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::settings::types::{Configuration, DEFAULT_PROJECT_KCL_FILE};
use super::Configuration;
/// State management for the application.
#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq)]
@ -18,88 +16,6 @@ pub struct ProjectState {
pub current_file: Option<String>,
}
impl ProjectState {
/// Create a new project state from a path.
#[cfg(not(target_arch = "wasm32"))]
pub async fn new_from_path(path: PathBuf) -> Result<ProjectState> {
// Fix for "." path, which is the current directory.
let source_path = if path == Path::new(".") {
std::env::current_dir().map_err(|e| anyhow::anyhow!("Error getting the current directory: {:?}", e))?
} else {
path
};
// If the path does not start with a slash, it is a relative path.
// We need to convert it to an absolute path.
let source_path = if source_path.is_relative() {
std::env::current_dir()
.map_err(|e| anyhow::anyhow!("Error getting the current directory: {:?}", e))?
.join(source_path)
} else {
source_path
};
// If the path is a directory, let's assume it is a project directory.
if source_path.is_dir() {
// Load the details about the project from the path.
let project = Project::from_path(&source_path)
.await
.map_err(|e| anyhow::anyhow!("Error loading project from path {}: {:?}", source_path.display(), e))?;
// Check if we have a main.kcl file in the project.
let project_file = source_path.join(DEFAULT_PROJECT_KCL_FILE);
if !project_file.exists() {
// Create the default file in the project.
// Write the initial project file.
tokio::fs::write(&project_file, vec![]).await?;
}
return Ok(ProjectState {
project,
current_file: Some(project_file.display().to_string()),
});
}
// Check if the extension on what we are trying to open is a relevant file type.
// Get the extension of the file.
let extension = source_path
.extension()
.ok_or_else(|| anyhow::anyhow!("Error getting the extension of the file: {}", source_path.display()))?;
let ext = extension.to_string_lossy().to_string();
// Check if the extension is a relevant file type.
if !crate::settings::utils::RELEVANT_EXTENSIONS.contains(&ext) || ext == "toml" {
return Err(anyhow::anyhow!(
"File type ({}) cannot be opened with this app: {}, try opening one of the following file types: {}",
ext,
source_path.display(),
crate::settings::utils::RELEVANT_EXTENSIONS.join(", ")
));
}
// We were given a file path, not a directory.
// Let's get the parent directory of the file.
let parent = source_path.parent().ok_or_else(|| {
anyhow::anyhow!(
"Error getting the parent directory of the file: {}",
source_path.display()
)
})?;
// Load the details about the project from the parent directory.
let project = Project::from_path(&parent)
.await
.map_err(|e| anyhow::anyhow!("Error loading project from path {}: {:?}", source_path.display(), e))?;
Ok(ProjectState {
project,
current_file: Some(source_path.display().to_string()),
})
}
}
/// Project route information.
#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq)]
#[ts(export)]

View File

@ -31,7 +31,7 @@ impl Configuration {
if let Some(project_directory) = &settings.settings.app.project_directory {
if settings.settings.project.directory.to_string_lossy().is_empty() {
settings.settings.project.directory.clone_from(project_directory);
settings.settings.project.directory = project_directory.clone();
settings.settings.app.project_directory = None;
}
}

View File

@ -8,8 +8,8 @@ use clap::ValueEnum;
use crate::settings::types::file::FileEntry;
lazy_static::lazy_static! {
pub static ref RELEVANT_EXTENSIONS: Vec<String> = {
let mut relevant_extensions = vec!["kcl".to_string(), "stp".to_string(), "glb".to_string(), "fbxb".to_string()];
static ref RELEVANT_EXTENSIONS: Vec<String> = {
let mut relevant_extensions = vec!["kcl".to_string(), "stp".to_string(), "glb".to_string()];
let named_extensions = kittycad::types::FileImportFormat::value_variants()
.iter()
.map(|x| format!("{}", x))
@ -22,10 +22,7 @@ lazy_static::lazy_static! {
/// Walk a directory recursively and return a list of all files.
#[async_recursion::async_recursion]
pub async fn walk_dir<P>(dir: P) -> Result<FileEntry>
where
P: AsRef<Path> + Send,
{
pub async fn walk_dir<P: AsRef<Path> + Send>(dir: P) -> Result<FileEntry> {
let mut entry = FileEntry {
name: dir
.as_ref()

View File

@ -947,7 +947,6 @@ async fn start_sketch_on_face(
ortho: false,
entity_id: extrude_plane_id,
adjust_camera: false,
planar_normal: None,
},
)
.await?;
@ -1006,13 +1005,12 @@ async fn start_sketch_on_plane(data: PlaneData, args: Args) -> Result<Box<Plane>
// Enter sketch mode on the plane.
args.send_modeling_cmd(
uuid::Uuid::new_v4(),
ModelingCmd::EnableSketchMode {
ModelingCmd::SketchModeEnable {
animated: false,
ortho: false,
entity_id: plane.id,
plane_id: plane.id,
// We pass in the normal for the plane here.
planar_normal: Some(plane.z_axis.clone().into()),
adjust_camera: false,
disable_camera_with_plane: Some(plane.z_axis.clone().into()),
},
)
.await?;

View File

@ -31,10 +31,6 @@ pub enum TokenType {
Type,
/// A brace.
Brace,
/// A hash.
Hash,
/// A bang.
Bang,
/// Whitespace.
Whitespace,
/// A comma.
@ -78,8 +74,6 @@ impl TryFrom<TokenType> for SemanticTokenType {
| TokenType::Colon
| TokenType::Period
| TokenType::DoublePeriod
| TokenType::Hash
| TokenType::Bang
| TokenType::Unknown => {
anyhow::bail!("unsupported token type: {:?}", token_type)
}

View File

@ -25,8 +25,6 @@ pub fn token(i: &mut Located<&str>) -> PResult<Token> {
'0'..='9' => number,
':' => colon,
'.' => alt((number, double_period, period)),
'#' => hash,
'!' => bang,
' ' | '\t' | '\n' => whitespace,
_ => alt((operator, keyword,type_, word))
}
@ -111,16 +109,6 @@ fn comma(i: &mut Located<&str>) -> PResult<Token> {
Ok(Token::from_range(range, TokenType::Comma, value.to_string()))
}
fn hash(i: &mut Located<&str>) -> PResult<Token> {
let (value, range) = '#'.with_span().parse_next(i)?;
Ok(Token::from_range(range, TokenType::Hash, value.to_string()))
}
fn bang(i: &mut Located<&str>) -> PResult<Token> {
let (value, range) = '!'.with_span().parse_next(i)?;
Ok(Token::from_range(range, TokenType::Bang, value.to_string()))
}
fn question_mark(i: &mut Located<&str>) -> PResult<Token> {
let (value, range) = '?'.with_span().parse_next(i)?;
Ok(Token::from_range(range, TokenType::QuestionMark, value.to_string()))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

@ -67,12 +67,11 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid
.send_modeling_cmd(
uuid::Uuid::new_v4(),
SourceRange::default(),
ModelingCmd::EnableSketchMode {
ModelingCmd::SketchModeEnable {
animated: false,
ortho: true,
entity_id: plane_id,
planar_normal: Some(Point3D { x: 0.0, y: 0.0, z: 1.0 }),
adjust_camera: false,
plane_id,
disable_camera_with_plane: Some(Point3D { x: 0.0, y: 0.0, z: 1.0 }),
},
)
.await?;

View File

@ -8,8 +8,7 @@
"vite/client",
"@types/wicg-file-system-access",
"node",
"@wdio/globals/types",
"mocha"
"@wdio/globals/types"
],
"target": "esnext",
"lib": ["dom", "dom.iterable", "esnext"],

View File

@ -22,7 +22,6 @@ export const config = {
reporters: ['spec'],
framework: 'mocha',
mochaOpts: {
bail: true,
ui: 'bdd',
timeout: 600000,
},

View File

@ -1831,10 +1831,10 @@
resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60"
integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==
"@kittycad/lib@^0.0.60":
version "0.0.60"
resolved "https://registry.yarnpkg.com/@kittycad/lib/-/lib-0.0.60.tgz#478aa1f750ab05cd4e67503de96f2f3bbc075329"
integrity sha512-LW9NFy2gv0pm1GJyquMXPiFKOBSdJJxYGkmacDton6jluGhAa8Qtcuj3O5vqUEeq9ObSM1Jt8gp39P9nvXG9yg==
"@kittycad/lib@^0.0.58":
version "0.0.58"
resolved "https://registry.yarnpkg.com/@kittycad/lib/-/lib-0.0.58.tgz#4b47a295d8238e64d30d855336e4bcc4aab380f9"
integrity sha512-iSkJNXumtlEjey/K1Lsl/j1aVlcjcPONW7+6YwqccioKJ6zwSCgXF4AFB3oxVmM27Gm8YjEmaHxbcOhGP02X9g==
dependencies:
node-fetch "3.3.2"
openapi-types "^12.0.0"
@ -2427,7 +2427,7 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
"@types/mocha@^10.0.0", "@types/mocha@^10.0.6":
"@types/mocha@^10.0.0":
version "10.0.6"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.6.tgz#818551d39113081048bdddbef96701b4e8bb9d1b"
integrity sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==