Merge remote-tracking branch 'origin' into kurt-circle
This commit is contained in:
		
							
								
								
									
										12
									
								
								.github/workflows/playwright.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.github/workflows/playwright.yml
									
									
									
									
										vendored
									
									
								
							@ -139,7 +139,7 @@ jobs:
 | 
			
		||||
    - uses: actions/upload-artifact@v4
 | 
			
		||||
      if: ${{ !cancelled() && (success() || failure()) }}
 | 
			
		||||
      with:
 | 
			
		||||
        name: playwright-report-ubuntu-snapshot-${{ matrix.shardIndex }}-${{ github.sha }}
 | 
			
		||||
        name: playwright-report-${{ matrix.os }}-snapshot-${{ matrix.shardIndex }}-${{ github.sha }}
 | 
			
		||||
        path: playwright-report/
 | 
			
		||||
        retention-days: 30
 | 
			
		||||
        overwrite: true
 | 
			
		||||
@ -174,14 +174,14 @@ jobs:
 | 
			
		||||
    - uses: actions/upload-artifact@v4
 | 
			
		||||
      if: steps.git-check.outputs.modified == 'true'
 | 
			
		||||
      with:
 | 
			
		||||
        name: playwright-report-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
 | 
			
		||||
        name: playwright-report-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
 | 
			
		||||
        path: playwright-report/
 | 
			
		||||
        retention-days: 30
 | 
			
		||||
    - uses: actions/download-artifact@v4
 | 
			
		||||
      if: ${{ !cancelled() && (success() || failure()) }}
 | 
			
		||||
      continue-on-error: true
 | 
			
		||||
      with:
 | 
			
		||||
        name: test-results-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
 | 
			
		||||
        name: test-results-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
 | 
			
		||||
        path: test-results/
 | 
			
		||||
    - name: Run playwright/chrome flow (with retries)
 | 
			
		||||
      id: retry
 | 
			
		||||
@ -244,14 +244,14 @@ jobs:
 | 
			
		||||
    - uses: actions/upload-artifact@v4
 | 
			
		||||
      if: always()
 | 
			
		||||
      with:
 | 
			
		||||
        name: test-results-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
 | 
			
		||||
        name: test-results-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
 | 
			
		||||
        path: test-results/
 | 
			
		||||
        retention-days: 30
 | 
			
		||||
        overwrite: true
 | 
			
		||||
    - uses: actions/upload-artifact@v4
 | 
			
		||||
      if: always()
 | 
			
		||||
      with:
 | 
			
		||||
        name: playwright-report-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
 | 
			
		||||
        name: playwright-report-${{ matrix.os }}-${{ matrix.shardIndex }}-${{ github.sha }}
 | 
			
		||||
        path: playwright-report/
 | 
			
		||||
        retention-days: 30
 | 
			
		||||
        overwrite: true
 | 
			
		||||
@ -351,7 +351,7 @@ jobs:
 | 
			
		||||
      if: ${{ !cancelled() && (success() || failure()) }}
 | 
			
		||||
      continue-on-error: true
 | 
			
		||||
      with:
 | 
			
		||||
        name: test-results-ubuntu-${{ github.sha }}
 | 
			
		||||
        name: test-results-${{ matrix.os }}-${{ github.sha }}
 | 
			
		||||
        path: test-results/
 | 
			
		||||
    - name: Run electron tests (with retries)
 | 
			
		||||
      id: retry
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										204
									
								
								e2e/playwright/file-tree.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								e2e/playwright/file-tree.spec.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,204 @@
 | 
			
		||||
import { test, expect } from '@playwright/test'
 | 
			
		||||
import * as fsp from 'fs/promises'
 | 
			
		||||
import { getUtils, setup, setupElectron, tearDown } from './test-utils'
 | 
			
		||||
 | 
			
		||||
test.beforeEach(async ({ context, page }) => {
 | 
			
		||||
  await setup(context, page)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
test.afterEach(async ({ page }, testInfo) => {
 | 
			
		||||
  await tearDown(page, testInfo)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
test.describe('when using the file tree to', () => {
 | 
			
		||||
  const fromFile = 'main.kcl'
 | 
			
		||||
  const toFile = 'hello.kcl'
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `rename ${fromFile} to ${toFile}, and doesn't crash on reload and settings load`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    async ({ browser: _ }, testInfo) => {
 | 
			
		||||
      const { electronApp, page } = await setupElectron({
 | 
			
		||||
        testInfo,
 | 
			
		||||
        folderSetupFn: async () => {},
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      const {
 | 
			
		||||
        panesOpen,
 | 
			
		||||
        createAndSelectProject,
 | 
			
		||||
        pasteCodeInEditor,
 | 
			
		||||
        renameFile,
 | 
			
		||||
        editorTextMatches,
 | 
			
		||||
      } = await getUtils(page, test)
 | 
			
		||||
 | 
			
		||||
      await page.setViewportSize({ width: 1200, height: 500 })
 | 
			
		||||
      page.on('console', console.log)
 | 
			
		||||
 | 
			
		||||
      await panesOpen(['files', 'code'])
 | 
			
		||||
 | 
			
		||||
      await createAndSelectProject('project-000')
 | 
			
		||||
 | 
			
		||||
      // File the main.kcl with contents
 | 
			
		||||
      const kclCube = await fsp.readFile(
 | 
			
		||||
        'src/wasm-lib/tests/executor/inputs/cube.kcl',
 | 
			
		||||
        'utf-8'
 | 
			
		||||
      )
 | 
			
		||||
      await pasteCodeInEditor(kclCube)
 | 
			
		||||
 | 
			
		||||
      await renameFile(fromFile, toFile)
 | 
			
		||||
      await page.reload()
 | 
			
		||||
 | 
			
		||||
      await test.step('Postcondition: editor has same content as before the rename', async () => {
 | 
			
		||||
        await editorTextMatches(kclCube)
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      await test.step('Postcondition: opening and closing settings works', async () => {
 | 
			
		||||
        const settingsOpenButton = page.getByRole('link', {
 | 
			
		||||
          name: 'settings Settings',
 | 
			
		||||
        })
 | 
			
		||||
        const settingsCloseButton = page.getByTestId('settings-close-button')
 | 
			
		||||
        await settingsOpenButton.click()
 | 
			
		||||
        await settingsCloseButton.click()
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      await electronApp.close()
 | 
			
		||||
    }
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    `create many new untitled files they increment their names`,
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    async ({ browser: _ }, testInfo) => {
 | 
			
		||||
      const { electronApp, page } = await setupElectron({
 | 
			
		||||
        testInfo,
 | 
			
		||||
        folderSetupFn: async () => {},
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      const { panesOpen, createAndSelectProject, createNewFile } =
 | 
			
		||||
        await getUtils(page, test)
 | 
			
		||||
 | 
			
		||||
      await page.setViewportSize({ width: 1200, height: 500 })
 | 
			
		||||
      page.on('console', console.log)
 | 
			
		||||
 | 
			
		||||
      await panesOpen(['files'])
 | 
			
		||||
 | 
			
		||||
      await createAndSelectProject('project-000')
 | 
			
		||||
 | 
			
		||||
      await createNewFile('')
 | 
			
		||||
      await createNewFile('')
 | 
			
		||||
      await createNewFile('')
 | 
			
		||||
      await createNewFile('')
 | 
			
		||||
      await createNewFile('')
 | 
			
		||||
 | 
			
		||||
      await test.step('Postcondition: there are 5 new Untitled-*.kcl files', async () => {
 | 
			
		||||
        await expect(
 | 
			
		||||
          page
 | 
			
		||||
            .locator('[data-testid="file-pane-scroll-container"] button')
 | 
			
		||||
            .filter({ hasText: /Untitled[-]?[0-5]?/ })
 | 
			
		||||
        ).toHaveCount(5)
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      await electronApp.close()
 | 
			
		||||
    }
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'create a new file with the same name as an existing file cancels the operation',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    async ({ browser: _ }, testInfo) => {
 | 
			
		||||
      const { electronApp, page } = await setupElectron({
 | 
			
		||||
        testInfo,
 | 
			
		||||
        folderSetupFn: async () => {},
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      const {
 | 
			
		||||
        panesOpen,
 | 
			
		||||
        createAndSelectProject,
 | 
			
		||||
        pasteCodeInEditor,
 | 
			
		||||
        createNewFileAndSelect,
 | 
			
		||||
        renameFile,
 | 
			
		||||
        selectFile,
 | 
			
		||||
        editorTextMatches,
 | 
			
		||||
      } = await getUtils(page, test)
 | 
			
		||||
 | 
			
		||||
      await page.setViewportSize({ width: 1200, height: 500 })
 | 
			
		||||
      page.on('console', console.log)
 | 
			
		||||
 | 
			
		||||
      await panesOpen(['files', 'code'])
 | 
			
		||||
 | 
			
		||||
      await createAndSelectProject('project-000')
 | 
			
		||||
      // File the main.kcl with contents
 | 
			
		||||
      const kclCube = await fsp.readFile(
 | 
			
		||||
        'src/wasm-lib/tests/executor/inputs/cube.kcl',
 | 
			
		||||
        'utf-8'
 | 
			
		||||
      )
 | 
			
		||||
      await pasteCodeInEditor(kclCube)
 | 
			
		||||
 | 
			
		||||
      const kcl1 = 'main.kcl'
 | 
			
		||||
      const kcl2 = '2.kcl'
 | 
			
		||||
 | 
			
		||||
      await createNewFileAndSelect(kcl2)
 | 
			
		||||
      const kclCylinder = await fsp.readFile(
 | 
			
		||||
        'src/wasm-lib/tests/executor/inputs/cylinder.kcl',
 | 
			
		||||
        'utf-8'
 | 
			
		||||
      )
 | 
			
		||||
      await pasteCodeInEditor(kclCylinder)
 | 
			
		||||
 | 
			
		||||
      await renameFile(kcl2, kcl1)
 | 
			
		||||
 | 
			
		||||
      await test.step(`Postcondition: ${kcl1} still has the original content`, async () => {
 | 
			
		||||
        await selectFile(kcl1)
 | 
			
		||||
        await editorTextMatches(kclCube)
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      await test.step(`Postcondition: ${kcl2} still exists with the original content`, async () => {
 | 
			
		||||
        await selectFile(kcl2)
 | 
			
		||||
        await editorTextMatches(kclCylinder)
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      await electronApp.close()
 | 
			
		||||
    }
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  test(
 | 
			
		||||
    'deleting all files recreates a default main.kcl with no code',
 | 
			
		||||
    { tag: '@electron' },
 | 
			
		||||
    async ({ browser: _ }, testInfo) => {
 | 
			
		||||
      const { electronApp, page } = await setupElectron({
 | 
			
		||||
        testInfo,
 | 
			
		||||
        folderSetupFn: async () => {},
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      const {
 | 
			
		||||
        panesOpen,
 | 
			
		||||
        createAndSelectProject,
 | 
			
		||||
        pasteCodeInEditor,
 | 
			
		||||
        deleteFile,
 | 
			
		||||
        editorTextMatches,
 | 
			
		||||
      } = await getUtils(page, test)
 | 
			
		||||
 | 
			
		||||
      await page.setViewportSize({ width: 1200, height: 500 })
 | 
			
		||||
      page.on('console', console.log)
 | 
			
		||||
 | 
			
		||||
      await panesOpen(['files', 'code'])
 | 
			
		||||
 | 
			
		||||
      await createAndSelectProject('project-000')
 | 
			
		||||
      // File the main.kcl with contents
 | 
			
		||||
      const kclCube = await fsp.readFile(
 | 
			
		||||
        'src/wasm-lib/tests/executor/inputs/cube.kcl',
 | 
			
		||||
        'utf-8'
 | 
			
		||||
      )
 | 
			
		||||
      await pasteCodeInEditor(kclCube)
 | 
			
		||||
 | 
			
		||||
      const kcl1 = 'main.kcl'
 | 
			
		||||
 | 
			
		||||
      await deleteFile(kcl1)
 | 
			
		||||
 | 
			
		||||
      await test.step(`Postcondition: ${kcl1} is recreated but has no content`, async () => {
 | 
			
		||||
        await editorTextMatches('')
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      await electronApp.close()
 | 
			
		||||
    }
 | 
			
		||||
  )
 | 
			
		||||
})
 | 
			
		||||
@ -511,10 +511,7 @@ export async function getUtils(page: Page, test_?: typeof test) {
 | 
			
		||||
 | 
			
		||||
    editorTextMatches: async (code: string) => {
 | 
			
		||||
      const editor = page.locator(editorSelector)
 | 
			
		||||
      const editorText = await editor.textContent()
 | 
			
		||||
      return expect(util.toNormalizedCode(editorText || '')).toBe(
 | 
			
		||||
        util.toNormalizedCode(code)
 | 
			
		||||
      )
 | 
			
		||||
      return expect(editor).toHaveText(code, { useInnerText: true })
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    pasteCodeInEditor: async (code: string) => {
 | 
			
		||||
@ -532,18 +529,62 @@ export async function getUtils(page: Page, test_?: typeof test) {
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createNewFile: async (name: string) => {
 | 
			
		||||
      return test?.step(`Create a file named ${name}`, async () => {
 | 
			
		||||
        await page.getByTestId('create-file-button').click()
 | 
			
		||||
        await page.getByTestId('file-rename-field').fill(name)
 | 
			
		||||
        await page.keyboard.press('Enter')
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    selectFile: async (name: string) => {
 | 
			
		||||
      return test?.step(`Select ${name}`, async () => {
 | 
			
		||||
        await page
 | 
			
		||||
          .locator('[data-testid="file-pane-scroll-container"] button')
 | 
			
		||||
          .filter({ hasText: name })
 | 
			
		||||
          .click()
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createNewFileAndSelect: async (name: string) => {
 | 
			
		||||
      return test?.step(`Create a file named ${name}, select it`, async () => {
 | 
			
		||||
        await page.getByTestId('create-file-button').click()
 | 
			
		||||
        await page.getByTestId('file-rename-field').fill(name)
 | 
			
		||||
        await page.keyboard.press('Enter')
 | 
			
		||||
        await page
 | 
			
		||||
          .getByTestId('file-pane-scroll-container')
 | 
			
		||||
          .locator('[data-testid="file-pane-scroll-container"] button')
 | 
			
		||||
          .filter({ hasText: name })
 | 
			
		||||
          .click()
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    renameFile: async (fromName: string, toName: string) => {
 | 
			
		||||
      return test?.step(`Rename ${fromName} to ${toName}`, async () => {
 | 
			
		||||
        await page
 | 
			
		||||
          .locator('[data-testid="file-pane-scroll-container"] button')
 | 
			
		||||
          .filter({ hasText: fromName })
 | 
			
		||||
          .click({ button: 'right' })
 | 
			
		||||
        await page.getByTestId('context-menu-rename').click()
 | 
			
		||||
        await page.getByTestId('file-rename-field').fill(toName)
 | 
			
		||||
        await page.keyboard.press('Enter')
 | 
			
		||||
        await page
 | 
			
		||||
          .locator('[data-testid="file-pane-scroll-container"] button')
 | 
			
		||||
          .filter({ hasText: toName })
 | 
			
		||||
          .click()
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    deleteFile: async (name: string) => {
 | 
			
		||||
      return test?.step(`Delete ${name}`, async () => {
 | 
			
		||||
        await page
 | 
			
		||||
          .locator('[data-testid="file-pane-scroll-container"] button')
 | 
			
		||||
          .filter({ hasText: name })
 | 
			
		||||
          .click({ button: 'right' })
 | 
			
		||||
        await page.getByTestId('context-menu-delete').click()
 | 
			
		||||
        await page.getByTestId('delete-confirmation').click()
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    panesOpen: async (paneIds: PaneId[]) => {
 | 
			
		||||
      return test?.step(`Setting ${paneIds} panes to be open`, async () => {
 | 
			
		||||
        await page.addInitScript(
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,5 @@
 | 
			
		||||
import { test, expect, Page } from '@playwright/test'
 | 
			
		||||
import {
 | 
			
		||||
  getUtils,
 | 
			
		||||
  setup,
 | 
			
		||||
  tearDown,
 | 
			
		||||
  setupElectron,
 | 
			
		||||
  createProjectAndRenameIt,
 | 
			
		||||
} from './test-utils'
 | 
			
		||||
import { getUtils, setup, tearDown, setupElectron } from './test-utils'
 | 
			
		||||
import { join } from 'path'
 | 
			
		||||
import fs from 'fs'
 | 
			
		||||
 | 
			
		||||
@ -698,13 +692,16 @@ test(
 | 
			
		||||
  async ({ browserName }, testInfo) => {
 | 
			
		||||
    const { electronApp, page, dir } = await setupElectron({ testInfo })
 | 
			
		||||
    const fileExists = () =>
 | 
			
		||||
      fs.existsSync(join(dir, 'test-000', 'lego-2x4.kcl'))
 | 
			
		||||
      fs.existsSync(join(dir, 'project-000', 'lego-2x4.kcl'))
 | 
			
		||||
 | 
			
		||||
    const { createAndSelectProject, panesOpen } = await getUtils(page, test)
 | 
			
		||||
 | 
			
		||||
    await page.setViewportSize({ width: 1200, height: 500 })
 | 
			
		||||
 | 
			
		||||
    await panesOpen(['code', 'files'])
 | 
			
		||||
 | 
			
		||||
    // Create and navigate to the project
 | 
			
		||||
    await createProjectAndRenameIt({ name: 'test-000', page })
 | 
			
		||||
    await page.getByTestId('project-link').click()
 | 
			
		||||
    await createAndSelectProject('project-000')
 | 
			
		||||
 | 
			
		||||
    // Wait for Start Sketch otherwise you will not have access Text-to-CAD command
 | 
			
		||||
    await expect(
 | 
			
		||||
@ -713,10 +710,6 @@ test(
 | 
			
		||||
      timeout: 20_000,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    // Open the files pane
 | 
			
		||||
    const filesPaneButton = page.getByTestId('files-pane-button')
 | 
			
		||||
    await filesPaneButton.click()
 | 
			
		||||
 | 
			
		||||
    await test.step(`Test file creation`, async () => {
 | 
			
		||||
      await sendPromptFromCommandBar(page, 'lego 2x4')
 | 
			
		||||
      // File is considered created if it shows up in the Project Files pane
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,31 +0,0 @@
 | 
			
		||||
import { defineConfig, devices } from '@playwright/test'
 | 
			
		||||
import dotenv from 'dotenv'
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * See https://playwright.dev/docs/test-configuration.
 | 
			
		||||
 */
 | 
			
		||||
export default defineConfig({
 | 
			
		||||
  timeout: 120_000, // override the default 30s timeout
 | 
			
		||||
  testDir: './e2e/playwright',
 | 
			
		||||
  /* Run tests in files in parallel */
 | 
			
		||||
  fullyParallel: true,
 | 
			
		||||
  /* Fail the build on CI if you accidentally left test.only in the source code. */
 | 
			
		||||
  forbidOnly: !!process.env.CI,
 | 
			
		||||
  /* Do not retry */
 | 
			
		||||
  retries: process.env.CI ? 0 : 0,
 | 
			
		||||
  /* Different amount of parallelism on CI and local. */
 | 
			
		||||
  workers: process.env.CI ? 1 : 4,
 | 
			
		||||
  /* Reporter to use. See https://playwright.dev/docs/test-reporters */
 | 
			
		||||
  reporter: [
 | 
			
		||||
    [process.env.CI ? 'dot' : 'list'],
 | 
			
		||||
    ['json', { outputFile: './test-results/report.json' }],
 | 
			
		||||
    ['html'],
 | 
			
		||||
  ],
 | 
			
		||||
  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
 | 
			
		||||
  use: {
 | 
			
		||||
    /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
 | 
			
		||||
    trace: 'retain-on-failure',
 | 
			
		||||
    actionTimeout: 15000,
 | 
			
		||||
    screenshot: 'only-on-failure',
 | 
			
		||||
  },
 | 
			
		||||
})
 | 
			
		||||
@ -135,16 +135,15 @@ interface ContextMenuItemProps {
 | 
			
		||||
  icon?: ActionIconProps['icon']
 | 
			
		||||
  onClick?: () => void
 | 
			
		||||
  hotkey?: string
 | 
			
		||||
  'data-testid'?: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function ContextMenuItem({
 | 
			
		||||
  children,
 | 
			
		||||
  icon,
 | 
			
		||||
  onClick,
 | 
			
		||||
  hotkey,
 | 
			
		||||
}: ContextMenuItemProps) {
 | 
			
		||||
export function ContextMenuItem(props: ContextMenuItemProps) {
 | 
			
		||||
  const { children, icon, onClick, hotkey } = props
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <button
 | 
			
		||||
      data-testid={props['data-testid']}
 | 
			
		||||
      className="flex items-center gap-2 py-1 px-2 cursor-pointer hover:bg-chalkboard-20 dark:hover:bg-chalkboard-80 border-none text-left"
 | 
			
		||||
      onClick={onClick}
 | 
			
		||||
    >
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,11 @@ import {
 | 
			
		||||
import { useCommandsContext } from 'hooks/useCommandsContext'
 | 
			
		||||
import { fileMachine } from 'machines/fileMachine'
 | 
			
		||||
import { isDesktop } from 'lib/isDesktop'
 | 
			
		||||
import { DEFAULT_FILE_NAME, FILE_EXT } from 'lib/constants'
 | 
			
		||||
import {
 | 
			
		||||
  DEFAULT_FILE_NAME,
 | 
			
		||||
  DEFAULT_PROJECT_KCL_FILE,
 | 
			
		||||
  FILE_EXT,
 | 
			
		||||
} from 'lib/constants'
 | 
			
		||||
import { getProjectInfo } from 'lib/desktop'
 | 
			
		||||
import { getNextDirName, getNextFileName } from 'lib/desktopFS'
 | 
			
		||||
 | 
			
		||||
@ -167,6 +171,25 @@ export const FileMachineProvider = ({
 | 
			
		||||
          name
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        // no-op
 | 
			
		||||
        if (oldPath === newPath) {
 | 
			
		||||
          return {
 | 
			
		||||
            message: `Old is the same as new.`,
 | 
			
		||||
            newPath,
 | 
			
		||||
            oldPath,
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // if there are any siblings with the same name, report error.
 | 
			
		||||
        const entries = await window.electron.readdir(
 | 
			
		||||
          window.electron.path.dirname(newPath)
 | 
			
		||||
        )
 | 
			
		||||
        for (let entry of entries) {
 | 
			
		||||
          if (entry === newName) {
 | 
			
		||||
            return Promise.reject(new Error('Filename already exists.'))
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        window.electron.rename(oldPath, newPath)
 | 
			
		||||
 | 
			
		||||
        if (!file) {
 | 
			
		||||
@ -209,6 +232,27 @@ export const FileMachineProvider = ({
 | 
			
		||||
            .catch((e) => console.error('Error deleting file', e))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If there are no more files at all in the project, create a main.kcl
 | 
			
		||||
        // for when we navigate to the root.
 | 
			
		||||
        if (!project?.path) {
 | 
			
		||||
          return Promise.reject(new Error('Project path not set.'))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const entries = await window.electron.readdir(project.path)
 | 
			
		||||
        const hasKclEntries =
 | 
			
		||||
          entries.filter((e: string) => e.endsWith('.kcl')).length !== 0
 | 
			
		||||
        if (!hasKclEntries) {
 | 
			
		||||
          await window.electron.writeFile(
 | 
			
		||||
            window.electron.path.join(project.path, DEFAULT_PROJECT_KCL_FILE),
 | 
			
		||||
            ''
 | 
			
		||||
          )
 | 
			
		||||
          // Refresh the route selected above because it's possible we're on
 | 
			
		||||
          // the same path on the navigate, which doesn't cause anything to
 | 
			
		||||
          // refresh, leaving a stale execution state.
 | 
			
		||||
          navigate(0)
 | 
			
		||||
          return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If we just deleted the current file or one of its parent directories,
 | 
			
		||||
        // navigate to the project root
 | 
			
		||||
        if (
 | 
			
		||||
 | 
			
		||||
@ -358,10 +358,18 @@ function FileTreeContextMenu({
 | 
			
		||||
    <ContextMenu
 | 
			
		||||
      menuTargetElement={itemRef}
 | 
			
		||||
      items={[
 | 
			
		||||
        <ContextMenuItem onClick={onRename} hotkey="Enter">
 | 
			
		||||
        <ContextMenuItem
 | 
			
		||||
          data-testid="context-menu-rename"
 | 
			
		||||
          onClick={onRename}
 | 
			
		||||
          hotkey="Enter"
 | 
			
		||||
        >
 | 
			
		||||
          Rename
 | 
			
		||||
        </ContextMenuItem>,
 | 
			
		||||
        <ContextMenuItem onClick={onDelete} hotkey={metaKey + ' + Del'}>
 | 
			
		||||
        <ContextMenuItem
 | 
			
		||||
          data-testid="context-menu-delete"
 | 
			
		||||
          onClick={onDelete}
 | 
			
		||||
          hotkey={metaKey + ' + Del'}
 | 
			
		||||
        >
 | 
			
		||||
          Delete
 | 
			
		||||
        </ContextMenuItem>,
 | 
			
		||||
      ]}
 | 
			
		||||
 | 
			
		||||
@ -49,7 +49,11 @@ export const NetworkMachineIndicator = ({
 | 
			
		||||
            {Object.entries(machineManager.machines).map(
 | 
			
		||||
              ([hostname, machine]) => (
 | 
			
		||||
                <li key={hostname} className={'px-2 py-4 gap-1 last:mb-0 '}>
 | 
			
		||||
                  <p className="">{machine.model || machine.manufacturer}</p>
 | 
			
		||||
                  <p className="">
 | 
			
		||||
                    {machine.make_model.model ||
 | 
			
		||||
                      machine.make_model.manufacturer ||
 | 
			
		||||
                      'Unknown Machine'}
 | 
			
		||||
                  </p>
 | 
			
		||||
                  <p className="text-chalkboard-60 dark:text-chalkboard-50 text-xs">
 | 
			
		||||
                    Hostname {hostname}
 | 
			
		||||
                  </p>
 | 
			
		||||
 | 
			
		||||
@ -152,7 +152,7 @@ const extrude001 = extrude(-15, sketch001)`
 | 
			
		||||
      selectedSegmentSnippet,
 | 
			
		||||
      expectedExtrudeSnippet
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  }, 5_000)
 | 
			
		||||
  it('should return the correct paths for a valid selection and extrusion in case of several extrusions and sketches', async () => {
 | 
			
		||||
    const code = `const sketch001 = startSketchOn('XY')
 | 
			
		||||
  |> startProfileAt([-30, 30], %)
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ export type ModelingCommandSchema = {
 | 
			
		||||
    storage?: StorageUnion
 | 
			
		||||
  }
 | 
			
		||||
  Make: {
 | 
			
		||||
    machine: components['schemas']['Machine']
 | 
			
		||||
    machine: components['schemas']['MachineInfoResponse']
 | 
			
		||||
  }
 | 
			
		||||
  Extrude: {
 | 
			
		||||
    selection: Selections // & { type: 'face' } would be cool to lock that down
 | 
			
		||||
@ -179,21 +179,25 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
 | 
			
		||||
      machine: {
 | 
			
		||||
        inputType: 'options',
 | 
			
		||||
        required: true,
 | 
			
		||||
        valueSummary: (machine: components['schemas']['Machine']) =>
 | 
			
		||||
          machine.model || machine.manufacturer,
 | 
			
		||||
        valueSummary: (machine: components['schemas']['MachineInfoResponse']) =>
 | 
			
		||||
          machine.make_model.model ||
 | 
			
		||||
          machine.make_model.manufacturer ||
 | 
			
		||||
          'Unknown Machine',
 | 
			
		||||
        options: () => {
 | 
			
		||||
          return Object.entries(machineManager.machines).map(
 | 
			
		||||
            ([hostname, machine]) => ({
 | 
			
		||||
              name: `${machine.model || machine.manufacturer}, ${hostname}`,
 | 
			
		||||
            ([_, machine]) => ({
 | 
			
		||||
              name: `${machine.id} (${
 | 
			
		||||
                machine.make_model.model || machine.make_model.manufacturer
 | 
			
		||||
              }) via ${machineManager.machineApiIp || 'the local network'}`,
 | 
			
		||||
              isCurrent: false,
 | 
			
		||||
              value: machine as components['schemas']['Machine'],
 | 
			
		||||
              value: machine as components['schemas']['MachineInfoResponse'],
 | 
			
		||||
            })
 | 
			
		||||
          )
 | 
			
		||||
        },
 | 
			
		||||
        defaultValue: () => {
 | 
			
		||||
          return Object.values(
 | 
			
		||||
            machineManager.machines
 | 
			
		||||
          )[0] as components['schemas']['Machine']
 | 
			
		||||
          )[0] as components['schemas']['MachineInfoResponse']
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ export const MAX_PADDING = 7
 | 
			
		||||
 * This is available for users to edit as a setting.
 | 
			
		||||
 */
 | 
			
		||||
export const DEFAULT_PROJECT_NAME = 'project-$nnn'
 | 
			
		||||
export const DEFAULT_PROJECT_KCL_FILE = 'main.kcl'
 | 
			
		||||
/** Name given the temporary "project" in the browser version of the app */
 | 
			
		||||
export const BROWSER_PROJECT_NAME = 'browser'
 | 
			
		||||
/** Name given the temporary file in the browser version of the app */
 | 
			
		||||
 | 
			
		||||
@ -26,15 +26,7 @@ export async function exportMake(data: ArrayBuffer): Promise<Response | null> {
 | 
			
		||||
    return null
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let machineId = null
 | 
			
		||||
  if ('id' in currentMachine) {
 | 
			
		||||
    machineId = currentMachine.id
 | 
			
		||||
  } else if ('hostname' in currentMachine && currentMachine.hostname) {
 | 
			
		||||
    machineId = currentMachine.hostname
 | 
			
		||||
  } else if ('ip' in currentMachine && currentMachine.ip) {
 | 
			
		||||
    machineId = currentMachine.ip
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let machineId = currentMachine?.id
 | 
			
		||||
  if (!machineId) {
 | 
			
		||||
    console.error('No machine id available', currentMachine)
 | 
			
		||||
    toast.error('No machine id available')
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										851
									
								
								src/lib/machine-api.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										851
									
								
								src/lib/machine-api.d.ts
									
									
									
									
										vendored
									
									
								
							@ -93,587 +93,56 @@ export interface paths {
 | 
			
		||||
export type webhooks = Record<string, never>
 | 
			
		||||
export interface components {
 | 
			
		||||
  schemas: {
 | 
			
		||||
    /** @description The type of accessory. */
 | 
			
		||||
    AccessoryType: 'none'
 | 
			
		||||
    /** @description Error information from a response. */
 | 
			
		||||
    Error: {
 | 
			
		||||
      error_code?: string
 | 
			
		||||
      message: string
 | 
			
		||||
      request_id: string
 | 
			
		||||
    }
 | 
			
		||||
    /** @description An info command. */
 | 
			
		||||
    Info: {
 | 
			
		||||
      /** @enum {string} */
 | 
			
		||||
      command: 'get_version'
 | 
			
		||||
      /** @description The info module. */
 | 
			
		||||
      module: components['schemas']['InfoModule'][]
 | 
			
		||||
      /** @description The reason of the info command. */
 | 
			
		||||
      reason?: components['schemas']['Reason'] | null
 | 
			
		||||
      /** @description The result of the info command. */
 | 
			
		||||
      result?: components['schemas']['Result'] | null
 | 
			
		||||
      /** @description The sequence id. */
 | 
			
		||||
      sequence_id: components['schemas']['SequenceId']
 | 
			
		||||
    } & {
 | 
			
		||||
      [key: string]: unknown
 | 
			
		||||
    /** @description Extra machine-specific information regarding a connected machine. */
 | 
			
		||||
    ExtraMachineInfoResponse:
 | 
			
		||||
      | {
 | 
			
		||||
          Moonraker: Record<string, never>
 | 
			
		||||
        }
 | 
			
		||||
      | {
 | 
			
		||||
          Usb: Record<string, never>
 | 
			
		||||
        }
 | 
			
		||||
      | {
 | 
			
		||||
          Bambu: Record<string, never>
 | 
			
		||||
        }
 | 
			
		||||
    /** @description Information regarding a connected machine. */
 | 
			
		||||
    MachineInfoResponse: {
 | 
			
		||||
      /** @description Additional, per-machine information which is specific to the underlying machine type. */
 | 
			
		||||
      extra?: components['schemas']['ExtraMachineInfoResponse'] | null
 | 
			
		||||
      /** @description Machine Identifier (ID) for the specific Machine. */
 | 
			
		||||
      id: string
 | 
			
		||||
      /** @description Information regarding the method of manufacture. */
 | 
			
		||||
      machine_type: components['schemas']['MachineType']
 | 
			
		||||
      /** @description Information regarding the make and model of the attached Machine. */
 | 
			
		||||
      make_model: components['schemas']['MachineMakeModel']
 | 
			
		||||
      /** @description Maximum part size that can be manufactured by this device. This may be some sort of theoretical upper bound, getting close to this limit seems like maybe a bad idea.
 | 
			
		||||
       *
 | 
			
		||||
       *     This may be `None` if the maximum size is not knowable by the Machine API.
 | 
			
		||||
       *
 | 
			
		||||
       *     What "close" means is up to you! */
 | 
			
		||||
      max_part_volume?: components['schemas']['Volume'] | null
 | 
			
		||||
    }
 | 
			
		||||
    /** @description An info module. */
 | 
			
		||||
    InfoModule: {
 | 
			
		||||
      /** @description The hardware version. */
 | 
			
		||||
      hw_ver: string
 | 
			
		||||
      /** @description The loader version. */
 | 
			
		||||
      loader_ver?: string | null
 | 
			
		||||
      /** @description The module name. */
 | 
			
		||||
      name: string
 | 
			
		||||
      /** @description The ota version. */
 | 
			
		||||
      ota_ver?: string | null
 | 
			
		||||
      /** @description The project name. */
 | 
			
		||||
      project_name?: string | null
 | 
			
		||||
      /** @description The serial number. */
 | 
			
		||||
      sn: string
 | 
			
		||||
      /** @description The software version. */
 | 
			
		||||
      sw_ver: string
 | 
			
		||||
    }
 | 
			
		||||
    /** @description The mode for the led. */
 | 
			
		||||
    LedMode: 'on' | 'off' | 'flashing'
 | 
			
		||||
    /** @description The node for the led. */
 | 
			
		||||
    LedNode: 'chamber_light' | 'work_light'
 | 
			
		||||
    /** @description A liveview message. */
 | 
			
		||||
    LiveView: {
 | 
			
		||||
      /** @enum {string} */
 | 
			
		||||
      command: 'init'
 | 
			
		||||
      /** @description The op protocols. */
 | 
			
		||||
      op_protocols: components['schemas']['OperationProtocol'][]
 | 
			
		||||
      /** @description The peer host. */
 | 
			
		||||
      peer_host: string
 | 
			
		||||
      /** @description The reason for the message. */
 | 
			
		||||
      reason?: components['schemas']['Reason'] | null
 | 
			
		||||
      /** @description The result of the command. */
 | 
			
		||||
      result: components['schemas']['Result']
 | 
			
		||||
      /** @description The sequence id. */
 | 
			
		||||
      sequence_id: components['schemas']['SequenceId']
 | 
			
		||||
    } & {
 | 
			
		||||
      [key: string]: unknown
 | 
			
		||||
    }
 | 
			
		||||
    /** @description Details for a 3d printer connected over USB. */
 | 
			
		||||
    Machine:
 | 
			
		||||
      | {
 | 
			
		||||
          id: string
 | 
			
		||||
          manufacturer: string
 | 
			
		||||
          model: string
 | 
			
		||||
          port: string
 | 
			
		||||
          /** @enum {string} */
 | 
			
		||||
          type: 'UsbPrinter'
 | 
			
		||||
        }
 | 
			
		||||
      | {
 | 
			
		||||
          /** @description The hostname of the printer. */
 | 
			
		||||
          hostname?: string | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: ip
 | 
			
		||||
           * @description The IP address of the printer.
 | 
			
		||||
           */
 | 
			
		||||
          ip: string
 | 
			
		||||
          /** @description The manufacturer of the printer. */
 | 
			
		||||
          manufacturer: components['schemas']['NetworkPrinterManufacturer']
 | 
			
		||||
          /** @description The model of the printer. */
 | 
			
		||||
          model?: string | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: uint16
 | 
			
		||||
           * @description The port of the printer.
 | 
			
		||||
           */
 | 
			
		||||
          port?: number | null
 | 
			
		||||
          /** @description The serial number of the printer. */
 | 
			
		||||
          serial?: string | null
 | 
			
		||||
          /** @enum {string} */
 | 
			
		||||
          type: 'NetworkPrinter'
 | 
			
		||||
        }
 | 
			
		||||
    /** @description A message from a machine. */
 | 
			
		||||
    Message:
 | 
			
		||||
      | {
 | 
			
		||||
          UsbPrinter: components['schemas']['Message2']
 | 
			
		||||
        }
 | 
			
		||||
      | {
 | 
			
		||||
          NetworkPrinter: components['schemas']['Message3']
 | 
			
		||||
        }
 | 
			
		||||
    /**
 | 
			
		||||
     * @description A message from the printer.
 | 
			
		||||
     * @enum {string}
 | 
			
		||||
     */
 | 
			
		||||
    Message2: 'ok'
 | 
			
		||||
    /** @description A message from the printer. */
 | 
			
		||||
    Message3:
 | 
			
		||||
      | {
 | 
			
		||||
          Bambu: components['schemas']['Message4']
 | 
			
		||||
        }
 | 
			
		||||
      | {
 | 
			
		||||
          Formlabs: Record<string, never>
 | 
			
		||||
        }
 | 
			
		||||
    /** @description A message from/to the printer. */
 | 
			
		||||
    Message4:
 | 
			
		||||
      | {
 | 
			
		||||
          print: components['schemas']['Print']
 | 
			
		||||
        }
 | 
			
		||||
      | {
 | 
			
		||||
          info: components['schemas']['Info']
 | 
			
		||||
        }
 | 
			
		||||
      | {
 | 
			
		||||
          system: components['schemas']['System']
 | 
			
		||||
        }
 | 
			
		||||
      | {
 | 
			
		||||
          security: components['schemas']['Security']
 | 
			
		||||
        }
 | 
			
		||||
      | {
 | 
			
		||||
          live_view: components['schemas']['LiveView']
 | 
			
		||||
        }
 | 
			
		||||
      | {
 | 
			
		||||
          json: unknown
 | 
			
		||||
        }
 | 
			
		||||
      | {
 | 
			
		||||
          unknown: string | null
 | 
			
		||||
        }
 | 
			
		||||
    /** @description Network printer manufacturer. */
 | 
			
		||||
    NetworkPrinterManufacturer: 'Bambu' | 'Formlabs'
 | 
			
		||||
    /** @description A nozzle type. */
 | 
			
		||||
    NozzleType: 'hardened_steel' | 'stainless_steel'
 | 
			
		||||
    /** @description An operation protocol. */
 | 
			
		||||
    OperationProtocol: {
 | 
			
		||||
      /** @description The protocol. */
 | 
			
		||||
      protocol: string
 | 
			
		||||
      /** @description The version. */
 | 
			
		||||
      version: string
 | 
			
		||||
    } & {
 | 
			
		||||
      [key: string]: unknown
 | 
			
		||||
    /** @description Information regarding the make/model of a discovered endpoint. */
 | 
			
		||||
    MachineMakeModel: {
 | 
			
		||||
      /** @description The manufacturer that built the connected Machine. */
 | 
			
		||||
      manufacturer?: string | null
 | 
			
		||||
      /** @description The model of the connected Machine. */
 | 
			
		||||
      model?: string | null
 | 
			
		||||
      /** @description The unique serial number of the connected Machine. */
 | 
			
		||||
      serial?: string | null
 | 
			
		||||
    }
 | 
			
		||||
    /** @description Specific technique by which this Machine takes a design, and produces a real-world 3D object. */
 | 
			
		||||
    MachineType: 'Stereolithography' | 'FusedDeposition' | 'Cnc'
 | 
			
		||||
    /** @description The response from the `/ping` endpoint. */
 | 
			
		||||
    Pong: {
 | 
			
		||||
      /** @description The pong response. */
 | 
			
		||||
      message: string
 | 
			
		||||
    }
 | 
			
		||||
    /** @description A print command. */
 | 
			
		||||
    Print:
 | 
			
		||||
      | ({
 | 
			
		||||
          /** @enum {string} */
 | 
			
		||||
          command: 'ams_control'
 | 
			
		||||
          /** @description The param. */
 | 
			
		||||
          param?: string | null
 | 
			
		||||
          /** @description The reason for the message. */
 | 
			
		||||
          reason: components['schemas']['Reason']
 | 
			
		||||
          /** @description The result of the command. */
 | 
			
		||||
          result: components['schemas']['Result']
 | 
			
		||||
          /** @description The sequence id. */
 | 
			
		||||
          sequence_id: components['schemas']['SequenceId']
 | 
			
		||||
        } & {
 | 
			
		||||
          [key: string]: unknown
 | 
			
		||||
        })
 | 
			
		||||
      | ({
 | 
			
		||||
          /** @enum {string} */
 | 
			
		||||
          command: 'ams_change_filament'
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The error number.
 | 
			
		||||
           */
 | 
			
		||||
          errorno?: number | null
 | 
			
		||||
          /** @description The reason for the message. */
 | 
			
		||||
          reason?: components['schemas']['Reason'] | null
 | 
			
		||||
          /** @description The result of the command. */
 | 
			
		||||
          result: components['schemas']['Result']
 | 
			
		||||
          /** @description The sequence id. */
 | 
			
		||||
          sequence_id: components['schemas']['SequenceId']
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The target temperature.
 | 
			
		||||
           */
 | 
			
		||||
          tar_temp?: number | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The target.
 | 
			
		||||
           */
 | 
			
		||||
          target: number
 | 
			
		||||
        } & {
 | 
			
		||||
          [key: string]: unknown
 | 
			
		||||
        })
 | 
			
		||||
      | ({
 | 
			
		||||
          /** @enum {string} */
 | 
			
		||||
          command: 'calibration'
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The option.
 | 
			
		||||
           */
 | 
			
		||||
          option: number
 | 
			
		||||
          /** @description The reason for the message. */
 | 
			
		||||
          reason?: components['schemas']['Reason'] | null
 | 
			
		||||
          /** @description The result of the command. */
 | 
			
		||||
          result: components['schemas']['Result']
 | 
			
		||||
          /** @description The sequence id. */
 | 
			
		||||
          sequence_id: components['schemas']['SequenceId']
 | 
			
		||||
        } & {
 | 
			
		||||
          [key: string]: unknown
 | 
			
		||||
        })
 | 
			
		||||
      | ({
 | 
			
		||||
          /** @description The ams. */
 | 
			
		||||
          ams?: components['schemas']['PrintAms'] | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The ams rfid status.
 | 
			
		||||
           */
 | 
			
		||||
          ams_rfid_status?: number | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The ams status.
 | 
			
		||||
           */
 | 
			
		||||
          ams_status?: number | null
 | 
			
		||||
          /** @description The aux part fan. */
 | 
			
		||||
          aux_part_fan?: boolean | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: double
 | 
			
		||||
           * @description The target bed temperature.
 | 
			
		||||
           */
 | 
			
		||||
          bed_target_temper?: number | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: double
 | 
			
		||||
           * @description The bed temperature.
 | 
			
		||||
           */
 | 
			
		||||
          bed_temper?: number | null
 | 
			
		||||
          /** @description The big fan 1 speed. */
 | 
			
		||||
          big_fan1_speed?: string | null
 | 
			
		||||
          /** @description The big fan 2 speed. */
 | 
			
		||||
          big_fan2_speed?: string | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: double
 | 
			
		||||
           * @description The chamber temperature.
 | 
			
		||||
           */
 | 
			
		||||
          chamber_temper?: number | null
 | 
			
		||||
          /** @enum {string} */
 | 
			
		||||
          command: 'push_status'
 | 
			
		||||
          /** @description The cooling fan speed. */
 | 
			
		||||
          cooling_fan_speed?: string | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The fan gear.
 | 
			
		||||
           */
 | 
			
		||||
          fan_gear?: number | null
 | 
			
		||||
          /** @description Force upgrade? */
 | 
			
		||||
          force_upgrade?: boolean | null
 | 
			
		||||
          /** @description The gcode file. */
 | 
			
		||||
          gcode_file?: string | null
 | 
			
		||||
          /** @description The gcode file prepare percent. */
 | 
			
		||||
          gcode_file_prepare_percent?: string | null
 | 
			
		||||
          /** @description The gcode state. */
 | 
			
		||||
          gcode_state?: string | null
 | 
			
		||||
          /** @description The heatbreak fan speed. */
 | 
			
		||||
          heatbreak_fan_speed?: string | null
 | 
			
		||||
          /** @description The hms. */
 | 
			
		||||
          hms?: unknown[] | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The home flag.
 | 
			
		||||
           */
 | 
			
		||||
          home_flag?: number | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The hw switch state.
 | 
			
		||||
           */
 | 
			
		||||
          hw_switch_state?: number | null
 | 
			
		||||
          /** @description The ipcam. */
 | 
			
		||||
          ipcam?: components['schemas']['PrintIpcam'] | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The layer num.
 | 
			
		||||
           */
 | 
			
		||||
          layer_num?: number | null
 | 
			
		||||
          /** @description The lifecycle. */
 | 
			
		||||
          lifecycle?: string | null
 | 
			
		||||
          /** @description The lights report. */
 | 
			
		||||
          lights_report?: components['schemas']['PrintLightsReport'][] | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The percentage of the print completed.
 | 
			
		||||
           */
 | 
			
		||||
          mc_percent?: number | null
 | 
			
		||||
          /** @description The mc print line number. */
 | 
			
		||||
          mc_print_line_number?: string | null
 | 
			
		||||
          /** @description The print stage. */
 | 
			
		||||
          mc_print_stage?: string | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The mc print sub stage.
 | 
			
		||||
           */
 | 
			
		||||
          mc_print_sub_stage?: number | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The remaining time of the print.
 | 
			
		||||
           */
 | 
			
		||||
          mc_remaining_time?: number | null
 | 
			
		||||
          /** @description The mess production state. */
 | 
			
		||||
          mess_production_state?: string | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The message.
 | 
			
		||||
           */
 | 
			
		||||
          msg?: number | null
 | 
			
		||||
          /** @description The nozzle diameter. */
 | 
			
		||||
          nozzle_diameter?: string | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: double
 | 
			
		||||
           * @description The target nozzle temperature.
 | 
			
		||||
           */
 | 
			
		||||
          nozzle_target_temper?: number | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: double
 | 
			
		||||
           * @description The nozzle temperature.
 | 
			
		||||
           */
 | 
			
		||||
          nozzle_temper?: number | null
 | 
			
		||||
          /** @description The nozzle type. */
 | 
			
		||||
          nozzle_type?: components['schemas']['NozzleType'] | null
 | 
			
		||||
          /** @description Online status. */
 | 
			
		||||
          online?: components['schemas']['PrintOnline'] | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The print error.
 | 
			
		||||
           */
 | 
			
		||||
          print_error?: number | null
 | 
			
		||||
          /** @description The print type. */
 | 
			
		||||
          print_type?: string | null
 | 
			
		||||
          /** @description The profile id. */
 | 
			
		||||
          profile_id?: string | null
 | 
			
		||||
          /** @description The project id. */
 | 
			
		||||
          project_id?: string | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The queue est.
 | 
			
		||||
           */
 | 
			
		||||
          queue_est?: number | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The queue number.
 | 
			
		||||
           */
 | 
			
		||||
          queue_number?: number | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The queue sts.
 | 
			
		||||
           */
 | 
			
		||||
          queue_sts?: number | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The queue total.
 | 
			
		||||
           */
 | 
			
		||||
          queue_total?: number | null
 | 
			
		||||
          /** @description The s obj. */
 | 
			
		||||
          s_obj?: unknown[] | null
 | 
			
		||||
          /** @description Sdcard? */
 | 
			
		||||
          sdcard?: boolean | null
 | 
			
		||||
          /** @description The sequence id. */
 | 
			
		||||
          sequence_id: components['schemas']['SequenceId']
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The spd lvl.
 | 
			
		||||
           */
 | 
			
		||||
          spd_lvl?: number | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The spd mag.
 | 
			
		||||
           */
 | 
			
		||||
          spd_mag?: number | null
 | 
			
		||||
          /** @description The stg. */
 | 
			
		||||
          stg?: unknown[] | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The stg cur.
 | 
			
		||||
           */
 | 
			
		||||
          stg_cur?: number | null
 | 
			
		||||
          /** @description The subtask id. */
 | 
			
		||||
          subtask_id?: string | null
 | 
			
		||||
          /** @description The subtask name. */
 | 
			
		||||
          subtask_name?: string | null
 | 
			
		||||
          /** @description The task id. */
 | 
			
		||||
          task_id?: string | null
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The total layer num.
 | 
			
		||||
           */
 | 
			
		||||
          total_layer_num?: number | null
 | 
			
		||||
          /** @description The upgrade state. */
 | 
			
		||||
          upgrade_state?: components['schemas']['PrintUpgradeState'] | null
 | 
			
		||||
          /** @description The upload. */
 | 
			
		||||
          upload?: components['schemas']['PrintUpload'] | null
 | 
			
		||||
          /** @description The tray. */
 | 
			
		||||
          vt_tray?: components['schemas']['PrintTray'] | null
 | 
			
		||||
          /** @description The wifi signal. */
 | 
			
		||||
          wifi_signal?: string | null
 | 
			
		||||
        } & {
 | 
			
		||||
          [key: string]: unknown
 | 
			
		||||
        })
 | 
			
		||||
      | ({
 | 
			
		||||
          /** @enum {string} */
 | 
			
		||||
          command: 'gcode_line'
 | 
			
		||||
          /** @description The gcode line. */
 | 
			
		||||
          param?: string | null
 | 
			
		||||
          /** @description The reason for the message. */
 | 
			
		||||
          reason: components['schemas']['Reason']
 | 
			
		||||
          /** @description The result of the command. */
 | 
			
		||||
          result: components['schemas']['Result']
 | 
			
		||||
          /** @description The return code. */
 | 
			
		||||
          return_code?: string | null
 | 
			
		||||
          /** @description The sequence id. */
 | 
			
		||||
          sequence_id: components['schemas']['SequenceId']
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: int64
 | 
			
		||||
           * @description The source.
 | 
			
		||||
           */
 | 
			
		||||
          source?: number | null
 | 
			
		||||
        } & {
 | 
			
		||||
          [key: string]: unknown
 | 
			
		||||
        })
 | 
			
		||||
      | ({
 | 
			
		||||
          /** @enum {string} */
 | 
			
		||||
          command: 'gcode_file'
 | 
			
		||||
          /** @description The param. */
 | 
			
		||||
          param?: string | null
 | 
			
		||||
          /** @description The print type. */
 | 
			
		||||
          print_type?: string | null
 | 
			
		||||
          /** @description The reason for the message. */
 | 
			
		||||
          reason: components['schemas']['Reason']
 | 
			
		||||
          /** @description The result of the command. */
 | 
			
		||||
          result: components['schemas']['Result']
 | 
			
		||||
          /** @description The sequence id. */
 | 
			
		||||
          sequence_id: components['schemas']['SequenceId']
 | 
			
		||||
        } & {
 | 
			
		||||
          [key: string]: unknown
 | 
			
		||||
        })
 | 
			
		||||
      | ({
 | 
			
		||||
          /** @enum {string} */
 | 
			
		||||
          command: 'project_file'
 | 
			
		||||
          /** @description The gcode file. */
 | 
			
		||||
          gcode_file?: string | null
 | 
			
		||||
          /** @description The profile id. */
 | 
			
		||||
          profile_id: string
 | 
			
		||||
          /** @description The project id. */
 | 
			
		||||
          project_id: string
 | 
			
		||||
          /** @description The sequence id. */
 | 
			
		||||
          sequence_id: components['schemas']['SequenceId']
 | 
			
		||||
          /** @description The subtask id. */
 | 
			
		||||
          subtask_id: string
 | 
			
		||||
          /** @description The subtask name. */
 | 
			
		||||
          subtask_name: string
 | 
			
		||||
          /** @description The task id. */
 | 
			
		||||
          task_id: string
 | 
			
		||||
        } & {
 | 
			
		||||
          [key: string]: unknown
 | 
			
		||||
        })
 | 
			
		||||
      | ({
 | 
			
		||||
          /** @enum {string} */
 | 
			
		||||
          command: 'pause'
 | 
			
		||||
          /** @description The reason for the message. */
 | 
			
		||||
          reason: components['schemas']['Reason']
 | 
			
		||||
          /** @description The result of the command. */
 | 
			
		||||
          result: components['schemas']['Result']
 | 
			
		||||
          /** @description The sequence id. */
 | 
			
		||||
          sequence_id: components['schemas']['SequenceId']
 | 
			
		||||
        } & {
 | 
			
		||||
          [key: string]: unknown
 | 
			
		||||
        })
 | 
			
		||||
      | ({
 | 
			
		||||
          /** @enum {string} */
 | 
			
		||||
          command: 'print_speed'
 | 
			
		||||
          /** @description The param. */
 | 
			
		||||
          param: string
 | 
			
		||||
          /** @description The reason for the message. */
 | 
			
		||||
          reason?: components['schemas']['Reason'] | null
 | 
			
		||||
          /** @description The result of the command. */
 | 
			
		||||
          result: components['schemas']['Result']
 | 
			
		||||
          /** @description The sequence id. */
 | 
			
		||||
          sequence_id: components['schemas']['SequenceId']
 | 
			
		||||
        } & {
 | 
			
		||||
          [key: string]: unknown
 | 
			
		||||
        })
 | 
			
		||||
      | ({
 | 
			
		||||
          /** @enum {string} */
 | 
			
		||||
          command: 'resume'
 | 
			
		||||
          /** @description The reason for the message. */
 | 
			
		||||
          reason: components['schemas']['Reason']
 | 
			
		||||
          /** @description The result of the command. */
 | 
			
		||||
          result: components['schemas']['Result']
 | 
			
		||||
          /** @description The sequence id. */
 | 
			
		||||
          sequence_id: components['schemas']['SequenceId']
 | 
			
		||||
        } & {
 | 
			
		||||
          [key: string]: unknown
 | 
			
		||||
        })
 | 
			
		||||
      | ({
 | 
			
		||||
          /** @enum {string} */
 | 
			
		||||
          command: 'stop'
 | 
			
		||||
          /** @description The sequence id. */
 | 
			
		||||
          sequence_id: components['schemas']['SequenceId']
 | 
			
		||||
        } & {
 | 
			
		||||
          [key: string]: unknown
 | 
			
		||||
        })
 | 
			
		||||
      | ({
 | 
			
		||||
          /** @enum {string} */
 | 
			
		||||
          command: 'extrusion_cali_get'
 | 
			
		||||
          /** @description The sequence id. */
 | 
			
		||||
          sequence_id: components['schemas']['SequenceId']
 | 
			
		||||
        } & {
 | 
			
		||||
          [key: string]: unknown
 | 
			
		||||
        })
 | 
			
		||||
    /** @description The print ams. */
 | 
			
		||||
    PrintAms: {
 | 
			
		||||
      /** @description The ams. */
 | 
			
		||||
      ams?: components['schemas']['PrintAmsData'][] | null
 | 
			
		||||
      /** @description The ams exist bits. */
 | 
			
		||||
      ams_exist_bits?: string | null
 | 
			
		||||
      /** @description The insert flag. */
 | 
			
		||||
      insert_flag?: boolean | null
 | 
			
		||||
      /** @description The power on flag. */
 | 
			
		||||
      power_on_flag?: boolean | null
 | 
			
		||||
      /** @description The tray exist bits. */
 | 
			
		||||
      tray_exist_bits?: string | null
 | 
			
		||||
      /** @description The tray is bbl bits. */
 | 
			
		||||
      tray_is_bbl_bits?: string | null
 | 
			
		||||
      /** @description The tray now. */
 | 
			
		||||
      tray_now?: string | null
 | 
			
		||||
      /** @description The tray pre. */
 | 
			
		||||
      tray_pre?: string | null
 | 
			
		||||
      /** @description The tray read done bits. */
 | 
			
		||||
      tray_read_done_bits?: string | null
 | 
			
		||||
      /** @description The tray reading bits. */
 | 
			
		||||
      tray_reading_bits?: string | null
 | 
			
		||||
      /** @description The tray tar. */
 | 
			
		||||
      tray_tar?: string | null
 | 
			
		||||
      /**
 | 
			
		||||
       * Format: int64
 | 
			
		||||
       * @description The version.
 | 
			
		||||
       */
 | 
			
		||||
      version?: number | null
 | 
			
		||||
    } & {
 | 
			
		||||
      [key: string]: unknown
 | 
			
		||||
    }
 | 
			
		||||
    /** @description The print ams data. */
 | 
			
		||||
    PrintAmsData: {
 | 
			
		||||
      /** @description The humidity. */
 | 
			
		||||
      humidity: string
 | 
			
		||||
      /** @description The id. */
 | 
			
		||||
      id: string
 | 
			
		||||
      /** @description The temperature. */
 | 
			
		||||
      temp: string
 | 
			
		||||
      /** @description The tray. */
 | 
			
		||||
      tray: components['schemas']['PrintTray'][]
 | 
			
		||||
    } & {
 | 
			
		||||
      [key: string]: unknown
 | 
			
		||||
    }
 | 
			
		||||
    /** @description The print ipcam. */
 | 
			
		||||
    PrintIpcam: {
 | 
			
		||||
      /** @description The ipcam dev. */
 | 
			
		||||
      ipcam_dev?: string | null
 | 
			
		||||
      /** @description The ipcam record. */
 | 
			
		||||
      ipcam_record?: string | null
 | 
			
		||||
      /**
 | 
			
		||||
       * Format: int64
 | 
			
		||||
       * @description The mode bits.
 | 
			
		||||
       */
 | 
			
		||||
      mode_bits?: number | null
 | 
			
		||||
      /** @description The timelapse. */
 | 
			
		||||
      timelapse?: string | null
 | 
			
		||||
    } & {
 | 
			
		||||
      [key: string]: unknown
 | 
			
		||||
    }
 | 
			
		||||
    /** @description The response from the `/print` endpoint. */
 | 
			
		||||
    PrintJobResponse: {
 | 
			
		||||
      /** @description The job id used for this print. */
 | 
			
		||||
@ -681,29 +150,6 @@ export interface components {
 | 
			
		||||
      /** @description The parameters used for this print. */
 | 
			
		||||
      parameters: components['schemas']['PrintParameters']
 | 
			
		||||
    }
 | 
			
		||||
    /** @description A print lights report. */
 | 
			
		||||
    PrintLightsReport: {
 | 
			
		||||
      /** @description The mode. */
 | 
			
		||||
      mode: components['schemas']['LedMode']
 | 
			
		||||
      /** @description The node. */
 | 
			
		||||
      node: components['schemas']['LedNode']
 | 
			
		||||
    } & {
 | 
			
		||||
      [key: string]: unknown
 | 
			
		||||
    }
 | 
			
		||||
    /** @description The print online. */
 | 
			
		||||
    PrintOnline: {
 | 
			
		||||
      /** @description The ahb. */
 | 
			
		||||
      ahb: boolean
 | 
			
		||||
      /** @description The rfid. */
 | 
			
		||||
      rfid?: boolean | null
 | 
			
		||||
      /**
 | 
			
		||||
       * Format: int64
 | 
			
		||||
       * @description The version.
 | 
			
		||||
       */
 | 
			
		||||
      version: number
 | 
			
		||||
    } & {
 | 
			
		||||
      [key: string]: unknown
 | 
			
		||||
    }
 | 
			
		||||
    /** @description Parameters for printing. */
 | 
			
		||||
    PrintParameters: {
 | 
			
		||||
      /** @description The name for the job. */
 | 
			
		||||
@ -711,219 +157,26 @@ export interface components {
 | 
			
		||||
      /** @description The machine id to print to. */
 | 
			
		||||
      machine_id: string
 | 
			
		||||
    }
 | 
			
		||||
    /** @description The print tray. */
 | 
			
		||||
    PrintTray: {
 | 
			
		||||
      /** @description The bed temperature. */
 | 
			
		||||
      bed_temp?: string | null
 | 
			
		||||
      /** @description The bed temperature type. */
 | 
			
		||||
      bed_temp_type?: string | null
 | 
			
		||||
      /** @description The id. */
 | 
			
		||||
      id: string
 | 
			
		||||
    /** @description Set of three values to represent the extent of a 3-D Volume. This contains the width, depth, and height values, generally used to represent some maximum or minimum.
 | 
			
		||||
     *
 | 
			
		||||
     *     All measurements are in millimeters. */
 | 
			
		||||
    Volume: {
 | 
			
		||||
      /**
 | 
			
		||||
       * Format: double
 | 
			
		||||
       * @description The tray k.
 | 
			
		||||
       * @description Depth of the volume ("front to back"), in millimeters.
 | 
			
		||||
       */
 | 
			
		||||
      k?: number | null
 | 
			
		||||
      depth: number
 | 
			
		||||
      /**
 | 
			
		||||
       * Format: int64
 | 
			
		||||
       * @description The tray n.
 | 
			
		||||
       * Format: double
 | 
			
		||||
       * @description Height of the volume ("up and down"), in millimeters.
 | 
			
		||||
       */
 | 
			
		||||
      n?: number | null
 | 
			
		||||
      /** @description The nozzle temperature max. */
 | 
			
		||||
      nozzle_temp_max?: string | null
 | 
			
		||||
      /** @description The nozzle temperature min. */
 | 
			
		||||
      nozzle_temp_min?: string | null
 | 
			
		||||
      height: number
 | 
			
		||||
      /**
 | 
			
		||||
       * Format: int64
 | 
			
		||||
       * @description The tray remain.
 | 
			
		||||
       * Format: double
 | 
			
		||||
       * @description Width of the volume ("left and right"), in millimeters.
 | 
			
		||||
       */
 | 
			
		||||
      remain?: number | null
 | 
			
		||||
      /** @description The tag uid. */
 | 
			
		||||
      tag_uid?: string | null
 | 
			
		||||
      /** @description The tray color. */
 | 
			
		||||
      tray_color?: string | null
 | 
			
		||||
      /** @description The tray diameter. */
 | 
			
		||||
      tray_diameter?: string | null
 | 
			
		||||
      /** @description The tray id name. */
 | 
			
		||||
      tray_id_name?: string | null
 | 
			
		||||
      /** @description The tray info index. */
 | 
			
		||||
      tray_info_idx?: string | null
 | 
			
		||||
      /** @description The tray sub brands. */
 | 
			
		||||
      tray_sub_brands?: string | null
 | 
			
		||||
      /** @description The tray temperature. */
 | 
			
		||||
      tray_temp?: string | null
 | 
			
		||||
      /** @description The tray time. */
 | 
			
		||||
      tray_time?: string | null
 | 
			
		||||
      /** @description The tray type. */
 | 
			
		||||
      tray_type?: string | null
 | 
			
		||||
      /** @description The tray uuid. */
 | 
			
		||||
      tray_uuid?: string | null
 | 
			
		||||
      /** @description The tray weight. */
 | 
			
		||||
      tray_weight?: string | null
 | 
			
		||||
      /** @description The xcam info. */
 | 
			
		||||
      xcam_info?: string | null
 | 
			
		||||
    } & {
 | 
			
		||||
      [key: string]: unknown
 | 
			
		||||
      width: number
 | 
			
		||||
    }
 | 
			
		||||
    /** @description A print upgrade state. */
 | 
			
		||||
    PrintUpgradeState: {
 | 
			
		||||
      /** @description The consistency request. */
 | 
			
		||||
      consistency_request?: boolean | null
 | 
			
		||||
      /**
 | 
			
		||||
       * Format: int64
 | 
			
		||||
       * @description The dis state.
 | 
			
		||||
       */
 | 
			
		||||
      dis_state?: number | null
 | 
			
		||||
      /**
 | 
			
		||||
       * Format: int64
 | 
			
		||||
       * @description The error code.
 | 
			
		||||
       */
 | 
			
		||||
      err_code?: number | null
 | 
			
		||||
      /** @description Force upgrade? */
 | 
			
		||||
      force_upgrade?: boolean | null
 | 
			
		||||
      /** @description The message. */
 | 
			
		||||
      message?: string | null
 | 
			
		||||
      /** @description The module. */
 | 
			
		||||
      module?: string | null
 | 
			
		||||
      /** @description The new version list. */
 | 
			
		||||
      new_ver_list?: unknown[] | null
 | 
			
		||||
      /**
 | 
			
		||||
       * Format: int64
 | 
			
		||||
       * @description The new version state.
 | 
			
		||||
       */
 | 
			
		||||
      new_version_state?: number | null
 | 
			
		||||
      /** @description The progress. */
 | 
			
		||||
      progress?: string | null
 | 
			
		||||
      /**
 | 
			
		||||
       * Format: int64
 | 
			
		||||
       * @description The sequence id.
 | 
			
		||||
       */
 | 
			
		||||
      sequence_id?: number | null
 | 
			
		||||
      /** @description The status. */
 | 
			
		||||
      status?: string | null
 | 
			
		||||
    } & {
 | 
			
		||||
      [key: string]: unknown
 | 
			
		||||
    }
 | 
			
		||||
    /** @description The print upload. */
 | 
			
		||||
    PrintUpload: {
 | 
			
		||||
      /** @description The message. */
 | 
			
		||||
      message: string
 | 
			
		||||
      /**
 | 
			
		||||
       * Format: int64
 | 
			
		||||
       * @description The progress.
 | 
			
		||||
       */
 | 
			
		||||
      progress: number
 | 
			
		||||
      /** @description The status. */
 | 
			
		||||
      status: string
 | 
			
		||||
    } & {
 | 
			
		||||
      [key: string]: unknown
 | 
			
		||||
    }
 | 
			
		||||
    /** @description A reason for a message. */
 | 
			
		||||
    Reason:
 | 
			
		||||
      | 'SUCCESS'
 | 
			
		||||
      | 'FAIL'
 | 
			
		||||
      | {
 | 
			
		||||
          UNKNOWN: string
 | 
			
		||||
        }
 | 
			
		||||
    /** @description The result of a message. */
 | 
			
		||||
    Result: 'SUCCESS' | 'FAIL'
 | 
			
		||||
    /** @description A security message. */
 | 
			
		||||
    Security: {
 | 
			
		||||
      /**
 | 
			
		||||
       * Format: int64
 | 
			
		||||
       * @description The address.
 | 
			
		||||
       */
 | 
			
		||||
      address: number
 | 
			
		||||
      /** @description The chip sn. */
 | 
			
		||||
      chip_sn: string
 | 
			
		||||
      /**
 | 
			
		||||
       * Format: int64
 | 
			
		||||
       * @description The chip sn length.
 | 
			
		||||
       */
 | 
			
		||||
      chipsn_len: number
 | 
			
		||||
      /** @enum {string} */
 | 
			
		||||
      command: 'get_sn'
 | 
			
		||||
      /**
 | 
			
		||||
       * Format: int64
 | 
			
		||||
       * @description The length.
 | 
			
		||||
       */
 | 
			
		||||
      length: number
 | 
			
		||||
      /** @description The module. */
 | 
			
		||||
      module: string
 | 
			
		||||
      /** @description The reason for the message. */
 | 
			
		||||
      reason?: components['schemas']['Reason'] | null
 | 
			
		||||
      /** @description The sequence id. */
 | 
			
		||||
      sequence_id: components['schemas']['SequenceId']
 | 
			
		||||
      /** @description The serial number. */
 | 
			
		||||
      sn: string
 | 
			
		||||
      /** @description The status. */
 | 
			
		||||
      status: string
 | 
			
		||||
    } & {
 | 
			
		||||
      [key: string]: unknown
 | 
			
		||||
    }
 | 
			
		||||
    /** @description The sequence id type. */
 | 
			
		||||
    SequenceId: string | number
 | 
			
		||||
    /** @description A system command. */
 | 
			
		||||
    System:
 | 
			
		||||
      | ({
 | 
			
		||||
          /** @enum {string} */
 | 
			
		||||
          command: 'ledctrl'
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: uint32
 | 
			
		||||
           * @description The interval time.
 | 
			
		||||
           */
 | 
			
		||||
          interval_time: number
 | 
			
		||||
          /** @description The LED mode. */
 | 
			
		||||
          led_mode: components['schemas']['LedMode']
 | 
			
		||||
          /** @description The LED node. */
 | 
			
		||||
          led_node: components['schemas']['LedNode']
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: uint32
 | 
			
		||||
           * @description The LED off time.
 | 
			
		||||
           */
 | 
			
		||||
          led_off_time: number
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: uint32
 | 
			
		||||
           * @description The LED on time.
 | 
			
		||||
           */
 | 
			
		||||
          led_on_time: number
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: uint32
 | 
			
		||||
           * @description The loop times.
 | 
			
		||||
           */
 | 
			
		||||
          loop_times: number
 | 
			
		||||
          /** @description The reason for the message. */
 | 
			
		||||
          reason?: components['schemas']['Reason'] | null
 | 
			
		||||
          /** @description The result of the command. */
 | 
			
		||||
          result: components['schemas']['Result']
 | 
			
		||||
          /** @description The sequence id. */
 | 
			
		||||
          sequence_id: components['schemas']['SequenceId']
 | 
			
		||||
        } & {
 | 
			
		||||
          [key: string]: unknown
 | 
			
		||||
        })
 | 
			
		||||
      | ({
 | 
			
		||||
          /** @description The accessory type. */
 | 
			
		||||
          accessory_type: components['schemas']['AccessoryType']
 | 
			
		||||
          /** @description The aux part fan. */
 | 
			
		||||
          aux_part_fan: boolean
 | 
			
		||||
          /** @enum {string} */
 | 
			
		||||
          command: 'get_accessories'
 | 
			
		||||
          /**
 | 
			
		||||
           * Format: double
 | 
			
		||||
           * @description The nozzle diameter.
 | 
			
		||||
           */
 | 
			
		||||
          nozzle_diameter: number
 | 
			
		||||
          /** @description The nozzle type. */
 | 
			
		||||
          nozzle_type: components['schemas']['NozzleType']
 | 
			
		||||
          /** @description The reason for the message. */
 | 
			
		||||
          reason?: components['schemas']['Reason'] | null
 | 
			
		||||
          /** @description The result of the command. */
 | 
			
		||||
          result: components['schemas']['Result']
 | 
			
		||||
          /** @description The sequence id. */
 | 
			
		||||
          sequence_id: components['schemas']['SequenceId']
 | 
			
		||||
        } & {
 | 
			
		||||
          [key: string]: unknown
 | 
			
		||||
        })
 | 
			
		||||
  }
 | 
			
		||||
  responses: {
 | 
			
		||||
    /** @description Error */
 | 
			
		||||
@ -980,9 +233,7 @@ export interface operations {
 | 
			
		||||
          [name: string]: unknown
 | 
			
		||||
        }
 | 
			
		||||
        content: {
 | 
			
		||||
          'application/json': {
 | 
			
		||||
            [key: string]: components['schemas']['Machine']
 | 
			
		||||
          }
 | 
			
		||||
          'application/json': components['schemas']['MachineInfoResponse'][]
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      '4XX': components['responses']['Error']
 | 
			
		||||
@ -1007,7 +258,7 @@ export interface operations {
 | 
			
		||||
          [name: string]: unknown
 | 
			
		||||
        }
 | 
			
		||||
        content: {
 | 
			
		||||
          'application/json': components['schemas']['Message']
 | 
			
		||||
          'application/json': components['schemas']['MachineInfoResponse']
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      '4XX': components['responses']['Error']
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,16 @@
 | 
			
		||||
import { isDesktop } from './isDesktop'
 | 
			
		||||
import { components } from './machine-api'
 | 
			
		||||
 | 
			
		||||
export type MachinesListing = {
 | 
			
		||||
  [key: string]: components['schemas']['Machine']
 | 
			
		||||
}
 | 
			
		||||
export type MachinesListing = Array<
 | 
			
		||||
  components['schemas']['MachineInfoResponse']
 | 
			
		||||
>
 | 
			
		||||
 | 
			
		||||
export class MachineManager {
 | 
			
		||||
  private _isDesktop: boolean = isDesktop()
 | 
			
		||||
  private _machines: MachinesListing = {}
 | 
			
		||||
  private _machines: MachinesListing = []
 | 
			
		||||
  private _machineApiIp: string | null = null
 | 
			
		||||
  private _currentMachine: components['schemas']['Machine'] | null = null
 | 
			
		||||
  private _currentMachine: components['schemas']['MachineInfoResponse'] | null =
 | 
			
		||||
    null
 | 
			
		||||
 | 
			
		||||
  constructor() {
 | 
			
		||||
    if (!this._isDesktop) {
 | 
			
		||||
@ -44,7 +45,7 @@ export class MachineManager {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  machineCount(): number {
 | 
			
		||||
    return Object.keys(this._machines).length
 | 
			
		||||
    return this._machines.length
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get machineApiIp(): string | null {
 | 
			
		||||
@ -64,11 +65,13 @@ export class MachineManager {
 | 
			
		||||
    return 'Machine API server was discovered, but no machines are available'
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get currentMachine(): components['schemas']['Machine'] | null {
 | 
			
		||||
  get currentMachine(): components['schemas']['MachineInfoResponse'] | null {
 | 
			
		||||
    return this._currentMachine
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  set currentMachine(machine: components['schemas']['Machine'] | null) {
 | 
			
		||||
  set currentMachine(
 | 
			
		||||
    machine: components['schemas']['MachineInfoResponse'] | null
 | 
			
		||||
  ) {
 | 
			
		||||
    this._currentMachine = machine
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -90,12 +90,24 @@ export const fileLoader: LoaderFunction = async (
 | 
			
		||||
    let code = ''
 | 
			
		||||
 | 
			
		||||
    if (!urlObj.pathname.endsWith('/settings')) {
 | 
			
		||||
      if (!currentFileName || !currentFilePath || !projectName) {
 | 
			
		||||
      const fallbackFile = isDesktop()
 | 
			
		||||
        ? (await getProjectInfo(projectPath)).default_file
 | 
			
		||||
        : ''
 | 
			
		||||
      let fileExists = isDesktop()
 | 
			
		||||
      if (currentFilePath && fileExists) {
 | 
			
		||||
        try {
 | 
			
		||||
          await window.electron.stat(currentFilePath)
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
          if (e === 'ENOENT') {
 | 
			
		||||
            fileExists = false
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!fileExists || !currentFileName || !currentFilePath || !projectName) {
 | 
			
		||||
        return redirect(
 | 
			
		||||
          `${PATHS.FILE}/${encodeURIComponent(
 | 
			
		||||
            isDesktop()
 | 
			
		||||
              ? (await getProjectInfo(projectPath)).default_file
 | 
			
		||||
              : params.id + '/' + PROJECT_ENTRYPOINT
 | 
			
		||||
            isDesktop() ? fallbackFile : params.id + '/' + PROJECT_ENTRYPOINT
 | 
			
		||||
          )}`
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@ -52,7 +52,7 @@ const kittycad = (access: string, args: any) =>
 | 
			
		||||
// bite our butts.
 | 
			
		||||
const listMachines = async (): Promise<MachinesListing> => {
 | 
			
		||||
  const machineApi = await ipcRenderer.invoke('find_machine_api')
 | 
			
		||||
  if (!machineApi) return {}
 | 
			
		||||
  if (!machineApi) return []
 | 
			
		||||
 | 
			
		||||
  return fetch(`http://${machineApi}/machines`).then((resp) => resp.json())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user