internal: Add lints for promises (#3733)
* Add lints for floating and misued promises * Add logging async errors in main * Add async error catch in test-utils * Change any to unknown * Trap promise errors and ignore more await warnings * Add more ignores and toSync helper * Fix more lint warnings * Add more ignores and fixes * Add more reject reporting * Add accepting arbitrary parameters to toSync() * Fix more lints * Revert unintentional change to non-arrow function * Revert unintentional change to use arrow function * Fix new warnings in main with auto updater * Fix formatting * Change lints to error This is what the recommended type checked rules do. * Fix to properly report promise rejections * Fix formatting * Fix formatting * Remove unused import * Remove unused convenience function * Move type helpers * Fix to not return promise when caller doesn't expect it * Add ignores to lsp code
This commit is contained in:
@ -13,6 +13,8 @@
|
||||
"plugin:css-modules/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
"@typescript-eslint/no-misused-promises": "error",
|
||||
"semi": [
|
||||
"error",
|
||||
"never"
|
||||
@ -24,7 +26,6 @@
|
||||
{
|
||||
"files": ["e2e/**/*.ts"], // Update the pattern based on your file structure
|
||||
"rules": {
|
||||
"@typescript-eslint/no-floating-promises": "warn",
|
||||
"suggest-no-throw/suggest-no-throw": "off",
|
||||
"testing-library/prefer-screen-queries": "off",
|
||||
"jest/valid-expect": "off"
|
||||
|
@ -27,6 +27,7 @@ import * as TOML from '@iarna/toml'
|
||||
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
||||
import { SETTINGS_FILE_NAME } from 'lib/constants'
|
||||
import { isArray } from 'lib/utils'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
type TestColor = [number, number, number]
|
||||
export const TEST_COLORS = {
|
||||
@ -439,8 +440,9 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
||||
}
|
||||
return maxDiff
|
||||
},
|
||||
doAndWaitForImageDiff: (fn: () => Promise<any>, diffCount = 200) =>
|
||||
new Promise(async (resolve) => {
|
||||
doAndWaitForImageDiff: (fn: () => Promise<unknown>, diffCount = 200) =>
|
||||
new Promise<boolean>((resolve) => {
|
||||
;(async () => {
|
||||
await page.screenshot({
|
||||
path: './e2e/playwright/temp1.png',
|
||||
fullPage: true,
|
||||
@ -469,7 +471,8 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
||||
|
||||
// run isImageDiff every 50ms until it returns true or 5 seconds have passed (100 times)
|
||||
let count = 0
|
||||
const interval = setInterval(async () => {
|
||||
const interval = setInterval(() => {
|
||||
;(async () => {
|
||||
count++
|
||||
if (await isImageDiff()) {
|
||||
clearInterval(interval)
|
||||
@ -478,7 +481,9 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
||||
clearInterval(interval)
|
||||
resolve(false)
|
||||
}
|
||||
})().catch(reportRejection)
|
||||
}, 50)
|
||||
})().catch(reportRejection)
|
||||
}),
|
||||
emulateNetworkConditions: async (
|
||||
networkOptions: Protocol.Network.emulateNetworkConditionsParameters
|
||||
|
@ -72,6 +72,7 @@ export class LanguageServerClient {
|
||||
async initialize() {
|
||||
// Start the client in the background.
|
||||
this.client.setNotifyFn(this.processNotifications.bind(this))
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.client.start()
|
||||
|
||||
this.ready = true
|
||||
@ -195,6 +196,9 @@ export class LanguageServerClient {
|
||||
}
|
||||
|
||||
private processNotifications(notification: LSP.NotificationMessage) {
|
||||
for (const plugin of this.plugins) plugin.processNotification(notification)
|
||||
for (const plugin of this.plugins) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
plugin.processNotification(notification)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ export default function lspFormatExt(
|
||||
run: (view: EditorView) => {
|
||||
let value = view.plugin(plugin)
|
||||
if (!value) return false
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
value.requestFormatting()
|
||||
return true
|
||||
},
|
||||
|
@ -117,6 +117,7 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
|
||||
this.processLspNotification = options.processLspNotification
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.initialize({
|
||||
documentText: this.getDocText(),
|
||||
})
|
||||
@ -149,6 +150,7 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
}
|
||||
|
||||
async initialize({ documentText }: { documentText: string }) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
if (this.client.initializePromise) {
|
||||
await this.client.initializePromise
|
||||
}
|
||||
@ -162,7 +164,9 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
},
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.requestSemanticTokens()
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.updateFoldingRanges()
|
||||
}
|
||||
|
||||
@ -225,7 +229,9 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
contentChanges: [{ text: this.view.state.doc.toString() }],
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.requestSemanticTokens()
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.updateFoldingRanges()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
|
@ -26,6 +26,7 @@ import useHotkeyWrapper from 'lib/hotkeyWrapper'
|
||||
import Gizmo from 'components/Gizmo'
|
||||
import { CoreDumpManager } from 'lib/coredump'
|
||||
import { UnitsMenu } from 'components/UnitsMenu'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
export function App() {
|
||||
const { project, file } = useLoaderData() as IndexLoaderData
|
||||
@ -80,7 +81,7 @@ export function App() {
|
||||
useEngineConnectionSubscriptions()
|
||||
|
||||
const debounceSocketSend = throttle<EngineCommand>((message) => {
|
||||
engineCommandManager.sendSceneCommand(message)
|
||||
engineCommandManager.sendSceneCommand(message).catch(reportRejection)
|
||||
}, 1000 / 15)
|
||||
const handleMouseMove: MouseEventHandler<HTMLDivElement> = (e) => {
|
||||
if (state.matches('Sketch')) {
|
||||
|
@ -41,6 +41,7 @@ import toast from 'react-hot-toast'
|
||||
import { coreDump } from 'lang/wasm'
|
||||
import { useMemo } from 'react'
|
||||
import { AppStateProvider } from 'AppState'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
const createRouter = isDesktop() ? createHashRouter : createBrowserRouter
|
||||
|
||||
@ -173,7 +174,8 @@ function CoreDump() {
|
||||
[]
|
||||
)
|
||||
useHotkeyWrapper(['mod + shift + .'], () => {
|
||||
toast.promise(
|
||||
toast
|
||||
.promise(
|
||||
coreDump(coreDumpManager, true),
|
||||
{
|
||||
loading: 'Starting core dump...',
|
||||
@ -188,6 +190,7 @@ function CoreDump() {
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch(reportRejection)
|
||||
})
|
||||
return null
|
||||
}
|
||||
|
@ -22,11 +22,12 @@ import {
|
||||
UnreliableSubscription,
|
||||
} from 'lang/std/engineConnection'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import { toSync, uuidv4 } from 'lib/utils'
|
||||
import { deg2Rad } from 'lib/utils2d'
|
||||
import { isReducedMotion, roundOff, throttle } from 'lib/utils'
|
||||
import * as TWEEN from '@tweenjs/tween.js'
|
||||
import { isQuaternionVertical } from './helpers'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
const ORTHOGRAPHIC_CAMERA_SIZE = 20
|
||||
const FRAMES_TO_ANIMATE_IN = 30
|
||||
@ -100,6 +101,7 @@ export class CameraControls {
|
||||
camProps.type === 'perspective' &&
|
||||
this.camera instanceof OrthographicCamera
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.usePerspectiveCamera()
|
||||
} else if (
|
||||
camProps.type === 'orthographic' &&
|
||||
@ -127,6 +129,7 @@ export class CameraControls {
|
||||
}
|
||||
|
||||
throttledEngCmd = throttle((cmd: EngineCommand) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.engineCommandManager.sendSceneCommand(cmd)
|
||||
}, 1000 / 30)
|
||||
|
||||
@ -139,6 +142,7 @@ export class CameraControls {
|
||||
...convertThreeCamValuesToEngineCam(threeValues),
|
||||
},
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.engineCommandManager.sendSceneCommand(cmd)
|
||||
}, 1000 / 15)
|
||||
|
||||
@ -151,6 +155,7 @@ export class CameraControls {
|
||||
this.lastPerspectiveCmd &&
|
||||
Date.now() - this.lastPerspectiveCmdTime >= lastCmdDelay
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.engineCommandManager.sendSceneCommand(this.lastPerspectiveCmd, true)
|
||||
this.lastPerspectiveCmdTime = Date.now()
|
||||
}
|
||||
@ -218,6 +223,7 @@ export class CameraControls {
|
||||
this.useOrthographicCamera()
|
||||
}
|
||||
if (this.camera instanceof OrthographicCamera && !camSettings.ortho) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.usePerspectiveCamera()
|
||||
}
|
||||
if (this.camera instanceof PerspectiveCamera && camSettings.fov_y) {
|
||||
@ -249,6 +255,7 @@ export class CameraControls {
|
||||
const doZoom = () => {
|
||||
if (this.zoomDataFromLastFrame !== undefined) {
|
||||
this.handleStart()
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
@ -266,6 +273,7 @@ export class CameraControls {
|
||||
|
||||
const doMove = () => {
|
||||
if (this.moveDataFromLastFrame !== undefined) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
@ -459,6 +467,7 @@ export class CameraControls {
|
||||
|
||||
this.camera.quaternion.set(qx, qy, qz, qw)
|
||||
this.camera.updateProjectionMatrix()
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
@ -929,6 +938,7 @@ export class CameraControls {
|
||||
}
|
||||
|
||||
if (isReducedMotion()) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
onComplete()
|
||||
return
|
||||
}
|
||||
@ -937,7 +947,7 @@ export class CameraControls {
|
||||
.to({ t: tweenEnd }, duration)
|
||||
.easing(TWEEN.Easing.Quadratic.InOut)
|
||||
.onUpdate(({ t }) => cameraAtTime(t))
|
||||
.onComplete(onComplete)
|
||||
.onComplete(toSync(onComplete, reportRejection))
|
||||
.start()
|
||||
})
|
||||
}
|
||||
@ -962,6 +972,7 @@ export class CameraControls {
|
||||
// Decrease the FOV
|
||||
currentFov = Math.max(currentFov - fovAnimationStep, targetFov)
|
||||
this.camera.updateProjectionMatrix()
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.dollyZoom(currentFov)
|
||||
requestAnimationFrame(animateFovChange) // Continue the animation
|
||||
} else if (frameWaitOnFinish > 0) {
|
||||
@ -991,6 +1002,7 @@ export class CameraControls {
|
||||
this.lastPerspectiveFov = 4
|
||||
let currentFov = 4
|
||||
const initialCameraUp = this.camera.up.clone()
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.usePerspectiveCamera()
|
||||
const tempVec = new Vector3()
|
||||
|
||||
@ -999,6 +1011,7 @@ export class CameraControls {
|
||||
this.lastPerspectiveFov + (targetFov - this.lastPerspectiveFov) * t
|
||||
const currentUp = tempVec.lerpVectors(initialCameraUp, targetCamUp, t)
|
||||
this.camera.up.copy(currentUp)
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.dollyZoom(currentFov)
|
||||
}
|
||||
|
||||
@ -1027,6 +1040,7 @@ export class CameraControls {
|
||||
this.lastPerspectiveFov = 4
|
||||
let currentFov = 4
|
||||
const initialCameraUp = this.camera.up.clone()
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.usePerspectiveCamera()
|
||||
const tempVec = new Vector3()
|
||||
|
||||
|
@ -5,7 +5,7 @@ import { cameraMouseDragGuards } from 'lib/cameraControls'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { ARROWHEAD, DEBUG_SHOW_BOTH_SCENES } from './sceneInfra'
|
||||
import { ReactCameraProperties } from './CameraControls'
|
||||
import { throttle } from 'lib/utils'
|
||||
import { throttle, toSync } from 'lib/utils'
|
||||
import {
|
||||
sceneInfra,
|
||||
kclManager,
|
||||
@ -44,7 +44,7 @@ import {
|
||||
removeSingleConstraintInfo,
|
||||
} from 'lang/modifyAst'
|
||||
import { ActionButton } from 'components/ActionButton'
|
||||
import { err, trap } from 'lib/trap'
|
||||
import { err, reportRejection, trap } from 'lib/trap'
|
||||
|
||||
function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } {
|
||||
const [isCamMoving, setIsCamMoving] = useState(false)
|
||||
@ -582,7 +582,7 @@ const ConstraintSymbol = ({
|
||||
}}
|
||||
// disabled={isConstrained || !convertToVarEnabled}
|
||||
// disabled={implicitDesc} TODO why does this change styles that are hard to override?
|
||||
onClick={async () => {
|
||||
onClick={toSync(async () => {
|
||||
if (!isConstrained) {
|
||||
send({
|
||||
type: 'Convert to variable',
|
||||
@ -616,13 +616,14 @@ const ConstraintSymbol = ({
|
||||
)
|
||||
if (!transform) return
|
||||
const { modifiedAst } = transform
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
kclManager.updateAst(modifiedAst, true)
|
||||
} catch (e) {
|
||||
console.log('error', e)
|
||||
}
|
||||
toast.success('Constraint removed')
|
||||
}
|
||||
}}
|
||||
}, reportRejection)}
|
||||
>
|
||||
<CustomIcon name={name} />
|
||||
</button>
|
||||
@ -688,7 +689,7 @@ const ConstraintSymbol = ({
|
||||
|
||||
const throttled = throttle((a: ReactCameraProperties) => {
|
||||
if (a.type === 'perspective' && a.fov) {
|
||||
sceneInfra.camControls.dollyZoom(a.fov)
|
||||
sceneInfra.camControls.dollyZoom(a.fov).catch(reportRejection)
|
||||
}
|
||||
}, 1000 / 15)
|
||||
|
||||
@ -718,6 +719,7 @@ export const CamDebugSettings = () => {
|
||||
if (camSettings.type === 'perspective') {
|
||||
sceneInfra.camControls.useOrthographicCamera()
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
sceneInfra.camControls.usePerspectiveCamera(true)
|
||||
}
|
||||
}}
|
||||
@ -725,7 +727,7 @@ export const CamDebugSettings = () => {
|
||||
<div>
|
||||
<button
|
||||
onClick={() => {
|
||||
sceneInfra.camControls.resetCameraPosition()
|
||||
sceneInfra.camControls.resetCameraPosition().catch(reportRejection)
|
||||
}}
|
||||
>
|
||||
Reset Camera Position
|
||||
|
@ -103,7 +103,7 @@ import {
|
||||
updateRectangleSketch,
|
||||
} from 'lib/rectangleTool'
|
||||
import { getThemeColorForThreeJs } from 'lib/theme'
|
||||
import { err, trap } from 'lib/trap'
|
||||
import { err, reportRejection, trap } from 'lib/trap'
|
||||
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
|
||||
import { Point3d } from 'wasm-lib/kcl/bindings/Point3d'
|
||||
|
||||
@ -324,6 +324,7 @@ export class SceneEntities {
|
||||
)
|
||||
}
|
||||
sceneInfra.setCallbacks({
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
onClick: async (args) => {
|
||||
if (!args) return
|
||||
if (args.mouseEvent.which !== 1) return
|
||||
@ -634,6 +635,7 @@ export class SceneEntities {
|
||||
draftExpressionsIndices,
|
||||
})
|
||||
sceneInfra.setCallbacks({
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
onClick: async (args) => {
|
||||
if (!args) return
|
||||
if (args.mouseEvent.which !== 1) return
|
||||
@ -701,7 +703,7 @@ export class SceneEntities {
|
||||
if (profileStart) {
|
||||
sceneInfra.modelingSend({ type: 'CancelSketch' })
|
||||
} else {
|
||||
this.setUpDraftSegment(
|
||||
await this.setUpDraftSegment(
|
||||
sketchPathToNode,
|
||||
forward,
|
||||
up,
|
||||
@ -771,6 +773,7 @@ export class SceneEntities {
|
||||
})
|
||||
|
||||
sceneInfra.setCallbacks({
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
onMove: async (args) => {
|
||||
// Update the width and height of the draft rectangle
|
||||
const pathToNodeTwo = structuredClone(sketchPathToNode)
|
||||
@ -818,6 +821,7 @@ export class SceneEntities {
|
||||
this.updateSegment(seg, index, 0, _ast, orthoFactor, sketchGroup)
|
||||
)
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
onClick: async (args) => {
|
||||
// Commit the rectangle to the full AST/code and return to sketch.idle
|
||||
const cornerPoint = args.intersectionPoint?.twoD
|
||||
@ -892,9 +896,11 @@ export class SceneEntities {
|
||||
}) => {
|
||||
let addingNewSegmentStatus: 'nothing' | 'pending' | 'added' = 'nothing'
|
||||
sceneInfra.setCallbacks({
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
onDragEnd: async () => {
|
||||
if (addingNewSegmentStatus !== 'nothing') {
|
||||
await this.tearDownSketch({ removeAxis: false })
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.setupSketch({
|
||||
sketchPathToNode: pathToNode,
|
||||
maybeModdedAst: kclManager.ast,
|
||||
@ -911,6 +917,7 @@ export class SceneEntities {
|
||||
})
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
onDrag: async ({
|
||||
selected,
|
||||
intersectionPoint,
|
||||
@ -958,6 +965,7 @@ export class SceneEntities {
|
||||
|
||||
await kclManager.executeAstMock(mod.modifiedAst)
|
||||
await this.tearDownSketch({ removeAxis: false })
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.setupSketch({
|
||||
sketchPathToNode: pathToNode,
|
||||
maybeModdedAst: kclManager.ast,
|
||||
@ -1161,7 +1169,7 @@ export class SceneEntities {
|
||||
)
|
||||
)
|
||||
sceneInfra.overlayCallbacks(callBacks)
|
||||
})()
|
||||
})().catch(reportRejection)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,6 +151,7 @@ export function useCalc({
|
||||
})
|
||||
if (trap(error)) return
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
executeAst({
|
||||
ast,
|
||||
engineCommandManager,
|
||||
|
@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'
|
||||
import { EngineCommandManagerEvents } from 'lang/std/engineConnection'
|
||||
import { engineCommandManager, sceneInfra } from 'lib/singletons'
|
||||
import { throttle, isReducedMotion } from 'lib/utils'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
const updateDollyZoom = throttle(
|
||||
(newFov: number) => sceneInfra.camControls.dollyZoom(newFov),
|
||||
@ -16,8 +17,8 @@ export const CamToggle = () => {
|
||||
useEffect(() => {
|
||||
engineCommandManager.addEventListener(
|
||||
EngineCommandManagerEvents.SceneReady,
|
||||
async () => {
|
||||
sceneInfra.camControls.dollyZoom(fov)
|
||||
() => {
|
||||
sceneInfra.camControls.dollyZoom(fov).catch(reportRejection)
|
||||
}
|
||||
)
|
||||
}, [])
|
||||
@ -26,11 +27,11 @@ export const CamToggle = () => {
|
||||
if (isPerspective) {
|
||||
isReducedMotion()
|
||||
? sceneInfra.camControls.useOrthographicCamera()
|
||||
: sceneInfra.camControls.animateToOrthographic()
|
||||
: sceneInfra.camControls.animateToOrthographic().catch(reportRejection)
|
||||
} else {
|
||||
isReducedMotion()
|
||||
? sceneInfra.camControls.usePerspectiveCamera()
|
||||
: sceneInfra.camControls.animateToPerspective()
|
||||
? sceneInfra.camControls.usePerspectiveCamera().catch(reportRejection)
|
||||
: sceneInfra.camControls.animateToPerspective().catch(reportRejection)
|
||||
}
|
||||
setIsPerspective(!isPerspective)
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { CommandLog } from 'lang/std/engineConnection'
|
||||
import { engineCommandManager } from 'lib/singletons'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
export function useEngineCommands(): [CommandLog[], () => void] {
|
||||
@ -77,9 +78,11 @@ export const EngineCommands = () => {
|
||||
/>
|
||||
<button
|
||||
data-testid="custom-cmd-send-button"
|
||||
onClick={() =>
|
||||
engineCommandManager.sendSceneCommand(JSON.parse(customCmd))
|
||||
}
|
||||
onClick={() => {
|
||||
engineCommandManager
|
||||
.sendSceneCommand(JSON.parse(customCmd))
|
||||
.catch(reportRejection)
|
||||
}}
|
||||
>
|
||||
Send custom command
|
||||
</button>
|
||||
|
@ -176,9 +176,11 @@ const FileTreeItem = ({
|
||||
`import("${fileOrDir.path.replace(project.path, '.')}")\n` +
|
||||
codeManager.code
|
||||
)
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
codeManager.writeToFile()
|
||||
|
||||
// Prevent seeing the model built one piece at a time when changing files
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
kclManager.executeCode(true)
|
||||
} else {
|
||||
// Let the lsp servers know we closed a file.
|
||||
@ -388,14 +390,14 @@ interface FileTreeProps {
|
||||
export const FileTreeMenu = () => {
|
||||
const { send } = useFileContext()
|
||||
|
||||
async function createFile() {
|
||||
function createFile() {
|
||||
send({
|
||||
type: 'Create file',
|
||||
data: { name: '', makeDir: false },
|
||||
})
|
||||
}
|
||||
|
||||
async function createFolder() {
|
||||
function createFolder() {
|
||||
send({
|
||||
type: 'Create file',
|
||||
data: { name: '', makeDir: true },
|
||||
|
@ -27,6 +27,7 @@ import {
|
||||
} from './ContextMenu'
|
||||
import { Popover } from '@headlessui/react'
|
||||
import { CustomIcon } from './CustomIcon'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
const CANVAS_SIZE = 80
|
||||
const FRUSTUM_SIZE = 0.5
|
||||
@ -67,7 +68,9 @@ export default function Gizmo() {
|
||||
<ContextMenuItem
|
||||
key={axisName}
|
||||
onClick={() => {
|
||||
sceneInfra.camControls.updateCameraToAxis(axisName as AxisNames)
|
||||
sceneInfra.camControls
|
||||
.updateCameraToAxis(axisName as AxisNames)
|
||||
.catch(reportRejection)
|
||||
}}
|
||||
>
|
||||
{axisSemantic} view
|
||||
@ -75,7 +78,7 @@ export default function Gizmo() {
|
||||
)),
|
||||
<ContextMenuItem
|
||||
onClick={() => {
|
||||
sceneInfra.camControls.resetCameraPosition()
|
||||
sceneInfra.camControls.resetCameraPosition().catch(reportRejection)
|
||||
}}
|
||||
>
|
||||
Reset view
|
||||
@ -299,7 +302,7 @@ const initializeMouseEvents = (
|
||||
const handleClick = () => {
|
||||
if (raycasterIntersect.current) {
|
||||
const axisName = raycasterIntersect.current.object.name as AxisNames
|
||||
sceneInfra.camControls.updateCameraToAxis(axisName)
|
||||
sceneInfra.camControls.updateCameraToAxis(axisName).catch(reportRejection)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import { createAndOpenNewProject } from 'lib/desktopFS'
|
||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||
import { useLspContext } from './LspProvider'
|
||||
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
const HelpMenuDivider = () => (
|
||||
<div className="h-[1px] bg-chalkboard-110 dark:bg-chalkboard-80" />
|
||||
@ -115,7 +116,9 @@ export function HelpMenu(props: React.PropsWithChildren) {
|
||||
if (isInProject) {
|
||||
navigate(filePath + PATHS.ONBOARDING.INDEX)
|
||||
} else {
|
||||
createAndOpenNewProject({ onProjectOpen, navigate })
|
||||
createAndOpenNewProject({ onProjectOpen, navigate }).catch(
|
||||
reportRejection
|
||||
)
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
@ -12,6 +12,7 @@ import { CoreDumpManager } from 'lib/coredump'
|
||||
import openWindow, { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
||||
import { NetworkMachineIndicator } from './NetworkMachineIndicator'
|
||||
import { ModelStateIndicator } from './ModelStateIndicator'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
export function LowerRightControls({
|
||||
children,
|
||||
@ -25,7 +26,7 @@ export function LowerRightControls({
|
||||
const linkOverrideClassName =
|
||||
'!text-chalkboard-70 hover:!text-chalkboard-80 dark:!text-chalkboard-40 dark:hover:!text-chalkboard-30'
|
||||
|
||||
async function reportbug(event: {
|
||||
function reportbug(event: {
|
||||
preventDefault: () => void
|
||||
stopPropagation: () => void
|
||||
}) {
|
||||
@ -34,7 +35,9 @@ export function LowerRightControls({
|
||||
|
||||
if (!coreDumpManager) {
|
||||
// open default reporting option
|
||||
openWindow('https://github.com/KittyCAD/modeling-app/issues/new/choose')
|
||||
openWindow(
|
||||
'https://github.com/KittyCAD/modeling-app/issues/new/choose'
|
||||
).catch(reportRejection)
|
||||
} else {
|
||||
toast
|
||||
.promise(
|
||||
@ -56,7 +59,7 @@ export function LowerRightControls({
|
||||
if (err) {
|
||||
openWindow(
|
||||
'https://github.com/KittyCAD/modeling-app/issues/new/choose'
|
||||
)
|
||||
).catch(reportRejection)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -160,7 +160,9 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
// Update the folding ranges, since the AST has changed.
|
||||
// This is a hack since codemirror does not support async foldService.
|
||||
// When they do we can delete this.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
plugin.updateFoldingRanges()
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
plugin.requestSemanticTokens()
|
||||
break
|
||||
case 'kcl/memoryUpdated':
|
||||
|
@ -73,7 +73,7 @@ import { EditorSelection, Transaction } from '@codemirror/state'
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom'
|
||||
import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls'
|
||||
import { getVarNameModal } from 'hooks/useToolbarGuards'
|
||||
import { err, trap } from 'lib/trap'
|
||||
import { err, reportRejection, trap } from 'lib/trap'
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { modelingMachineEvent } from 'editor/manager'
|
||||
import { hasValidFilletSelection } from 'lang/modifyAst/addFillet'
|
||||
@ -152,14 +152,17 @@ export const ModelingMachineProvider = ({
|
||||
|
||||
store.videoElement?.pause()
|
||||
|
||||
kclManager.executeCode().then(() => {
|
||||
kclManager
|
||||
.executeCode()
|
||||
.then(() => {
|
||||
if (engineCommandManager.engineConnection?.idleMode) return
|
||||
|
||||
store.videoElement?.play().catch((e) => {
|
||||
console.warn('Video playing was prevented', e)
|
||||
})
|
||||
})
|
||||
})()
|
||||
.catch(reportRejection)
|
||||
})().catch(reportRejection)
|
||||
},
|
||||
'Set mouse state': assign(({ context, event }) => {
|
||||
if (event.type !== 'Set mouse state') return {}
|
||||
@ -316,9 +319,10 @@ export const ModelingMachineProvider = ({
|
||||
})
|
||||
codeMirrorSelection && dispatchSelection(codeMirrorSelection)
|
||||
engineEvents &&
|
||||
engineEvents.forEach((event) =>
|
||||
engineEvents.forEach((event) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
engineCommandManager.sendSceneCommand(event)
|
||||
)
|
||||
})
|
||||
updateSceneObjectColors()
|
||||
|
||||
return {
|
||||
@ -349,9 +353,10 @@ export const ModelingMachineProvider = ({
|
||||
selections,
|
||||
})
|
||||
engineEvents &&
|
||||
engineEvents.forEach((event) =>
|
||||
engineEvents.forEach((event) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
engineCommandManager.sendSceneCommand(event)
|
||||
)
|
||||
})
|
||||
updateSceneObjectColors()
|
||||
return {
|
||||
selectionRanges: selections,
|
||||
@ -378,7 +383,7 @@ export const ModelingMachineProvider = ({
|
||||
return {}
|
||||
}
|
||||
),
|
||||
Make: async ({ event }) => {
|
||||
Make: ({ event }) => {
|
||||
if (event.type !== 'Make') return
|
||||
// Check if we already have an export intent.
|
||||
if (engineCommandManager.exportIntent) {
|
||||
@ -410,7 +415,8 @@ export const ModelingMachineProvider = ({
|
||||
}
|
||||
|
||||
// Artificially delay the export in playwright tests
|
||||
toast.promise(
|
||||
toast
|
||||
.promise(
|
||||
exportFromEngine({
|
||||
format: format,
|
||||
}),
|
||||
@ -421,8 +427,9 @@ export const ModelingMachineProvider = ({
|
||||
error: 'Error while starting print',
|
||||
}
|
||||
)
|
||||
.catch(reportRejection)
|
||||
},
|
||||
'Engine export': async ({ event }) => {
|
||||
'Engine export': ({ event }) => {
|
||||
if (event.type !== 'Export') return
|
||||
if (engineCommandManager.exportIntent) {
|
||||
toast.error('Already exporting')
|
||||
@ -474,7 +481,8 @@ export const ModelingMachineProvider = ({
|
||||
format.selection = { type: 'default_scene' }
|
||||
}
|
||||
|
||||
toast.promise(
|
||||
toast
|
||||
.promise(
|
||||
exportFromEngine({
|
||||
format: format as Models['OutputFormat_type'],
|
||||
}),
|
||||
@ -484,13 +492,14 @@ export const ModelingMachineProvider = ({
|
||||
error: 'Error while exporting',
|
||||
}
|
||||
)
|
||||
.catch(reportRejection)
|
||||
},
|
||||
'Submit to Text-to-CAD API': async ({ event }) => {
|
||||
'Submit to Text-to-CAD API': ({ event }) => {
|
||||
if (event.type !== 'Text-to-CAD') return
|
||||
const trimmedPrompt = event.data.prompt.trim()
|
||||
if (!trimmedPrompt) return
|
||||
|
||||
void submitAndAwaitTextToKcl({
|
||||
submitAndAwaitTextToKcl({
|
||||
trimmedPrompt,
|
||||
fileMachineSend,
|
||||
navigate,
|
||||
@ -501,7 +510,7 @@ export const ModelingMachineProvider = ({
|
||||
theme: theme.current,
|
||||
highlightEdges: highlightEdges.current,
|
||||
},
|
||||
})
|
||||
}).catch(reportRejection)
|
||||
},
|
||||
},
|
||||
guards: {
|
||||
|
@ -8,6 +8,7 @@ import { editorShortcutMeta } from './KclEditorPane'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
export const KclEditorMenu = ({ children }: PropsWithChildren) => {
|
||||
const { enable: convertToVarEnabled, handleClick: handleConvertToVarClick } =
|
||||
@ -47,7 +48,9 @@ export const KclEditorMenu = ({ children }: PropsWithChildren) => {
|
||||
{convertToVarEnabled && (
|
||||
<Menu.Item>
|
||||
<button
|
||||
onClick={() => handleConvertToVarClick()}
|
||||
onClick={() => {
|
||||
handleConvertToVarClick().catch(reportRejection)
|
||||
}}
|
||||
className={styles.button}
|
||||
>
|
||||
<span>Convert to Variable</span>
|
||||
|
@ -57,6 +57,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
icon: 'printer3d',
|
||||
iconClassName: '!p-0',
|
||||
keybinding: 'Ctrl + Shift + M',
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
action: async () => {
|
||||
commandBarSend({
|
||||
type: 'Find and select command',
|
||||
|
@ -4,6 +4,8 @@ import Tooltip from './Tooltip'
|
||||
import { ConnectingTypeGroup } from '../lang/std/engineConnection'
|
||||
import { useNetworkContext } from '../hooks/useNetworkContext'
|
||||
import { NetworkHealthState } from '../hooks/useNetworkStatus'
|
||||
import { toSync } from 'lib/utils'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
export const NETWORK_HEALTH_TEXT: Record<NetworkHealthState, string> = {
|
||||
[NetworkHealthState.Ok]: 'Connected',
|
||||
@ -160,13 +162,13 @@ export const NetworkHealthIndicator = () => {
|
||||
</div>
|
||||
{issues[name as ConnectingTypeGroup] && (
|
||||
<button
|
||||
onClick={async () => {
|
||||
onClick={toSync(async () => {
|
||||
await navigator.clipboard.writeText(
|
||||
JSON.stringify(error, null, 2) || ''
|
||||
)
|
||||
setHasCopied(true)
|
||||
setTimeout(() => setHasCopied(false), 5000)
|
||||
}}
|
||||
}, reportRejection)}
|
||||
className="flex w-fit gap-2 items-center bg-transparent text-sm p-1 py-0 my-0 -mx-1 text-destroy-80 dark:text-destroy-10 hover:bg-transparent border-transparent dark:border-transparent hover:border-destroy-80 dark:hover:border-destroy-80 dark:hover:bg-destroy-80"
|
||||
>
|
||||
{hasCopied ? 'Copied' : 'Copy Error'}
|
||||
|
@ -8,6 +8,8 @@ import Tooltip from '../Tooltip'
|
||||
import { DeleteConfirmationDialog } from './DeleteProjectDialog'
|
||||
import { ProjectCardRenameForm } from './ProjectCardRenameForm'
|
||||
import { Project } from 'lib/project'
|
||||
import { toSync } from 'lib/utils'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
function ProjectCard({
|
||||
project,
|
||||
@ -165,10 +167,10 @@ function ProjectCard({
|
||||
{isConfirmingDelete && (
|
||||
<DeleteConfirmationDialog
|
||||
title="Delete Project"
|
||||
onConfirm={async () => {
|
||||
onConfirm={toSync(async () => {
|
||||
await handleDeleteProject(project)
|
||||
setIsConfirmingDelete(false)
|
||||
}}
|
||||
}, reportRejection)}
|
||||
onDismiss={() => setIsConfirmingDelete(false)}
|
||||
>
|
||||
<p className="my-4">
|
||||
|
@ -6,6 +6,8 @@ import React, { useMemo } from 'react'
|
||||
import toast from 'react-hot-toast'
|
||||
import Tooltip from './Tooltip'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
import { toSync } from 'lib/utils'
|
||||
|
||||
export const RefreshButton = ({ children }: React.PropsWithChildren) => {
|
||||
const { auth } = useSettingsAuthContext()
|
||||
@ -50,11 +52,12 @@ export const RefreshButton = ({ children }: React.PropsWithChildren) => {
|
||||
// Window may not be available in some environments
|
||||
window?.location.reload()
|
||||
})
|
||||
.catch(reportRejection)
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={refresh}
|
||||
onClick={toSync(refresh, reportRejection)}
|
||||
className="p-1 m-0 bg-chalkboard-10/80 dark:bg-chalkboard-100/50 hover:bg-chalkboard-10 dark:hover:bg-chalkboard-100 rounded-full border border-solid border-chalkboard-20 dark:border-chalkboard-90"
|
||||
>
|
||||
<CustomIcon name="exclamationMark" className="w-5 h-5" />
|
||||
|
@ -20,6 +20,8 @@ import { createAndOpenNewProject, getSettingsFolderPaths } from 'lib/desktopFS'
|
||||
import { useDotDotSlash } from 'hooks/useDotDotSlash'
|
||||
import { ForwardedRef, forwardRef, useEffect } from 'react'
|
||||
import { useLspContext } from 'components/LspProvider'
|
||||
import { toSync } from 'lib/utils'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
interface AllSettingsFieldsProps {
|
||||
searchParamTab: SettingsLevel
|
||||
@ -54,7 +56,7 @@ export const AllSettingsFields = forwardRef(
|
||||
)
|
||||
: undefined
|
||||
|
||||
async function restartOnboarding() {
|
||||
function restartOnboarding() {
|
||||
send({
|
||||
type: `set.app.onboardingStatus`,
|
||||
data: { level: 'user', value: '' },
|
||||
@ -82,6 +84,7 @@ export const AllSettingsFields = forwardRef(
|
||||
}
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
navigateToOnboardingStart()
|
||||
}, [isFileSettings, navigate, state])
|
||||
|
||||
@ -190,7 +193,7 @@ export const AllSettingsFields = forwardRef(
|
||||
{isDesktop() && (
|
||||
<ActionButton
|
||||
Element="button"
|
||||
onClick={async () => {
|
||||
onClick={toSync(async () => {
|
||||
const paths = await getSettingsFolderPaths(
|
||||
projectPath ? decodeURIComponent(projectPath) : undefined
|
||||
)
|
||||
@ -199,7 +202,7 @@ export const AllSettingsFields = forwardRef(
|
||||
return new Error('finalPath undefined')
|
||||
}
|
||||
window.electron.showInFolder(finalPath)
|
||||
}}
|
||||
}, reportRejection)}
|
||||
iconStart={{
|
||||
icon: 'folder',
|
||||
size: 'sm',
|
||||
@ -211,14 +214,14 @@ export const AllSettingsFields = forwardRef(
|
||||
)}
|
||||
<ActionButton
|
||||
Element="button"
|
||||
onClick={async () => {
|
||||
onClick={toSync(async () => {
|
||||
const defaultDirectory = await getInitialDefaultDir()
|
||||
send({
|
||||
type: 'Reset settings',
|
||||
defaultDirectory,
|
||||
})
|
||||
toast.success('Settings restored to default')
|
||||
}}
|
||||
}, reportRejection)}
|
||||
iconStart={{
|
||||
icon: 'refresh',
|
||||
size: 'sm',
|
||||
|
@ -108,6 +108,7 @@ export const SettingsAuthProviderBase = ({
|
||||
sceneInfra.baseUnit = newBaseUnit
|
||||
},
|
||||
setEngineTheme: ({ context }) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
engineCommandManager.sendSceneCommand({
|
||||
cmd_id: uuidv4(),
|
||||
type: 'modeling_cmd_req',
|
||||
@ -118,6 +119,7 @@ export const SettingsAuthProviderBase = ({
|
||||
})
|
||||
|
||||
const opposingTheme = getOppositeTheme(context.app.theme.current)
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
engineCommandManager.sendSceneCommand({
|
||||
cmd_id: uuidv4(),
|
||||
type: 'modeling_cmd_req',
|
||||
@ -137,6 +139,7 @@ export const SettingsAuthProviderBase = ({
|
||||
sceneInfra.theme = opposingTheme
|
||||
},
|
||||
setEngineEdges: ({ context }) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
engineCommandManager.sendSceneCommand({
|
||||
cmd_id: uuidv4(),
|
||||
type: 'modeling_cmd_req',
|
||||
@ -186,6 +189,7 @@ export const SettingsAuthProviderBase = ({
|
||||
resetSettingsIncludesUnitChange
|
||||
) {
|
||||
// Unit changes requires a re-exec of code
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
kclManager.executeCode(true)
|
||||
} else {
|
||||
// For any future logging we'd like to do
|
||||
@ -197,8 +201,10 @@ export const SettingsAuthProviderBase = ({
|
||||
console.error('Error executing AST after settings change', e)
|
||||
}
|
||||
},
|
||||
persistSettings: ({ context }) =>
|
||||
saveSettings(context, loadedProject?.project?.path),
|
||||
persistSettings: ({ context }) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
saveSettings(context, loadedProject?.project?.path)
|
||||
},
|
||||
},
|
||||
}),
|
||||
{ input: loadedSettings }
|
||||
@ -289,6 +295,7 @@ export const SettingsAuthProviderBase = ({
|
||||
actions: {
|
||||
goToSignInPage: () => {
|
||||
navigate(PATHS.SIGN_IN)
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
logout()
|
||||
},
|
||||
goToIndexPage: () => {
|
||||
@ -330,13 +337,11 @@ export const SettingsAuthProviderBase = ({
|
||||
|
||||
export default SettingsAuthProvider
|
||||
|
||||
export function logout() {
|
||||
export async function logout() {
|
||||
localStorage.removeItem(TOKEN_PERSIST_KEY)
|
||||
return (
|
||||
!isDesktop() &&
|
||||
fetch(withBaseUrl('/logout'), {
|
||||
if (isDesktop()) return Promise.resolve(null)
|
||||
return fetch(withBaseUrl('/logout'), {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
})
|
||||
)
|
||||
}
|
||||
|
@ -53,9 +53,10 @@ export const Stream = () => {
|
||||
* executed. If we can find a way to do this from a more
|
||||
* central place, we can move this code there.
|
||||
*/
|
||||
async function executeCodeAndPlayStream() {
|
||||
kclManager.executeCode(true).then(() => {
|
||||
videoRef.current?.play().catch((e) => {
|
||||
function executeCodeAndPlayStream() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
kclManager.executeCode(true).then(async () => {
|
||||
await videoRef.current?.play().catch((e) => {
|
||||
console.warn('Video playing was prevented', e, videoRef.current)
|
||||
})
|
||||
setStreamState(StreamState.Playing)
|
||||
@ -218,12 +219,12 @@ export const Stream = () => {
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!kclManager.isExecuting) {
|
||||
setTimeout(() =>
|
||||
setTimeout(() => {
|
||||
// execute in the next event loop
|
||||
videoRef.current?.play().catch((e) => {
|
||||
console.warn('Video playing was prevented', e, videoRef.current)
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
}, [kclManager.isExecuting])
|
||||
|
||||
@ -290,6 +291,7 @@ export const Stream = () => {
|
||||
if (state.matches({ idle: 'showPlanes' })) return
|
||||
|
||||
if (!context.store?.didDragInStream && btnName(e).left) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
sendSelectEventToEngine(
|
||||
e,
|
||||
videoRef.current,
|
||||
|
@ -28,6 +28,7 @@ import { ActionButton } from './ActionButton'
|
||||
import { commandBarMachine } from 'machines/commandBarMachine'
|
||||
import { EventFrom } from 'xstate'
|
||||
import { fileMachine } from 'machines/fileMachine'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
const CANVAS_SIZE = 128
|
||||
const PROMPT_TRUNCATE_LENGTH = 128
|
||||
@ -297,7 +298,7 @@ export function ToastTextToCadSuccess({
|
||||
name={hasCopied ? 'Close' : 'Reject'}
|
||||
onClick={() => {
|
||||
if (!hasCopied) {
|
||||
sendTelemetry(modelId, 'rejected', token)
|
||||
sendTelemetry(modelId, 'rejected', token).catch(reportRejection)
|
||||
}
|
||||
if (isDesktop()) {
|
||||
// Delete the file from the project
|
||||
@ -323,6 +324,7 @@ export function ToastTextToCadSuccess({
|
||||
}}
|
||||
name="Accept"
|
||||
onClick={() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
sendTelemetry(modelId, 'accepted', token)
|
||||
navigate(
|
||||
`${PATHS.FILE}/${encodeURIComponent(
|
||||
@ -342,7 +344,9 @@ export function ToastTextToCadSuccess({
|
||||
}}
|
||||
name="Copy to clipboard"
|
||||
onClick={() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
sendTelemetry(modelId, 'accepted', token)
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
navigator.clipboard.writeText(data.code || '// no code found')
|
||||
setShowCopiedUi(true)
|
||||
setHasCopied(true)
|
||||
|
@ -285,8 +285,9 @@ export default class EditorManager {
|
||||
|
||||
this._lastEvent = { event: stringEvent, time: Date.now() }
|
||||
this._modelingSend(eventInfo.modelingEvent)
|
||||
eventInfo.engineEvents.forEach((event) =>
|
||||
eventInfo.engineEvents.forEach((event) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
engineCommandManager.sendSceneCommand(event)
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import { CopilotCompletionResponse } from 'wasm-lib/kcl/bindings/CopilotCompleti
|
||||
import { CopilotAcceptCompletionParams } from 'wasm-lib/kcl/bindings/CopilotAcceptCompletionParams'
|
||||
import { CopilotRejectCompletionParams } from 'wasm-lib/kcl/bindings/CopilotRejectCompletionParams'
|
||||
import { editorManager } from 'lib/singletons'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
const copilotPluginAnnotation = Annotation.define<boolean>()
|
||||
export const copilotPluginEvent = copilotPluginAnnotation.of(true)
|
||||
@ -266,7 +267,7 @@ export class CompletionRequester implements PluginValue {
|
||||
|
||||
if (!this.client.ready) return
|
||||
try {
|
||||
this.requestCompletions()
|
||||
this.requestCompletions().catch(reportRejection)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
@ -462,7 +463,7 @@ export class CompletionRequester implements PluginValue {
|
||||
annotations: [copilotPluginEvent, Transaction.addToHistory.of(true)],
|
||||
})
|
||||
|
||||
this.accept(ghostText.uuid)
|
||||
this.accept(ghostText.uuid).catch(reportRejection)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -490,7 +491,7 @@ export class CompletionRequester implements PluginValue {
|
||||
],
|
||||
})
|
||||
|
||||
this.reject()
|
||||
this.reject().catch(reportRejection)
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -96,6 +96,7 @@ export class KclPlugin implements PluginValue {
|
||||
|
||||
const newCode = viewUpdate.state.doc.toString()
|
||||
codeManager.code = newCode
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
codeManager.writeToFile()
|
||||
|
||||
this.scheduleUpdateDoc()
|
||||
@ -117,6 +118,7 @@ export class KclPlugin implements PluginValue {
|
||||
}
|
||||
|
||||
if (!this.client.ready) return
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
kclManager.executeCode()
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ import {
|
||||
CopilotWorkerOptions,
|
||||
} from 'editor/plugins/lsp/types'
|
||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||
import { err } from 'lib/trap'
|
||||
import { err, reportRejection } from 'lib/trap'
|
||||
|
||||
const intoServer: IntoServer = new IntoServer()
|
||||
const fromServer: FromServer | Error = FromServer.create()
|
||||
@ -60,7 +60,8 @@ export async function kclLspRun(
|
||||
}
|
||||
}
|
||||
|
||||
onmessage = function (event) {
|
||||
// WebWorker message handler.
|
||||
onmessage = function (event: MessageEvent) {
|
||||
if (err(fromServer)) return
|
||||
const { worker, eventType, eventData }: LspWorkerEvent = event.data
|
||||
|
||||
@ -70,7 +71,7 @@ onmessage = function (event) {
|
||||
| KclWorkerOptions
|
||||
| CopilotWorkerOptions
|
||||
initialise(wasmUrl)
|
||||
.then((instantiatedModule) => {
|
||||
.then(async (instantiatedModule) => {
|
||||
console.log('Worker: WASM module loaded', worker, instantiatedModule)
|
||||
const config = new ServerConfig(
|
||||
intoServer,
|
||||
@ -81,7 +82,7 @@ onmessage = function (event) {
|
||||
switch (worker) {
|
||||
case LspWorker.Kcl:
|
||||
const kclData = eventData as KclWorkerOptions
|
||||
kclLspRun(
|
||||
await kclLspRun(
|
||||
config,
|
||||
null,
|
||||
kclData.token,
|
||||
@ -91,7 +92,11 @@ onmessage = function (event) {
|
||||
break
|
||||
case LspWorker.Copilot:
|
||||
let copilotData = eventData as CopilotWorkerOptions
|
||||
copilotLspRun(config, copilotData.token, copilotData.apiBaseUrl)
|
||||
await copilotLspRun(
|
||||
config,
|
||||
copilotData.token,
|
||||
copilotData.apiBaseUrl
|
||||
)
|
||||
break
|
||||
}
|
||||
})
|
||||
@ -104,7 +109,7 @@ onmessage = function (event) {
|
||||
intoServer.enqueue(data)
|
||||
const json: jsrpc.JSONRPCRequest = Codec.decode(data)
|
||||
if (null != json.id) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises, @typescript-eslint/no-non-null-assertion
|
||||
fromServer.responses.get(json.id)!.then((response) => {
|
||||
const encoded = Codec.encode(response as jsrpc.JSONRPCResponse)
|
||||
postMessage(encoded)
|
||||
@ -115,19 +120,17 @@ onmessage = function (event) {
|
||||
console.error('Worker: Unknown message type', worker, eventType)
|
||||
}
|
||||
}
|
||||
|
||||
new Promise<void>(async (resolve) => {
|
||||
;(async () => {
|
||||
if (err(fromServer)) return
|
||||
for await (const requests of fromServer.requests) {
|
||||
const encoded = Codec.encode(requests as jsrpc.JSONRPCRequest)
|
||||
postMessage(encoded)
|
||||
}
|
||||
})
|
||||
|
||||
new Promise<void>(async (resolve) => {
|
||||
})().catch(reportRejection)
|
||||
;(async () => {
|
||||
if (err(fromServer)) return
|
||||
for await (const notification of fromServer.notifications) {
|
||||
const encoded = Codec.encode(notification as jsrpc.JSONRPCRequest)
|
||||
postMessage(encoded)
|
||||
}
|
||||
})
|
||||
})().catch(reportRejection)
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
getSolid2dCodeRef,
|
||||
getWallCodeRef,
|
||||
} from 'lang/std/artifactGraph'
|
||||
import { err } from 'lib/trap'
|
||||
import { err, reportRejection } from 'lib/trap'
|
||||
import { DefaultPlaneStr, getFaceDetails } from 'clientSideScene/sceneEntities'
|
||||
import { getNodePathFromSourceRange } from 'lang/queryAst'
|
||||
|
||||
@ -86,9 +86,11 @@ export function useEngineConnectionSubscriptions() {
|
||||
})
|
||||
const unSubClick = engineCommandManager.subscribeTo({
|
||||
event: 'select_with_point',
|
||||
callback: async (engineEvent) => {
|
||||
callback: (engineEvent) => {
|
||||
;(async () => {
|
||||
const event = await getEventForSelectWithPoint(engineEvent)
|
||||
event && send(event)
|
||||
})().catch(reportRejection)
|
||||
},
|
||||
})
|
||||
return () => {
|
||||
@ -101,7 +103,8 @@ export function useEngineConnectionSubscriptions() {
|
||||
const unSub = engineCommandManager.subscribeTo({
|
||||
event: 'select_with_point',
|
||||
callback: state.matches('Sketch no face')
|
||||
? async ({ data }) => {
|
||||
? ({ data }) => {
|
||||
;(async () => {
|
||||
let planeOrFaceId = data.entity_id
|
||||
if (!planeOrFaceId) return
|
||||
if (
|
||||
@ -213,6 +216,7 @@ export function useEngineConnectionSubscriptions() {
|
||||
},
|
||||
})
|
||||
return
|
||||
})().catch(reportRejection)
|
||||
}
|
||||
: () => {},
|
||||
})
|
||||
|
@ -3,13 +3,14 @@ import {
|
||||
createSetVarNameModal,
|
||||
} from 'components/SetVarNameModal'
|
||||
import { editorManager, kclManager } from 'lib/singletons'
|
||||
import { trap } from 'lib/trap'
|
||||
import { reportRejection, trap } from 'lib/trap'
|
||||
import { moveValueIntoNewVariable } from 'lang/modifyAst'
|
||||
import { isNodeSafeToReplace } from 'lang/queryAst'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useModelingContext } from './useModelingContext'
|
||||
import { PathToNode, SourceRange } from 'lang/wasm'
|
||||
import { useKclContext } from 'lang/KclProvider'
|
||||
import { toSync } from 'lib/utils'
|
||||
|
||||
export const getVarNameModal = createSetVarNameModal(SetVarNameModal)
|
||||
|
||||
@ -62,7 +63,7 @@ export function useConvertToVariable(range?: SourceRange) {
|
||||
}
|
||||
}
|
||||
|
||||
editorManager.convertToVariableCallback = handleClick
|
||||
editorManager.convertToVariableCallback = toSync(handleClick, reportRejection)
|
||||
|
||||
return { enable, handleClick }
|
||||
}
|
||||
|
@ -129,8 +129,8 @@ export class KclManager {
|
||||
if (!isExecuting && this.executeIsStale) {
|
||||
const args = this.executeIsStale
|
||||
this.executeIsStale = null
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.executeAst(args)
|
||||
} else {
|
||||
}
|
||||
this._isExecutingCallback(isExecuting)
|
||||
}
|
||||
@ -154,6 +154,7 @@ export class KclManager {
|
||||
constructor(engineCommandManager: EngineCommandManager) {
|
||||
this.engineCommandManager = engineCommandManager
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.ensureWasmInit().then(() => {
|
||||
this.ast = this.safeParse(codeManager.code) || this.ast
|
||||
})
|
||||
@ -400,9 +401,11 @@ export class KclManager {
|
||||
// Update the code state and the editor.
|
||||
codeManager.updateCodeStateEditor(code)
|
||||
// Write back to the file system.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
codeManager.writeToFile()
|
||||
|
||||
// execute the code.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.executeCode()
|
||||
}
|
||||
// There's overlapping responsibility between updateAst and executeAst.
|
||||
@ -541,6 +544,7 @@ function defaultSelectionFilter(
|
||||
programMemory: ProgramMemory,
|
||||
engineCommandManager: EngineCommandManager
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
programMemory.hasSketchOrExtrudeGroup() &&
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
|
@ -64,6 +64,7 @@ export async function executeAst({
|
||||
try {
|
||||
if (!useFakeExecutor) {
|
||||
engineCommandManager.endSession()
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
engineCommandManager.startNewSession()
|
||||
}
|
||||
const programMemory = await (useFakeExecutor
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Selection } from 'lib/selections'
|
||||
import { err, trap } from 'lib/trap'
|
||||
import { err, reportRejection, trap } from 'lib/trap'
|
||||
import {
|
||||
Program,
|
||||
CallExpression,
|
||||
@ -938,7 +938,8 @@ export async function deleteFromSelection(
|
||||
const expressionIndex = pathToNode[1][0] as number
|
||||
astClone.body.splice(expressionIndex, 1)
|
||||
if (extrudeNameToDelete) {
|
||||
await new Promise(async (resolve) => {
|
||||
await new Promise((resolve) => {
|
||||
;(async () => {
|
||||
let currentVariableName = ''
|
||||
const pathsDependingOnExtrude: Array<{
|
||||
path: PathToNode
|
||||
@ -950,7 +951,8 @@ export async function deleteFromSelection(
|
||||
currentVariableName = ''
|
||||
}
|
||||
},
|
||||
enter: async (node, path) => {
|
||||
enter: (node, path) => {
|
||||
;(async () => {
|
||||
if (node.type === 'VariableDeclaration') {
|
||||
currentVariableName = node.declarations[0].id.name
|
||||
}
|
||||
@ -966,6 +968,7 @@ export async function deleteFromSelection(
|
||||
sketchName: currentVariableName,
|
||||
})
|
||||
}
|
||||
})().catch(reportRejection)
|
||||
},
|
||||
})
|
||||
const roundLiteral = (x: number) => createLiteral(roundOff(x))
|
||||
@ -1047,6 +1050,7 @@ export async function deleteFromSelection(
|
||||
])
|
||||
}
|
||||
resolve(true)
|
||||
})().catch(reportRejection)
|
||||
})
|
||||
}
|
||||
// await prom
|
||||
|
@ -36,7 +36,7 @@ beforeAll(async () => {
|
||||
setMediaStream: () => {},
|
||||
setIsStreamReady: () => {},
|
||||
modifyGrid: async () => {},
|
||||
callbackOnEngineLiteConnect: async () => {
|
||||
callbackOnEngineLiteConnect: () => {
|
||||
resolve(true)
|
||||
},
|
||||
})
|
||||
|
@ -56,6 +56,7 @@ export function applyFilletToSelection(
|
||||
const { modifiedAst, pathToFilletNode } = result
|
||||
|
||||
// 3. update ast
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
updateAstAndFocus(modifiedAst, pathToFilletNode)
|
||||
}
|
||||
|
||||
|
@ -124,6 +124,7 @@ beforeAll(async () => {
|
||||
setMediaStream: () => {},
|
||||
setIsStreamReady: () => {},
|
||||
modifyGrid: async () => {},
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
callbackOnEngineLiteConnect: async () => {
|
||||
const cacheEntries = Object.entries(codeToWriteCacheFor) as [
|
||||
CodeKey,
|
||||
|
@ -18,6 +18,7 @@ import toast from 'react-hot-toast'
|
||||
import { SettingsViaQueryString } from 'lib/settings/settingsTypes'
|
||||
import { EXECUTE_AST_INTERRUPT_ERROR_MESSAGE } from 'lib/constants'
|
||||
import { KclManager } from 'lang/KclSingleton'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
// TODO(paultag): This ought to be tweakable.
|
||||
const pingIntervalMs = 5_000
|
||||
@ -388,11 +389,12 @@ class EngineConnection extends EventTarget {
|
||||
default:
|
||||
if (this.isConnecting()) break
|
||||
// Means we never could do an initial connection. Reconnect everything.
|
||||
if (!this.pingPongSpan.ping) this.connect()
|
||||
if (!this.pingPongSpan.ping) this.connect().catch(reportRejection)
|
||||
break
|
||||
}
|
||||
}, pingIntervalMs)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.connect()
|
||||
}
|
||||
|
||||
@ -1464,6 +1466,7 @@ export class EngineCommandManager extends EventTarget {
|
||||
})
|
||||
)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
this.onEngineConnectionOpened = async () => {
|
||||
// Set the stream background color
|
||||
// This takes RGBA values from 0-1
|
||||
@ -1480,6 +1483,7 @@ export class EngineCommandManager extends EventTarget {
|
||||
|
||||
// Sets the default line colors
|
||||
const opposingTheme = getOppositeTheme(this.settings.theme)
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.sendSceneCommand({
|
||||
cmd_id: uuidv4(),
|
||||
type: 'modeling_cmd_req',
|
||||
@ -1490,6 +1494,7 @@ export class EngineCommandManager extends EventTarget {
|
||||
})
|
||||
|
||||
// Set the edge lines visibility
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
@ -1500,6 +1505,7 @@ export class EngineCommandManager extends EventTarget {
|
||||
})
|
||||
|
||||
this._camControlsCameraChange()
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.sendSceneCommand({
|
||||
// CameraControls subscribes to default_camera_get_settings response events
|
||||
// firing this at connection ensure the camera's are synced initially
|
||||
@ -1512,6 +1518,7 @@ export class EngineCommandManager extends EventTarget {
|
||||
// We want modify the grid first because we don't want it to flash.
|
||||
// Ideally these would already be default hidden in engine (TODO do
|
||||
// that) https://github.com/KittyCAD/engine/issues/2282
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.modifyGrid(!this.settings.showScaleGrid)?.then(async () => {
|
||||
await this.initPlanes()
|
||||
setIsStreamReady(true)
|
||||
@ -1715,6 +1722,7 @@ export class EngineCommandManager extends EventTarget {
|
||||
this.onEngineConnectionNewTrack as EventListener
|
||||
)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.engineConnection?.connect()
|
||||
}
|
||||
this.engineConnection.addEventListener(
|
||||
@ -2125,6 +2133,7 @@ export class EngineCommandManager extends EventTarget {
|
||||
* @param visible - whether to show or hide the scale grid
|
||||
*/
|
||||
setScaleGridVisibility(visible: boolean) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.modifyGrid(!visible)
|
||||
}
|
||||
|
||||
|
@ -360,6 +360,7 @@ export const executor = async (
|
||||
): Promise<ProgramMemory> => {
|
||||
if (err(programMemory)) return Promise.reject(programMemory)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
engineCommandManager.startNewSession()
|
||||
const _programMemory = await _executor(
|
||||
node,
|
||||
@ -569,6 +570,7 @@ export async function coreDump(
|
||||
a new GitHub issue for the user.
|
||||
*/
|
||||
if (openGithubIssue && dump.github_issue_url) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
openWindow(dump.github_issue_url)
|
||||
} else {
|
||||
console.error(
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { isDesktop } from './isDesktop'
|
||||
import { components } from './machine-api'
|
||||
import { reportRejection } from './trap'
|
||||
import { toSync } from './utils'
|
||||
|
||||
export type MachinesListing = Array<
|
||||
components['schemas']['MachineInfoResponse']
|
||||
@ -17,7 +19,7 @@ export class MachineManager {
|
||||
return
|
||||
}
|
||||
|
||||
this.updateMachines()
|
||||
this.updateMachines().catch(reportRejection)
|
||||
}
|
||||
|
||||
start() {
|
||||
@ -31,11 +33,14 @@ export class MachineManager {
|
||||
let timeoutId: ReturnType<typeof setTimeout> | undefined = undefined
|
||||
const timeoutLoop = () => {
|
||||
clearTimeout(timeoutId)
|
||||
timeoutId = setTimeout(async () => {
|
||||
timeoutId = setTimeout(
|
||||
toSync(async () => {
|
||||
await this.updateMachineApiIp()
|
||||
await this.updateMachines()
|
||||
timeoutLoop()
|
||||
}, 10000)
|
||||
}, reportRejection),
|
||||
10000
|
||||
)
|
||||
}
|
||||
timeoutLoop()
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
import { MouseEventHandler } from 'react'
|
||||
import { isDesktop } from 'lib/isDesktop'
|
||||
import { reportRejection } from './trap'
|
||||
|
||||
export const openExternalBrowserIfDesktop = (to?: string) =>
|
||||
function (e) {
|
||||
if (isDesktop()) {
|
||||
// Ignoring because currentTarget could be a few different things
|
||||
// @ts-ignore
|
||||
window.electron.openExternal(to || e.currentTarget?.href)
|
||||
window.electron
|
||||
.openExternal(to || e.currentTarget?.href)
|
||||
.catch(reportRejection)
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return false
|
||||
|
@ -16,6 +16,8 @@ import { isDesktop } from 'lib/isDesktop'
|
||||
import { useRef } from 'react'
|
||||
import { CustomIcon } from 'components/CustomIcon'
|
||||
import Tooltip from 'components/Tooltip'
|
||||
import { toSync } from 'lib/utils'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
/**
|
||||
* A setting that can be set at the user or project level
|
||||
@ -206,7 +208,7 @@ export function createSettings() {
|
||||
ref={inputRef}
|
||||
/>
|
||||
<button
|
||||
onClick={async () => {
|
||||
onClick={toSync(async () => {
|
||||
// In desktop end-to-end tests we can't control the file picker,
|
||||
// so we seed the new directory value in the element's dataset
|
||||
const inputRefVal = inputRef.current?.dataset.testValue
|
||||
@ -225,7 +227,7 @@ export function createSettings() {
|
||||
if (newPath.canceled) return
|
||||
updateValue(newPath.filePaths[0])
|
||||
}
|
||||
}}
|
||||
}, reportRejection)}
|
||||
className="p-0 m-0 border-none hover:bg-primary/10 focus:bg-primary/10 dark:hover:bg-primary/20 dark:focus::bg-primary/20"
|
||||
data-testid="project-directory-button"
|
||||
>
|
||||
|
@ -7,7 +7,8 @@ import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
||||
import { err } from 'lib/trap'
|
||||
import { err, reportRejection } from 'lib/trap'
|
||||
import { toSync } from './utils'
|
||||
|
||||
type WebSocketResponse = Models['WebSocketResponse_type']
|
||||
|
||||
@ -85,6 +86,7 @@ export async function enginelessExecutor(
|
||||
setIsStreamReady: () => {},
|
||||
setMediaStream: () => {},
|
||||
}) as any as EngineCommandManager
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
mockEngineCommandManager.startNewSession()
|
||||
const programMemory = await _executor(ast, pm, mockEngineCommandManager, true)
|
||||
await mockEngineCommandManager.waitForAllCommands()
|
||||
@ -112,7 +114,8 @@ export async function executor(
|
||||
return new Promise((resolve) => {
|
||||
engineCommandManager.addEventListener(
|
||||
EngineCommandManagerEvents.SceneReady,
|
||||
async () => {
|
||||
toSync(async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
engineCommandManager.startNewSession()
|
||||
const programMemory = await _executor(
|
||||
ast,
|
||||
@ -121,8 +124,8 @@ export async function executor(
|
||||
false
|
||||
)
|
||||
await engineCommandManager.waitForAllCommands()
|
||||
Promise.resolve(programMemory)
|
||||
}
|
||||
resolve(programMemory)
|
||||
}, reportRejection)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -14,6 +14,8 @@ import { isDesktop } from 'lib/isDesktop'
|
||||
import { Themes } from './theme'
|
||||
import { commandBarMachine } from 'machines/commandBarMachine'
|
||||
import { getNextFileName } from './desktopFS'
|
||||
import { reportRejection } from './trap'
|
||||
import { toSync } from './utils'
|
||||
|
||||
export async function submitTextToCadPrompt(
|
||||
prompt: string,
|
||||
@ -128,7 +130,8 @@ export async function submitAndAwaitTextToKcl({
|
||||
// Check the status of the text-to-cad API job
|
||||
// until it is completed
|
||||
const textToCadComplete = new Promise<Models['TextToCad_type']>(
|
||||
async (resolve, reject) => {
|
||||
(resolve, reject) => {
|
||||
;(async () => {
|
||||
const value = await textToCadQueued
|
||||
if (value instanceof Error) {
|
||||
reject(value)
|
||||
@ -138,7 +141,8 @@ export async function submitAndAwaitTextToKcl({
|
||||
const CHECK_INTERVAL = 3000
|
||||
|
||||
let timeElapsed = 0
|
||||
const interval = setInterval(async () => {
|
||||
const interval = setInterval(
|
||||
toSync(async () => {
|
||||
timeElapsed += CHECK_INTERVAL
|
||||
if (timeElapsed >= MAX_CHECK_TIMEOUT) {
|
||||
clearInterval(interval)
|
||||
@ -158,7 +162,10 @@ export async function submitAndAwaitTextToKcl({
|
||||
clearInterval(interval)
|
||||
resolve(check)
|
||||
}
|
||||
}, CHECK_INTERVAL)
|
||||
}, reportRejection),
|
||||
CHECK_INTERVAL
|
||||
)
|
||||
})().catch(reportRejection)
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -2,13 +2,22 @@ import toast from 'react-hot-toast'
|
||||
|
||||
type ExcludeErr<T> = Exclude<T, Error>
|
||||
|
||||
/**
|
||||
* This is intentionally *not* exported due to misuse. We'd like to add a lint.
|
||||
*/
|
||||
function isErr<T>(value: ExcludeErr<T> | Error): value is Error {
|
||||
return value instanceof Error
|
||||
}
|
||||
|
||||
// Used to bubble errors up
|
||||
export function err<T>(value: ExcludeErr<T> | Error): value is Error {
|
||||
if (!(value instanceof Error)) {
|
||||
if (!isErr(value)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO: Remove this once we have a lint to prevent misuse of this function.
|
||||
console.error(value)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -21,7 +30,7 @@ export function cleanErrs<T>(
|
||||
const argsWOutErr: Array<ExcludeErr<T>> = []
|
||||
const argsWErr: Array<Error> = []
|
||||
for (const v of value) {
|
||||
if (err(v)) {
|
||||
if (isErr(v)) {
|
||||
argsWErr.push(v)
|
||||
} else {
|
||||
argsWOutErr.push(v)
|
||||
@ -30,9 +39,28 @@ export function cleanErrs<T>(
|
||||
return [argsWOutErr.length !== value.length, argsWOutErr, argsWErr]
|
||||
}
|
||||
|
||||
export function report(
|
||||
message: string,
|
||||
{ showToast }: { showToast: boolean } = { showToast: false }
|
||||
) {
|
||||
console.error(message)
|
||||
if (showToast) {
|
||||
toast.error(message, { id: 'error' })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to report errors to user at a certain point in execution
|
||||
* @returns boolean
|
||||
* Report a promise rejection. The type of reason is `any` so that it matches
|
||||
* Promise.prototype.catch.
|
||||
*/
|
||||
export function reportRejection(reason: any) {
|
||||
report((reason ?? 'Unknown promise rejection').toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Report an error to the user. Trapping is the opposite of propagating an
|
||||
* error. We should propagate errors in low-level functions and trap at the top
|
||||
* level.
|
||||
*/
|
||||
export function trap<T>(
|
||||
value: ExcludeErr<T> | Error,
|
||||
@ -41,7 +69,7 @@ export function trap<T>(
|
||||
suppress?: boolean
|
||||
}
|
||||
): value is Error {
|
||||
if (!err(value)) {
|
||||
if (!isErr(value)) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -98,3 +98,18 @@ export function isEnumMember<T extends Record<string, unknown>>(
|
||||
export type DeepPartial<T> = {
|
||||
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace a function's return type with another type.
|
||||
*/
|
||||
export type WithReturnType<F extends (...args: any[]) => any, NewReturn> = (
|
||||
...args: Parameters<F>
|
||||
) => NewReturn
|
||||
|
||||
/**
|
||||
* Assert that a function type is async, preserving its parameter types.
|
||||
*/
|
||||
export type AsyncFn<F extends (...args: any[]) => any> = WithReturnType<
|
||||
F,
|
||||
Promise<unknown>
|
||||
>
|
||||
|
@ -3,6 +3,7 @@ import { SourceRange } from '../lang/wasm'
|
||||
import { v4 } from 'uuid'
|
||||
import { isDesktop } from './isDesktop'
|
||||
import { AnyMachineSnapshot } from 'xstate'
|
||||
import { AsyncFn } from './types'
|
||||
|
||||
export const uuidv4 = v4
|
||||
|
||||
@ -106,6 +107,28 @@ export function deferExecution<T>(func: (args: T) => any, wait: number) {
|
||||
return deferred
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap an async function so that it can be called in a sync context, catching
|
||||
* rejections.
|
||||
*
|
||||
* It's common to want to run an async function in a sync context, like an event
|
||||
* handler or callback. But we want to catch errors.
|
||||
*
|
||||
* Note: The returned function doesn't block. This isn't magic.
|
||||
*
|
||||
* @param onReject This callback type is from Promise.prototype.catch.
|
||||
*/
|
||||
export function toSync<F extends AsyncFn<F>>(
|
||||
fn: F,
|
||||
onReject: (
|
||||
reason: any
|
||||
) => void | PromiseLike<void | null | undefined> | null | undefined
|
||||
): (...args: Parameters<F>) => void {
|
||||
return (...args: Parameters<F>) => {
|
||||
fn(...args).catch(onReject)
|
||||
}
|
||||
}
|
||||
|
||||
export function getNormalisedCoordinates({
|
||||
clientX,
|
||||
clientY,
|
||||
|
@ -116,6 +116,7 @@ export const authMachine = setup({
|
||||
'Log out': {
|
||||
target: 'loggedOut',
|
||||
actions: () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
if (isDesktop()) writeTokenFile('')
|
||||
},
|
||||
},
|
||||
@ -219,6 +220,7 @@ async function getAndSyncStoredToken(input: {
|
||||
if (token) {
|
||||
// has just logged in, update storage
|
||||
localStorage.setItem(TOKEN_PERSIST_KEY, token)
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
isDesktop() && writeTokenFile(token)
|
||||
return token
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ import {
|
||||
applyConstraintAxisAlign,
|
||||
} from 'components/Toolbar/SetAbsDistance'
|
||||
import { ModelingCommandSchema } from 'lib/commandBarConfigs/modelingCommandConfig'
|
||||
import { err, trap } from 'lib/trap'
|
||||
import { err, reportRejection, trap } from 'lib/trap'
|
||||
import { DefaultPlaneStr, getFaceDetails } from 'clientSideScene/sceneEntities'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import { Coords2d } from 'lang/std/sketch'
|
||||
@ -492,13 +492,15 @@ export const modelingMachine = setup({
|
||||
}
|
||||
}
|
||||
),
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
'hide default planes': () => kclManager.hidePlanes(),
|
||||
'reset sketch metadata': assign({
|
||||
sketchDetails: null,
|
||||
sketchEnginePathId: '',
|
||||
sketchPlaneId: '',
|
||||
}),
|
||||
'reset camera position': () =>
|
||||
'reset camera position': () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
@ -508,7 +510,8 @@ export const modelingMachine = setup({
|
||||
vantage: { x: 0, y: -1250, z: 580 },
|
||||
up: { x: 0, y: 0, z: 1 },
|
||||
},
|
||||
}),
|
||||
})
|
||||
},
|
||||
'set new sketch metadata': assign(({ event }) => {
|
||||
if (
|
||||
event.type !== 'xstate.done.actor.animate-to-sketch' &&
|
||||
@ -519,8 +522,9 @@ export const modelingMachine = setup({
|
||||
sketchDetails: event.output,
|
||||
}
|
||||
}),
|
||||
'AST extrude': async ({ context: { store }, event }) => {
|
||||
'AST extrude': ({ context: { store }, event }) => {
|
||||
if (event.type !== 'Extrude') return
|
||||
;(async () => {
|
||||
if (!event.data) return
|
||||
const { selection, distance } = event.data
|
||||
let ast = kclManager.ast
|
||||
@ -530,7 +534,11 @@ export const modelingMachine = setup({
|
||||
distance.insertIndex !== undefined
|
||||
) {
|
||||
const newBody = [...ast.body]
|
||||
newBody.splice(distance.insertIndex, 0, distance.variableDeclarationAst)
|
||||
newBody.splice(
|
||||
distance.insertIndex,
|
||||
0,
|
||||
distance.variableDeclarationAst
|
||||
)
|
||||
ast.body = newBody
|
||||
}
|
||||
const pathToNode = getNodePathFromSourceRange(
|
||||
@ -565,8 +573,10 @@ export const modelingMachine = setup({
|
||||
if (updatedAst?.selections) {
|
||||
editorManager.selectRange(updatedAst?.selections)
|
||||
}
|
||||
})().catch(reportRejection)
|
||||
},
|
||||
'AST delete selection': async ({ context: { selectionRanges } }) => {
|
||||
'AST delete selection': ({ context: { selectionRanges } }) => {
|
||||
;(async () => {
|
||||
let ast = kclManager.ast
|
||||
|
||||
const modifiedAst = await deleteFromSelection(
|
||||
@ -588,8 +598,9 @@ export const modelingMachine = setup({
|
||||
}
|
||||
|
||||
await kclManager.updateAst(modifiedAst, true)
|
||||
})().catch(reportRejection)
|
||||
},
|
||||
'AST fillet': async ({ event }) => {
|
||||
'AST fillet': ({ event }) => {
|
||||
if (event.type !== 'Fillet') return
|
||||
if (!event.data) return
|
||||
|
||||
@ -635,16 +646,18 @@ export const modelingMachine = setup({
|
||||
up: sketchDetails.yAxis,
|
||||
position: sketchDetails.origin,
|
||||
})
|
||||
})()
|
||||
})().catch(reportRejection)
|
||||
},
|
||||
'tear down client sketch': () => {
|
||||
if (sceneEntitiesManager.activeSegments) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
sceneEntitiesManager.tearDownSketch({ removeAxis: false })
|
||||
}
|
||||
},
|
||||
'remove sketch grid': () => sceneEntitiesManager.removeSketchGrid(),
|
||||
'set up draft line': ({ context: { sketchDetails } }) => {
|
||||
if (!sketchDetails) return
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
sceneEntitiesManager.setUpDraftSegment(
|
||||
sketchDetails.sketchPathToNode,
|
||||
sketchDetails.zAxis,
|
||||
@ -655,6 +668,7 @@ export const modelingMachine = setup({
|
||||
},
|
||||
'set up draft arc': ({ context: { sketchDetails } }) => {
|
||||
if (!sketchDetails) return
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
sceneEntitiesManager.setUpDraftSegment(
|
||||
sketchDetails.sketchPathToNode,
|
||||
sketchDetails.zAxis,
|
||||
@ -683,6 +697,7 @@ export const modelingMachine = setup({
|
||||
'set up draft rectangle': ({ context: { sketchDetails }, event }) => {
|
||||
if (event.type !== 'Add rectangle origin') return
|
||||
if (!sketchDetails || !event.data) return
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
sceneEntitiesManager.setupDraftRectangle(
|
||||
sketchDetails.sketchPathToNode,
|
||||
sketchDetails.zAxis,
|
||||
@ -693,6 +708,7 @@ export const modelingMachine = setup({
|
||||
},
|
||||
'set up draft line without teardown': ({ context: { sketchDetails } }) => {
|
||||
if (!sketchDetails) return
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
sceneEntitiesManager.setUpDraftSegment(
|
||||
sketchDetails.sketchPathToNode,
|
||||
sketchDetails.zAxis,
|
||||
@ -702,7 +718,10 @@ export const modelingMachine = setup({
|
||||
false
|
||||
)
|
||||
},
|
||||
'show default planes': () => kclManager.showPlanes(),
|
||||
'show default planes': () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
kclManager.showPlanes()
|
||||
},
|
||||
'setup noPoints onClick listener': ({ context: { sketchDetails } }) => {
|
||||
if (!sketchDetails) return
|
||||
|
||||
@ -732,7 +751,8 @@ export const modelingMachine = setup({
|
||||
'engineToClient cam sync direction': () => {
|
||||
sceneInfra.camControls.syncDirection = 'engineToClient'
|
||||
},
|
||||
'set selection filter to faces only': () =>
|
||||
'set selection filter to faces only': () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
@ -740,13 +760,15 @@ export const modelingMachine = setup({
|
||||
type: 'set_selection_filter',
|
||||
filter: ['face', 'object'],
|
||||
},
|
||||
}),
|
||||
})
|
||||
},
|
||||
'set selection filter to defaults': () =>
|
||||
kclManager.defaultSelectionFilter(),
|
||||
'Delete segment': ({ context: { sketchDetails }, event }) => {
|
||||
if (event.type !== 'Delete segment') return
|
||||
if (!sketchDetails || !event.data) return
|
||||
return deleteSegment({
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
deleteSegment({
|
||||
pathToNode: event.data,
|
||||
sketchDetails,
|
||||
})
|
||||
|
18
src/main.ts
18
src/main.ts
@ -19,6 +19,7 @@ import electronUpdater, { type AppUpdater } from 'electron-updater'
|
||||
import minimist from 'minimist'
|
||||
import getCurrentProjectFile from 'lib/getCurrentProjectFile'
|
||||
import os from 'node:os'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
let mainWindow: BrowserWindow | null = null
|
||||
|
||||
@ -87,16 +88,17 @@ const createWindow = (filePath?: string): BrowserWindow => {
|
||||
|
||||
// and load the index.html of the app.
|
||||
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
|
||||
newWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL)
|
||||
newWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL).catch(reportRejection)
|
||||
} else {
|
||||
getProjectPathAtStartup(filePath).then((projectPath) => {
|
||||
getProjectPathAtStartup(filePath)
|
||||
.then(async (projectPath) => {
|
||||
const startIndex = path.join(
|
||||
__dirname,
|
||||
`../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`
|
||||
)
|
||||
|
||||
if (projectPath === null) {
|
||||
newWindow.loadFile(startIndex)
|
||||
await newWindow.loadFile(startIndex)
|
||||
return
|
||||
}
|
||||
|
||||
@ -105,10 +107,11 @@ const createWindow = (filePath?: string): BrowserWindow => {
|
||||
const fullUrl = `/file/${encodeURIComponent(projectPath)}`
|
||||
console.log('Full URL', fullUrl)
|
||||
|
||||
newWindow.loadFile(startIndex, {
|
||||
await newWindow.loadFile(startIndex, {
|
||||
hash: fullUrl,
|
||||
})
|
||||
})
|
||||
.catch(reportRejection)
|
||||
}
|
||||
|
||||
// Open the DevTools.
|
||||
@ -175,6 +178,7 @@ ipcMain.handle('login', async (event, host) => {
|
||||
|
||||
const handle = await client.deviceAuthorization()
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
shell.openExternal(handle.verification_uri_complete)
|
||||
|
||||
// Wait for the user to login.
|
||||
@ -241,12 +245,12 @@ export async function checkForUpdates(autoUpdater: AppUpdater) {
|
||||
console.log(result)
|
||||
}
|
||||
|
||||
app.on('ready', async () => {
|
||||
app.on('ready', () => {
|
||||
const autoUpdater = getAutoUpdater()
|
||||
checkForUpdates(autoUpdater)
|
||||
checkForUpdates(autoUpdater).catch(reportRejection)
|
||||
const fifteenMinutes = 15 * 60 * 1000
|
||||
setInterval(() => {
|
||||
checkForUpdates(autoUpdater)
|
||||
checkForUpdates(autoUpdater).catch(reportRejection)
|
||||
}, fifteenMinutes)
|
||||
|
||||
autoUpdater.on('update-available', (info) => {
|
||||
|
@ -1,14 +1,17 @@
|
||||
import { reportRejection } from 'lib/trap'
|
||||
import { ReportHandler } from 'web-vitals'
|
||||
|
||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
import('web-vitals')
|
||||
.then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry)
|
||||
getFID(onPerfEntry)
|
||||
getFCP(onPerfEntry)
|
||||
getLCP(onPerfEntry)
|
||||
getTTFB(onPerfEntry)
|
||||
})
|
||||
.catch(reportRejection)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ export default function FutureWork() {
|
||||
useDemoCode()
|
||||
useEffect(() => {
|
||||
send({ type: 'Cancel' }) // in case the user hit 'Next' while still in sketch mode
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
sceneInfra.camControls.resetCameraPosition()
|
||||
}, [send])
|
||||
|
||||
|
@ -13,6 +13,8 @@ import { IndexLoaderData } from 'lib/types'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { useFileContext } from 'hooks/useFileContext'
|
||||
import { useLspContext } from 'components/LspProvider'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
import { toSync } from 'lib/utils'
|
||||
|
||||
/**
|
||||
* Show either a welcome screen or a warning screen
|
||||
@ -80,7 +82,7 @@ function OnboardingWarningDesktop(props: OnboardingResetWarningProps) {
|
||||
<OnboardingButtons
|
||||
className="mt-6"
|
||||
dismiss={dismiss}
|
||||
next={onAccept}
|
||||
next={toSync(onAccept, reportRejection)}
|
||||
nextText="Make a new project"
|
||||
/>
|
||||
</>
|
||||
@ -102,14 +104,14 @@ function OnboardingWarningWeb(props: OnboardingResetWarningProps) {
|
||||
<OnboardingButtons
|
||||
className="mt-6"
|
||||
dismiss={dismiss}
|
||||
next={async () => {
|
||||
next={toSync(async () => {
|
||||
// We do want to update both the state and editor here.
|
||||
codeManager.updateCodeStateEditor(bracket)
|
||||
await codeManager.writeToFile()
|
||||
|
||||
await kclManager.executeCode(true)
|
||||
props.setShouldShowWarning(false)
|
||||
}}
|
||||
}, reportRejection)}
|
||||
nextText="Overwrite code and continue"
|
||||
/>
|
||||
</>
|
||||
|
@ -16,6 +16,7 @@ export default function Sketching() {
|
||||
await kclManager.executeCode(true)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
clearEditor()
|
||||
}, [])
|
||||
|
||||
|
@ -21,6 +21,8 @@ import { ActionButton } from 'components/ActionButton'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { codeManager, editorManager, kclManager } from 'lib/singletons'
|
||||
import { bracket } from 'lib/exampleKcl'
|
||||
import { toSync } from 'lib/utils'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
export const kbdClasses =
|
||||
'py-0.5 px-1 text-sm rounded bg-chalkboard-10 dark:bg-chalkboard-100 border border-chalkboard-50 border-b-2'
|
||||
@ -80,11 +82,13 @@ export const onboardingRoutes = [
|
||||
export function useDemoCode() {
|
||||
useEffect(() => {
|
||||
if (!editorManager.editorView || codeManager.code === bracket) return
|
||||
setTimeout(async () => {
|
||||
setTimeout(
|
||||
toSync(async () => {
|
||||
codeManager.updateCodeStateEditor(bracket)
|
||||
await kclManager.executeCode(true)
|
||||
await codeManager.writeToFile()
|
||||
})
|
||||
}, reportRejection)
|
||||
)
|
||||
}, [editorManager.editorView])
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,8 @@ import { CustomIcon } from 'components/CustomIcon'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { APP_VERSION } from './Settings'
|
||||
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
||||
import { toSync } from 'lib/utils'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
|
||||
const subtleBorder =
|
||||
'border border-solid border-chalkboard-30 dark:border-chalkboard-80'
|
||||
@ -104,7 +106,7 @@ const SignIn = () => {
|
||||
</p>
|
||||
{isDesktop() ? (
|
||||
<button
|
||||
onClick={signInDesktop}
|
||||
onClick={toSync(signInDesktop, reportRejection)}
|
||||
className={
|
||||
'm-0 mt-8 flex gap-4 items-center px-3 py-1 ' +
|
||||
'!border-transparent !text-lg !text-chalkboard-10 !bg-primary hover:hue-rotate-15'
|
||||
|
Reference in New Issue
Block a user