Compare commits
1 Commits
v0.26.2
...
achalmers/
Author | SHA1 | Date | |
---|---|---|---|
976d5d2b4f |
2
.github/workflows/cargo-test.yml
vendored
2
.github/workflows/cargo-test.yml
vendored
@ -62,7 +62,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |-
|
||||
cd "${{ matrix.dir }}"
|
||||
cargo llvm-cov nextest --workspace --lcov --output-path lcov.info --test-threads=1 --no-fail-fast -P ci 2>&1 | tee /tmp/github-actions.log
|
||||
cargo llvm-cov nextest --all --lcov --output-path lcov.info --test-threads=1 --no-fail-fast -P ci 2>&1 | tee /tmp/github-actions.log
|
||||
env:
|
||||
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
||||
RUST_MIN_STACK: 10485760000
|
||||
|
9288
docs/kcl/std.json
9288
docs/kcl/std.json
File diff suppressed because it is too large
Load Diff
@ -162,28 +162,6 @@ A base path.
|
||||
|
||||
|
||||
----
|
||||
A circular arc, not necessarily tangential to the current point.
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Arc`| | No |
|
||||
| `center` |`[number, number]`| Center of the circle that this arc is drawn on. | No |
|
||||
| `radius` |`number`| Radius of the circle that this arc is drawn on. | No |
|
||||
| `from` |`[number, number]`| The from point. | No |
|
||||
| `to` |`[number, number]`| The to point. | No |
|
||||
| `tag` |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag of the path. | No |
|
||||
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
2
interface.d.ts
vendored
2
interface.d.ts
vendored
@ -2,7 +2,7 @@ import fs from 'node:fs/promises'
|
||||
import fsSync from 'node:fs'
|
||||
import path from 'path'
|
||||
import { dialog, shell } from 'electron'
|
||||
import { MachinesListing } from 'components/MachineManagerProvider'
|
||||
import { MachinesListing } from 'lib/machineManager'
|
||||
|
||||
type EnvFn = (value?: string) => string
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "zoo-modeling-app",
|
||||
"version": "0.26.2",
|
||||
"version": "0.26.1",
|
||||
"private": true,
|
||||
"productName": "Zoo Modeling App",
|
||||
"author": {
|
||||
|
@ -21,7 +21,6 @@ import { WasmErrBanner } from 'components/WasmErrBanner'
|
||||
import { CommandBar } from 'components/CommandBar/CommandBar'
|
||||
import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
||||
import FileMachineProvider from 'components/FileMachineProvider'
|
||||
import { MachineManagerProvider } from 'components/MachineManagerProvider'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import {
|
||||
fileLoader,
|
||||
@ -50,7 +49,6 @@ const router = createRouter([
|
||||
{
|
||||
loader: settingsLoader,
|
||||
id: PATHS.INDEX,
|
||||
// TODO: Re-evaluate if this is true
|
||||
/* Make sure auth is the outermost provider or else we will have
|
||||
* inefficient re-renders, use the react profiler to see. */
|
||||
element: (
|
||||
@ -59,9 +57,7 @@ const router = createRouter([
|
||||
<LspProvider>
|
||||
<KclContextProvider>
|
||||
<AppStateProvider>
|
||||
<MachineManagerProvider>
|
||||
<Outlet />
|
||||
</MachineManagerProvider>
|
||||
<Outlet />
|
||||
</AppStateProvider>
|
||||
</KclContextProvider>
|
||||
</LspProvider>
|
||||
|
@ -23,7 +23,6 @@ export function LowerRightControls({
|
||||
}) {
|
||||
const location = useLocation()
|
||||
const filePath = useAbsoluteFilePath()
|
||||
|
||||
const linkOverrideClassName =
|
||||
'!text-chalkboard-70 hover:!text-chalkboard-80 dark:!text-chalkboard-40 dark:hover:!text-chalkboard-30'
|
||||
|
||||
|
@ -1,123 +0,0 @@
|
||||
import { createContext, useEffect, useState } from 'react'
|
||||
|
||||
import { engineCommandManager } from 'lib/singletons'
|
||||
import { CommandsContext } from 'components/CommandBar/CommandBarProvider'
|
||||
import { isDesktop } from 'lib/isDesktop'
|
||||
import { components } from 'lib/machine-api'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
import { toSync } from 'lib/utils'
|
||||
|
||||
export type MachinesListing = Array<
|
||||
components['schemas']['MachineInfoResponse']
|
||||
>
|
||||
|
||||
export interface MachineManager {
|
||||
machines: MachinesListing
|
||||
machineApiIp: string | null
|
||||
currentMachine: components['schemas']['MachineInfoResponse'] | null
|
||||
noMachinesReason: () => string | undefined
|
||||
setCurrentMachine: (
|
||||
m: components['schemas']['MachineInfoResponse'] | null
|
||||
) => void
|
||||
}
|
||||
|
||||
export const MachineManagerContext = createContext<MachineManager>({
|
||||
machines: [],
|
||||
machineApiIp: null,
|
||||
currentMachine: null,
|
||||
setCurrentMachine: (
|
||||
_: components['schemas']['MachineInfoResponse'] | null
|
||||
) => {},
|
||||
noMachinesReason: () => undefined,
|
||||
})
|
||||
|
||||
export const MachineManagerProvider = ({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) => {
|
||||
const [machines, setMachines] = useState<MachinesListing>([])
|
||||
const [machineApiIp, setMachineApiIp] = useState<string | null>(null)
|
||||
const [currentMachine, setCurrentMachine] = useState<
|
||||
components['schemas']['MachineInfoResponse'] | null
|
||||
>(null)
|
||||
|
||||
const commandBarActor = CommandsContext.useActorRef()
|
||||
|
||||
// Get the reason message for why there are no machines.
|
||||
const noMachinesReason = (): string | undefined => {
|
||||
if (machines.length > 0) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (machineApiIp === null) {
|
||||
return 'Machine API server was not discovered'
|
||||
}
|
||||
|
||||
return 'Machine API server was discovered, but no machines are available'
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDesktop()) return
|
||||
|
||||
const update = async () => {
|
||||
const _machineApiIp = await window.electron.getMachineApiIp()
|
||||
if (_machineApiIp === null) return
|
||||
|
||||
setMachineApiIp(_machineApiIp)
|
||||
|
||||
const _machines = await window.electron.listMachines(_machineApiIp)
|
||||
setMachines(_machines)
|
||||
}
|
||||
|
||||
// Start a background job to update the machines every ten seconds.
|
||||
// If MDNS is already watching, this timeout will wait until it's done to trigger the
|
||||
// finding again.
|
||||
let timeoutId: ReturnType<typeof setTimeout> | undefined = undefined
|
||||
const timeoutLoop = () => {
|
||||
clearTimeout(timeoutId)
|
||||
timeoutId = setTimeout(
|
||||
toSync(async () => {
|
||||
await update()
|
||||
timeoutLoop()
|
||||
}, reportRejection),
|
||||
1000
|
||||
)
|
||||
}
|
||||
timeoutLoop()
|
||||
update().catch(reportRejection)
|
||||
}, [])
|
||||
|
||||
// Update engineCommandManager's copy of this data.
|
||||
useEffect(() => {
|
||||
const machineManagerNext = {
|
||||
machines,
|
||||
machineApiIp,
|
||||
currentMachine,
|
||||
noMachinesReason,
|
||||
setCurrentMachine,
|
||||
}
|
||||
|
||||
engineCommandManager.machineManager = machineManagerNext
|
||||
|
||||
commandBarActor.send({
|
||||
type: 'Set machine manager',
|
||||
data: machineManagerNext,
|
||||
})
|
||||
}, [machines, machineApiIp, currentMachine])
|
||||
|
||||
return (
|
||||
<MachineManagerContext.Provider
|
||||
value={{
|
||||
machines,
|
||||
machineApiIp,
|
||||
currentMachine,
|
||||
setCurrentMachine,
|
||||
noMachinesReason,
|
||||
}}
|
||||
>
|
||||
{' '}
|
||||
{children}{' '}
|
||||
</MachineManagerContext.Provider>
|
||||
)
|
||||
}
|
@ -1,11 +1,5 @@
|
||||
import { useMachine } from '@xstate/react'
|
||||
import React, {
|
||||
createContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useContext,
|
||||
} from 'react'
|
||||
import React, { createContext, useEffect, useMemo, useRef } from 'react'
|
||||
import {
|
||||
Actor,
|
||||
AnyStateMachine,
|
||||
@ -34,7 +28,7 @@ import {
|
||||
editorManager,
|
||||
sceneEntitiesManager,
|
||||
} from 'lib/singletons'
|
||||
import { MachineManagerContext } from 'components/MachineManagerProvider'
|
||||
import { machineManager } from 'lib/machineManager'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance'
|
||||
import {
|
||||
@ -146,8 +140,6 @@ export const ModelingMachineProvider = ({
|
||||
// >
|
||||
// )
|
||||
|
||||
const machineManager = useContext(MachineManagerContext)
|
||||
|
||||
const [modelingState, modelingSend, modelingActor] = useMachine(
|
||||
modelingMachine.provide({
|
||||
actions: {
|
||||
@ -416,7 +408,7 @@ export const ModelingMachineProvider = ({
|
||||
return {}
|
||||
}
|
||||
),
|
||||
Make: ({ context, event }) => {
|
||||
Make: ({ event }) => {
|
||||
if (event.type !== 'Make') return
|
||||
// Check if we already have an export intent.
|
||||
if (engineCommandManager.exportInfo) {
|
||||
@ -430,21 +422,7 @@ export const ModelingMachineProvider = ({
|
||||
}
|
||||
|
||||
// Set the current machine.
|
||||
// Due to our use of singeton pattern, we need to do this to reliably
|
||||
// update this object across React and non-React boundary.
|
||||
// We need to do this eagerly because of the exportToEngine call below.
|
||||
if (engineCommandManager.machineManager === null) {
|
||||
console.warn(
|
||||
"engineCommandManager.machineManager is null. It shouldn't be at this point. Aborting operation."
|
||||
)
|
||||
return
|
||||
} else {
|
||||
engineCommandManager.machineManager.currentMachine =
|
||||
event.data.machine
|
||||
}
|
||||
|
||||
// Update the rest of the UI that needs to know the current machine
|
||||
context.machineManager.setCurrentMachine(event.data.machine)
|
||||
machineManager.currentMachine = event.data.machine
|
||||
|
||||
const format: Models['OutputFormat_type'] = {
|
||||
type: 'stl',
|
||||
@ -1017,7 +995,6 @@ export const ModelingMachineProvider = ({
|
||||
...modelingMachineDefaultContext.store,
|
||||
...persistedContext,
|
||||
},
|
||||
machineManager,
|
||||
},
|
||||
// devTools: true,
|
||||
}
|
||||
|
@ -1,12 +1,6 @@
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { Resizable } from 're-resizable'
|
||||
import {
|
||||
MouseEventHandler,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useContext,
|
||||
} from 'react'
|
||||
import { MouseEventHandler, useCallback, useEffect, useMemo } from 'react'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { SidebarAction, SidebarType, sidebarPanes } from './ModelingPanes'
|
||||
import Tooltip from 'components/Tooltip'
|
||||
@ -19,7 +13,7 @@ import { CustomIconName } from 'components/CustomIcon'
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { IconDefinition } from '@fortawesome/free-solid-svg-icons'
|
||||
import { useKclContext } from 'lang/KclProvider'
|
||||
import { MachineManagerContext } from 'components/MachineManagerProvider'
|
||||
import { machineManager } from 'lib/machineManager'
|
||||
|
||||
interface ModelingSidebarProps {
|
||||
paneOpacity: '' | 'opacity-20' | 'opacity-40'
|
||||
@ -35,7 +29,6 @@ function getPlatformString(): 'web' | 'desktop' {
|
||||
}
|
||||
|
||||
export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
const machineManager = useContext(MachineManagerContext)
|
||||
const { commandBarSend } = useCommandsContext()
|
||||
const kclContext = useKclContext()
|
||||
const { settings } = useSettingsAuthContext()
|
||||
|
@ -1,9 +1,7 @@
|
||||
import { Popover } from '@headlessui/react'
|
||||
import { useContext } from 'react'
|
||||
import Tooltip from './Tooltip'
|
||||
import { machineManager } from 'lib/machineManager'
|
||||
import { isDesktop } from 'lib/isDesktop'
|
||||
import { components } from 'lib/machine-api'
|
||||
import { MachineManagerContext } from 'components/MachineManagerProvider'
|
||||
import { CustomIcon } from './CustomIcon'
|
||||
|
||||
export const NetworkMachineIndicator = ({
|
||||
@ -11,12 +9,9 @@ export const NetworkMachineIndicator = ({
|
||||
}: {
|
||||
className?: string
|
||||
}) => {
|
||||
const {
|
||||
noMachinesReason,
|
||||
machines,
|
||||
machines: { length: machineCount },
|
||||
} = useContext(MachineManagerContext)
|
||||
const reason = noMachinesReason()
|
||||
const machineCount = machineManager.machineCount()
|
||||
const reason = machineManager.noMachinesReason()
|
||||
const machines = machineManager.machines
|
||||
|
||||
return isDesktop() ? (
|
||||
<Popover className="relative">
|
||||
@ -52,36 +47,34 @@ export const NetworkMachineIndicator = ({
|
||||
</div>
|
||||
{machineCount > 0 && (
|
||||
<ul className="divide-y divide-chalkboard-20 dark:divide-chalkboard-80">
|
||||
{machines.map(
|
||||
(machine: components['schemas']['MachineInfoResponse']) => {
|
||||
return (
|
||||
<li key={machine.id} className={'px-2 py-4 gap-1 last:mb-0 '}>
|
||||
<p className="">{machine.id.toUpperCase()}</p>
|
||||
<p className="text-chalkboard-60 dark:text-chalkboard-50 text-xs">
|
||||
{machine.make_model.model}
|
||||
</p>
|
||||
{machine.extra &&
|
||||
machine.extra.type === 'bambu' &&
|
||||
machine.extra.nozzle_diameter && (
|
||||
<p className="text-chalkboard-60 dark:text-chalkboard-50 text-xs">
|
||||
Nozzle Diameter: {machine.extra.nozzle_diameter}
|
||||
</p>
|
||||
)}
|
||||
<p className="text-chalkboard-60 dark:text-chalkboard-50 text-xs">
|
||||
{`Status: ${machine.state.state
|
||||
.charAt(0)
|
||||
.toUpperCase()}${machine.state.state.slice(1)}`}
|
||||
{machine.state.state === 'failed' && machine.state.message
|
||||
? ` (${machine.state.message})`
|
||||
: ''}
|
||||
{machine.state.state === 'running' && machine.progress
|
||||
? ` (${Math.round(machine.progress)}%)`
|
||||
: ''}
|
||||
</p>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
)}
|
||||
{machines.map((machine) => {
|
||||
return (
|
||||
<li key={machine.id} className={'px-2 py-4 gap-1 last:mb-0 '}>
|
||||
<p className="">{machine.id.toUpperCase()}</p>
|
||||
<p className="text-chalkboard-60 dark:text-chalkboard-50 text-xs">
|
||||
{machine.make_model.model}
|
||||
</p>
|
||||
{machine.extra &&
|
||||
machine.extra.type === 'bambu' &&
|
||||
machine.extra.nozzle_diameter && (
|
||||
<p className="text-chalkboard-60 dark:text-chalkboard-50 text-xs">
|
||||
Nozzle Diameter: {machine.extra.nozzle_diameter}
|
||||
</p>
|
||||
)}
|
||||
<p className="text-chalkboard-60 dark:text-chalkboard-50 text-xs">
|
||||
{`Status: ${machine.state.state
|
||||
.charAt(0)
|
||||
.toUpperCase()}${machine.state.state.slice(1)}`}
|
||||
{machine.state.state === 'failed' && machine.state.message
|
||||
? ` (${machine.state.message})`
|
||||
: ''}
|
||||
{machine.state.state === 'running' && machine.progress
|
||||
? ` (${Math.round(machine.progress)}%)`
|
||||
: ''}
|
||||
</p>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</Popover.Panel>
|
||||
|
@ -4,14 +4,14 @@ import { type IndexLoaderData } from 'lib/types'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { isDesktop } from '../lib/isDesktop'
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom'
|
||||
import { Fragment, useMemo, useContext } from 'react'
|
||||
import { Fragment, useMemo } from 'react'
|
||||
import { Logo } from './Logo'
|
||||
import { APP_NAME } from 'lib/constants'
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { CustomIcon } from './CustomIcon'
|
||||
import { useLspContext } from './LspProvider'
|
||||
import { engineCommandManager } from 'lib/singletons'
|
||||
import { MachineManagerContext } from 'components/MachineManagerProvider'
|
||||
import { machineManager } from 'lib/machineManager'
|
||||
import usePlatform from 'hooks/usePlatform'
|
||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||
import Tooltip from './Tooltip'
|
||||
@ -96,8 +96,6 @@ function ProjectMenuPopover({
|
||||
const location = useLocation()
|
||||
const navigate = useNavigate()
|
||||
const filePath = useAbsoluteFilePath()
|
||||
const machineManager = useContext(MachineManagerContext)
|
||||
|
||||
const { commandBarState, commandBarSend } = useCommandsContext()
|
||||
const { onProjectClose } = useLspContext()
|
||||
const exportCommandInfo = { name: 'Export', groupId: 'modeling' }
|
||||
@ -108,7 +106,7 @@ function ProjectMenuPopover({
|
||||
(c) => c.name === obj.name && c.groupId === obj.groupId
|
||||
)
|
||||
)
|
||||
const machineCount = machineManager.machines.length
|
||||
const machineCount = machineManager.machineCount()
|
||||
|
||||
// We filter this memoized list so that no orphan "break" elements are rendered.
|
||||
const projectMenuItems = useMemo<(ActionButtonProps | 'break')[]>(
|
||||
|
@ -28,7 +28,6 @@ import {
|
||||
} from 'lib/constants'
|
||||
import { KclManager } from 'lang/KclSingleton'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
import { MachineManager } from 'components/MachineManagerProvider'
|
||||
|
||||
// TODO(paultag): This ought to be tweakable.
|
||||
const pingIntervalMs = 5_000
|
||||
@ -1416,9 +1415,6 @@ export class EngineCommandManager extends EventTarget {
|
||||
(() => {}) as any
|
||||
kclManager: null | KclManager = null
|
||||
|
||||
// The current "manufacturing machine" aka 3D printer, CNC, etc.
|
||||
public machineManager: MachineManager | null = null
|
||||
|
||||
set exportInfo(info: ExportInfo | null) {
|
||||
this._exportInfo = info
|
||||
}
|
||||
@ -1634,16 +1630,10 @@ export class EngineCommandManager extends EventTarget {
|
||||
break
|
||||
}
|
||||
case ExportIntent.Make: {
|
||||
if (!this.machineManager) {
|
||||
console.warn('Some how, no manufacturing machine is selected.')
|
||||
break
|
||||
}
|
||||
|
||||
exportMake(
|
||||
event.data,
|
||||
this.exportInfo.name,
|
||||
this.pendingExport.toastId,
|
||||
this.machineManager
|
||||
this.pendingExport.toastId
|
||||
).then((result) => {
|
||||
if (result) {
|
||||
this.pendingExport?.resolve(null)
|
||||
|
@ -3,6 +3,7 @@ import { StateMachineCommandSetConfig, KclCommandValue } from 'lib/commandTypes'
|
||||
import { KCL_DEFAULT_LENGTH, KCL_DEFAULT_DEGREE } from 'lib/constants'
|
||||
import { components } from 'lib/machine-api'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { machineManager } from 'lib/machineManager'
|
||||
import { modelingMachine, SketchTool } from 'machines/modelingMachine'
|
||||
|
||||
type OutputFormat = Models['OutputFormat_type']
|
||||
@ -186,41 +187,41 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||
machine.make_model.model ||
|
||||
machine.make_model.manufacturer ||
|
||||
'Unknown Machine',
|
||||
options: (commandBarContext) => {
|
||||
return Object.values(
|
||||
commandBarContext.machineManager?.machines || []
|
||||
).map((machine: components['schemas']['MachineInfoResponse']) => ({
|
||||
name:
|
||||
`${machine.id} (${
|
||||
machine.make_model.model || machine.make_model.manufacturer
|
||||
}) (${machine.state.state})` +
|
||||
(machine.hardware_configuration &&
|
||||
machine.hardware_configuration.type !== 'none' &&
|
||||
machine.hardware_configuration.config.nozzle_diameter
|
||||
? ` - Nozzle Diameter: ${machine.hardware_configuration.config.nozzle_diameter}`
|
||||
: '') +
|
||||
(machine.hardware_configuration &&
|
||||
machine.hardware_configuration.type !== 'none' &&
|
||||
machine.hardware_configuration.config.filaments &&
|
||||
machine.hardware_configuration.config.filaments[0]
|
||||
? ` - ${
|
||||
machine.hardware_configuration.config.filaments[0].name
|
||||
} #${
|
||||
machine.hardware_configuration.config &&
|
||||
machine.hardware_configuration.config.filaments[0].color?.slice(
|
||||
0,
|
||||
6
|
||||
)
|
||||
}`
|
||||
: ''),
|
||||
isCurrent: false,
|
||||
disabled: machine.state.state !== 'idle',
|
||||
value: machine,
|
||||
}))
|
||||
options: () => {
|
||||
return Object.entries(machineManager.machines).map(
|
||||
([_, machine]) => ({
|
||||
name:
|
||||
`${machine.id} (${
|
||||
machine.make_model.model || machine.make_model.manufacturer
|
||||
}) (${machine.state.state})` +
|
||||
(machine.hardware_configuration &&
|
||||
machine.hardware_configuration.type !== 'none' &&
|
||||
machine.hardware_configuration.config.nozzle_diameter
|
||||
? ` - Nozzle Diameter: ${machine.hardware_configuration.config.nozzle_diameter}`
|
||||
: '') +
|
||||
(machine.hardware_configuration &&
|
||||
machine.hardware_configuration.type !== 'none' &&
|
||||
machine.hardware_configuration.config.filaments &&
|
||||
machine.hardware_configuration.config.filaments[0]
|
||||
? ` - ${
|
||||
machine.hardware_configuration.config.filaments[0].name
|
||||
} #${
|
||||
machine.hardware_configuration.config &&
|
||||
machine.hardware_configuration.config.filaments[0].color?.slice(
|
||||
0,
|
||||
6
|
||||
)
|
||||
}`
|
||||
: ''),
|
||||
isCurrent: false,
|
||||
disabled: machine.state.state !== 'idle',
|
||||
value: machine as components['schemas']['MachineInfoResponse'],
|
||||
})
|
||||
)
|
||||
},
|
||||
defaultValue: (commandBarContext) => {
|
||||
defaultValue: () => {
|
||||
return Object.values(
|
||||
commandBarContext.machineManager.machines || []
|
||||
machineManager.machines
|
||||
)[0] as components['schemas']['MachineInfoResponse']
|
||||
},
|
||||
},
|
||||
|
@ -5,7 +5,6 @@ import { Selection } from './selections'
|
||||
import { Identifier, Expr, VariableDeclaration } from 'lang/wasm'
|
||||
import { commandBarMachine } from 'machines/commandBarMachine'
|
||||
import { ReactNode } from 'react'
|
||||
import { MachineManager } from 'components/MachineManagerProvider'
|
||||
|
||||
type Icon = CustomIconName
|
||||
const PLATFORMS = ['both', 'web', 'desktop'] as const
|
||||
@ -128,7 +127,6 @@ export type CommandArgumentConfig<
|
||||
| ((
|
||||
commandBarContext: {
|
||||
argumentsToSubmit: Record<string, unknown>
|
||||
machineManager?: MachineManager
|
||||
}, // Should be the commandbarMachine's context, but it creates a circular dependency
|
||||
machineContext?: C
|
||||
) => CommandArgumentOption<OutputType>[])
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { deserialize_files } from 'wasm-lib/pkg/wasm_lib'
|
||||
import { MachineManager } from 'components/MachineManagerProvider'
|
||||
import { machineManager } from './machineManager'
|
||||
import toast from 'react-hot-toast'
|
||||
import { components } from './machine-api'
|
||||
import ModelingAppFile from './modelingAppFile'
|
||||
@ -9,8 +9,7 @@ import { MAKE_TOAST_MESSAGES } from './constants'
|
||||
export async function exportMake(
|
||||
data: ArrayBuffer,
|
||||
name: string,
|
||||
toastId: string,
|
||||
machineManager: MachineManager
|
||||
toastId: string
|
||||
): Promise<Response | null> {
|
||||
if (name === '') {
|
||||
console.error(MAKE_TOAST_MESSAGES.NO_NAME)
|
||||
@ -18,7 +17,7 @@ export async function exportMake(
|
||||
return null
|
||||
}
|
||||
|
||||
if (machineManager.machines.length === 0) {
|
||||
if (machineManager.machineCount() === 0) {
|
||||
console.error(MAKE_TOAST_MESSAGES.NO_MACHINES)
|
||||
toast.error(MAKE_TOAST_MESSAGES.NO_MACHINES, { id: toastId })
|
||||
return null
|
||||
|
105
src/lib/machineManager.ts
Normal file
105
src/lib/machineManager.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import { isDesktop } from './isDesktop'
|
||||
import { components } from './machine-api'
|
||||
import { reportRejection } from './trap'
|
||||
import { toSync } from './utils'
|
||||
|
||||
export type MachinesListing = Array<
|
||||
components['schemas']['MachineInfoResponse']
|
||||
>
|
||||
|
||||
export class MachineManager {
|
||||
private _isDesktop: boolean = isDesktop()
|
||||
private _machines: MachinesListing = []
|
||||
private _machineApiIp: string | null = null
|
||||
private _currentMachine: components['schemas']['MachineInfoResponse'] | null =
|
||||
null
|
||||
|
||||
constructor() {
|
||||
if (!this._isDesktop) {
|
||||
return
|
||||
}
|
||||
|
||||
this.updateMachines().catch(reportRejection)
|
||||
}
|
||||
|
||||
start() {
|
||||
if (!this._isDesktop) {
|
||||
return
|
||||
}
|
||||
|
||||
// Start a background job to update the machines every ten seconds.
|
||||
// If MDNS is already watching, this timeout will wait until it's done to trigger the
|
||||
// finding again.
|
||||
let timeoutId: ReturnType<typeof setTimeout> | undefined = undefined
|
||||
const timeoutLoop = () => {
|
||||
clearTimeout(timeoutId)
|
||||
timeoutId = setTimeout(
|
||||
toSync(async () => {
|
||||
await this.updateMachineApiIp()
|
||||
await this.updateMachines()
|
||||
timeoutLoop()
|
||||
}, reportRejection),
|
||||
10000
|
||||
)
|
||||
}
|
||||
timeoutLoop()
|
||||
}
|
||||
|
||||
get machines(): MachinesListing {
|
||||
return this._machines
|
||||
}
|
||||
|
||||
machineCount(): number {
|
||||
return this._machines.length
|
||||
}
|
||||
|
||||
get machineApiIp(): string | null {
|
||||
return this._machineApiIp
|
||||
}
|
||||
|
||||
// Get the reason message for why there are no machines.
|
||||
noMachinesReason(): string | undefined {
|
||||
if (this.machineCount() > 0) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (this.machineApiIp === null) {
|
||||
return 'Machine API server was not discovered'
|
||||
}
|
||||
|
||||
return 'Machine API server was discovered, but no machines are available'
|
||||
}
|
||||
|
||||
get currentMachine(): components['schemas']['MachineInfoResponse'] | null {
|
||||
return this._currentMachine
|
||||
}
|
||||
|
||||
set currentMachine(
|
||||
machine: components['schemas']['MachineInfoResponse'] | null
|
||||
) {
|
||||
this._currentMachine = machine
|
||||
}
|
||||
|
||||
private async updateMachines(): Promise<void> {
|
||||
if (!this._isDesktop) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._machineApiIp === null) {
|
||||
return
|
||||
}
|
||||
|
||||
this._machines = await window.electron.listMachines(this._machineApiIp)
|
||||
}
|
||||
|
||||
private async updateMachineApiIp(): Promise<void> {
|
||||
if (!this._isDesktop) {
|
||||
return
|
||||
}
|
||||
|
||||
this._machineApiIp = await window.electron.getMachineApiIp()
|
||||
}
|
||||
}
|
||||
|
||||
export const machineManager = new MachineManager()
|
||||
machineManager.start()
|
@ -7,7 +7,6 @@ import {
|
||||
} from 'lib/commandTypes'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { getCommandArgumentKclValuesOnly } from 'lib/commandUtils'
|
||||
import { MachineManager } from 'components/MachineManagerProvider'
|
||||
|
||||
export type CommandBarContext = {
|
||||
commands: Command[]
|
||||
@ -15,7 +14,6 @@ export type CommandBarContext = {
|
||||
currentArgument?: CommandArgument<unknown> & { name: string }
|
||||
selectionRanges: Selections
|
||||
argumentsToSubmit: { [x: string]: unknown }
|
||||
machineManager: MachineManager
|
||||
}
|
||||
|
||||
export type CommandBarMachineEvent =
|
||||
@ -73,7 +71,6 @@ export type CommandBarMachineEvent =
|
||||
type: 'Change current argument'
|
||||
data: { [x: string]: CommandArgumentWithName<unknown> }
|
||||
}
|
||||
| { type: 'Set machine manager'; data: MachineManager }
|
||||
|
||||
export const commandBarMachine = setup({
|
||||
types: {
|
||||
@ -93,12 +90,6 @@ export const commandBarMachine = setup({
|
||||
}
|
||||
},
|
||||
}),
|
||||
'Set machine manager': assign({
|
||||
machineManager: ({ event, context }) => {
|
||||
if (event.type !== 'Set machine manager') return context.machineManager
|
||||
return event.data
|
||||
},
|
||||
}),
|
||||
'Execute command': ({ context, event }) => {
|
||||
const { selectedCommand } = context
|
||||
if (!selectedCommand) return
|
||||
@ -348,13 +339,6 @@ export const commandBarMachine = setup({
|
||||
codeBasedSelections: [],
|
||||
},
|
||||
argumentsToSubmit: {},
|
||||
machineManager: {
|
||||
machines: [],
|
||||
machineApiIp: null,
|
||||
currentMachine: null,
|
||||
setCurrentMachine: () => {},
|
||||
noMachinesReason: () => undefined,
|
||||
},
|
||||
},
|
||||
id: 'Command Bar',
|
||||
initial: 'Closed',
|
||||
@ -536,11 +520,6 @@ export const commandBarMachine = setup({
|
||||
},
|
||||
},
|
||||
on: {
|
||||
'Set machine manager': {
|
||||
reenter: false,
|
||||
actions: 'Set machine manager',
|
||||
},
|
||||
|
||||
Close: {
|
||||
target: '.Closed',
|
||||
},
|
||||
|
@ -64,7 +64,6 @@ import toast from 'react-hot-toast'
|
||||
import { ToolbarModeName } from 'lib/toolbar'
|
||||
import { quaternionFromUpNForward } from 'clientSideScene/helpers'
|
||||
import { Vector3 } from 'three'
|
||||
import { MachineManager } from 'components/MachineManagerProvider'
|
||||
|
||||
export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY'
|
||||
|
||||
@ -302,7 +301,6 @@ export const getPersistedContext = (): Partial<PersistedModelingContext> => {
|
||||
export interface ModelingMachineContext {
|
||||
currentMode: ToolbarModeName
|
||||
currentTool: SketchTool
|
||||
machineManager: MachineManager
|
||||
selection: string[]
|
||||
selectionRanges: Selections
|
||||
sketchDetails: SketchDetails | null
|
||||
@ -317,13 +315,6 @@ export interface ModelingMachineContext {
|
||||
export const modelingMachineDefaultContext: ModelingMachineContext = {
|
||||
currentMode: 'modeling',
|
||||
currentTool: 'none',
|
||||
machineManager: {
|
||||
machines: [],
|
||||
machineApiIp: null,
|
||||
currentMachine: null,
|
||||
setCurrentMachine: () => {},
|
||||
noMachinesReason: () => undefined,
|
||||
},
|
||||
selection: [],
|
||||
selectionRanges: {
|
||||
otherSelections: [],
|
||||
|
@ -4,7 +4,7 @@ import fs from 'node:fs/promises'
|
||||
import os from 'node:os'
|
||||
import fsSync from 'node:fs'
|
||||
import packageJson from '../package.json'
|
||||
import { MachinesListing } from 'components/MachineManagerProvider'
|
||||
import { MachinesListing } from 'lib/machineManager'
|
||||
import chokidar from 'chokidar'
|
||||
|
||||
const open = (args: any) => ipcRenderer.invoke('dialog.showOpenDialog', args)
|
||||
|
8
src/wasm-lib/Cargo.lock
generated
8
src/wasm-lib/Cargo.lock
generated
@ -1542,7 +1542,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.2.23"
|
||||
version = "0.2.22"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"approx 0.5.1",
|
||||
@ -1617,7 +1617,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-test-server"
|
||||
version = "0.1.15"
|
||||
version = "0.1.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hyper 0.14.30",
|
||||
@ -1644,9 +1644,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad"
|
||||
version = "0.3.25"
|
||||
version = "0.3.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6359cc0a1bbccbcf78775eea17a033cf2aa89d3fe6a9784f8ce94e5f882c185"
|
||||
checksum = "71b6f0c34165939697548dd0c94221200dbb8b5d1c84b5d8e803e70f9f720ea7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -71,7 +71,7 @@ members = [
|
||||
|
||||
[workspace.dependencies]
|
||||
http = "1"
|
||||
kittycad = { version = "0.3.25", default-features = false, features = ["js", "requests"] }
|
||||
kittycad = { version = "0.3.23", default-features = false, features = ["js", "requests"] }
|
||||
kittycad-modeling-cmds = { version = "0.2.71", features = ["websocket"] }
|
||||
|
||||
[[test]]
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-test-server"
|
||||
description = "A test server for KCL"
|
||||
version = "0.1.15"
|
||||
version = "0.1.14"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-lib"
|
||||
description = "KittyCAD Language implementation and tools"
|
||||
version = "0.2.23"
|
||||
version = "0.2.22"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
@ -1656,15 +1656,6 @@ pub enum Path {
|
||||
#[serde(flatten)]
|
||||
base: BasePath,
|
||||
},
|
||||
/// A circular arc, not necessarily tangential to the current point.
|
||||
Arc {
|
||||
#[serde(flatten)]
|
||||
base: BasePath,
|
||||
/// Center of the circle that this arc is drawn on.
|
||||
center: [f64; 2],
|
||||
/// Radius of the circle that this arc is drawn on.
|
||||
radius: f64,
|
||||
},
|
||||
}
|
||||
|
||||
/// What kind of path is this?
|
||||
@ -1677,11 +1668,10 @@ enum PathType {
|
||||
Circle,
|
||||
Horizontal,
|
||||
AngledLineTo,
|
||||
Arc,
|
||||
}
|
||||
|
||||
impl From<&Path> for PathType {
|
||||
fn from(value: &Path) -> Self {
|
||||
impl From<Path> for PathType {
|
||||
fn from(value: Path) -> Self {
|
||||
match value {
|
||||
Path::ToPoint { .. } => Self::ToPoint,
|
||||
Path::TangentialArcTo { .. } => Self::TangentialArcTo,
|
||||
@ -1690,7 +1680,6 @@ impl From<&Path> for PathType {
|
||||
Path::Horizontal { .. } => Self::Horizontal,
|
||||
Path::AngledLineTo { .. } => Self::AngledLineTo,
|
||||
Path::Base { .. } => Self::Base,
|
||||
Path::Arc { .. } => Self::Arc,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1705,7 +1694,6 @@ impl Path {
|
||||
Path::TangentialArcTo { base, .. } => base.geo_meta.id,
|
||||
Path::TangentialArc { base, .. } => base.geo_meta.id,
|
||||
Path::Circle { base, .. } => base.geo_meta.id,
|
||||
Path::Arc { base, .. } => base.geo_meta.id,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1718,7 +1706,6 @@ impl Path {
|
||||
Path::TangentialArcTo { base, .. } => base.tag.clone(),
|
||||
Path::TangentialArc { base, .. } => base.tag.clone(),
|
||||
Path::Circle { base, .. } => base.tag.clone(),
|
||||
Path::Arc { base, .. } => base.tag.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1731,7 +1718,6 @@ impl Path {
|
||||
Path::TangentialArcTo { base, .. } => base,
|
||||
Path::TangentialArc { base, .. } => base,
|
||||
Path::Circle { base, .. } => base,
|
||||
Path::Arc { base, .. } => base,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1746,33 +1732,8 @@ impl Path {
|
||||
|
||||
/// Length of this path segment, in cartesian plane.
|
||||
pub fn length(&self) -> f64 {
|
||||
match self {
|
||||
Self::ToPoint { .. } | Self::Base { .. } | Self::Horizontal { .. } | Self::AngledLineTo { .. } => {
|
||||
linear_distance(self.get_from(), self.get_to())
|
||||
}
|
||||
Self::TangentialArc {
|
||||
base: _,
|
||||
center,
|
||||
ccw: _,
|
||||
}
|
||||
| Self::TangentialArcTo {
|
||||
base: _,
|
||||
center,
|
||||
ccw: _,
|
||||
} => {
|
||||
// The radius can be calculated as the linear distance between `to` and `center`,
|
||||
// or between `from` and `center`. They should be the same.
|
||||
let radius = linear_distance(self.get_from(), center);
|
||||
debug_assert_eq!(radius, linear_distance(self.get_to(), center));
|
||||
// TODO: Call engine utils to figure this out.
|
||||
linear_distance(self.get_from(), self.get_to())
|
||||
}
|
||||
Self::Circle { radius, .. } => 2.0 * std::f64::consts::PI * radius,
|
||||
Self::Arc { .. } => {
|
||||
// TODO: Call engine utils to figure this out.
|
||||
linear_distance(self.get_from(), self.get_to())
|
||||
}
|
||||
}
|
||||
// TODO 4297: check what type of line this path is, don't assume linear.
|
||||
((self.get_from()[1] - self.get_to()[1]).powi(2) + (self.get_from()[0] - self.get_to()[0]).powi(2)).sqrt()
|
||||
}
|
||||
|
||||
pub fn get_base_mut(&mut self) -> Option<&mut BasePath> {
|
||||
@ -1784,22 +1745,10 @@ impl Path {
|
||||
Path::TangentialArcTo { base, .. } => Some(base),
|
||||
Path::TangentialArc { base, .. } => Some(base),
|
||||
Path::Circle { base, .. } => Some(base),
|
||||
Path::Arc { base, .. } => Some(base),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the straight-line distance between a pair of (2D) points.
|
||||
#[rustfmt::skip]
|
||||
fn linear_distance(
|
||||
[x0, y0]: &[f64; 2],
|
||||
[x1, y1]: &[f64; 2]
|
||||
) -> f64 {
|
||||
let y_sq = (y1 - y0).powi(2);
|
||||
let x_sq = (x1 - x0).powi(2);
|
||||
(y_sq + x_sq).sqrt()
|
||||
}
|
||||
|
||||
/// An extrude surface.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
@ -1980,73 +1929,9 @@ impl From<crate::settings::types::ModelingSettings> for ExecutorSettings {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new zoo api client.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn new_zoo_client(token: Option<String>, engine_addr: Option<String>) -> Result<kittycad::Client> {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
// For file conversions we need this to be long.
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
// For file conversions we need this to be long.
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
|
||||
let zoo_token_env = std::env::var("ZOO_API_TOKEN");
|
||||
|
||||
let token = if let Some(token) = token {
|
||||
token
|
||||
} else if let Ok(token) = std::env::var("KITTYCAD_API_TOKEN") {
|
||||
if let Ok(zoo_token) = zoo_token_env {
|
||||
if zoo_token != token {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Both environment variables KITTYCAD_API_TOKEN=`{}` and ZOO_API_TOKEN=`{}` are set. Use only one.",
|
||||
token,
|
||||
zoo_token
|
||||
));
|
||||
}
|
||||
}
|
||||
token
|
||||
} else if let Ok(token) = zoo_token_env {
|
||||
token
|
||||
} else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"No API token found in environment variables. Use KITTYCAD_API_TOKEN or ZOO_API_TOKEN"
|
||||
));
|
||||
};
|
||||
|
||||
// Create the client.
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
// Set an engine address if it's set.
|
||||
let kittycad_host_env = std::env::var("KITTYCAD_HOST");
|
||||
if let Some(addr) = engine_addr {
|
||||
client.set_base_url(addr);
|
||||
} else if let Ok(addr) = std::env::var("ZOO_HOST") {
|
||||
if let Ok(kittycad_host) = kittycad_host_env {
|
||||
if kittycad_host != addr {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Both environment variables KITTYCAD_HOST=`{}` and ZOO_HOST=`{}` are set. Use only one.",
|
||||
kittycad_host,
|
||||
addr
|
||||
));
|
||||
}
|
||||
}
|
||||
client.set_base_url(addr);
|
||||
} else if let Ok(addr) = kittycad_host_env {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
impl ExecutorContext {
|
||||
/// Create a new default executor context.
|
||||
/// Also returns the response HTTP headers from the server.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub async fn new(client: &kittycad::Client, settings: ExecutorSettings) -> Result<Self> {
|
||||
let (ws, _headers) = client
|
||||
@ -2091,35 +1976,6 @@ impl ExecutorContext {
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new default executor context.
|
||||
/// With a kittycad client.
|
||||
/// This allows for passing in `ZOO_API_TOKEN` and `ZOO_HOST` as environment
|
||||
/// variables.
|
||||
/// But also allows for passing in a token and engine address directly.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub async fn new_with_client(
|
||||
settings: ExecutorSettings,
|
||||
token: Option<String>,
|
||||
engine_addr: Option<String>,
|
||||
) -> Result<Self> {
|
||||
// Create the client.
|
||||
let client = new_zoo_client(token, engine_addr)?;
|
||||
|
||||
let ctx = Self::new(&client, settings).await?;
|
||||
Ok(ctx)
|
||||
}
|
||||
|
||||
/// Create a new default executor context.
|
||||
/// With the default kittycad client.
|
||||
/// This allows for passing in `ZOO_API_TOKEN` and `ZOO_HOST` as environment
|
||||
/// variables.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub async fn new_with_default_client(settings: ExecutorSettings) -> Result<Self> {
|
||||
// Create the client.
|
||||
let ctx = Self::new_with_client(settings, None, None).await?;
|
||||
Ok(ctx)
|
||||
}
|
||||
|
||||
pub fn is_mock(&self) -> bool {
|
||||
self.context_type == ContextType::Mock || self.context_type == ContextType::MockCustomForwarded
|
||||
}
|
||||
@ -2127,7 +1983,35 @@ impl ExecutorContext {
|
||||
/// For executing unit tests.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub async fn new_for_unit_test(units: UnitLength, engine_addr: Option<String>) -> Result<Self> {
|
||||
let ctx = ExecutorContext::new_with_client(
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"));
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
// For file conversions we need this to be long.
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
// For file conversions we need this to be long.
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
|
||||
// Create the client.
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
// Set a local engine address if it's set.
|
||||
if let Ok(addr) = std::env::var("ZOO_HOST") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
if let Some(addr) = engine_addr {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let ctx = ExecutorContext::new(
|
||||
&client,
|
||||
ExecutorSettings {
|
||||
units,
|
||||
highlight_edges: true,
|
||||
@ -2135,8 +2019,6 @@ impl ExecutorContext {
|
||||
show_grid: false,
|
||||
replay: None,
|
||||
},
|
||||
None,
|
||||
engine_addr,
|
||||
)
|
||||
.await?;
|
||||
Ok(ctx)
|
||||
|
@ -3,13 +3,41 @@ use std::sync::{Arc, RwLock};
|
||||
use anyhow::Result;
|
||||
use tower_lsp::LanguageServer;
|
||||
|
||||
fn new_zoo_client() -> kittycad::Client {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
// For file conversions we need this to be long.
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
// For file conversions we need this to be long.
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
|
||||
// Create the client.
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
// Set a local engine address if it's set.
|
||||
if let Ok(addr) = std::env::var("ZOO_HOST") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
client
|
||||
}
|
||||
|
||||
// Create a fake kcl lsp server for testing.
|
||||
pub async fn kcl_lsp_server(execute: bool) -> Result<crate::lsp::kcl::Backend> {
|
||||
let stdlib = crate::std::StdLib::new();
|
||||
let stdlib_completions = crate::lsp::kcl::get_completions_from_stdlib(&stdlib)?;
|
||||
let stdlib_signatures = crate::lsp::kcl::get_signatures_from_stdlib(&stdlib)?;
|
||||
|
||||
let zoo_client = crate::executor::new_zoo_client(None, None)?;
|
||||
let zoo_client = new_zoo_client();
|
||||
|
||||
let executor_ctx = if execute {
|
||||
Some(crate::executor::ExecutorContext::new(&zoo_client, Default::default()).await?)
|
||||
|
@ -149,7 +149,7 @@ pub(crate) async fn do_post_extrude(
|
||||
}
|
||||
|
||||
let edge_id = sketch.paths.iter().find_map(|segment| match segment {
|
||||
Path::ToPoint { base } | Path::Circle { base, .. } | Path::Arc { base, .. } => Some(base.geo_meta.id),
|
||||
Path::ToPoint { base } | Path::Circle { base, .. } => Some(base.geo_meta.id),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
@ -234,10 +234,7 @@ pub(crate) async fn do_post_extrude(
|
||||
.flat_map(|path| {
|
||||
if let Some(Some(actual_face_id)) = face_id_map.get(&path.get_base().geo_meta.id) {
|
||||
match path {
|
||||
Path::Arc { .. }
|
||||
| Path::TangentialArc { .. }
|
||||
| Path::TangentialArcTo { .. }
|
||||
| Path::Circle { .. } => {
|
||||
Path::TangentialArc { .. } | Path::TangentialArcTo { .. } | Path::Circle { .. } => {
|
||||
let extrude_surface = ExtrudeSurface::ExtrudeArc(crate::executor::ExtrudeArc {
|
||||
face_id: *actual_face_id,
|
||||
tag: path.get_base().tag.clone(),
|
||||
|
@ -1530,7 +1530,7 @@ pub(crate) async fn inner_arc(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let current_path = Path::Arc {
|
||||
let current_path = Path::ToPoint {
|
||||
base: BasePath {
|
||||
from: from.into(),
|
||||
to: end.into(),
|
||||
@ -1540,8 +1540,6 @@ pub(crate) async fn inner_arc(
|
||||
metadata: args.source_range.into(),
|
||||
},
|
||||
},
|
||||
center: center.into(),
|
||||
radius,
|
||||
};
|
||||
|
||||
let mut new_sketch = sketch.clone();
|
||||
|
@ -21,7 +21,7 @@ pub fn normalize(angle: Angle) -> Angle {
|
||||
Angle::from_degrees(if result > 180.0 { result - 360.0 } else { result })
|
||||
}
|
||||
|
||||
/// Gives the ▲-angle between from and to angles (shortest path)
|
||||
/// Gives the ▲-angle between from and to angles (shortest path), use radians.
|
||||
///
|
||||
/// Sign of the returned angle denotes direction, positive means counterClockwise 🔄
|
||||
/// # Examples
|
||||
|
@ -43,7 +43,38 @@ async fn do_execute_and_snapshot(ctx: &ExecutorContext, code: &str) -> anyhow::R
|
||||
}
|
||||
|
||||
async fn new_context(units: UnitLength, with_auth: bool) -> anyhow::Result<ExecutorContext> {
|
||||
let ctx = ExecutorContext::new_with_client(
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
// For file conversions we need this to be long.
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
// For file conversions we need this to be long.
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.connection_verbose(true)
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
|
||||
let token = if with_auth {
|
||||
std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set")
|
||||
} else {
|
||||
"bad_token".to_string()
|
||||
};
|
||||
|
||||
// Create the client.
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
// Set a local engine address if it's set.
|
||||
if let Ok(addr) = std::env::var("ZOO_HOST") {
|
||||
if with_auth {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
}
|
||||
|
||||
let ctx = ExecutorContext::new(
|
||||
&client,
|
||||
ExecutorSettings {
|
||||
units,
|
||||
highlight_edges: true,
|
||||
@ -51,8 +82,6 @@ async fn new_context(units: UnitLength, with_auth: bool) -> anyhow::Result<Execu
|
||||
show_grid: false,
|
||||
replay: None,
|
||||
},
|
||||
if with_auth { None } else { Some("bad_token".to_string()) },
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
Ok(ctx)
|
||||
|
@ -8,10 +8,33 @@ use pretty_assertions::assert_eq;
|
||||
|
||||
/// Setup the engine and parse code for an ast.
|
||||
async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid::Uuid)> {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
// For file conversions we need this to be long.
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60));
|
||||
let ws_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
// For file conversions we need this to be long.
|
||||
.timeout(std::time::Duration::from_secs(600))
|
||||
.connect_timeout(std::time::Duration::from_secs(60))
|
||||
.tcp_keepalive(std::time::Duration::from_secs(600))
|
||||
.http1_only();
|
||||
|
||||
let token = std::env::var("KITTYCAD_API_TOKEN").expect("KITTYCAD_API_TOKEN not set");
|
||||
|
||||
// Create the client.
|
||||
let mut client = kittycad::Client::new_from_reqwest(token, http_client, ws_client);
|
||||
// Set a local engine address if it's set.
|
||||
if let Ok(addr) = std::env::var("ZOO_HOST") {
|
||||
client.set_base_url(addr);
|
||||
}
|
||||
|
||||
let tokens = kcl_lib::token::lexer(code)?;
|
||||
let parser = kcl_lib::parser::Parser::new(tokens);
|
||||
let program = parser.ast()?;
|
||||
let ctx = kcl_lib::executor::ExecutorContext::new_with_default_client(Default::default()).await?;
|
||||
let ctx = kcl_lib::executor::ExecutorContext::new(&client, Default::default()).await?;
|
||||
let exec_state = ctx.run(&program, None, IdGenerator::default(), None).await?;
|
||||
|
||||
// We need to get the sketch ID.
|
||||
|
Reference in New Issue
Block a user