initial migration from zustand (#2852)
* inital migration with a couple lingering concerns
* move is stream ready back
* put htmlRef back in useStore
* final tidy of useStore
* test tweaks
* tweak more
* more test tweaks
* fmt
* test tweaks
* attempts at fixing 'Basic default modeling and sketch hotkeys work'
* more tries
* 😭
* try again
* fmt
			
			
This commit is contained in:
		@ -30,6 +30,8 @@ import { EngineCommand } from 'lang/std/engineConnection'
 | 
			
		||||
import { onboardingPaths } from 'routes/Onboarding/paths'
 | 
			
		||||
import { bracket } from 'lib/exampleKcl'
 | 
			
		||||
 | 
			
		||||
const PERSIST_MODELING_CONTEXT = 'persistModelingContext'
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
debug helper: unfortunately we do rely on exact coord mouse clicks in a few places
 | 
			
		||||
just from the nature of the stream, running the test with debugger and pasting the below
 | 
			
		||||
@ -198,35 +200,17 @@ async function doBasicSketch(page: Page, openPanes: string[]) {
 | 
			
		||||
 | 
			
		||||
test.describe('Basic sketch', () => {
 | 
			
		||||
  test('code pane open at start', async ({ page }) => {
 | 
			
		||||
    // Load the app with the code panes
 | 
			
		||||
    await page.addInitScript(async () => {
 | 
			
		||||
      localStorage.setItem(
 | 
			
		||||
        'store',
 | 
			
		||||
        JSON.stringify({
 | 
			
		||||
          state: {
 | 
			
		||||
            openPanes: ['code'],
 | 
			
		||||
          },
 | 
			
		||||
          version: 0,
 | 
			
		||||
        })
 | 
			
		||||
      )
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    await doBasicSketch(page, ['code'])
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test('code pane closed at start', async ({ page }) => {
 | 
			
		||||
    // Load the app with the code panes
 | 
			
		||||
    await page.addInitScript(async () => {
 | 
			
		||||
    await page.addInitScript(async (persistModelingContext) => {
 | 
			
		||||
      localStorage.setItem(
 | 
			
		||||
        'store',
 | 
			
		||||
        JSON.stringify({
 | 
			
		||||
          state: {
 | 
			
		||||
            openPanes: [],
 | 
			
		||||
          },
 | 
			
		||||
          version: 0,
 | 
			
		||||
        })
 | 
			
		||||
        persistModelingContext,
 | 
			
		||||
        JSON.stringify({ openPanes: [] })
 | 
			
		||||
      )
 | 
			
		||||
    })
 | 
			
		||||
    }, PERSIST_MODELING_CONTEXT)
 | 
			
		||||
    await doBasicSketch(page, [])
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
@ -457,7 +441,9 @@ test.describe('Testing Camera Movement', () => {
 | 
			
		||||
      // await u.canvasLocator.hover({position: {x: 700, y: 325}})
 | 
			
		||||
      await page.mouse.move(700, 325)
 | 
			
		||||
      await page.waitForTimeout(100)
 | 
			
		||||
      await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
 | 
			
		||||
      await expect(page.getByTestId('hover-highlight')).not.toBeVisible({
 | 
			
		||||
        timeout: 10_000,
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
 | 
			
		||||
@ -465,12 +451,18 @@ test.describe('Testing Camera Movement', () => {
 | 
			
		||||
    await page.waitForTimeout(100)
 | 
			
		||||
    // hover over horizontal line
 | 
			
		||||
    await u.canvasLocator.hover({ position: { x: 800, y } })
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight')).toBeVisible()
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
 | 
			
		||||
      timeout: 10_000,
 | 
			
		||||
    })
 | 
			
		||||
    await page.waitForTimeout(200)
 | 
			
		||||
 | 
			
		||||
    await hoverOverNothing()
 | 
			
		||||
    await page.waitForTimeout(200)
 | 
			
		||||
    // hover over vertical line
 | 
			
		||||
    await u.canvasLocator.hover({ position: { x, y: 325 } })
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight')).toBeVisible()
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
 | 
			
		||||
      timeout: 10_000,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    await hoverOverNothing()
 | 
			
		||||
 | 
			
		||||
@ -479,22 +471,28 @@ test.describe('Testing Camera Movement', () => {
 | 
			
		||||
    await page.waitForTimeout(400)
 | 
			
		||||
 | 
			
		||||
    await hoverOverNothing()
 | 
			
		||||
    await page.waitForTimeout(100)
 | 
			
		||||
    await page.waitForTimeout(200)
 | 
			
		||||
    // hover over horizontal line
 | 
			
		||||
    await page.mouse.move(858, y, { steps: 5 })
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight').first()).toBeVisible()
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
 | 
			
		||||
      timeout: 10_000,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    await hoverOverNothing()
 | 
			
		||||
 | 
			
		||||
    // hover over vertical line
 | 
			
		||||
    await page.mouse.move(x, 325)
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight').first()).toBeVisible()
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
 | 
			
		||||
      timeout: 10_000,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    await hoverOverNothing()
 | 
			
		||||
 | 
			
		||||
    // hover over vertical line
 | 
			
		||||
    await page.mouse.move(857, y)
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight').first()).toBeVisible()
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
 | 
			
		||||
      timeout: 10_000,
 | 
			
		||||
    })
 | 
			
		||||
    // now click it
 | 
			
		||||
    await page.mouse.click(857, y)
 | 
			
		||||
 | 
			
		||||
@ -511,27 +509,36 @@ test.describe('Testing Camera Movement', () => {
 | 
			
		||||
 | 
			
		||||
    await page.waitForTimeout(100)
 | 
			
		||||
    await page.mouse.move(x, 419, { steps: 5 })
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight').first()).toBeVisible()
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
 | 
			
		||||
      timeout: 10_000,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    await hoverOverNothing()
 | 
			
		||||
 | 
			
		||||
    await page.mouse.move(855, y)
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight').first()).toBeVisible()
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
 | 
			
		||||
      timeout: 10_000,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    await hoverOverNothing()
 | 
			
		||||
 | 
			
		||||
    await page.getByRole('button', { name: 'Exit Sketch' }).click()
 | 
			
		||||
    await page.waitForTimeout(400)
 | 
			
		||||
    await page.waitForTimeout(200)
 | 
			
		||||
 | 
			
		||||
    await hoverOverNothing()
 | 
			
		||||
    await page.waitForTimeout(200)
 | 
			
		||||
 | 
			
		||||
    await page.mouse.move(x, 419)
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight').first()).toBeVisible()
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
 | 
			
		||||
      timeout: 10_000,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    await hoverOverNothing()
 | 
			
		||||
 | 
			
		||||
    await page.mouse.move(855, y)
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight').first()).toBeVisible()
 | 
			
		||||
    await expect(page.getByTestId('hover-highlight').first()).toBeVisible({
 | 
			
		||||
      timeout: 10_000,
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -3734,17 +3741,12 @@ test.describe('Can edit segments by dragging their handles', () => {
 | 
			
		||||
 | 
			
		||||
  test('code pane closed at start-handles', async ({ page }) => {
 | 
			
		||||
    // Load the app with the code panes
 | 
			
		||||
    await page.addInitScript(async () => {
 | 
			
		||||
    await page.addInitScript(async (persistModelingContext) => {
 | 
			
		||||
      localStorage.setItem(
 | 
			
		||||
        'store',
 | 
			
		||||
        JSON.stringify({
 | 
			
		||||
          state: {
 | 
			
		||||
            openPanes: [],
 | 
			
		||||
          },
 | 
			
		||||
          version: 0,
 | 
			
		||||
        })
 | 
			
		||||
        persistModelingContext,
 | 
			
		||||
        JSON.stringify({ openPanes: [] })
 | 
			
		||||
      )
 | 
			
		||||
    })
 | 
			
		||||
    }, PERSIST_MODELING_CONTEXT)
 | 
			
		||||
    await doEditSegmentsByDraggingHandle(page, [])
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
@ -5081,6 +5083,7 @@ const part002 = startSketchOn('XZ')
 | 
			
		||||
  test('Horizontally constrained line remains selected after applying constraint', async ({
 | 
			
		||||
    page,
 | 
			
		||||
  }) => {
 | 
			
		||||
    test.setTimeout(70_000)
 | 
			
		||||
    await page.addInitScript(async () => {
 | 
			
		||||
      localStorage.setItem(
 | 
			
		||||
        'persistCode',
 | 
			
		||||
@ -5096,6 +5099,9 @@ const part002 = startSketchOn('XZ')
 | 
			
		||||
    await u.waitForAuthSkipAppStart()
 | 
			
		||||
 | 
			
		||||
    await page.getByText('line([3.79, 2.68], %, $seg01)').click()
 | 
			
		||||
    await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeEnabled(
 | 
			
		||||
      { timeout: 10_000 }
 | 
			
		||||
    )
 | 
			
		||||
    await page.getByRole('button', { name: 'Edit Sketch' }).click()
 | 
			
		||||
 | 
			
		||||
    await page.waitForTimeout(100)
 | 
			
		||||
@ -5136,12 +5142,16 @@ const part002 = startSketchOn('XZ')
 | 
			
		||||
      await u.getGreatestPixDiff(lineAfter, TEST_COLORS.BLUE)
 | 
			
		||||
    ).toBeLessThan(3)
 | 
			
		||||
 | 
			
		||||
    await page.waitForTimeout(300)
 | 
			
		||||
    await page
 | 
			
		||||
      .getByRole('button', {
 | 
			
		||||
        name: 'Constraints',
 | 
			
		||||
      })
 | 
			
		||||
      .click()
 | 
			
		||||
    await page.getByRole('button', { name: 'length', exact: true }).click()
 | 
			
		||||
    // await expect(page.getByRole('button', { name: 'length', exact: true })).toBeVisible()
 | 
			
		||||
    await page.waitForTimeout(200)
 | 
			
		||||
    // await page.getByRole('button', { name: 'length', exact: true }).click()
 | 
			
		||||
    await page.locator('[data-testid="length"]').click()
 | 
			
		||||
 | 
			
		||||
    await page.getByLabel('length Value').fill('10')
 | 
			
		||||
    await page.getByRole('button', { name: 'Add constraining value' }).click()
 | 
			
		||||
@ -6519,23 +6529,28 @@ test('Basic default modeling and sketch hotkeys work', async ({ page }) => {
 | 
			
		||||
  // Test that the hotkeys do nothing when
 | 
			
		||||
  // focus is on the code pane
 | 
			
		||||
  await codePane.click()
 | 
			
		||||
  await page.keyboard.press('/')
 | 
			
		||||
  await page.keyboard.press('/')
 | 
			
		||||
  await page.keyboard.press('s')
 | 
			
		||||
  await page.keyboard.press('l')
 | 
			
		||||
  await page.keyboard.press('a')
 | 
			
		||||
  await page.keyboard.press('e')
 | 
			
		||||
  await expect(page.locator('.cm-content')).toHaveText('slae')
 | 
			
		||||
  await expect(page.locator('.cm-content')).toHaveText('//slae')
 | 
			
		||||
  await page.keyboard.press('Meta+/')
 | 
			
		||||
 | 
			
		||||
  await page.waitForTimeout(2000)
 | 
			
		||||
  // Test these hotkeys perform actions when
 | 
			
		||||
  // focus is on the canvas
 | 
			
		||||
  await page.mouse.move(600, 250)
 | 
			
		||||
  await page.mouse.click(600, 250)
 | 
			
		||||
  // Start a sketch
 | 
			
		||||
  await page.keyboard.press('s')
 | 
			
		||||
  await page.mouse.move(800, 300)
 | 
			
		||||
  await page.waitForTimeout(2000)
 | 
			
		||||
  await page.mouse.move(800, 300, { steps: 5 })
 | 
			
		||||
  await page.mouse.click(800, 300)
 | 
			
		||||
  await page.waitForTimeout(1000)
 | 
			
		||||
  await expect(lineButton).toHaveAttribute('aria-pressed', 'true')
 | 
			
		||||
  await page.waitForTimeout(2000)
 | 
			
		||||
  await expect(lineButton).toHaveAttribute('aria-pressed', 'true', {
 | 
			
		||||
    timeout: 15_000,
 | 
			
		||||
  })
 | 
			
		||||
  /**
 | 
			
		||||
   * TODO: There is a bug somewhere that causes this test to fail
 | 
			
		||||
   * if you toggle the codePane closed before your trigger the
 | 
			
		||||
@ -6546,10 +6561,13 @@ test('Basic default modeling and sketch hotkeys work', async ({ page }) => {
 | 
			
		||||
   * https://discuss.codemirror.net/t/how-to-force-unfocus-of-the-codemirror-element-in-safari/8095/3
 | 
			
		||||
   */
 | 
			
		||||
  await codePaneButton.click()
 | 
			
		||||
  await expect(u.codeLocator).not.toBeVisible()
 | 
			
		||||
  await page.waitForTimeout(300)
 | 
			
		||||
 | 
			
		||||
  // Draw a line
 | 
			
		||||
  await page.mouse.move(700, 200, { steps: 5 })
 | 
			
		||||
  await page.mouse.click(700, 200)
 | 
			
		||||
  await page.waitForTimeout(300)
 | 
			
		||||
  await page.mouse.move(800, 250, { steps: 5 })
 | 
			
		||||
  await page.mouse.click(800, 250)
 | 
			
		||||
  // Unequip line tool
 | 
			
		||||
@ -6557,7 +6575,9 @@ test('Basic default modeling and sketch hotkeys work', async ({ page }) => {
 | 
			
		||||
  await expect(lineButton).not.toHaveAttribute('aria-pressed', 'true')
 | 
			
		||||
  // Equip arc tool
 | 
			
		||||
  await page.keyboard.press('a')
 | 
			
		||||
  await expect(arcButton).toHaveAttribute('aria-pressed', 'true')
 | 
			
		||||
  await expect(arcButton).toHaveAttribute('aria-pressed', 'true', {
 | 
			
		||||
    timeout: 10_000,
 | 
			
		||||
  })
 | 
			
		||||
  await page.mouse.move(1000, 100, { steps: 5 })
 | 
			
		||||
  await page.mouse.click(1000, 100)
 | 
			
		||||
  await page.keyboard.press('Escape')
 | 
			
		||||
@ -6582,8 +6602,13 @@ test('Basic default modeling and sketch hotkeys work', async ({ page }) => {
 | 
			
		||||
  await page.waitForTimeout(100)
 | 
			
		||||
  await page.mouse.move(800, 200, { steps: 5 })
 | 
			
		||||
  await page.mouse.click(800, 200)
 | 
			
		||||
  await page.waitForTimeout(100)
 | 
			
		||||
  await page.waitForTimeout(300)
 | 
			
		||||
  await expect(page.getByRole('button', { name: 'Continue' })).toBeVisible()
 | 
			
		||||
  await page.getByRole('button', { name: 'Continue' }).click()
 | 
			
		||||
  await page.waitForTimeout(300)
 | 
			
		||||
  await expect(
 | 
			
		||||
    page.getByRole('button', { name: 'Submit command' })
 | 
			
		||||
  ).toBeVisible()
 | 
			
		||||
  await page.getByRole('button', { name: 'Submit command' }).click()
 | 
			
		||||
 | 
			
		||||
  await codePaneButton.click()
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										21
									
								
								src/App.tsx
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								src/App.tsx
									
									
									
									
									
								
							@ -1,6 +1,5 @@
 | 
			
		||||
import { MouseEventHandler, useEffect, useRef } from 'react'
 | 
			
		||||
import { uuidv4 } from 'lib/utils'
 | 
			
		||||
import { useStore } from './useStore'
 | 
			
		||||
import { useHotKeyListener } from './hooks/useHotKeyListener'
 | 
			
		||||
import { Stream } from './components/Stream'
 | 
			
		||||
import { EngineCommand } from './lang/std/engineConnection'
 | 
			
		||||
@ -26,6 +25,7 @@ import ModalContainer from 'react-modal-promise'
 | 
			
		||||
import useHotkeyWrapper from 'lib/hotkeyWrapper'
 | 
			
		||||
import Gizmo from 'components/Gizmo'
 | 
			
		||||
import { CoreDumpManager } from 'lib/coredump'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
 | 
			
		||||
export function App() {
 | 
			
		||||
  useRefreshSettings(paths.FILE + 'SETTINGS')
 | 
			
		||||
@ -44,13 +44,10 @@ export function App() {
 | 
			
		||||
  }, [projectName, projectPath])
 | 
			
		||||
 | 
			
		||||
  useHotKeyListener()
 | 
			
		||||
  const { buttonDownInStream, didDragInStream, streamDimensions, setHtmlRef } =
 | 
			
		||||
    useStore((s) => ({
 | 
			
		||||
      buttonDownInStream: s.buttonDownInStream,
 | 
			
		||||
      didDragInStream: s.didDragInStream,
 | 
			
		||||
      streamDimensions: s.streamDimensions,
 | 
			
		||||
      setHtmlRef: s.setHtmlRef,
 | 
			
		||||
    }))
 | 
			
		||||
  const { context } = useModelingContext()
 | 
			
		||||
  const { setHtmlRef } = useStore((s) => ({
 | 
			
		||||
    setHtmlRef: s.setHtmlRef,
 | 
			
		||||
  }))
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setHtmlRef(ref)
 | 
			
		||||
@ -81,7 +78,7 @@ export function App() {
 | 
			
		||||
    (p) => p === onboardingStatus.current
 | 
			
		||||
  )
 | 
			
		||||
    ? 'opacity-20'
 | 
			
		||||
    : didDragInStream
 | 
			
		||||
    : context.store?.didDragInStream
 | 
			
		||||
    ? 'opacity-40'
 | 
			
		||||
    : ''
 | 
			
		||||
 | 
			
		||||
@ -99,11 +96,11 @@ export function App() {
 | 
			
		||||
      clientX: e.clientX,
 | 
			
		||||
      clientY: e.clientY,
 | 
			
		||||
      el: e.currentTarget,
 | 
			
		||||
      ...streamDimensions,
 | 
			
		||||
      ...context.store?.streamDimensions,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const newCmdId = uuidv4()
 | 
			
		||||
    if (buttonDownInStream === undefined) {
 | 
			
		||||
    if (context.store?.buttonDownInStream === undefined) {
 | 
			
		||||
      debounceSocketSend({
 | 
			
		||||
        type: 'modeling_cmd_req',
 | 
			
		||||
        cmd: {
 | 
			
		||||
@ -125,7 +122,7 @@ export function App() {
 | 
			
		||||
        className={
 | 
			
		||||
          'transition-opacity transition-duration-75 ' +
 | 
			
		||||
          paneOpacity +
 | 
			
		||||
          (buttonDownInStream ? ' pointer-events-none' : '')
 | 
			
		||||
          (context.store?.buttonDownInStream ? ' pointer-events-none' : '')
 | 
			
		||||
        }
 | 
			
		||||
        project={{ project, file }}
 | 
			
		||||
        enableMenu={true}
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,14 @@ import LspProvider from 'components/LspProvider'
 | 
			
		||||
import { KclContextProvider } from 'lang/KclProvider'
 | 
			
		||||
import { BROWSER_PROJECT_NAME } from 'lib/constants'
 | 
			
		||||
import { getState, setState } from 'lib/tauri'
 | 
			
		||||
import { CoreDumpManager } from 'lib/coredump'
 | 
			
		||||
import { engineCommandManager } from 'lib/singletons'
 | 
			
		||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
 | 
			
		||||
import useHotkeyWrapper from 'lib/hotkeyWrapper'
 | 
			
		||||
import toast from 'react-hot-toast'
 | 
			
		||||
import { coreDump } from 'lang/wasm'
 | 
			
		||||
import { useMemo } from 'react'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
 | 
			
		||||
const router = createBrowserRouter([
 | 
			
		||||
  {
 | 
			
		||||
@ -87,6 +95,7 @@ const router = createBrowserRouter([
 | 
			
		||||
          <Auth>
 | 
			
		||||
            <FileMachineProvider>
 | 
			
		||||
              <ModelingMachineProvider>
 | 
			
		||||
                <CoreDump />
 | 
			
		||||
                <Outlet />
 | 
			
		||||
                <App />
 | 
			
		||||
                <CommandBar />
 | 
			
		||||
@ -165,3 +174,33 @@ export const Router = () => {
 | 
			
		||||
    </NetworkContext.Provider>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function CoreDump() {
 | 
			
		||||
  const { auth } = useSettingsAuthContext()
 | 
			
		||||
  const token = auth?.context?.token
 | 
			
		||||
  const { htmlRef } = useStore((s) => ({
 | 
			
		||||
    htmlRef: s.htmlRef,
 | 
			
		||||
  }))
 | 
			
		||||
  const coreDumpManager = useMemo(
 | 
			
		||||
    () => new CoreDumpManager(engineCommandManager, htmlRef, token),
 | 
			
		||||
    []
 | 
			
		||||
  )
 | 
			
		||||
  useHotkeyWrapper(['meta + shift + .'], () => {
 | 
			
		||||
    toast.promise(
 | 
			
		||||
      coreDump(coreDumpManager, true),
 | 
			
		||||
      {
 | 
			
		||||
        loading: 'Starting core dump...',
 | 
			
		||||
        success: 'Core dump completed successfully',
 | 
			
		||||
        error: 'Error while exporting core dump',
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        success: {
 | 
			
		||||
          // Note: this extended duration is especially important for Playwright e2e testing
 | 
			
		||||
          // default duration is 2000 - https://react-hot-toast.com/docs/toast#default-durations
 | 
			
		||||
          duration: 6000,
 | 
			
		||||
        },
 | 
			
		||||
      }
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  return null
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -8,10 +8,10 @@ import { NetworkHealthState } from 'hooks/useNetworkStatus'
 | 
			
		||||
import { ActionButton } from 'components/ActionButton'
 | 
			
		||||
import { isSingleCursorInPipe } from 'lang/queryAst'
 | 
			
		||||
import { useKclContext } from 'lang/KclProvider'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
import { ActionButtonDropdown } from 'components/ActionButtonDropdown'
 | 
			
		||||
import { useHotkeys } from 'react-hotkeys-hook'
 | 
			
		||||
import Tooltip from 'components/Tooltip'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
 | 
			
		||||
export function Toolbar({
 | 
			
		||||
  className = '',
 | 
			
		||||
 | 
			
		||||
@ -37,7 +37,7 @@ import { Dialog, Popover, Transition } from '@headlessui/react'
 | 
			
		||||
import { LineInputsType } from 'lang/std/sketchcombos'
 | 
			
		||||
import toast from 'react-hot-toast'
 | 
			
		||||
import { InstanceProps, create } from 'react-modal-promise'
 | 
			
		||||
import { executeAst } from 'useStore'
 | 
			
		||||
import { executeAst } from 'lang/langHelpers'
 | 
			
		||||
import {
 | 
			
		||||
  deleteSegmentFromPipeExpression,
 | 
			
		||||
  makeRemoveSingleConstraintInput,
 | 
			
		||||
 | 
			
		||||
@ -58,7 +58,7 @@ import {
 | 
			
		||||
  editorManager,
 | 
			
		||||
} from 'lib/singletons'
 | 
			
		||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
 | 
			
		||||
import { executeAst, useStore } from 'useStore'
 | 
			
		||||
import { executeAst } from 'lang/langHelpers'
 | 
			
		||||
import {
 | 
			
		||||
  createArcGeometry,
 | 
			
		||||
  dashedStraight,
 | 
			
		||||
@ -1444,11 +1444,10 @@ export class SceneEntities {
 | 
			
		||||
        selected.material.color = defaultPlaneColor(type)
 | 
			
		||||
      },
 | 
			
		||||
      onClick: async (args) => {
 | 
			
		||||
        const { streamDimensions } = useStore.getState()
 | 
			
		||||
        const { entity_id } = await sendSelectEventToEngine(
 | 
			
		||||
          args?.mouseEvent,
 | 
			
		||||
          document.getElementById('video-stream') as HTMLVideoElement,
 | 
			
		||||
          streamDimensions
 | 
			
		||||
          sceneInfra._streamDimensions
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        let _entity_id = entity_id
 | 
			
		||||
 | 
			
		||||
@ -103,6 +103,10 @@ export class SceneInfra {
 | 
			
		||||
  _baseUnit: BaseUnit = 'mm'
 | 
			
		||||
  _baseUnitMultiplier = 1
 | 
			
		||||
  _theme: Themes = Themes.System
 | 
			
		||||
  _streamDimensions: { streamWidth: number; streamHeight: number } = {
 | 
			
		||||
    streamWidth: 1280,
 | 
			
		||||
    streamHeight: 720,
 | 
			
		||||
  }
 | 
			
		||||
  extraSegmentTexture: Texture
 | 
			
		||||
  lastMouseState: MouseState = { type: 'idle' }
 | 
			
		||||
  onDragStartCallback: (arg: OnDragCallbackArgs) => void = () => {}
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst'
 | 
			
		||||
import { engineCommandManager, kclManager } from 'lib/singletons'
 | 
			
		||||
import { useKclContext } from 'lang/KclProvider'
 | 
			
		||||
import { useModelingContext } from 'hooks/useModelingContext'
 | 
			
		||||
import { executeAst } from 'useStore'
 | 
			
		||||
import { executeAst } from 'lang/langHelpers'
 | 
			
		||||
import { trap } from 'lib/trap'
 | 
			
		||||
 | 
			
		||||
export const AvailableVars = ({
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,6 @@ import {
 | 
			
		||||
  applyConstraintAngleBetween,
 | 
			
		||||
} from './Toolbar/SetAngleBetween'
 | 
			
		||||
import { applyConstraintAngleLength } from './Toolbar/setAngleLength'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
import {
 | 
			
		||||
  Selections,
 | 
			
		||||
  canExtrudeSelection,
 | 
			
		||||
@ -54,13 +53,7 @@ import {
 | 
			
		||||
  sketchOnExtrudedFace,
 | 
			
		||||
  startSketchOnDefault,
 | 
			
		||||
} from 'lang/modifyAst'
 | 
			
		||||
import {
 | 
			
		||||
  Program,
 | 
			
		||||
  VariableDeclaration,
 | 
			
		||||
  coreDump,
 | 
			
		||||
  parse,
 | 
			
		||||
  recast,
 | 
			
		||||
} from 'lang/wasm'
 | 
			
		||||
import { Program, VariableDeclaration, parse, recast } from 'lang/wasm'
 | 
			
		||||
import {
 | 
			
		||||
  getNodeFromPath,
 | 
			
		||||
  getNodePathFromSourceRange,
 | 
			
		||||
@ -72,11 +65,9 @@ import { exportFromEngine } from 'lib/exportFromEngine'
 | 
			
		||||
import { Models } from '@kittycad/lib/dist/types/src'
 | 
			
		||||
import toast from 'react-hot-toast'
 | 
			
		||||
import { EditorSelection, Transaction } from '@uiw/react-codemirror'
 | 
			
		||||
import { CoreDumpManager } from 'lib/coredump'
 | 
			
		||||
import { useSearchParams } from 'react-router-dom'
 | 
			
		||||
import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls'
 | 
			
		||||
import { getVarNameModal } from 'hooks/useToolbarGuards'
 | 
			
		||||
import useHotkeyWrapper from 'lib/hotkeyWrapper'
 | 
			
		||||
import { uuidv4 } from 'lib/utils'
 | 
			
		||||
import { err, trap } from 'lib/trap'
 | 
			
		||||
import { useCommandsContext } from 'hooks/useCommandsContext'
 | 
			
		||||
@ -112,38 +103,6 @@ export const ModelingMachineProvider = ({
 | 
			
		||||
  let [searchParams] = useSearchParams()
 | 
			
		||||
  const pool = searchParams.get('pool')
 | 
			
		||||
 | 
			
		||||
  useSetupEngineManager(streamRef, token, {
 | 
			
		||||
    pool: pool,
 | 
			
		||||
    theme: theme.current,
 | 
			
		||||
    highlightEdges: highlightEdges.current,
 | 
			
		||||
    enableSSAO: enableSSAO.current,
 | 
			
		||||
    showScaleGrid: showScaleGrid.current,
 | 
			
		||||
  })
 | 
			
		||||
  const { htmlRef } = useStore((s) => ({
 | 
			
		||||
    htmlRef: s.htmlRef,
 | 
			
		||||
  }))
 | 
			
		||||
  const coreDumpManager = new CoreDumpManager(
 | 
			
		||||
    engineCommandManager,
 | 
			
		||||
    htmlRef,
 | 
			
		||||
    token
 | 
			
		||||
  )
 | 
			
		||||
  useHotkeyWrapper(['meta + shift + .'], () => {
 | 
			
		||||
    toast.promise(
 | 
			
		||||
      coreDump(coreDumpManager, true),
 | 
			
		||||
      {
 | 
			
		||||
        loading: 'Starting core dump...',
 | 
			
		||||
        success: 'Core dump completed successfully',
 | 
			
		||||
        error: 'Error while exporting core dump',
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        success: {
 | 
			
		||||
          // Note: this extended duration is especially important for Playwright e2e testing
 | 
			
		||||
          // default duration is 2000 - https://react-hot-toast.com/docs/toast#default-durations
 | 
			
		||||
          duration: 6000,
 | 
			
		||||
        },
 | 
			
		||||
      }
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  const { commandBarState } = useCommandsContext()
 | 
			
		||||
 | 
			
		||||
  // Settings machine setup
 | 
			
		||||
@ -904,6 +863,16 @@ export const ModelingMachineProvider = ({
 | 
			
		||||
    }
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  useSetupEngineManager(streamRef, token, {
 | 
			
		||||
    pool: pool,
 | 
			
		||||
    theme: theme.current,
 | 
			
		||||
    highlightEdges: highlightEdges.current,
 | 
			
		||||
    enableSSAO: enableSSAO.current,
 | 
			
		||||
    modelingSend,
 | 
			
		||||
    modelingContext: modelingState.context,
 | 
			
		||||
    showScaleGrid: showScaleGrid.current,
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    kclManager.registerExecuteCallback(() => {
 | 
			
		||||
      modelingSend({ type: 'Re-execute' })
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
import styles from './ModelingPane.module.css'
 | 
			
		||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
 | 
			
		||||
import { useModelingContext } from 'hooks/useModelingContext'
 | 
			
		||||
 | 
			
		||||
export interface ModelingPaneProps
 | 
			
		||||
  extends React.PropsWithChildren,
 | 
			
		||||
@ -33,11 +33,9 @@ export const ModelingPane = ({
 | 
			
		||||
}: ModelingPaneProps) => {
 | 
			
		||||
  const { settings } = useSettingsAuthContext()
 | 
			
		||||
  const onboardingStatus = settings.context.app.onboardingStatus
 | 
			
		||||
  const { buttonDownInStream } = useStore((s) => ({
 | 
			
		||||
    buttonDownInStream: s.buttonDownInStream,
 | 
			
		||||
  }))
 | 
			
		||||
  const { context } = useModelingContext()
 | 
			
		||||
  const pointerEventsCssClass =
 | 
			
		||||
    buttonDownInStream || onboardingStatus.current === 'camera'
 | 
			
		||||
    context.store?.buttonDownInStream || onboardingStatus.current === 'camera'
 | 
			
		||||
      ? 'pointer-events-none '
 | 
			
		||||
      : 'pointer-events-auto '
 | 
			
		||||
  return (
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,6 @@ import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
 | 
			
		||||
import { Resizable } from 're-resizable'
 | 
			
		||||
import { HTMLAttributes, useCallback, useEffect, useState } from 'react'
 | 
			
		||||
import { useHotkeys } from 'react-hotkeys-hook'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
import { Tab } from '@headlessui/react'
 | 
			
		||||
import {
 | 
			
		||||
  SidebarPane,
 | 
			
		||||
@ -15,6 +14,7 @@ import { ActionIcon } from 'components/ActionIcon'
 | 
			
		||||
import styles from './ModelingSidebar.module.css'
 | 
			
		||||
import { ModelingPane } from './ModelingPane'
 | 
			
		||||
import { isTauri } from 'lib/isTauri'
 | 
			
		||||
import { useModelingContext } from 'hooks/useModelingContext'
 | 
			
		||||
 | 
			
		||||
interface ModelingSidebarProps {
 | 
			
		||||
  paneOpacity: '' | 'opacity-20' | 'opacity-40'
 | 
			
		||||
@ -23,14 +23,11 @@ interface ModelingSidebarProps {
 | 
			
		||||
export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
 | 
			
		||||
  const { settings } = useSettingsAuthContext()
 | 
			
		||||
  const onboardingStatus = settings.context.app.onboardingStatus
 | 
			
		||||
  const { openPanes, buttonDownInStream } = useStore((s) => ({
 | 
			
		||||
    buttonDownInStream: s.buttonDownInStream,
 | 
			
		||||
    openPanes: s.openPanes,
 | 
			
		||||
  }))
 | 
			
		||||
  const { context } = useModelingContext()
 | 
			
		||||
  const pointerEventsCssClass =
 | 
			
		||||
    buttonDownInStream ||
 | 
			
		||||
    context.store?.buttonDownInStream ||
 | 
			
		||||
    onboardingStatus.current === 'camera' ||
 | 
			
		||||
    openPanes.length === 0
 | 
			
		||||
    context.store?.openPanes.length === 0
 | 
			
		||||
      ? 'pointer-events-none '
 | 
			
		||||
      : 'pointer-events-auto '
 | 
			
		||||
 | 
			
		||||
@ -45,7 +42,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
 | 
			
		||||
      maxWidth={800}
 | 
			
		||||
      handleClasses={{
 | 
			
		||||
        right:
 | 
			
		||||
          (openPanes.length === 0 ? 'hidden ' : 'block ') +
 | 
			
		||||
          (context.store?.openPanes.length === 0 ? 'hidden ' : 'block ') +
 | 
			
		||||
          'translate-x-1/2 hover:bg-chalkboard-10 hover:dark:bg-chalkboard-110 bg-transparent transition-colors duration-75 transition-ease-out delay-100 ',
 | 
			
		||||
        left: 'hidden',
 | 
			
		||||
        top: 'hidden',
 | 
			
		||||
@ -82,11 +79,10 @@ function ModelingSidebarSection({
 | 
			
		||||
  const { settings } = useSettingsAuthContext()
 | 
			
		||||
  const showDebugPanel = settings.context.modeling.showDebugPanel
 | 
			
		||||
  const paneIds = panes.map((pane) => pane.id)
 | 
			
		||||
  const { openPanes, setOpenPanes } = useStore((s) => ({
 | 
			
		||||
    openPanes: s.openPanes,
 | 
			
		||||
    setOpenPanes: s.setOpenPanes,
 | 
			
		||||
  }))
 | 
			
		||||
  const foundOpenPane = openPanes.find((pane) => paneIds.includes(pane))
 | 
			
		||||
  const { send, context } = useModelingContext()
 | 
			
		||||
  const foundOpenPane = context.store?.openPanes.find((pane) =>
 | 
			
		||||
    paneIds.includes(pane)
 | 
			
		||||
  )
 | 
			
		||||
  const [currentPane, setCurrentPane] = useState(
 | 
			
		||||
    foundOpenPane || ('none' as SidebarType | 'none')
 | 
			
		||||
  )
 | 
			
		||||
@ -94,17 +90,37 @@ function ModelingSidebarSection({
 | 
			
		||||
  const togglePane = useCallback(
 | 
			
		||||
    (newPane: SidebarType | 'none') => {
 | 
			
		||||
      if (newPane === 'none') {
 | 
			
		||||
        setOpenPanes(openPanes.filter((p) => p !== currentPane))
 | 
			
		||||
        send({
 | 
			
		||||
          type: 'Set context',
 | 
			
		||||
          data: {
 | 
			
		||||
            openPanes: context.store?.openPanes.filter(
 | 
			
		||||
              (p) => p !== currentPane
 | 
			
		||||
            ),
 | 
			
		||||
          },
 | 
			
		||||
        })
 | 
			
		||||
        setCurrentPane('none')
 | 
			
		||||
      } else if (newPane === currentPane) {
 | 
			
		||||
        setCurrentPane('none')
 | 
			
		||||
        setOpenPanes(openPanes.filter((p) => p !== newPane))
 | 
			
		||||
        send({
 | 
			
		||||
          type: 'Set context',
 | 
			
		||||
          data: {
 | 
			
		||||
            openPanes: context.store?.openPanes.filter((p) => p !== newPane),
 | 
			
		||||
          },
 | 
			
		||||
        })
 | 
			
		||||
      } else {
 | 
			
		||||
        setOpenPanes([...openPanes.filter((p) => p !== currentPane), newPane])
 | 
			
		||||
        send({
 | 
			
		||||
          type: 'Set context',
 | 
			
		||||
          data: {
 | 
			
		||||
            openPanes: [
 | 
			
		||||
              ...context.store?.openPanes.filter((p) => p !== currentPane),
 | 
			
		||||
              newPane,
 | 
			
		||||
            ],
 | 
			
		||||
          },
 | 
			
		||||
        })
 | 
			
		||||
        setCurrentPane(newPane)
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    [openPanes, setOpenPanes, currentPane, setCurrentPane]
 | 
			
		||||
    [context.store?.openPanes, send, currentPane, setCurrentPane]
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  // Filter out the debug panel if it's not supposed to be shown
 | 
			
		||||
@ -122,11 +138,11 @@ function ModelingSidebarSection({
 | 
			
		||||
    if (
 | 
			
		||||
      !showDebugPanel.current &&
 | 
			
		||||
      currentPane === 'debug' &&
 | 
			
		||||
      openPanes.includes('debug')
 | 
			
		||||
      context.store?.openPanes.includes('debug')
 | 
			
		||||
    ) {
 | 
			
		||||
      togglePane('debug')
 | 
			
		||||
    }
 | 
			
		||||
  }, [showDebugPanel.current, togglePane, openPanes])
 | 
			
		||||
  }, [showDebugPanel.current, togglePane, context.store?.openPanes])
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className={'group contents ' + className} {...props}>
 | 
			
		||||
@ -152,7 +168,9 @@ function ModelingSidebarSection({
 | 
			
		||||
              : ' border-r-0') +
 | 
			
		||||
            ' p-2 col-start-1 col-span-1 h-fit w-fit flex flex-col items-start gap-2 ' +
 | 
			
		||||
            'bg-chalkboard-10 border border-solid border-chalkboard-20 dark:bg-chalkboard-90 dark:border-chalkboard-80 group-focus-within:border-primary dark:group-focus-within:border-chalkboard-50 ' +
 | 
			
		||||
            (openPanes.length === 1 && currentPane === 'none' ? 'pr-0.5' : '')
 | 
			
		||||
            (context.store?.openPanes.length === 1 && currentPane === 'none'
 | 
			
		||||
              ? 'pr-0.5'
 | 
			
		||||
              : '')
 | 
			
		||||
          }
 | 
			
		||||
        >
 | 
			
		||||
          <Tab key="none" className="sr-only">
 | 
			
		||||
@ -172,7 +190,7 @@ function ModelingSidebarSection({
 | 
			
		||||
          as="article"
 | 
			
		||||
          className={
 | 
			
		||||
            'col-start-2 col-span-1 ' +
 | 
			
		||||
            (openPanes.length === 1
 | 
			
		||||
            (context.store?.openPanes.length === 1
 | 
			
		||||
              ? currentPane !== 'none'
 | 
			
		||||
                ? `row-start-1 row-end-3`
 | 
			
		||||
                : `hidden`
 | 
			
		||||
 | 
			
		||||
@ -2,11 +2,11 @@ import { coreDump } from 'lang/wasm'
 | 
			
		||||
import { CoreDumpManager } from 'lib/coredump'
 | 
			
		||||
import { CustomIcon } from './CustomIcon'
 | 
			
		||||
import { engineCommandManager } from 'lib/singletons'
 | 
			
		||||
import React from 'react'
 | 
			
		||||
import React, { useMemo } from 'react'
 | 
			
		||||
import toast from 'react-hot-toast'
 | 
			
		||||
import Tooltip from './Tooltip'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
 | 
			
		||||
export const RefreshButton = ({ children }: React.PropsWithChildren) => {
 | 
			
		||||
  const { auth } = useSettingsAuthContext()
 | 
			
		||||
@ -14,10 +14,9 @@ export const RefreshButton = ({ children }: React.PropsWithChildren) => {
 | 
			
		||||
  const { htmlRef } = useStore((s) => ({
 | 
			
		||||
    htmlRef: s.htmlRef,
 | 
			
		||||
  }))
 | 
			
		||||
  const coreDumpManager = new CoreDumpManager(
 | 
			
		||||
    engineCommandManager,
 | 
			
		||||
    htmlRef,
 | 
			
		||||
    token
 | 
			
		||||
  const coreDumpManager = useMemo(
 | 
			
		||||
    () => new CoreDumpManager(engineCommandManager, htmlRef, token),
 | 
			
		||||
    []
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  async function refresh() {
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,4 @@
 | 
			
		||||
import { MouseEventHandler, useEffect, useRef, useState } from 'react'
 | 
			
		||||
import { useStore } from '../useStore'
 | 
			
		||||
import { getNormalisedCoordinates } from '../lib/utils'
 | 
			
		||||
import Loading from './Loading'
 | 
			
		||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
 | 
			
		||||
@ -10,25 +9,12 @@ import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp'
 | 
			
		||||
import { butName } from 'lib/cameraControls'
 | 
			
		||||
import { sendSelectEventToEngine } from 'lib/selections'
 | 
			
		||||
 | 
			
		||||
export const Stream = ({ className = '' }: { className?: string }) => {
 | 
			
		||||
export const Stream = () => {
 | 
			
		||||
  const [isLoading, setIsLoading] = useState(true)
 | 
			
		||||
  const [clickCoords, setClickCoords] = useState<{ x: number; y: number }>()
 | 
			
		||||
  const videoRef = useRef<HTMLVideoElement>(null)
 | 
			
		||||
  const {
 | 
			
		||||
    mediaStream,
 | 
			
		||||
    setButtonDownInStream,
 | 
			
		||||
    didDragInStream,
 | 
			
		||||
    setDidDragInStream,
 | 
			
		||||
    streamDimensions,
 | 
			
		||||
  } = useStore((s) => ({
 | 
			
		||||
    mediaStream: s.mediaStream,
 | 
			
		||||
    setButtonDownInStream: s.setButtonDownInStream,
 | 
			
		||||
    didDragInStream: s.didDragInStream,
 | 
			
		||||
    setDidDragInStream: s.setDidDragInStream,
 | 
			
		||||
    streamDimensions: s.streamDimensions,
 | 
			
		||||
  }))
 | 
			
		||||
  const { settings } = useSettingsAuthContext()
 | 
			
		||||
  const { state } = useModelingContext()
 | 
			
		||||
  const { state, send, context } = useModelingContext()
 | 
			
		||||
  const { overallState } = useNetworkContext()
 | 
			
		||||
 | 
			
		||||
  const isNetworkOkay =
 | 
			
		||||
@ -74,9 +60,9 @@ export const Stream = ({ className = '' }: { className?: string }) => {
 | 
			
		||||
    )
 | 
			
		||||
      return
 | 
			
		||||
    if (!videoRef.current) return
 | 
			
		||||
    if (!mediaStream) return
 | 
			
		||||
    videoRef.current.srcObject = mediaStream
 | 
			
		||||
  }, [mediaStream])
 | 
			
		||||
    if (!context.store?.mediaStream) return
 | 
			
		||||
    videoRef.current.srcObject = context.store.mediaStream
 | 
			
		||||
  }, [context.store?.mediaStream])
 | 
			
		||||
 | 
			
		||||
  const handleMouseDown: MouseEventHandler<HTMLDivElement> = (e) => {
 | 
			
		||||
    if (!isNetworkOkay) return
 | 
			
		||||
@ -88,25 +74,44 @@ export const Stream = ({ className = '' }: { className?: string }) => {
 | 
			
		||||
      clientX: e.clientX,
 | 
			
		||||
      clientY: e.clientY,
 | 
			
		||||
      el: videoRef.current,
 | 
			
		||||
      ...streamDimensions,
 | 
			
		||||
      ...context.store?.streamDimensions,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    setButtonDownInStream(e.button)
 | 
			
		||||
    send({
 | 
			
		||||
      type: 'Set context',
 | 
			
		||||
      data: {
 | 
			
		||||
        buttonDownInStream: e.button,
 | 
			
		||||
      },
 | 
			
		||||
    })
 | 
			
		||||
    setClickCoords({ x, y })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const handleMouseUp: MouseEventHandler<HTMLDivElement> = (e) => {
 | 
			
		||||
    if (!isNetworkOkay) return
 | 
			
		||||
    if (!videoRef.current) return
 | 
			
		||||
    setButtonDownInStream(undefined)
 | 
			
		||||
    send({
 | 
			
		||||
      type: 'Set context',
 | 
			
		||||
      data: {
 | 
			
		||||
        buttonDownInStream: undefined,
 | 
			
		||||
      },
 | 
			
		||||
    })
 | 
			
		||||
    if (state.matches('Sketch')) return
 | 
			
		||||
    if (state.matches('Sketch no face')) return
 | 
			
		||||
 | 
			
		||||
    if (!didDragInStream && butName(e).left) {
 | 
			
		||||
      sendSelectEventToEngine(e, videoRef.current, streamDimensions)
 | 
			
		||||
    if (!context.store?.didDragInStream && butName(e).left) {
 | 
			
		||||
      sendSelectEventToEngine(
 | 
			
		||||
        e,
 | 
			
		||||
        videoRef.current,
 | 
			
		||||
        context.store?.streamDimensions
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setDidDragInStream(false)
 | 
			
		||||
    send({
 | 
			
		||||
      type: 'Set context',
 | 
			
		||||
      data: {
 | 
			
		||||
        didDragInStream: false,
 | 
			
		||||
      },
 | 
			
		||||
    })
 | 
			
		||||
    setClickCoords(undefined)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -120,8 +125,13 @@ export const Stream = ({ className = '' }: { className?: string }) => {
 | 
			
		||||
      ((clickCoords.x - e.clientX) ** 2 + (clickCoords.y - e.clientY) ** 2) **
 | 
			
		||||
      0.5
 | 
			
		||||
 | 
			
		||||
    if (delta > 5 && !didDragInStream) {
 | 
			
		||||
      setDidDragInStream(true)
 | 
			
		||||
    if (delta > 5 && !context.store?.didDragInStream) {
 | 
			
		||||
      send({
 | 
			
		||||
        type: 'Set context',
 | 
			
		||||
        data: {
 | 
			
		||||
          didDragInStream: true,
 | 
			
		||||
        },
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { toolTips } from '../../useStore'
 | 
			
		||||
import { toolTips } from 'lang/langHelpers'
 | 
			
		||||
import { Selections } from 'lib/selections'
 | 
			
		||||
import { Program, Value, VariableDeclarator } from '../../lang/wasm'
 | 
			
		||||
import {
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { toolTips } from '../../useStore'
 | 
			
		||||
import { toolTips } from 'lang/langHelpers'
 | 
			
		||||
import { Selections } from 'lib/selections'
 | 
			
		||||
import { Program, Value, VariableDeclarator } from '../../lang/wasm'
 | 
			
		||||
import {
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { toolTips } from '../../useStore'
 | 
			
		||||
import { toolTips } from 'lang/langHelpers'
 | 
			
		||||
import { Selections } from 'lib/selections'
 | 
			
		||||
import { Program, ProgramMemory, Value } from '../../lang/wasm'
 | 
			
		||||
import {
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { toolTips } from '../../useStore'
 | 
			
		||||
import { toolTips } from 'lang/langHelpers'
 | 
			
		||||
import { Selections } from 'lib/selections'
 | 
			
		||||
import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm'
 | 
			
		||||
import {
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { toolTips } from '../../useStore'
 | 
			
		||||
import { toolTips } from 'lang/langHelpers'
 | 
			
		||||
import { Selection, Selections } from 'lib/selections'
 | 
			
		||||
import { PathToNode, Program, Value } from '../../lang/wasm'
 | 
			
		||||
import {
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { toolTips } from '../../useStore'
 | 
			
		||||
import { toolTips } from 'lang/langHelpers'
 | 
			
		||||
import { Selections } from 'lib/selections'
 | 
			
		||||
import { BinaryPart, Program, Value } from '../../lang/wasm'
 | 
			
		||||
import {
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { toolTips } from '../../useStore'
 | 
			
		||||
import { toolTips } from 'lang/langHelpers'
 | 
			
		||||
import { Selections } from 'lib/selections'
 | 
			
		||||
import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm'
 | 
			
		||||
import {
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { toolTips } from '../../useStore'
 | 
			
		||||
import { toolTips } from 'lang/langHelpers'
 | 
			
		||||
import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm'
 | 
			
		||||
import {
 | 
			
		||||
  getNodePathFromSourceRange,
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { toolTips } from '../../useStore'
 | 
			
		||||
import { toolTips } from 'lang/langHelpers'
 | 
			
		||||
import { Selections } from 'lib/selections'
 | 
			
		||||
import { BinaryPart, Program, Value } from '../../lang/wasm'
 | 
			
		||||
import {
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,10 @@
 | 
			
		||||
import { useLayoutEffect, useEffect, useRef } from 'react'
 | 
			
		||||
import { useStore } from '../useStore'
 | 
			
		||||
import { engineCommandManager, kclManager } from 'lib/singletons'
 | 
			
		||||
import { deferExecution } from 'lib/utils'
 | 
			
		||||
import { Themes } from 'lib/theme'
 | 
			
		||||
import { makeDefaultPlanes, modifyGrid } from 'lang/wasm'
 | 
			
		||||
import { useModelingContext } from './useModelingContext'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
 | 
			
		||||
export function useSetupEngineManager(
 | 
			
		||||
  streamRef: React.RefObject<HTMLDivElement>,
 | 
			
		||||
@ -13,25 +14,21 @@ export function useSetupEngineManager(
 | 
			
		||||
    theme: Themes.System,
 | 
			
		||||
    highlightEdges: true,
 | 
			
		||||
    enableSSAO: true,
 | 
			
		||||
    modelingSend: (() => {}) as any,
 | 
			
		||||
    modelingContext: {} as any,
 | 
			
		||||
    showScaleGrid: false,
 | 
			
		||||
  } as {
 | 
			
		||||
    pool: string | null
 | 
			
		||||
    theme: Themes
 | 
			
		||||
    highlightEdges: boolean
 | 
			
		||||
    enableSSAO: boolean
 | 
			
		||||
    modelingSend: ReturnType<typeof useModelingContext>['send']
 | 
			
		||||
    modelingContext: ReturnType<typeof useModelingContext>['context']
 | 
			
		||||
    showScaleGrid: boolean
 | 
			
		||||
  }
 | 
			
		||||
) {
 | 
			
		||||
  const {
 | 
			
		||||
    setMediaStream,
 | 
			
		||||
    setIsStreamReady,
 | 
			
		||||
    setStreamDimensions,
 | 
			
		||||
    streamDimensions,
 | 
			
		||||
  } = useStore((s) => ({
 | 
			
		||||
    setMediaStream: s.setMediaStream,
 | 
			
		||||
  const { setIsStreamReady } = useStore((s) => ({
 | 
			
		||||
    setIsStreamReady: s.setIsStreamReady,
 | 
			
		||||
    setStreamDimensions: s.setStreamDimensions,
 | 
			
		||||
    streamDimensions: s.streamDimensions,
 | 
			
		||||
  }))
 | 
			
		||||
 | 
			
		||||
  const streamWidth = streamRef?.current?.offsetWidth
 | 
			
		||||
@ -52,9 +49,18 @@ export function useSetupEngineManager(
 | 
			
		||||
      streamWidth,
 | 
			
		||||
      streamHeight
 | 
			
		||||
    )
 | 
			
		||||
    if (!hasSetNonZeroDimensions.current && quadHeight && quadWidth) {
 | 
			
		||||
    if (
 | 
			
		||||
      !hasSetNonZeroDimensions.current &&
 | 
			
		||||
      quadHeight &&
 | 
			
		||||
      quadWidth &&
 | 
			
		||||
      settings.modelingSend
 | 
			
		||||
    ) {
 | 
			
		||||
      engineCommandManager.start({
 | 
			
		||||
        setMediaStream,
 | 
			
		||||
        setMediaStream: (mediaStream) =>
 | 
			
		||||
          settings.modelingSend({
 | 
			
		||||
            type: 'Set context',
 | 
			
		||||
            data: { mediaStream },
 | 
			
		||||
          }),
 | 
			
		||||
        setIsStreamReady,
 | 
			
		||||
        width: quadWidth,
 | 
			
		||||
        height: quadHeight,
 | 
			
		||||
@ -72,9 +78,14 @@ export function useSetupEngineManager(
 | 
			
		||||
          return modifyGrid(kclManager.engineCommandManager, hidden)
 | 
			
		||||
        },
 | 
			
		||||
      })
 | 
			
		||||
      setStreamDimensions({
 | 
			
		||||
        streamWidth: quadWidth,
 | 
			
		||||
        streamHeight: quadHeight,
 | 
			
		||||
      settings.modelingSend({
 | 
			
		||||
        type: 'Set context',
 | 
			
		||||
        data: {
 | 
			
		||||
          streamDimensions: {
 | 
			
		||||
            streamWidth: quadWidth,
 | 
			
		||||
            streamHeight: quadHeight,
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      })
 | 
			
		||||
      hasSetNonZeroDimensions.current = true
 | 
			
		||||
    }
 | 
			
		||||
@ -83,6 +94,7 @@ export function useSetupEngineManager(
 | 
			
		||||
  useLayoutEffect(startEngineInstance, [
 | 
			
		||||
    streamRef?.current?.offsetWidth,
 | 
			
		||||
    streamRef?.current?.offsetHeight,
 | 
			
		||||
    settings.modelingSend,
 | 
			
		||||
  ])
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
@ -92,16 +104,21 @@ export function useSetupEngineManager(
 | 
			
		||||
        streamRef?.current?.offsetHeight
 | 
			
		||||
      )
 | 
			
		||||
      if (
 | 
			
		||||
        streamDimensions.streamWidth !== width ||
 | 
			
		||||
        streamDimensions.streamHeight !== height
 | 
			
		||||
        settings.modelingContext.store.streamDimensions.streamWidth !== width ||
 | 
			
		||||
        settings.modelingContext.store.streamDimensions.streamHeight !== height
 | 
			
		||||
      ) {
 | 
			
		||||
        engineCommandManager.handleResize({
 | 
			
		||||
          streamWidth: width,
 | 
			
		||||
          streamHeight: height,
 | 
			
		||||
        })
 | 
			
		||||
        setStreamDimensions({
 | 
			
		||||
          streamWidth: width,
 | 
			
		||||
          streamHeight: height,
 | 
			
		||||
        settings.modelingSend({
 | 
			
		||||
          type: 'Set context',
 | 
			
		||||
          data: {
 | 
			
		||||
            streamDimensions: {
 | 
			
		||||
              streamWidth: width,
 | 
			
		||||
              streamHeight: height,
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
    }, 500)
 | 
			
		||||
 | 
			
		||||
@ -8,9 +8,9 @@ import { settingsMachine } from 'machines/settingsMachine'
 | 
			
		||||
import { homeMachine } from 'machines/homeMachine'
 | 
			
		||||
import { Command, CommandSetConfig, CommandSetSchema } from 'lib/commandTypes'
 | 
			
		||||
import { useKclContext } from 'lang/KclProvider'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
import { useNetworkContext } from 'hooks/useNetworkContext'
 | 
			
		||||
import { NetworkHealthState } from 'hooks/useNetworkStatus'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
 | 
			
		||||
// This might not be necessary, AnyStateMachine from xstate is working
 | 
			
		||||
export type AllMachines =
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { executeAst, lintAst } from 'useStore'
 | 
			
		||||
import { executeAst, lintAst } from 'lang/langHelpers'
 | 
			
		||||
import { Selections } from 'lib/selections'
 | 
			
		||||
import { KCLError, kclErrorsToDiagnostics } from './errors'
 | 
			
		||||
import { uuidv4 } from 'lib/utils'
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ import toast from 'react-hot-toast'
 | 
			
		||||
import { editorManager } from 'lib/singletons'
 | 
			
		||||
import { Annotation, KeyBinding, Transaction } from '@uiw/react-codemirror'
 | 
			
		||||
 | 
			
		||||
const PERSIST_CODE_TOKEN = 'persistCode'
 | 
			
		||||
const PERSIST_CODE_KEY = 'persistCode'
 | 
			
		||||
 | 
			
		||||
const codeManagerUpdateAnnotation = Annotation.define<null>()
 | 
			
		||||
export const codeManagerUpdateEvent = codeManagerUpdateAnnotation.of(null)
 | 
			
		||||
@ -25,7 +25,7 @@ export default class CodeManager {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const storedCode = safeLSGetItem(PERSIST_CODE_TOKEN)
 | 
			
		||||
    const storedCode = safeLSGetItem(PERSIST_CODE_KEY)
 | 
			
		||||
    // TODO #819 remove zustand persistence logic in a few months
 | 
			
		||||
    // short term migration, shouldn't make a difference for tauri app users
 | 
			
		||||
    // anyway since that's filesystem based.
 | 
			
		||||
@ -125,7 +125,7 @@ export default class CodeManager {
 | 
			
		||||
          })
 | 
			
		||||
      })
 | 
			
		||||
    } else {
 | 
			
		||||
      safeLSSetItem(PERSIST_CODE_TOKEN, this.code)
 | 
			
		||||
      safeLSSetItem(PERSIST_CODE_KEY, this.code)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										120
									
								
								src/lang/langHelpers.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								src/lang/langHelpers.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,120 @@
 | 
			
		||||
import {
 | 
			
		||||
  Program,
 | 
			
		||||
  _executor,
 | 
			
		||||
  ProgramMemory,
 | 
			
		||||
  programMemoryInit,
 | 
			
		||||
  kclLint,
 | 
			
		||||
} from 'lang/wasm'
 | 
			
		||||
import { enginelessExecutor } from 'lib/testHelpers'
 | 
			
		||||
import { EngineCommandManager } from 'lang/std/engineConnection'
 | 
			
		||||
import { KCLError } from 'lang/errors'
 | 
			
		||||
import { Diagnostic } from '@codemirror/lint'
 | 
			
		||||
 | 
			
		||||
export type ToolTip =
 | 
			
		||||
  | 'lineTo'
 | 
			
		||||
  | 'line'
 | 
			
		||||
  | 'angledLine'
 | 
			
		||||
  | 'angledLineOfXLength'
 | 
			
		||||
  | 'angledLineOfYLength'
 | 
			
		||||
  | 'angledLineToX'
 | 
			
		||||
  | 'angledLineToY'
 | 
			
		||||
  | 'xLine'
 | 
			
		||||
  | 'yLine'
 | 
			
		||||
  | 'xLineTo'
 | 
			
		||||
  | 'yLineTo'
 | 
			
		||||
  | 'angledLineThatIntersects'
 | 
			
		||||
  | 'tangentialArcTo'
 | 
			
		||||
 | 
			
		||||
export const toolTips = [
 | 
			
		||||
  'sketch_line',
 | 
			
		||||
  'move',
 | 
			
		||||
  // original tooltips
 | 
			
		||||
  'line',
 | 
			
		||||
  'lineTo',
 | 
			
		||||
  'angledLine',
 | 
			
		||||
  'angledLineOfXLength',
 | 
			
		||||
  'angledLineOfYLength',
 | 
			
		||||
  'angledLineToX',
 | 
			
		||||
  'angledLineToY',
 | 
			
		||||
  'xLine',
 | 
			
		||||
  'yLine',
 | 
			
		||||
  'xLineTo',
 | 
			
		||||
  'yLineTo',
 | 
			
		||||
  'angledLineThatIntersects',
 | 
			
		||||
  'tangentialArcTo',
 | 
			
		||||
] as any as ToolTip[]
 | 
			
		||||
 | 
			
		||||
export async function executeAst({
 | 
			
		||||
  ast,
 | 
			
		||||
  engineCommandManager,
 | 
			
		||||
  useFakeExecutor = false,
 | 
			
		||||
  programMemoryOverride,
 | 
			
		||||
}: {
 | 
			
		||||
  ast: Program
 | 
			
		||||
  engineCommandManager: EngineCommandManager
 | 
			
		||||
  useFakeExecutor?: boolean
 | 
			
		||||
  programMemoryOverride?: ProgramMemory
 | 
			
		||||
}): Promise<{
 | 
			
		||||
  logs: string[]
 | 
			
		||||
  errors: KCLError[]
 | 
			
		||||
  programMemory: ProgramMemory
 | 
			
		||||
}> {
 | 
			
		||||
  try {
 | 
			
		||||
    if (!useFakeExecutor) {
 | 
			
		||||
      engineCommandManager.endSession()
 | 
			
		||||
      engineCommandManager.startNewSession()
 | 
			
		||||
    }
 | 
			
		||||
    const programMemory = await (useFakeExecutor
 | 
			
		||||
      ? enginelessExecutor(ast, programMemoryOverride || programMemoryInit())
 | 
			
		||||
      : _executor(ast, programMemoryInit(), engineCommandManager, false))
 | 
			
		||||
 | 
			
		||||
    await engineCommandManager.waitForAllCommands()
 | 
			
		||||
    return {
 | 
			
		||||
      logs: [],
 | 
			
		||||
      errors: [],
 | 
			
		||||
      programMemory,
 | 
			
		||||
    }
 | 
			
		||||
  } catch (e: any) {
 | 
			
		||||
    if (e instanceof KCLError) {
 | 
			
		||||
      return {
 | 
			
		||||
        errors: [e],
 | 
			
		||||
        logs: [],
 | 
			
		||||
        programMemory: {
 | 
			
		||||
          root: {},
 | 
			
		||||
          return: null,
 | 
			
		||||
        },
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      console.log(e)
 | 
			
		||||
      return {
 | 
			
		||||
        logs: [e],
 | 
			
		||||
        errors: [],
 | 
			
		||||
        programMemory: {
 | 
			
		||||
          root: {},
 | 
			
		||||
          return: null,
 | 
			
		||||
        },
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function lintAst({
 | 
			
		||||
  ast,
 | 
			
		||||
}: {
 | 
			
		||||
  ast: Program
 | 
			
		||||
}): Promise<Array<Diagnostic>> {
 | 
			
		||||
  try {
 | 
			
		||||
    const discovered_findings = await kclLint(ast)
 | 
			
		||||
    return discovered_findings.map((lint) => {
 | 
			
		||||
      return {
 | 
			
		||||
        message: lint.finding.title,
 | 
			
		||||
        severity: 'info',
 | 
			
		||||
        from: lint.pos[0],
 | 
			
		||||
        to: lint.pos[1],
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  } catch (e: any) {
 | 
			
		||||
    console.log(e)
 | 
			
		||||
    return []
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { ToolTip } from '../useStore'
 | 
			
		||||
import { ToolTip } from 'lang/langHelpers'
 | 
			
		||||
import { Selection, Selections } from 'lib/selections'
 | 
			
		||||
import {
 | 
			
		||||
  ArrayExpression,
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ import {
 | 
			
		||||
  isLiteralArrayOrStatic,
 | 
			
		||||
  isNotLiteralArrayOrStatic,
 | 
			
		||||
} from 'lang/std/sketchcombos'
 | 
			
		||||
import { toolTips, ToolTip } from '../../useStore'
 | 
			
		||||
import { toolTips, ToolTip } from 'lang/langHelpers'
 | 
			
		||||
import { createPipeExpression, splitPathAtPipeExpression } from '../modifyAst'
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { getNodeFromPath } from 'lang/queryAst'
 | 
			
		||||
import { ToolTip, toolTips } from '../../useStore'
 | 
			
		||||
import { ToolTip, toolTips } from 'lang/langHelpers'
 | 
			
		||||
import {
 | 
			
		||||
  Program,
 | 
			
		||||
  VariableDeclarator,
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ import {
 | 
			
		||||
  ConstraintLevel,
 | 
			
		||||
  getConstraintLevelFromSourceRange,
 | 
			
		||||
} from './sketchcombos'
 | 
			
		||||
import { ToolTip } from '../../useStore'
 | 
			
		||||
import { ToolTip } from 'lang/langHelpers'
 | 
			
		||||
import { Selections } from 'lib/selections'
 | 
			
		||||
import { err } from 'lib/trap'
 | 
			
		||||
import { enginelessExecutor } from '../../lib/testHelpers'
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { TransformCallback, VarValues } from './stdTypes'
 | 
			
		||||
import { toolTips, ToolTip } from '../../useStore'
 | 
			
		||||
import { ToolTip, toolTips } from 'lang/langHelpers'
 | 
			
		||||
import { Selections, Selection } from 'lib/selections'
 | 
			
		||||
import { cleanErrs, err } from 'lib/trap'
 | 
			
		||||
import {
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { ToolTip } from 'useStore'
 | 
			
		||||
import { ToolTip } from 'lang/langHelpers'
 | 
			
		||||
import {
 | 
			
		||||
  ProgramMemory,
 | 
			
		||||
  Path,
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ import { findUniqueName } from 'lang/modifyAst'
 | 
			
		||||
import { PrevVariable, findAllPreviousVariables } from 'lang/queryAst'
 | 
			
		||||
import { Value, parse } from 'lang/wasm'
 | 
			
		||||
import { useEffect, useRef, useState } from 'react'
 | 
			
		||||
import { executeAst } from 'useStore'
 | 
			
		||||
import { executeAst } from 'lang/langHelpers'
 | 
			
		||||
import { trap } from 'lib/trap'
 | 
			
		||||
 | 
			
		||||
const isValidVariableName = (name: string) =>
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -1,6 +1,5 @@
 | 
			
		||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
 | 
			
		||||
import { onboardingPaths } from 'routes/Onboarding/paths'
 | 
			
		||||
import { useStore } from '../../useStore'
 | 
			
		||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
 | 
			
		||||
import {
 | 
			
		||||
  CameraSystem,
 | 
			
		||||
@ -8,11 +7,10 @@ import {
 | 
			
		||||
  cameraSystems,
 | 
			
		||||
} from 'lib/cameraControls'
 | 
			
		||||
import { SettingsSection } from 'components/Settings/SettingsSection'
 | 
			
		||||
import { useModelingContext } from 'hooks/useModelingContext'
 | 
			
		||||
 | 
			
		||||
export default function Units() {
 | 
			
		||||
  const { buttonDownInStream } = useStore((s) => ({
 | 
			
		||||
    buttonDownInStream: s.buttonDownInStream,
 | 
			
		||||
  }))
 | 
			
		||||
  const { context } = useModelingContext()
 | 
			
		||||
  const dismiss = useDismiss()
 | 
			
		||||
  const next = useNextClick(onboardingPaths.STREAMING)
 | 
			
		||||
  const {
 | 
			
		||||
@ -31,7 +29,7 @@ export default function Units() {
 | 
			
		||||
      <div
 | 
			
		||||
        className={
 | 
			
		||||
          'max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
 | 
			
		||||
          (buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
          (context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
        }
 | 
			
		||||
      >
 | 
			
		||||
        <SettingsSection
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,10 @@
 | 
			
		||||
import usePlatform from 'hooks/usePlatform'
 | 
			
		||||
import { OnboardingButtons, kbdClasses, useDismiss, useNextClick } from '.'
 | 
			
		||||
import { onboardingPaths } from 'routes/Onboarding/paths'
 | 
			
		||||
import { useStore } from '../../useStore'
 | 
			
		||||
import { useModelingContext } from 'hooks/useModelingContext'
 | 
			
		||||
 | 
			
		||||
export default function CmdK() {
 | 
			
		||||
  const { buttonDownInStream } = useStore((s) => ({
 | 
			
		||||
    buttonDownInStream: s.buttonDownInStream,
 | 
			
		||||
  }))
 | 
			
		||||
  const { context } = useModelingContext()
 | 
			
		||||
  const dismiss = useDismiss()
 | 
			
		||||
  const next = useNextClick(onboardingPaths.USER_MENU)
 | 
			
		||||
  const platformName = usePlatform()
 | 
			
		||||
@ -16,7 +14,7 @@ export default function CmdK() {
 | 
			
		||||
      <div
 | 
			
		||||
        className={
 | 
			
		||||
          'max-w-full xl:max-w-4xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
 | 
			
		||||
          (buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
          (context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
        }
 | 
			
		||||
      >
 | 
			
		||||
        <h2 className="text-2xl font-bold">Command Bar</h2>
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,10 @@
 | 
			
		||||
import { useModelingContext } from 'hooks/useModelingContext'
 | 
			
		||||
import { OnboardingButtons, useDemoCode, useDismiss, useNextClick } from '.'
 | 
			
		||||
import { onboardingPaths } from 'routes/Onboarding/paths'
 | 
			
		||||
import { useStore } from '../../useStore'
 | 
			
		||||
 | 
			
		||||
export default function OnboardingCodeEditor() {
 | 
			
		||||
  useDemoCode()
 | 
			
		||||
  const { buttonDownInStream } = useStore((s) => ({
 | 
			
		||||
    buttonDownInStream: s.buttonDownInStream,
 | 
			
		||||
  }))
 | 
			
		||||
  const { context } = useModelingContext()
 | 
			
		||||
  const dismiss = useDismiss()
 | 
			
		||||
  const next = useNextClick(onboardingPaths.PARAMETRIC_MODELING)
 | 
			
		||||
 | 
			
		||||
@ -15,7 +13,7 @@ export default function OnboardingCodeEditor() {
 | 
			
		||||
      <div
 | 
			
		||||
        className={
 | 
			
		||||
          'z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-[75vh] flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
 | 
			
		||||
          (buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
          (context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
        }
 | 
			
		||||
      >
 | 
			
		||||
        <section className="flex-1 overflow-y-auto">
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,10 @@
 | 
			
		||||
import { APP_NAME } from 'lib/constants'
 | 
			
		||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
 | 
			
		||||
import { onboardingPaths } from 'routes/Onboarding/paths'
 | 
			
		||||
import { useStore } from '../../useStore'
 | 
			
		||||
import { useModelingContext } from 'hooks/useModelingContext'
 | 
			
		||||
 | 
			
		||||
export default function Export() {
 | 
			
		||||
  const { buttonDownInStream } = useStore((s) => ({
 | 
			
		||||
    buttonDownInStream: s.buttonDownInStream,
 | 
			
		||||
  }))
 | 
			
		||||
  const { context } = useModelingContext()
 | 
			
		||||
  const dismiss = useDismiss()
 | 
			
		||||
  const next = useNextClick(onboardingPaths.SKETCHING)
 | 
			
		||||
 | 
			
		||||
@ -15,7 +13,7 @@ export default function Export() {
 | 
			
		||||
      <div
 | 
			
		||||
        className={
 | 
			
		||||
          'max-w-full xl:max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
 | 
			
		||||
          (buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
          (context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
        }
 | 
			
		||||
      >
 | 
			
		||||
        <section className="flex-1">
 | 
			
		||||
 | 
			
		||||
@ -6,14 +6,12 @@ import {
 | 
			
		||||
  useNextClick,
 | 
			
		||||
} from '.'
 | 
			
		||||
import { onboardingPaths } from 'routes/Onboarding/paths'
 | 
			
		||||
import { useStore } from '../../useStore'
 | 
			
		||||
import { bracketWidthConstantLine } from 'lib/exampleKcl'
 | 
			
		||||
import { useModelingContext } from 'hooks/useModelingContext'
 | 
			
		||||
 | 
			
		||||
export default function OnboardingInteractiveNumbers() {
 | 
			
		||||
  useDemoCode()
 | 
			
		||||
  const { buttonDownInStream } = useStore((s) => ({
 | 
			
		||||
    buttonDownInStream: s.buttonDownInStream,
 | 
			
		||||
  }))
 | 
			
		||||
  const { context } = useModelingContext()
 | 
			
		||||
  const dismiss = useDismiss()
 | 
			
		||||
  const next = useNextClick(onboardingPaths.COMMAND_K)
 | 
			
		||||
 | 
			
		||||
@ -22,7 +20,7 @@ export default function OnboardingInteractiveNumbers() {
 | 
			
		||||
      <div
 | 
			
		||||
        className={
 | 
			
		||||
          'z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-[75vh] flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
 | 
			
		||||
          (buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
          (context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
        }
 | 
			
		||||
      >
 | 
			
		||||
        <section className="flex-1 overflow-y-auto mb-6">
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,13 @@
 | 
			
		||||
import { OnboardingButtons, useDemoCode, useDismiss, useNextClick } from '.'
 | 
			
		||||
import { onboardingPaths } from 'routes/Onboarding/paths'
 | 
			
		||||
import { useStore } from '../../useStore'
 | 
			
		||||
import { Themes, getSystemTheme } from 'lib/theme'
 | 
			
		||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
 | 
			
		||||
import { bracketThicknessCalculationLine } from 'lib/exampleKcl'
 | 
			
		||||
import { useModelingContext } from 'hooks/useModelingContext'
 | 
			
		||||
 | 
			
		||||
export default function OnboardingParametricModeling() {
 | 
			
		||||
  useDemoCode()
 | 
			
		||||
  const { buttonDownInStream } = useStore((s) => ({
 | 
			
		||||
    buttonDownInStream: s.buttonDownInStream,
 | 
			
		||||
  }))
 | 
			
		||||
  const { context } = useModelingContext()
 | 
			
		||||
  const {
 | 
			
		||||
    settings: {
 | 
			
		||||
      context: {
 | 
			
		||||
@ -32,7 +30,7 @@ export default function OnboardingParametricModeling() {
 | 
			
		||||
      <div
 | 
			
		||||
        className={
 | 
			
		||||
          'z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-[75vh] flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
 | 
			
		||||
          (buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
          (context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
        }
 | 
			
		||||
      >
 | 
			
		||||
        <section className="flex-1 overflow-y-auto mb-6">
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,10 @@
 | 
			
		||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
 | 
			
		||||
import { onboardingPaths } from 'routes/Onboarding/paths'
 | 
			
		||||
import { useStore } from '../../useStore'
 | 
			
		||||
import { isTauri } from 'lib/isTauri'
 | 
			
		||||
import { useModelingContext } from 'hooks/useModelingContext'
 | 
			
		||||
 | 
			
		||||
export default function ProjectMenu() {
 | 
			
		||||
  const { buttonDownInStream } = useStore((s) => ({
 | 
			
		||||
    buttonDownInStream: s.buttonDownInStream,
 | 
			
		||||
  }))
 | 
			
		||||
  const { context } = useModelingContext()
 | 
			
		||||
  const dismiss = useDismiss()
 | 
			
		||||
  const next = useNextClick(onboardingPaths.EXPORT)
 | 
			
		||||
  const tauri = isTauri()
 | 
			
		||||
@ -16,7 +14,7 @@ export default function ProjectMenu() {
 | 
			
		||||
      <div
 | 
			
		||||
        className={
 | 
			
		||||
          'max-w-xl flex flex-col border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
 | 
			
		||||
          (buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
          (context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
        }
 | 
			
		||||
      >
 | 
			
		||||
        <section className="flex-1">
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,11 @@
 | 
			
		||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
 | 
			
		||||
import { onboardingPaths } from 'routes/Onboarding/paths'
 | 
			
		||||
import { useStore } from 'useStore'
 | 
			
		||||
import { useEffect } from 'react'
 | 
			
		||||
import { codeManager, kclManager } from 'lib/singletons'
 | 
			
		||||
import { useModelingContext } from 'hooks/useModelingContext'
 | 
			
		||||
 | 
			
		||||
export default function Sketching() {
 | 
			
		||||
  const buttonDownInStream = useStore((s) => s.buttonDownInStream)
 | 
			
		||||
  const { context } = useModelingContext()
 | 
			
		||||
  const dismiss = useDismiss()
 | 
			
		||||
  const next = useNextClick(onboardingPaths.FUTURE_WORK)
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ export default function Sketching() {
 | 
			
		||||
      <div
 | 
			
		||||
        className={
 | 
			
		||||
          'max-w-full xl:max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
 | 
			
		||||
          (buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
          (context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
        }
 | 
			
		||||
      >
 | 
			
		||||
        <h1 className="text-2xl font-bold">Sketching</h1>
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,9 @@
 | 
			
		||||
import { useModelingContext } from 'hooks/useModelingContext'
 | 
			
		||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
 | 
			
		||||
import { onboardingPaths } from 'routes/Onboarding/paths'
 | 
			
		||||
import { useStore } from '../../useStore'
 | 
			
		||||
 | 
			
		||||
export default function Streaming() {
 | 
			
		||||
  const { buttonDownInStream } = useStore((s) => ({
 | 
			
		||||
    buttonDownInStream: s.buttonDownInStream,
 | 
			
		||||
  }))
 | 
			
		||||
  const { context } = useModelingContext()
 | 
			
		||||
  const dismiss = useDismiss()
 | 
			
		||||
  const next = useNextClick(onboardingPaths.EDITOR)
 | 
			
		||||
 | 
			
		||||
@ -14,7 +12,7 @@ export default function Streaming() {
 | 
			
		||||
      <div
 | 
			
		||||
        className={
 | 
			
		||||
          'max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-[75vh] flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
 | 
			
		||||
          (buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
          (context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
        }
 | 
			
		||||
      >
 | 
			
		||||
        <section className="flex-1 overflow-y-auto">
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,10 @@
 | 
			
		||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
 | 
			
		||||
import { onboardingPaths } from 'routes/Onboarding/paths'
 | 
			
		||||
import { useStore } from '../../useStore'
 | 
			
		||||
import { useEffect, useState } from 'react'
 | 
			
		||||
import { useModelingContext } from 'hooks/useModelingContext'
 | 
			
		||||
 | 
			
		||||
export default function UserMenu() {
 | 
			
		||||
  const { buttonDownInStream } = useStore((s) => ({
 | 
			
		||||
    buttonDownInStream: s.buttonDownInStream,
 | 
			
		||||
  }))
 | 
			
		||||
  const { context } = useModelingContext()
 | 
			
		||||
  const dismiss = useDismiss()
 | 
			
		||||
  const next = useNextClick(onboardingPaths.PROJECT_MENU)
 | 
			
		||||
  const [avatarErrored, setAvatarErrored] = useState(false)
 | 
			
		||||
@ -33,7 +31,7 @@ export default function UserMenu() {
 | 
			
		||||
      <div
 | 
			
		||||
        className={
 | 
			
		||||
          'max-w-xl flex flex-col border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
 | 
			
		||||
          (buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
          (context.store?.buttonDownInStream ? '' : ' pointer-events-auto')
 | 
			
		||||
        }
 | 
			
		||||
      >
 | 
			
		||||
        <section className="flex-1">
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										199
									
								
								src/useStore.ts
									
									
									
									
									
								
							
							
						
						
									
										199
									
								
								src/useStore.ts
									
									
									
									
									
								
							@ -1,202 +1,19 @@
 | 
			
		||||
import { create } from 'zustand'
 | 
			
		||||
import { persist } from 'zustand/middleware'
 | 
			
		||||
import {
 | 
			
		||||
  Program,
 | 
			
		||||
  _executor,
 | 
			
		||||
  ProgramMemory,
 | 
			
		||||
  programMemoryInit,
 | 
			
		||||
  kclLint,
 | 
			
		||||
} from './lang/wasm'
 | 
			
		||||
import { enginelessExecutor } from './lib/testHelpers'
 | 
			
		||||
import { EngineCommandManager } from './lang/std/engineConnection'
 | 
			
		||||
import { KCLError } from './lang/errors'
 | 
			
		||||
import { SidebarType } from 'components/ModelingSidebar/ModelingPanes'
 | 
			
		||||
import { Diagnostic } from '@codemirror/lint'
 | 
			
		||||
 | 
			
		||||
export type ToolTip =
 | 
			
		||||
  | 'lineTo'
 | 
			
		||||
  | 'line'
 | 
			
		||||
  | 'angledLine'
 | 
			
		||||
  | 'angledLineOfXLength'
 | 
			
		||||
  | 'angledLineOfYLength'
 | 
			
		||||
  | 'angledLineToX'
 | 
			
		||||
  | 'angledLineToY'
 | 
			
		||||
  | 'xLine'
 | 
			
		||||
  | 'yLine'
 | 
			
		||||
  | 'xLineTo'
 | 
			
		||||
  | 'yLineTo'
 | 
			
		||||
  | 'angledLineThatIntersects'
 | 
			
		||||
  | 'tangentialArcTo'
 | 
			
		||||
 | 
			
		||||
export const toolTips = [
 | 
			
		||||
  'sketch_line',
 | 
			
		||||
  'move',
 | 
			
		||||
  // original tooltips
 | 
			
		||||
  'line',
 | 
			
		||||
  'lineTo',
 | 
			
		||||
  'angledLine',
 | 
			
		||||
  'angledLineOfXLength',
 | 
			
		||||
  'angledLineOfYLength',
 | 
			
		||||
  'angledLineToX',
 | 
			
		||||
  'angledLineToY',
 | 
			
		||||
  'xLine',
 | 
			
		||||
  'yLine',
 | 
			
		||||
  'xLineTo',
 | 
			
		||||
  'yLineTo',
 | 
			
		||||
  'angledLineThatIntersects',
 | 
			
		||||
  'tangentialArcTo',
 | 
			
		||||
] as any as ToolTip[]
 | 
			
		||||
 | 
			
		||||
export interface StoreState {
 | 
			
		||||
  mediaStream?: MediaStream
 | 
			
		||||
  setMediaStream: (mediaStream: MediaStream) => void
 | 
			
		||||
  isStreamReady: boolean
 | 
			
		||||
  setIsStreamReady: (isStreamReady: boolean) => void
 | 
			
		||||
  buttonDownInStream: number | undefined
 | 
			
		||||
  setButtonDownInStream: (buttonDownInStream: number | undefined) => void
 | 
			
		||||
  didDragInStream: boolean
 | 
			
		||||
  setDidDragInStream: (didDragInStream: boolean) => void
 | 
			
		||||
  fileId: string
 | 
			
		||||
  setFileId: (fileId: string) => void
 | 
			
		||||
  streamDimensions: { streamWidth: number; streamHeight: number }
 | 
			
		||||
  setStreamDimensions: (dimensions: {
 | 
			
		||||
    streamWidth: number
 | 
			
		||||
    streamHeight: number
 | 
			
		||||
  }) => void
 | 
			
		||||
  setHtmlRef: (ref: React.RefObject<HTMLDivElement>) => void
 | 
			
		||||
  htmlRef: React.RefObject<HTMLDivElement> | null
 | 
			
		||||
 | 
			
		||||
  showHomeMenu: boolean
 | 
			
		||||
  setHomeShowMenu: (showMenu: boolean) => void
 | 
			
		||||
  openPanes: SidebarType[]
 | 
			
		||||
  setOpenPanes: (panes: SidebarType[]) => void
 | 
			
		||||
  homeMenuItems: {
 | 
			
		||||
    name: string
 | 
			
		||||
    path: string
 | 
			
		||||
  }[]
 | 
			
		||||
  setHomeMenuItems: (items: { name: string; path: string }[]) => void
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const useStore = create<StoreState>()(
 | 
			
		||||
  persist(
 | 
			
		||||
    (set, get) => {
 | 
			
		||||
      return {
 | 
			
		||||
        setMediaStream: (mediaStream) => set({ mediaStream }),
 | 
			
		||||
        isStreamReady: false,
 | 
			
		||||
        setIsStreamReady: (isStreamReady) => set({ isStreamReady }),
 | 
			
		||||
        buttonDownInStream: undefined,
 | 
			
		||||
        setButtonDownInStream: (buttonDownInStream) => {
 | 
			
		||||
          set({ buttonDownInStream })
 | 
			
		||||
        },
 | 
			
		||||
        setHtmlRef: (htmlRef) => {
 | 
			
		||||
          set({ htmlRef })
 | 
			
		||||
        },
 | 
			
		||||
        htmlRef: null,
 | 
			
		||||
        didDragInStream: false,
 | 
			
		||||
        setDidDragInStream: (didDragInStream) => {
 | 
			
		||||
          set({ didDragInStream })
 | 
			
		||||
        },
 | 
			
		||||
        // For stream event handling
 | 
			
		||||
        fileId: '',
 | 
			
		||||
        setFileId: (fileId) => set({ fileId }),
 | 
			
		||||
        streamDimensions: { streamWidth: 1280, streamHeight: 720 },
 | 
			
		||||
        setStreamDimensions: (streamDimensions) => {
 | 
			
		||||
          set({ streamDimensions })
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        // tauri specific app settings
 | 
			
		||||
        defaultDir: {
 | 
			
		||||
          dir: '',
 | 
			
		||||
        },
 | 
			
		||||
        openPanes: ['code'],
 | 
			
		||||
        setOpenPanes: (openPanes) => set({ openPanes }),
 | 
			
		||||
        showHomeMenu: true,
 | 
			
		||||
        setHomeShowMenu: (showHomeMenu) => set({ showHomeMenu }),
 | 
			
		||||
        homeMenuItems: [],
 | 
			
		||||
        setHomeMenuItems: (homeMenuItems) => set({ homeMenuItems }),
 | 
			
		||||
      }
 | 
			
		||||
export const useStore = create<StoreState>()((set, get) => {
 | 
			
		||||
  return {
 | 
			
		||||
    setIsStreamReady: (isStreamReady) => set({ isStreamReady }),
 | 
			
		||||
    setHtmlRef: (htmlRef) => {
 | 
			
		||||
      set({ htmlRef })
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      name: 'store',
 | 
			
		||||
      partialize: (state) =>
 | 
			
		||||
        Object.fromEntries(
 | 
			
		||||
          Object.entries(state).filter(([key]) => ['openPanes'].includes(key))
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
  )
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
export async function executeAst({
 | 
			
		||||
  ast,
 | 
			
		||||
  engineCommandManager,
 | 
			
		||||
  useFakeExecutor = false,
 | 
			
		||||
  programMemoryOverride,
 | 
			
		||||
}: {
 | 
			
		||||
  ast: Program
 | 
			
		||||
  engineCommandManager: EngineCommandManager
 | 
			
		||||
  useFakeExecutor?: boolean
 | 
			
		||||
  programMemoryOverride?: ProgramMemory
 | 
			
		||||
}): Promise<{
 | 
			
		||||
  logs: string[]
 | 
			
		||||
  errors: KCLError[]
 | 
			
		||||
  programMemory: ProgramMemory
 | 
			
		||||
}> {
 | 
			
		||||
  try {
 | 
			
		||||
    if (!useFakeExecutor) {
 | 
			
		||||
      engineCommandManager.endSession()
 | 
			
		||||
      engineCommandManager.startNewSession()
 | 
			
		||||
    }
 | 
			
		||||
    const programMemory = await (useFakeExecutor
 | 
			
		||||
      ? enginelessExecutor(ast, programMemoryOverride || programMemoryInit())
 | 
			
		||||
      : _executor(ast, programMemoryInit(), engineCommandManager, false))
 | 
			
		||||
 | 
			
		||||
    await engineCommandManager.waitForAllCommands()
 | 
			
		||||
    return {
 | 
			
		||||
      logs: [],
 | 
			
		||||
      errors: [],
 | 
			
		||||
      programMemory,
 | 
			
		||||
    }
 | 
			
		||||
  } catch (e: any) {
 | 
			
		||||
    if (e instanceof KCLError) {
 | 
			
		||||
      return {
 | 
			
		||||
        errors: [e],
 | 
			
		||||
        logs: [],
 | 
			
		||||
        programMemory: {
 | 
			
		||||
          root: {},
 | 
			
		||||
          return: null,
 | 
			
		||||
        },
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      console.log(e)
 | 
			
		||||
      return {
 | 
			
		||||
        logs: [e],
 | 
			
		||||
        errors: [],
 | 
			
		||||
        programMemory: {
 | 
			
		||||
          root: {},
 | 
			
		||||
          return: null,
 | 
			
		||||
        },
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    htmlRef: null,
 | 
			
		||||
    isStreamReady: false,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function lintAst({
 | 
			
		||||
  ast,
 | 
			
		||||
}: {
 | 
			
		||||
  ast: Program
 | 
			
		||||
}): Promise<Array<Diagnostic>> {
 | 
			
		||||
  try {
 | 
			
		||||
    const discovered_findings = await kclLint(ast)
 | 
			
		||||
    return discovered_findings.map((lint) => {
 | 
			
		||||
      return {
 | 
			
		||||
        message: lint.finding.title,
 | 
			
		||||
        severity: 'info',
 | 
			
		||||
        from: lint.pos[0],
 | 
			
		||||
        to: lint.pos[1],
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  } catch (e: any) {
 | 
			
		||||
    console.log(e)
 | 
			
		||||
    return []
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user