Compare commits

...

6 Commits

Author SHA1 Message Date
cc145a267c WIP: Do we need to derive JsonSchema? 2024-09-24 12:30:50 -05:00
66e60f2ddb Bump clap from 4.5.17 to 4.5.18 in /src/wasm-lib (#3949)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.17 to 4.5.18.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.17...clap_complete-v4.5.18)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 12:42:27 -07:00
5f51a0f569 Bump thiserror from 1.0.63 to 1.0.64 in /src/wasm-lib (#3948)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.63 to 1.0.64.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.63...1.0.64)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 12:42:17 -07:00
aee1d66e56 Add ability to open KCL samples in-app (#3912)
* Add a shell script to get the list of KCL samples into the app

* Add support for overwriting current file with sample

* Move these KCL commands down into FileMachineProvider

* Add support for creating a new file on desktop

* Make it so these files aren't set to "renaming mode" right away

* Add support for initializing default values that are functions

* Add E2E tests

* Add a code menu item to load a sample

* Fix tsc issues

* Remove `yarn fetch:samples` from `yarn postinstall`

* Remove change to arg initialization logic, I was holding it wrong

* Switch to use new manifest file from kcl-samples repo

* Update tests now that we use proper sample titles

* Remove double-load from units menu test

* @jtran feedback

* Don't encode `https://` that's silly

* fmt

* Update e2e/playwright/testing-samples-loading.spec.ts

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>

* Test feedback

* Add a test step to actually check the file contents were written to (@Irev-Dev feedback)

---------

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2024-09-23 14:35:38 -04:00
1d1bb8cee0 Cut release v0.25.4 (#3952) 2024-09-23 13:10:12 -04:00
c7dd89e720 bump kcl-lib 2024-09-23 11:37:11 -04:00
24 changed files with 725 additions and 69 deletions

View File

@ -0,0 +1,195 @@
import { test, expect } from '@playwright/test'
import { getUtils, setup, setupElectron, tearDown } from './test-utils'
import { bracket } from 'lib/exampleKcl'
import * as fsp from 'fs/promises'
import { join } from 'path'
import { FILE_EXT } from 'lib/constants'
test.beforeEach(async ({ context, page }, testInfo) => {
await setup(context, page, testInfo)
})
test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo)
})
test.describe('Testing in-app sample loading', () => {
/**
* Note this test implicitly depends on the KCL sample "flange-with-patterns.kcl"
* and its title. https://github.com/KittyCAD/kcl-samples/blob/main/flange-with-patterns/flange-with-patterns.kcl
*/
test('Web: should overwrite current code, cannot create new file', async ({
page,
}) => {
const u = await getUtils(page)
await test.step(`Test setup`, async () => {
await page.addInitScript((code) => {
window.localStorage.setItem('persistCode', code)
}, bracket)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
})
// Locators and constants
const newSample = {
file: 'flange-with-patterns' + FILE_EXT,
title: 'Flange',
}
const commandBarButton = page.getByRole('button', { name: 'Commands' })
const samplesCommandOption = page.getByRole('option', {
name: 'Open Sample',
})
const commandSampleOption = page.getByRole('option', {
name: newSample.title,
exact: true,
})
const commandMethodArgButton = page.getByRole('button', {
name: 'Method',
})
const commandMethodOption = (name: 'Overwrite' | 'Create new file') =>
page.getByRole('option', {
name,
})
const warningText = page.getByText('Overwrite current file?')
const confirmButton = page.getByRole('button', { name: 'Submit command' })
const codeLocator = page.locator('.cm-content')
await test.step(`Precondition: check the initial code`, async () => {
await u.openKclCodePanel()
await expect(codeLocator).toContainText(bracket.split('\n')[0])
})
await test.step(`Load a KCL sample with the command palette`, async () => {
await commandBarButton.click()
await samplesCommandOption.click()
await commandSampleOption.click()
await commandMethodArgButton.click()
await expect(commandMethodOption('Create new file')).not.toBeVisible()
await commandMethodOption('Overwrite').click()
await expect(warningText).toBeVisible()
await confirmButton.click()
await expect(codeLocator).toContainText('// ' + newSample.title)
})
})
/**
* Note this test implicitly depends on the KCL samples:
* "flange-with-patterns.kcl": https://github.com/KittyCAD/kcl-samples/blob/main/flange-with-patterns/flange-with-patterns.kcl
* "gear-rack.kcl": https://github.com/KittyCAD/kcl-samples/blob/main/gear-rack/gear-rack.kcl
*/
test(
'Desktop: should create new file by default, optionally overwrite',
{ tag: '@electron' },
async ({ browserName: _ }, testInfo) => {
const { electronApp, page, dir } = await setupElectron({
testInfo,
folderSetupFn: async (dir) => {
const bracketDir = join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true })
await fsp.writeFile(join(bracketDir, 'main.kcl'), bracket, {
encoding: 'utf-8',
})
},
})
const u = await getUtils(page)
// Locators and constants
const sampleOne = {
file: 'flange-with-patterns' + FILE_EXT,
title: 'Flange',
}
const sampleTwo = {
file: 'gear-rack' + FILE_EXT,
title: '100mm Gear Rack',
}
const projectCard = page.getByRole('link', { name: 'bracket' })
const commandBarButton = page.getByRole('button', { name: 'Commands' })
const commandOption = page.getByRole('option', { name: 'Open Sample' })
const commandSampleOption = (name: string) =>
page.getByRole('option', {
name,
exact: true,
})
const commandMethodArgButton = page.getByRole('button', {
name: 'Method',
})
const commandMethodOption = page.getByRole('option', {
name: 'Overwrite',
})
const newFileWarning = page.getByText(
'Create a new file with the example code?'
)
const overwriteWarning = page.getByText('Overwrite current file?')
const confirmButton = page.getByRole('button', { name: 'Submit command' })
const projectMenuButton = page.getByTestId('project-sidebar-toggle')
const newlyCreatedFile = (name: string) =>
page.getByRole('listitem').filter({
has: page.getByRole('button', { name }),
})
const codeLocator = page.locator('.cm-content')
await test.step(`Test setup`, async () => {
await page.setViewportSize({ width: 1200, height: 500 })
await projectCard.click()
await u.waitForPageLoad()
})
await test.step(`Precondition: check the initial code`, async () => {
await u.openKclCodePanel()
await expect(codeLocator).toContainText(bracket.split('\n')[0])
await u.openFilePanel()
await expect(projectMenuButton).toContainText('main.kcl')
await expect(newlyCreatedFile(sampleOne.file)).not.toBeVisible()
})
await test.step(`Load a KCL sample with the command palette`, async () => {
await commandBarButton.click()
await commandOption.click()
await commandSampleOption(sampleOne.title).click()
await expect(overwriteWarning).not.toBeVisible()
await expect(newFileWarning).toBeVisible()
await confirmButton.click()
})
await test.step(`Ensure we made and opened a new file`, async () => {
await expect(codeLocator).toContainText('// ' + sampleOne.title)
await expect(newlyCreatedFile(sampleOne.file)).toBeVisible()
await expect(projectMenuButton).toContainText(sampleOne.file)
})
await test.step(`Now overwrite the current file`, async () => {
await commandBarButton.click()
await commandOption.click()
await commandSampleOption(sampleTwo.title).click()
await commandMethodArgButton.click()
await commandMethodOption.click()
await expect(commandMethodArgButton).toContainText('overwrite')
await expect(newFileWarning).not.toBeVisible()
await expect(overwriteWarning).toBeVisible()
await confirmButton.click()
})
await test.step(`Ensure we overwrote the current file without navigating`, async () => {
await expect(codeLocator).toContainText('// ' + sampleTwo.title)
await test.step(`Check actual file contents`, async () => {
await expect
.poll(async () => {
return await fsp.readFile(
join(dir, 'bracket', sampleOne.file),
'utf-8'
)
})
.toContain('// ' + sampleTwo.title)
})
await expect(newlyCreatedFile(sampleOne.file)).toBeVisible()
await expect(newlyCreatedFile(sampleTwo.file)).not.toBeVisible()
await expect(projectMenuButton).toContainText(sampleOne.file)
})
await electronApp.close()
}
)
})

View File

@ -13,9 +13,6 @@ test.afterEach(async ({ page }, testInfo) => {
test('Units menu', async ({ page }) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
await u.waitForAuthSkipAppStart()
const unitsMenuButton = page.getByRole('button', {

View File

@ -1,6 +1,6 @@
{
"name": "zoo-modeling-app",
"version": "0.25.3",
"version": "0.25.4",
"private": true,
"productName": "Zoo Modeling App",
"author": {
@ -83,6 +83,7 @@
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages",
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages",
"fetch:wasm": "./get-latest-wasm-bundle.sh",
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/kcl-samples/main/manifest.json",
"isomorphic-copy-wasm": "(copy src/wasm-lib/pkg/wasm_lib_bg.wasm public || cp src/wasm-lib/pkg/wasm_lib_bg.wasm public)",
"build:wasm-dev": "yarn wasm-prep && (cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && yarn isomorphic-copy-wasm && yarn fmt",
"build:wasm": "yarn wasm-prep && cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings && cd ../.. && yarn isomorphic-copy-wasm && yarn fmt",
@ -90,7 +91,7 @@
"wasm-prep": "rimraf src/wasm-lib/pkg && mkdirp src/wasm-lib/pkg && rimraf src/wasm-lib/kcl/bindings",
"lint": "eslint --fix src e2e packages/codemirror-lsp-client",
"bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json",
"postinstall": "yarn xstate:typegen && ./node_modules/.bin/electron-rebuild",
"postinstall": "yarn fetch:samples && yarn xstate:typegen && ./node_modules/.bin/electron-rebuild",
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\"",
"make:dev": "make dev",
"generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts",

View File

@ -0,0 +1,152 @@
[
{
"file": "80-20-rail.kcl",
"title": "80/20 Rail",
"description": "An 80/20 extruded aluminum linear rail. T-slot profile adjustable by profile height, rail length, and origin position"
},
{
"file": "a-parametric-bearing-pillow-block.kcl",
"title": "A Parametric Bearing Pillow Block",
"description": "A bearing pillow block, also known as a plummer block or pillow block bearing, is a pedestal used to provide support for a rotating shaft with the help of compatible bearings and various accessories. Housing a bearing, the pillow block provides a secure and stable foundation that allows the shaft to rotate smoothly within its machinery setup. These components are essential in a wide range of mechanical systems and machinery, playing a key role in reducing friction and supporting radial and axial loads."
},
{
"file": "ball-bearing.kcl",
"title": "Ball Bearing",
"description": "A ball bearing is a type of rolling-element bearing that uses balls to maintain the separation between the bearing races. The primary purpose of a ball bearing is to reduce rotational friction and support radial and axial loads."
},
{
"file": "bracket.kcl",
"title": "Shelf Bracket",
"description": "This is a bracket that holds a shelf. It is made of aluminum and is designed to hold a force of 300 lbs. The bracket is 6 inches wide and the force is applied at the end of the shelf, 12 inches from the wall. The bracket has a factor of safety of 1.2. The legs of the bracket are 5 inches and 2 inches long. The thickness of the bracket is calculated from the constraints provided."
},
{
"file": "brake-caliper.kcl",
"title": "Brake Caliper",
"description": "Brake calipers are used to squeeze the brake pads against the rotor, causing larger and larger amounts of friction depending on how hard the brakes are pressed."
},
{
"file": "car-wheel.kcl",
"title": "Car Wheel",
"description": "A sports car wheel with a circular lug pattern and spokes."
},
{
"file": "car-wheel-assembly.kcl",
"title": "Car Wheel Assembly",
"description": "A car wheel assembly with a rotor, tire, and lug nuts."
},
{
"file": "enclosure.kcl",
"title": "Enclosure",
"description": "An enclosure body and sealing lid for storing items"
},
{
"file": "flange-with-patterns.kcl",
"title": "Flange",
"description": "A flange is a flat rim, collar, or rib, typically forged or cast, that is used to strengthen an object, guide it, or attach it to another object. Flanges are known for their use in various applications, including piping, plumbing, and mechanical engineering, among others."
},
{
"file": "flange-xy.kcl",
"title": "Flange with XY coordinates",
"description": "A flange is a flat rim, collar, or rib, typically forged or cast, that is used to strengthen an object, guide it, or attach it to another object. Flanges are known for their use in various applications, including piping, plumbing, and mechanical engineering, among others."
},
{
"file": "focusrite-scarlett-mounting-bracket.kcl",
"title": "A mounting bracket for the Focusrite Scarlett Solo audio interface",
"description": "This is a bracket that holds an audio device underneath a desk or shelf. The audio device has dimensions of 144mm wide, 80mm length and 45mm depth with fillets of 6mm. This mounting bracket is designed to be 3D printed with PLA material"
},
{
"file": "french-press.kcl",
"title": "French Press",
"description": "A french press immersion coffee maker"
},
{
"file": "gear.kcl",
"title": "Gear",
"description": "A rotating machine part having cut teeth or, in the case of a cogwheel, inserted teeth (called cogs), which mesh with another toothed part to transmit torque. Geared devices can change the speed, torque, and direction of a power source. The two elements that define a gear are its circular shape and the teeth that are integrated into its outer edge, which are designed to fit into the teeth of another gear."
},
{
"file": "gear-rack.kcl",
"title": "100mm Gear Rack",
"description": "A flat bar or rail that is engraved with teeth along its length. These teeth are designed to mesh with the teeth of a gear, known as a pinion. When the pinion, a small cylindrical gear, rotates, its teeth engage with the teeth on the rack, causing the rack to move linearly. Conversely, linear motion applied to the rack will cause the pinion to rotate."
},
{
"file": "hex-nut.kcl",
"title": "Hex nut",
"description": "A hex nut is a type of fastener with a threaded hole and a hexagonal outer shape, used in a wide variety of applications to secure parts together. The hexagonal shape allows for a greater torque to be applied with wrenches or tools, making it one of the most common nut types in hardware."
},
{
"file": "kitt.kcl",
"title": "Kitt",
"description": "The beloved KittyCAD mascot in a voxelized style."
},
{
"file": "lego.kcl",
"title": "Lego Brick",
"description": "A standard Lego brick. This is a small, plastic construction block toy that can be interlocked with other blocks to build various structures, models, and figures. There are a lot of hacks used in this code."
},
{
"file": "lug-nut.kcl",
"title": "Lug Nut",
"description": "lug Nuts are essential components used to create secure connections, whether for electrical purposes, like terminating wires or grounding, or for mechanical purposes, such as providing mounting points or reinforcing structural joints."
},
{
"file": "mounting-plate.kcl",
"title": "Mounting Plate",
"description": "A flat piece of material, often metal or plastic, that serves as a support or base for attaching, securing, or mounting various types of equipment, devices, or components."
},
{
"file": "multi-axis-robot.kcl",
"title": "Robot Arm",
"description": "A 4 axis robotic arm for industrial use. These machines can be used for assembly, packaging, organization of goods, and quality inspection processes"
},
{
"file": "pipe.kcl",
"title": "Pipe",
"description": "A tubular section or hollow cylinder, usually but not necessarily of circular cross-section, used mainly to convey substances that can flow."
},
{
"file": "pipe-flange-assembly.kcl",
"title": "Pipe and Flange Assembly",
"description": "A crucial component in various piping systems, designed to facilitate the connection, disconnection, and access to piping for inspection, cleaning, and modifications. This assembly combines pipes (long cylindrical conduits) with flanges (plate-like fittings) to create a secure yet detachable joint."
},
{
"file": "poopy-shoe.kcl",
"title": "Poopy Shoe",
"description": "poop shute for bambu labs printer - optimized for printing."
},
{
"file": "router-template-cross-bar.kcl",
"title": "Router template for a cross bar",
"description": "A guide for routing a notch into a cross bar."
},
{
"file": "router-template-slate.kcl",
"title": "Router template for a slate",
"description": "A guide for routing a slate for a cross bar."
},
{
"file": "sheet-metal-bracket.kcl",
"title": "Sheet Metal Bracket",
"description": "A component typically made from flat sheet metal through various manufacturing processes such as bending, punching, cutting, and forming. These brackets are used to support, attach, or mount other hardware components, often providing a structural or functional base for assembly."
},
{
"file": "socket-head-cap-screw.kcl",
"title": "Socket Head Cap Screw",
"description": "This is for a #10-24 screw that is 1.00 inches long. A socket head cap screw is a type of fastener that is widely used in a variety of applications requiring a high strength fastening solution. It is characterized by its cylindrical head and internal hexagonal drive, which allows for tightening with an Allen wrench or hex key."
},
{
"file": "tire.kcl",
"title": "Tire",
"description": "A tire is a critical component of a vehicle that provides the necessary traction and grip between the car and the road. It supports the vehicle's weight and absorbs shocks from road irregularities."
},
{
"file": "washer.kcl",
"title": "Washer",
"description": "A small, typically disk-shaped component with a hole in the middle, used in a wide range of applications, primarily in conjunction with fasteners like bolts and screws. Washers distribute the load of a fastener across a broader area. This is especially important when the fastening surface is soft or uneven, as it helps to prevent damage to the surface and ensures the load is evenly distributed, reducing the risk of the fastener becoming loose over time."
},
{
"file": "wheel-rotor.kcl",
"title": "Wheel rotor",
"description": "A component of a disc brake system. It provides a surface for brake pads to press against, generating the friction needed to slow or stop the vehicle."
}
]

View File

@ -6,8 +6,8 @@ import { CommandArgument, CommandArgumentOption } from 'lib/commandTypes'
import { useEffect, useMemo, useRef, useState } from 'react'
import { AnyStateMachine, StateFrom } from 'xstate'
const contextSelector = (snapshot: StateFrom<AnyStateMachine>) =>
snapshot.context
const contextSelector = (snapshot: StateFrom<AnyStateMachine> | undefined) =>
snapshot?.context
function CommandArgOptionInput({
arg,

View File

@ -140,7 +140,11 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
JSON.stringify(argValue)
)
) : (
<em>{argValue}</em>
<em>
{arg.valueSummary
? arg.valueSummary(argValue)
: argValue}
</em>
)
) : null}
</span>

View File

@ -58,7 +58,17 @@ function CommandBarReview({ stepBack }: { stepBack: () => void }) {
return (
<CommandBarHeader>
<p className="px-4">Confirm {selectedCommand?.name}</p>
<p className="px-4">
{selectedCommand?.reviewMessage ? (
selectedCommand.reviewMessage instanceof Function ? (
selectedCommand.reviewMessage(commandBarState.context)
) : (
selectedCommand.reviewMessage
)
) : (
<>Confirm {selectedCommand?.name}</>
)}
</p>
<form
id="review-form"
className="absolute opacity-0 inset-0 pointer-events-none"

View File

@ -31,8 +31,8 @@ function getSemanticSelectionType(selectionType: Array<Selection['type']>) {
return Array.from(semanticSelectionType)
}
const selectionSelector = (snapshot: StateFrom<typeof modelingMachine>) =>
snapshot.context.selectionRanges
const selectionSelector = (snapshot?: StateFrom<typeof modelingMachine>) =>
snapshot?.context.selectionRanges
function CommandBarSelectionInput({
arg,
@ -49,7 +49,7 @@ function CommandBarSelectionInput({
const [hasSubmitted, setHasSubmitted] = useState(false)
const selection = useSelector(arg.machineActor, selectionSelector)
const selectionsByType = useMemo(() => {
const selectionRangeEnd = selection.codeBasedSelections[0]?.range[1]
const selectionRangeEnd = selection?.codeBasedSelections[0]?.range[1]
return !selectionRangeEnd || selectionRangeEnd === code.length
? 'none'
: getSelectionType(selection)

View File

@ -0,0 +1,16 @@
interface CommandBarOverwriteWarningProps {
heading?: string
message?: string
}
export function CommandBarOverwriteWarning({
heading = 'Overwrite current file?',
message = 'This will permanently replace the current code in the editor.',
}: CommandBarOverwriteWarningProps) {
return (
<>
<p className="font-bold text-destroy-60">{heading}</p>
<p>{message}</p>
</>
)
}

View File

@ -2,7 +2,7 @@ import { useMachine } from '@xstate/react'
import { useNavigate, useRouteLoaderData } from 'react-router-dom'
import { type IndexLoaderData } from 'lib/types'
import { PATHS } from 'lib/paths'
import React, { createContext } from 'react'
import React, { createContext, useEffect, useMemo } from 'react'
import { toast } from 'react-hot-toast'
import {
Actor,
@ -22,6 +22,12 @@ import {
} from 'lib/constants'
import { getProjectInfo } from 'lib/desktop'
import { getNextDirName, getNextFileName } from 'lib/desktopFS'
import { kclCommands } from 'lib/kclCommands'
import { codeManager, kclManager } from 'lib/singletons'
import {
getKclSamplesManifest,
KclSamplesManifestItem,
} from 'lib/getKclSamplesManifest'
type MachineContext<T extends AnyStateMachine> = {
state: StateFrom<T>
@ -41,6 +47,16 @@ export const FileMachineProvider = ({
const navigate = useNavigate()
const { commandBarSend } = useCommandsContext()
const { project, file } = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
const [kclSamples, setKclSamples] = React.useState<KclSamplesManifestItem[]>(
[]
)
useEffect(() => {
async function fetchKclSamples() {
setKclSamples(await getKclSamplesManifest())
}
fetchKclSamples().catch(reportError)
}, [])
const [state, send] = useMachine(
fileMachine.provide({
@ -121,6 +137,7 @@ export const FileMachineProvider = ({
return {
message: `Successfully created "${createdName}"`,
path: createdPath,
shouldSetToRename: input.shouldSetToRename,
}
}),
createFile: fromPromise(async ({ input }) => {
@ -271,6 +288,46 @@ export const FileMachineProvider = ({
}
)
const kclCommandMemo = useMemo(
() =>
kclCommands(
async (data) => {
if (data.method === 'overwrite') {
codeManager.updateCodeStateEditor(data.code)
await kclManager.executeCode(true)
await codeManager.writeToFile()
} else if (data.method === 'newFile' && isDesktop()) {
send({
type: 'Create file',
data: {
name: data.sampleName,
content: data.code,
makeDir: false,
},
})
}
},
kclSamples.map((sample) => ({
value: sample.file,
name: sample.title,
}))
).filter(
(command) => kclSamples.length || command.name !== 'open-kcl-example'
),
[codeManager, kclManager, send, kclSamples]
)
useEffect(() => {
commandBarSend({ type: 'Add commands', data: { commands: kclCommandMemo } })
return () => {
commandBarSend({
type: 'Remove commands',
data: { commands: kclCommandMemo },
})
}
}, [commandBarSend, kclCommandMemo])
return (
<FileContext.Provider
value={{

View File

@ -393,14 +393,14 @@ export const FileTreeMenu = () => {
function createFile() {
send({
type: 'Create file',
data: { name: '', makeDir: false },
data: { name: '', makeDir: false, shouldSetToRename: true },
})
}
function createFolder() {
send({
type: 'Create file',
data: { name: '', makeDir: true },
data: { name: '', makeDir: true, shouldSetToRename: true },
})
}

View File

@ -9,10 +9,12 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { kclManager } from 'lib/singletons'
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
import { reportRejection } from 'lib/trap'
import { useCommandsContext } from 'hooks/useCommandsContext'
export const KclEditorMenu = ({ children }: PropsWithChildren) => {
const { enable: convertToVarEnabled, handleClick: handleConvertToVarClick } =
useConvertToVariable()
const { commandBarSend } = useCommandsContext()
return (
<Menu>
@ -77,6 +79,22 @@ export const KclEditorMenu = ({ children }: PropsWithChildren) => {
</small>
</a>
</Menu.Item>
<Menu.Item>
<button
onClick={() => {
commandBarSend({
type: 'Find and select command',
data: {
groupId: 'code',
name: 'open-kcl-example',
},
})
}}
className={styles.button}
>
<span>Load a sample model</span>
</button>
</Menu.Item>
<Menu.Item>
<a
className={styles.button}
@ -85,7 +103,7 @@ export const KclEditorMenu = ({ children }: PropsWithChildren) => {
rel="noopener noreferrer"
onClick={openExternalBrowserIfDesktop()}
>
<span>KCL samples</span>
<span>View all samples</span>
<small>
zoo.dev
<FontAwesomeIcon

View File

@ -3,8 +3,6 @@ import { createContext, useContext, useEffect, useState } from 'react'
import { type IndexLoaderData } from 'lib/types'
import { useLoaderData } from 'react-router-dom'
import { codeManager, kclManager } from 'lib/singletons'
import { useCommandsContext } from 'hooks/useCommandsContext'
import { Command } from 'lib/commandTypes'
const KclContext = createContext({
code: codeManager?.code || '',
@ -37,7 +35,6 @@ export function KclContextProvider({
const [errors, setErrors] = useState<KCLError[]>([])
const [logs, setLogs] = useState<string[]>([])
const [wasmInitFailed, setWasmInitFailed] = useState(false)
const { commandBarSend } = useCommandsContext()
useEffect(() => {
codeManager.registerCallBacks({
@ -53,28 +50,6 @@ export function KclContextProvider({
})
}, [])
// Add format code to command palette.
useEffect(() => {
const commands: Command[] = [
{
name: 'format-code',
displayName: 'Format Code',
description: 'Nicely formats the KCL code in the editor.',
needsReview: false,
groupId: 'code',
icon: 'code',
onSubmit: (data) => {
kclManager.format()
},
},
]
commandBarSend({ type: 'Add commands', data: { commands } })
return () => {
commandBarSend({ type: 'Remove commands', data: { commands } })
}
}, [kclManager, commandBarSend])
return (
<KclContext.Provider
value={{

View File

@ -4,6 +4,7 @@ import { Actor, AnyStateMachine, ContextFrom, EventFrom } from 'xstate'
import { Selection } from './selections'
import { Identifier, Expr, VariableDeclaration } from 'lang/wasm'
import { commandBarMachine } from 'machines/commandBarMachine'
import { ReactNode } from 'react'
type Icon = CustomIconName
const PLATFORMS = ['both', 'web', 'desktop'] as const
@ -67,6 +68,12 @@ export type Command<
name: CommandName
groupId: T['id']
needsReview: boolean
reviewMessage?:
| string
| ReactNode
| ((
commandBarContext: { argumentsToSubmit: Record<string, unknown> } // Should be the commandbarMachine's context, but it creates a circular dependency
) => string | ReactNode)
onSubmit: (data?: CommandSchema) => void
onCancel?: () => void
args?: {
@ -181,7 +188,7 @@ export type CommandArgument<
machineContext?: ContextFrom<T>
) => boolean)
skip?: boolean
machineActor: Actor<T>
machineActor?: Actor<T>
/** For showing a summary display of the current value, such as in
* the command bar's header
*/

View File

@ -95,3 +95,10 @@ export const MAKE_TOAST_MESSAGES = {
ERROR_STARTING_PRINT: 'Error while starting print',
SUCCESS: 'Started print successfully',
}
/** The URL for the KCL samples manifest files */
export const KCL_SAMPLES_MANIFEST_URLS = {
remote:
'https://raw.githubusercontent.com/KittyCAD/kcl-samples/main/manifst.json',
localFallback: '/kcl-samples-manifest-fallback.json',
} as const

View File

@ -0,0 +1,31 @@
import { KCL_SAMPLES_MANIFEST_URLS } from './constants'
import { isDesktop } from './isDesktop'
export type KclSamplesManifestItem = {
file: string
title: string
description: string
}
export async function getKclSamplesManifest() {
let response = await fetch(KCL_SAMPLES_MANIFEST_URLS.remote)
if (!response.ok) {
console.warn(
'Failed to fetch latest remote KCL samples manifest, falling back to local:',
response.statusText
)
response = await fetch(
(isDesktop() ? '.' : '') + KCL_SAMPLES_MANIFEST_URLS.localFallback
)
if (!response.ok) {
console.error(
'Failed to fetch fallback KCL samples manifest:',
response.statusText
)
return []
}
}
return response.json().then((manifest) => {
return manifest as KclSamplesManifestItem[]
})
}

110
src/lib/kclCommands.ts Normal file
View File

@ -0,0 +1,110 @@
import { CommandBarOverwriteWarning } from 'components/CommandBarOverwriteWarning'
import { Command, CommandArgumentOption } from './commandTypes'
import { kclManager } from './singletons'
import { isDesktop } from './isDesktop'
import { FILE_EXT } from './constants'
interface OnSubmitProps {
sampleName: string
code: string
method: 'overwrite' | 'newFile'
}
export function kclCommands(
onSubmit: (p: OnSubmitProps) => Promise<void>,
providedOptions: CommandArgumentOption<string>[]
): Command[] {
return [
{
name: 'format-code',
displayName: 'Format Code',
description: 'Nicely formats the KCL code in the editor.',
needsReview: false,
groupId: 'code',
icon: 'code',
onSubmit: () => {
kclManager.format()
},
},
{
name: 'open-kcl-example',
displayName: 'Open sample',
description: 'Imports an example KCL program into the editor.',
needsReview: true,
icon: 'code',
reviewMessage: ({ argumentsToSubmit }) =>
argumentsToSubmit.method === 'newFile'
? 'Create a new file with the example code?'
: CommandBarOverwriteWarning({}),
groupId: 'code',
onSubmit(data) {
if (!data?.sample) {
return
}
const sampleCodeUrl = `https://raw.githubusercontent.com/KittyCAD/kcl-samples/main/${encodeURIComponent(
data.sample.replace(FILE_EXT, '')
)}/${encodeURIComponent(data.sample)}`
fetch(sampleCodeUrl)
.then(async (response) => {
if (!response.ok) {
console.error('Failed to fetch sample code:', response.statusText)
return
}
const code = await response.text()
return {
sampleName: data.sample,
code,
method: data.method,
}
})
.then((props) => {
if (props?.code) {
onSubmit(props).catch(reportError)
}
})
.catch(reportError)
},
args: {
method: {
inputType: 'options',
required: true,
skip: true,
defaultValue: isDesktop() ? 'newFile' : 'overwrite',
options() {
return [
{
value: 'overwrite',
name: 'Overwrite current code',
isCurrent: !isDesktop(),
},
...(isDesktop()
? [
{
value: 'newFile',
name: 'Create a new file',
isCurrent: true,
},
]
: []),
]
},
},
sample: {
inputType: 'options',
required: true,
valueSummary(value) {
const MAX_LENGTH = 12
if (typeof value === 'string') {
return value.length > MAX_LENGTH
? value.substring(0, MAX_LENGTH) + '...'
: value
}
return value
},
options: providedOptions,
},
},
},
]
}

View File

@ -0,0 +1,34 @@
[
"80-20-rail",
"a-parametric-bearing-pillow-block",
"ball-bearing",
"bracket",
"brake-caliper",
"car-wheel-assembly",
"car-wheel",
"enclosure",
"flange-with-patterns",
"flange-xy",
"focusrite-scarlett-mounting-bracket",
"french-press",
"gear-rack",
"gear",
"hex-nut",
"kitt",
"lego",
"lug-nut",
"mounting-plate",
"multi-axis-robot",
"pipe-flange-assembly",
"pipe",
"poopy-shoe",
"router-template-cross-bar",
"router-template-slate",
"screenshots",
"sheet-metal-bracket",
"socket-head-cap-screw",
"step",
"tire",
"washer",
"wheel-rotor"
]

View File

@ -469,8 +469,9 @@ export type ResolvedSelectionType = [Selection['type'] | 'other', number]
* @returns
*/
export function getSelectionType(
selection: Selections
selection?: Selections
): ResolvedSelectionType[] {
if (!selection) return []
const extrudableCount = selection.codeBasedSelections.filter((_, i) => {
const singleSelection = {
...selection,
@ -485,7 +486,7 @@ export function getSelectionType(
}
export function getSelectionTypeDisplayText(
selection: Selections
selection?: Selections
): string | null {
const selectionsByType = getSelectionType(selection)

View File

@ -283,7 +283,7 @@ export const commandBarMachine = setup({
typeof argConfig.options === 'function'
? argConfig.options(
input,
argConfig.machineActor.getSnapshot().context
argConfig.machineActor?.getSnapshot().context
)
: argConfig.options
).some((o) => o.value === argValue)

View File

@ -20,6 +20,7 @@ type FileMachineEvents =
makeDir: boolean
content?: string
silent?: boolean
shouldSetToRename?: boolean
}
}
| { type: 'Delete file'; data: FileEntry }
@ -42,6 +43,7 @@ type FileMachineEvents =
output: {
message: string
path: string
shouldSetToRename: boolean
}
}
| {
@ -107,6 +109,10 @@ export const fileMachine = setup({
},
'Is not silent': ({ event }) =>
event.type === 'Create file' ? !event.data.silent : false,
'Should set to rename': ({ event }) =>
(event.type === 'xstate.done.actor.create-and-open-file' &&
event.output.shouldSetToRename) ||
false,
},
actors: {
readFiles: fromPromise(({ input }: { input: Project }) =>
@ -119,6 +125,7 @@ export const fileMachine = setup({
makeDir: boolean
selectedDirectory: FileEntry
content: string
shouldSetToRename: boolean
}
}) => Promise.resolve({ message: '', path: '' })
),
@ -149,7 +156,7 @@ export const fileMachine = setup({
),
},
}).createMachine({
/** @xstate-layout N4IgpgJg5mDOIC5QDECWAbMACAtgQwGMALVAOzAGI9ZZUpSBtABgF1FQAHAe1oBdUupdiAAeiACwAmADQgAnogAcANgCsAOnHiAjOICcAZh3K9TRQHYAvpdlpMuQiXIUASmABmAJzhFmbJCDcfAJCAWIIUrIKCHpq6nraipJJKorahqrWthjY+MRkYOoAEtRYpFxY7jmwFADC3ni82FWYfsJBqPyCwuG6hurKTAYG5mlSyeJRiHqS2prKg6Mj2pKz4lkgdrmOBcWlLXCuYKR4OM05bQEdXaGgvdpD6qPiioqqieJM2gaqUxELT3MQz0eleBlMhmUGy2Dny5D2sEq1TqDSaSNarHaPE6IR6iD6BgGQxGY1Wikm8gkQM0n2UknERm0qmUw2hOVhTkKJURBxq9TAjXOrW0-k42JueIQBKJw1GujJFOiqkkTE0X3M5mUuiYgxmbPseU5CPRhwAImBMGiDpcxcFumF8dptMp1GZRlqNYYdXo-ml1IlzMkHuZVAZJAY1PrtnCuftkQB5DjHE02wLi3EOqX6QmDWWkiZ-HSKf3gph6ZWqcwJIZRjm7bkmmoAZTAvCwsAtYAITQgWAgqG83a4njkqeuGbu+MkLPU7x+iiGszJfwj4ldTvB6UURh0klrht2-MaZCgWDwpF7XCTpBPJooEEEhTIADcuABrQoEVFgAC054gP5XscP7WpiVzpvak5SnO6hJD8RYfJ8ir4kw06zqoTDiMyTA4WGPz7js8JHvwpCnv+WBATepF3mAnieMO6gcOgjTuMOODqF+ApNH+F6AdeIEXGBto4pBoj4u8GjOiMqjiKMqhJFhfyVqqEbJIoCTkmk3wETG6huCcOC3gc96PuoL7voU3gGb+oGimmdq3GJCARuY8RMroEk6MMihKeWrpYepEZMKMSw6Ua+mnEZOQULR9GeIxzG8KxnjsVZpw2YJdnjqJ4QjK59KaoGLKhh6fyBpIsFgtqKjKuCYW7OalpRZgJnwuZH7qBAnbcbZWIOZKeXqAVyhFT8EbaOYK44f6kjlhYG6FVYNibOyB7wo1rbNZQsUMUxLFsZ13UZRiWUQY5uUakNskjdOY2lZSCAqhV24LlhHpMl89Xwm4eD9tRvKtU+pCvh1DQAbyY5nZKMwqZWwxqMorzltoZUrK6YbOlJoazIoX2FD9f2ngDD5tcDFnqGDAmYLADAin1InndMKrqD85jw8ySPvH8pgulqoYWEjc16HjekCoTjYxXRu2JclqVi1TcCQ-1mYwyzcMRhz6lcw9C56Cz4Yatd05ISLxFbYDZlkx1nGCgrSsM5KTJVgMMmjKkEYmAYfwrOkQ30i8WFSF8mTLTCa2FGb-3RTt8V7UlB02z1mX0xKmZMgu8R6C8YahqYwUow9TqBkNxXiLmUgGEyIsRYZUctSTQMg5ZxzpXbdPgcrUEuW57xYUyXkGD5D2Bhog9aKsLyzQywsbOUXXwAEYeEWAKcTk5P7KH8G+ujhuHDDJTJZ0t2QGsvxrlI2q85fiBhlgMZcQq8+iqDJ3OzAML2qCCqxDEkIsNryK+jMpSV1clIck3xB6ViLIWEwmhXiJF0EYIqptUS3nIpRLaQDHajAqvKCwqxEZTxkIXVChJNTqUDCkB4L9q4t1rkTHI2DMyRAeosIawxFxDESMoLCIsNokUYZgZhUF1IDGdK7LyWgX6TULqCIagYcKSHMAya6VdQ6rTPgTLaC9hKpygnSOY8FA7kj0J6WR0QISzn0J8IYN0tIi0TMcLBHcHZp1wf6cB5UiFZxIdEcEhJKyvQ9BqGSqCuIuL0WvXoHj8HeKSL472E0KrBRfrVRGL9cbWEsEAA */
/** @xstate-layout N4IgpgJg5mDOIC5QDECWAbMACAtgQwGMALVAOzAGI9ZZUpSBtABgF1FQAHAe1oBdUupdiAAeiACwAmADQgAnogAcANgCsAOnHiAjOICcAZh3K9TRQHYAvpdlpMuQiXIUASmABmAJzhFmbJCDcfAJCAWIIUrIKCHpq6nraipJJKorahqrWthjY+MRkYOoAEtRYpFxY7jmwFADC3ni82FWYfsJBqPyCwuG6hurKTAYG5mlSyeJRiHqS2prKg6Mj2pKz4lkgdrmOBcWlLXCuYKR4OM05bQEdXaGgvdpD6qPiioqqieJM2gaqUxELT3MQz0eleBlMhmUGy2Dny5D2sEq1TqDSaSNarHaPE6IR6iF0ij08SGklUpikym0qkm8gkJie1KYklB70UBkkTChNk2OVhTkKJURBxq9TAjXOrW0-k42JueIQfQMAyGIzGq0UNOiqg5mi+5nMlM+gxm0N5eX5CPRhwAImBMGiDpcZcFumF8dptMp1GZRpT9YZOXo-ml1IlzMkHuZVOyDGpTfZzbtBVaagB5DjHK1OwKy3FuhX6JWDYajXTqzUSRKh8FMPTa1TmBJDePbOEC-bIgDKYF4WFgdrABCaECwEFQ3iHXE8cmz1zzd3xkmUSveP0UJJWyT+sfE3o94PSbK0KxbfN2osaZCgWDwpBHXAzpCvVooEEEhTIADcuABrQoEVEwAAWlvCAgIfY4gMdTErlzV0FwVH5dx3ZdtUSA9lD+B4qW9EkmE5ZkEnMAxT0TeEL34Uhr1ArAIKfKiXzfeEv1-f9AJAu9wMfKCLilLEXVuURpmUcw91JakDAsLQRiwplFHUIxlDeUxZlGRRSJ2cjUWfGi6OfA4KDATxPCndQOHQRp3CnHB1AAsUmg4sC6J4jFpRzAT5SpHDPRGalRlUJJxF+WkEAbJgFOUZJCQ+NJvg0tt1DcE4cH0nJX3fdQWL-dRvGS4DoLcud4KEhBY1EhJ3iCqkdGGRQ-jJDQmCCwlYyYUYlnii0ktOVLMHS5jSG-bLctOfLeMKuDBPCMr4i8qrqW+SS-nDDRJK0VYXmZcRwU63ZupShiDKMkzPDMizeCszwbJGs4XLAWdJvlEZRMkcQDXDVDY20cxltWdRXjZXQzDUSQdu5GEyMKW17V6ygmI-QbWPUCABwcgr+JxYrpv1dRXvepcfi+n6QoMfDQ2ZALzH3d6rHBs1NKh1HYcM4zTPMyzrOR1GxtcjG5XzZ7cbekSCejP0-g5SR-skprVD9Kkvl2+E3DwMdDuReHMsR4axTA4UHo8-MZnCn5iNjOXXjrbRlpWb12U9Hzo1mdS6YTBnEt12Gak1rLCgaPXqgYPjYMNhDjYUhthjUJTCXeP5TC9SlowsS260JJXChVtXr2FFmTrOjmrpy3W7tgA3Mam6YdVNqOLdj62QvXIkY31YWl0+dZXdbC0KOZn3tbY+yefumDnQrzyGyJNQ3teJTYxMAwsNmIkNpeIKpC+TIu7PLT7OZ462fOy6bLs8U7vL-mEKpdd4j0F52WjUw2ob6IPXDXHUPEYspAMKlrG5coKN4ABAhgzPm84SpAUwiFKBuF8L7iZGoEEPwM6WnKCmcBWN8Skynl-CErx9CqGpPHWYAw2TKRmBySSkhUHJmFJgyuiFvqaAmItN45gdB1RCngzQrxEi6CMB9VBvcGK6UfLDBhnlRhSzLBYVYSktoyBCg8UGTwlJ6HDCkB4RDUH7QkSHce+ZIghUWLjYYeFf4qCCqg6GPZ9Fj0viVQkAxPR+RqloIhxNX6glxuGfCkgOGCKTroz26tMDAIcRA8IkU5hIXXhqDRjYvHTFrOoakd98JlQjNoVB6Zjj2PcoYq+0jQxSDkUuJId8lHRHBCuUYTU-T6mpMI7SYSwCSPzN9JIpTkjhgqYorC30pZtSIdqWMbwAr-0sEAA */
id: 'File machine',
initial: 'Reading files',
@ -222,15 +229,41 @@ export const fileMachine = setup({
makeDir: false,
selectedDirectory: context.selectedDirectory,
content: '',
shouldSetToRename: false,
}
return {
name: event.data.name,
makeDir: event.data.makeDir,
selectedDirectory: context.selectedDirectory,
content: event.data.content ?? '',
shouldSetToRename: event.data.shouldSetToRename ?? false,
}
},
onDone: [
{
target: 'Reading files',
actions: [
{
type: 'createToastSuccess',
params: ({
event,
}: {
// TODO: rely on type inference
event: Extract<
FileMachineEvents,
{ type: 'xstate.done.actor.create-and-open-file' }
>
}) => {
return { message: event.output.message }
},
},
'addFileToRenamingQueue',
'navigateToFile',
],
guard: 'Should set to rename',
},
{
target: 'Reading files',
actions: [
@ -248,7 +281,6 @@ export const fileMachine = setup({
return { message: event.output.message }
},
},
'addFileToRenamingQueue',
'navigateToFile',
],
},

View File

@ -388,9 +388,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.17"
version = "4.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac"
checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3"
dependencies = [
"clap_builder",
"clap_derive",
@ -398,9 +398,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.17"
version = "4.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73"
checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b"
dependencies = [
"anstyle",
"clap_lex",
@ -408,9 +408,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.5.13"
version = "4.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
dependencies = [
"heck 0.5.0",
"proc-macro2",
@ -1400,7 +1400,7 @@ dependencies = [
[[package]]
name = "kcl-lib"
version = "0.2.17"
version = "0.2.18"
dependencies = [
"anyhow",
"approx 0.5.1",
@ -1526,9 +1526,8 @@ dependencies = [
[[package]]
name = "kittycad-modeling-cmds"
version = "0.2.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5128ba683e849388cac4214b65911c343842d559bec10827575c840a47733786"
version = "0.2.62"
source = "git+https://github.com/KittyCAD/modeling-api?branch=achalmers/no-more-jsonschema#a253330a9cd656afbc5ee04490acca5ea5337af0"
dependencies = [
"anyhow",
"chrono",
@ -1552,8 +1551,18 @@ dependencies = [
[[package]]
name = "kittycad-modeling-cmds-macros"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0cdc505a33bfffb87c317435ec41ced8f73474217cf30db685e479bf289757e"
source = "git+https://github.com/KittyCAD/modeling-api?branch=achalmers/no-more-jsonschema#a253330a9cd656afbc5ee04490acca5ea5337af0"
dependencies = [
"kittycad-modeling-cmds-macros-impl",
"proc-macro2",
"quote",
"syn 2.0.77",
]
[[package]]
name = "kittycad-modeling-cmds-macros-impl"
version = "0.1.9"
source = "git+https://github.com/KittyCAD/modeling-api?branch=achalmers/no-more-jsonschema#a253330a9cd656afbc5ee04490acca5ea5337af0"
dependencies = [
"proc-macro2",
"quote",
@ -3079,18 +3088,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.63"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.63"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
dependencies = [
"proc-macro2",
"quote",

View File

@ -73,7 +73,7 @@ members = [
http = "0.2.12"
kittycad = { version = "0.3.20", default-features = false, features = ["js", "requests"] }
kittycad-modeling-session = "0.1.4"
kittycad-modeling-cmds = { version = "0.2.61", features = ["websocket"] }
kittycad-modeling-cmds = { version = "0.2.62", features = ["websocket"] }
[[test]]
name = "executor"
@ -84,6 +84,6 @@ name = "modify"
path = "tests/modify/main.rs"
# Example: how to point modeling-api at a different repo (e.g. a branch or a local clone)
#[patch."https://github.com/KittyCAD/modeling-api"]
#kittycad-modeling-cmds = { path = "../../../modeling-api/modeling-cmds" }
#kittycad-modeling-session = { path = "../../../modeling-api/modeling-session" }
[patch.crates-io]
kittycad-modeling-cmds = { git = "https://github.com/KittyCAD/modeling-api", branch = "achalmers/no-more-jsonschema" }

View File

@ -16,7 +16,7 @@ async-recursion = "1.1.1"
async-trait = "0.1.82"
base64 = "0.22.1"
chrono = "0.4.38"
clap = { version = "4.5.17", default-features = false, optional = true, features = ["std", "derive"] }
clap = { version = "4.5.18", default-features = false, optional = true, features = ["std", "derive"] }
convert_case = "0.6.0"
dashmap = "6.1.0"
databake = { version = "0.1.8", features = ["derive"] }
@ -42,7 +42,7 @@ serde = { version = "1.0.210", features = ["derive"] }
serde_json = "1.0.128"
sha2 = "0.10.8"
tabled = { version = "0.15.0", optional = true }
thiserror = "1.0.63"
thiserror = "1.0.64"
toml = "0.8.19"
ts-rs = { version = "10.0.0", features = ["uuid-impl", "url-impl", "chrono-impl", "no-serde-warnings", "serde-json-impl"] }
url = { version = "2.5.2", features = ["serde"] }