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:
Kurt Hutten
2024-07-02 17:16:27 +10:00
committed by GitHub
parent de354ee5d3
commit f86a69f12a
46 changed files with 502 additions and 452 deletions

View File

@ -30,6 +30,8 @@ import { EngineCommand } from 'lang/std/engineConnection'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { bracket } from 'lib/exampleKcl' 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 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 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.describe('Basic sketch', () => {
test('code pane open at start', async ({ page }) => { 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']) await doBasicSketch(page, ['code'])
}) })
test('code pane closed at start', async ({ page }) => { test('code pane closed at start', async ({ page }) => {
// Load the app with the code panes // Load the app with the code panes
await page.addInitScript(async () => { await page.addInitScript(async (persistModelingContext) => {
localStorage.setItem( localStorage.setItem(
'store', persistModelingContext,
JSON.stringify({ JSON.stringify({ openPanes: [] })
state: {
openPanes: [],
},
version: 0,
})
) )
}) }, PERSIST_MODELING_CONTEXT)
await doBasicSketch(page, []) await doBasicSketch(page, [])
}) })
}) })
@ -457,7 +441,9 @@ test.describe('Testing Camera Movement', () => {
// await u.canvasLocator.hover({position: {x: 700, y: 325}}) // await u.canvasLocator.hover({position: {x: 700, y: 325}})
await page.mouse.move(700, 325) await page.mouse.move(700, 325)
await page.waitForTimeout(100) 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() await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
@ -465,12 +451,18 @@ test.describe('Testing Camera Movement', () => {
await page.waitForTimeout(100) await page.waitForTimeout(100)
// hover over horizontal line // hover over horizontal line
await u.canvasLocator.hover({ position: { x: 800, y } }) 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 hoverOverNothing()
await page.waitForTimeout(200)
// hover over vertical line // hover over vertical line
await u.canvasLocator.hover({ position: { x, y: 325 } }) 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() await hoverOverNothing()
@ -479,22 +471,28 @@ test.describe('Testing Camera Movement', () => {
await page.waitForTimeout(400) await page.waitForTimeout(400)
await hoverOverNothing() await hoverOverNothing()
await page.waitForTimeout(100) await page.waitForTimeout(200)
// hover over horizontal line // hover over horizontal line
await page.mouse.move(858, y, { steps: 5 }) 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() await hoverOverNothing()
// hover over vertical line // hover over vertical line
await page.mouse.move(x, 325) 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() await hoverOverNothing()
// hover over vertical line // hover over vertical line
await page.mouse.move(857, y) 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 // now click it
await page.mouse.click(857, y) await page.mouse.click(857, y)
@ -511,27 +509,36 @@ test.describe('Testing Camera Movement', () => {
await page.waitForTimeout(100) await page.waitForTimeout(100)
await page.mouse.move(x, 419, { steps: 5 }) 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 hoverOverNothing()
await page.mouse.move(855, y) 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 hoverOverNothing()
await page.getByRole('button', { name: 'Exit Sketch' }).click() await page.getByRole('button', { name: 'Exit Sketch' }).click()
await page.waitForTimeout(400) await page.waitForTimeout(200)
await hoverOverNothing() await hoverOverNothing()
await page.waitForTimeout(200)
await page.mouse.move(x, 419) 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 hoverOverNothing()
await page.mouse.move(855, y) 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 }) => { test('code pane closed at start-handles', async ({ page }) => {
// Load the app with the code panes // Load the app with the code panes
await page.addInitScript(async () => { await page.addInitScript(async (persistModelingContext) => {
localStorage.setItem( localStorage.setItem(
'store', persistModelingContext,
JSON.stringify({ JSON.stringify({ openPanes: [] })
state: {
openPanes: [],
},
version: 0,
})
) )
}) }, PERSIST_MODELING_CONTEXT)
await doEditSegmentsByDraggingHandle(page, []) await doEditSegmentsByDraggingHandle(page, [])
}) })
}) })
@ -5081,6 +5083,7 @@ const part002 = startSketchOn('XZ')
test('Horizontally constrained line remains selected after applying constraint', async ({ test('Horizontally constrained line remains selected after applying constraint', async ({
page, page,
}) => { }) => {
test.setTimeout(70_000)
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -5096,6 +5099,9 @@ const part002 = startSketchOn('XZ')
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
await page.getByText('line([3.79, 2.68], %, $seg01)').click() 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.getByRole('button', { name: 'Edit Sketch' }).click()
await page.waitForTimeout(100) await page.waitForTimeout(100)
@ -5136,12 +5142,16 @@ const part002 = startSketchOn('XZ')
await u.getGreatestPixDiff(lineAfter, TEST_COLORS.BLUE) await u.getGreatestPixDiff(lineAfter, TEST_COLORS.BLUE)
).toBeLessThan(3) ).toBeLessThan(3)
await page.waitForTimeout(300)
await page await page
.getByRole('button', { .getByRole('button', {
name: 'Constraints', name: 'Constraints',
}) })
.click() .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.getByLabel('length Value').fill('10')
await page.getByRole('button', { name: 'Add constraining value' }).click() 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 // Test that the hotkeys do nothing when
// focus is on the code pane // focus is on the code pane
await codePane.click() await codePane.click()
await page.keyboard.press('/')
await page.keyboard.press('/')
await page.keyboard.press('s') await page.keyboard.press('s')
await page.keyboard.press('l') await page.keyboard.press('l')
await page.keyboard.press('a') await page.keyboard.press('a')
await page.keyboard.press('e') 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.keyboard.press('Meta+/')
await page.waitForTimeout(2000)
// Test these hotkeys perform actions when // Test these hotkeys perform actions when
// focus is on the canvas // focus is on the canvas
await page.mouse.move(600, 250) await page.mouse.move(600, 250)
await page.mouse.click(600, 250) await page.mouse.click(600, 250)
// Start a sketch // Start a sketch
await page.keyboard.press('s') 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.mouse.click(800, 300)
await page.waitForTimeout(1000) await page.waitForTimeout(2000)
await expect(lineButton).toHaveAttribute('aria-pressed', 'true') await expect(lineButton).toHaveAttribute('aria-pressed', 'true', {
timeout: 15_000,
})
/** /**
* TODO: There is a bug somewhere that causes this test to fail * TODO: There is a bug somewhere that causes this test to fail
* if you toggle the codePane closed before your trigger the * 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 * https://discuss.codemirror.net/t/how-to-force-unfocus-of-the-codemirror-element-in-safari/8095/3
*/ */
await codePaneButton.click() await codePaneButton.click()
await expect(u.codeLocator).not.toBeVisible()
await page.waitForTimeout(300)
// Draw a line // Draw a line
await page.mouse.move(700, 200, { steps: 5 }) await page.mouse.move(700, 200, { steps: 5 })
await page.mouse.click(700, 200) await page.mouse.click(700, 200)
await page.waitForTimeout(300)
await page.mouse.move(800, 250, { steps: 5 }) await page.mouse.move(800, 250, { steps: 5 })
await page.mouse.click(800, 250) await page.mouse.click(800, 250)
// Unequip line tool // 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') await expect(lineButton).not.toHaveAttribute('aria-pressed', 'true')
// Equip arc tool // Equip arc tool
await page.keyboard.press('a') 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.move(1000, 100, { steps: 5 })
await page.mouse.click(1000, 100) await page.mouse.click(1000, 100)
await page.keyboard.press('Escape') 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.waitForTimeout(100)
await page.mouse.move(800, 200, { steps: 5 }) await page.mouse.move(800, 200, { steps: 5 })
await page.mouse.click(800, 200) 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.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 page.getByRole('button', { name: 'Submit command' }).click()
await codePaneButton.click() await codePaneButton.click()

View File

@ -1,6 +1,5 @@
import { MouseEventHandler, useEffect, useRef } from 'react' import { MouseEventHandler, useEffect, useRef } from 'react'
import { uuidv4 } from 'lib/utils' import { uuidv4 } from 'lib/utils'
import { useStore } from './useStore'
import { useHotKeyListener } from './hooks/useHotKeyListener' import { useHotKeyListener } from './hooks/useHotKeyListener'
import { Stream } from './components/Stream' import { Stream } from './components/Stream'
import { EngineCommand } from './lang/std/engineConnection' import { EngineCommand } from './lang/std/engineConnection'
@ -26,6 +25,7 @@ import ModalContainer from 'react-modal-promise'
import useHotkeyWrapper from 'lib/hotkeyWrapper' import useHotkeyWrapper from 'lib/hotkeyWrapper'
import Gizmo from 'components/Gizmo' import Gizmo from 'components/Gizmo'
import { CoreDumpManager } from 'lib/coredump' import { CoreDumpManager } from 'lib/coredump'
import { useStore } from 'useStore'
export function App() { export function App() {
useRefreshSettings(paths.FILE + 'SETTINGS') useRefreshSettings(paths.FILE + 'SETTINGS')
@ -44,11 +44,8 @@ export function App() {
}, [projectName, projectPath]) }, [projectName, projectPath])
useHotKeyListener() useHotKeyListener()
const { buttonDownInStream, didDragInStream, streamDimensions, setHtmlRef } = const { context } = useModelingContext()
useStore((s) => ({ const { setHtmlRef } = useStore((s) => ({
buttonDownInStream: s.buttonDownInStream,
didDragInStream: s.didDragInStream,
streamDimensions: s.streamDimensions,
setHtmlRef: s.setHtmlRef, setHtmlRef: s.setHtmlRef,
})) }))
@ -81,7 +78,7 @@ export function App() {
(p) => p === onboardingStatus.current (p) => p === onboardingStatus.current
) )
? 'opacity-20' ? 'opacity-20'
: didDragInStream : context.store?.didDragInStream
? 'opacity-40' ? 'opacity-40'
: '' : ''
@ -99,11 +96,11 @@ export function App() {
clientX: e.clientX, clientX: e.clientX,
clientY: e.clientY, clientY: e.clientY,
el: e.currentTarget, el: e.currentTarget,
...streamDimensions, ...context.store?.streamDimensions,
}) })
const newCmdId = uuidv4() const newCmdId = uuidv4()
if (buttonDownInStream === undefined) { if (context.store?.buttonDownInStream === undefined) {
debounceSocketSend({ debounceSocketSend({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
cmd: { cmd: {
@ -125,7 +122,7 @@ export function App() {
className={ className={
'transition-opacity transition-duration-75 ' + 'transition-opacity transition-duration-75 ' +
paneOpacity + paneOpacity +
(buttonDownInStream ? ' pointer-events-none' : '') (context.store?.buttonDownInStream ? ' pointer-events-none' : '')
} }
project={{ project, file }} project={{ project, file }}
enableMenu={true} enableMenu={true}

View File

@ -33,6 +33,14 @@ import LspProvider from 'components/LspProvider'
import { KclContextProvider } from 'lang/KclProvider' import { KclContextProvider } from 'lang/KclProvider'
import { BROWSER_PROJECT_NAME } from 'lib/constants' import { BROWSER_PROJECT_NAME } from 'lib/constants'
import { getState, setState } from 'lib/tauri' 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([ const router = createBrowserRouter([
{ {
@ -87,6 +95,7 @@ const router = createBrowserRouter([
<Auth> <Auth>
<FileMachineProvider> <FileMachineProvider>
<ModelingMachineProvider> <ModelingMachineProvider>
<CoreDump />
<Outlet /> <Outlet />
<App /> <App />
<CommandBar /> <CommandBar />
@ -165,3 +174,33 @@ export const Router = () => {
</NetworkContext.Provider> </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
}

View File

@ -8,10 +8,10 @@ import { NetworkHealthState } from 'hooks/useNetworkStatus'
import { ActionButton } from 'components/ActionButton' import { ActionButton } from 'components/ActionButton'
import { isSingleCursorInPipe } from 'lang/queryAst' import { isSingleCursorInPipe } from 'lang/queryAst'
import { useKclContext } from 'lang/KclProvider' import { useKclContext } from 'lang/KclProvider'
import { useStore } from 'useStore'
import { ActionButtonDropdown } from 'components/ActionButtonDropdown' import { ActionButtonDropdown } from 'components/ActionButtonDropdown'
import { useHotkeys } from 'react-hotkeys-hook' import { useHotkeys } from 'react-hotkeys-hook'
import Tooltip from 'components/Tooltip' import Tooltip from 'components/Tooltip'
import { useStore } from 'useStore'
export function Toolbar({ export function Toolbar({
className = '', className = '',

View File

@ -37,7 +37,7 @@ import { Dialog, Popover, Transition } from '@headlessui/react'
import { LineInputsType } from 'lang/std/sketchcombos' import { LineInputsType } from 'lang/std/sketchcombos'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
import { InstanceProps, create } from 'react-modal-promise' import { InstanceProps, create } from 'react-modal-promise'
import { executeAst } from 'useStore' import { executeAst } from 'lang/langHelpers'
import { import {
deleteSegmentFromPipeExpression, deleteSegmentFromPipeExpression,
makeRemoveSingleConstraintInput, makeRemoveSingleConstraintInput,

View File

@ -58,7 +58,7 @@ import {
editorManager, editorManager,
} from 'lib/singletons' } from 'lib/singletons'
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst' import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
import { executeAst, useStore } from 'useStore' import { executeAst } from 'lang/langHelpers'
import { import {
createArcGeometry, createArcGeometry,
dashedStraight, dashedStraight,
@ -1444,11 +1444,10 @@ export class SceneEntities {
selected.material.color = defaultPlaneColor(type) selected.material.color = defaultPlaneColor(type)
}, },
onClick: async (args) => { onClick: async (args) => {
const { streamDimensions } = useStore.getState()
const { entity_id } = await sendSelectEventToEngine( const { entity_id } = await sendSelectEventToEngine(
args?.mouseEvent, args?.mouseEvent,
document.getElementById('video-stream') as HTMLVideoElement, document.getElementById('video-stream') as HTMLVideoElement,
streamDimensions sceneInfra._streamDimensions
) )
let _entity_id = entity_id let _entity_id = entity_id

View File

@ -103,6 +103,10 @@ export class SceneInfra {
_baseUnit: BaseUnit = 'mm' _baseUnit: BaseUnit = 'mm'
_baseUnitMultiplier = 1 _baseUnitMultiplier = 1
_theme: Themes = Themes.System _theme: Themes = Themes.System
_streamDimensions: { streamWidth: number; streamHeight: number } = {
streamWidth: 1280,
streamHeight: 720,
}
extraSegmentTexture: Texture extraSegmentTexture: Texture
lastMouseState: MouseState = { type: 'idle' } lastMouseState: MouseState = { type: 'idle' }
onDragStartCallback: (arg: OnDragCallbackArgs) => void = () => {} onDragStartCallback: (arg: OnDragCallbackArgs) => void = () => {}

View File

@ -10,7 +10,7 @@ import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst'
import { engineCommandManager, kclManager } from 'lib/singletons' import { engineCommandManager, kclManager } from 'lib/singletons'
import { useKclContext } from 'lang/KclProvider' import { useKclContext } from 'lang/KclProvider'
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
import { executeAst } from 'useStore' import { executeAst } from 'lang/langHelpers'
import { trap } from 'lib/trap' import { trap } from 'lib/trap'
export const AvailableVars = ({ export const AvailableVars = ({

View File

@ -30,7 +30,6 @@ import {
applyConstraintAngleBetween, applyConstraintAngleBetween,
} from './Toolbar/SetAngleBetween' } from './Toolbar/SetAngleBetween'
import { applyConstraintAngleLength } from './Toolbar/setAngleLength' import { applyConstraintAngleLength } from './Toolbar/setAngleLength'
import { useStore } from 'useStore'
import { import {
Selections, Selections,
canExtrudeSelection, canExtrudeSelection,
@ -54,13 +53,7 @@ import {
sketchOnExtrudedFace, sketchOnExtrudedFace,
startSketchOnDefault, startSketchOnDefault,
} from 'lang/modifyAst' } from 'lang/modifyAst'
import { import { Program, VariableDeclaration, parse, recast } from 'lang/wasm'
Program,
VariableDeclaration,
coreDump,
parse,
recast,
} from 'lang/wasm'
import { import {
getNodeFromPath, getNodeFromPath,
getNodePathFromSourceRange, getNodePathFromSourceRange,
@ -72,11 +65,9 @@ import { exportFromEngine } from 'lib/exportFromEngine'
import { Models } from '@kittycad/lib/dist/types/src' import { Models } from '@kittycad/lib/dist/types/src'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
import { EditorSelection, Transaction } from '@uiw/react-codemirror' import { EditorSelection, Transaction } from '@uiw/react-codemirror'
import { CoreDumpManager } from 'lib/coredump'
import { useSearchParams } from 'react-router-dom' import { useSearchParams } from 'react-router-dom'
import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls' import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls'
import { getVarNameModal } from 'hooks/useToolbarGuards' import { getVarNameModal } from 'hooks/useToolbarGuards'
import useHotkeyWrapper from 'lib/hotkeyWrapper'
import { uuidv4 } from 'lib/utils' import { uuidv4 } from 'lib/utils'
import { err, trap } from 'lib/trap' import { err, trap } from 'lib/trap'
import { useCommandsContext } from 'hooks/useCommandsContext' import { useCommandsContext } from 'hooks/useCommandsContext'
@ -112,38 +103,6 @@ export const ModelingMachineProvider = ({
let [searchParams] = useSearchParams() let [searchParams] = useSearchParams()
const pool = searchParams.get('pool') 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() const { commandBarState } = useCommandsContext()
// Settings machine setup // 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(() => { useEffect(() => {
kclManager.registerExecuteCallback(() => { kclManager.registerExecuteCallback(() => {
modelingSend({ type: 'Re-execute' }) modelingSend({ type: 'Re-execute' })

View File

@ -1,6 +1,6 @@
import { useStore } from 'useStore'
import styles from './ModelingPane.module.css' import styles from './ModelingPane.module.css'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { useModelingContext } from 'hooks/useModelingContext'
export interface ModelingPaneProps export interface ModelingPaneProps
extends React.PropsWithChildren, extends React.PropsWithChildren,
@ -33,11 +33,9 @@ export const ModelingPane = ({
}: ModelingPaneProps) => { }: ModelingPaneProps) => {
const { settings } = useSettingsAuthContext() const { settings } = useSettingsAuthContext()
const onboardingStatus = settings.context.app.onboardingStatus const onboardingStatus = settings.context.app.onboardingStatus
const { buttonDownInStream } = useStore((s) => ({ const { context } = useModelingContext()
buttonDownInStream: s.buttonDownInStream,
}))
const pointerEventsCssClass = const pointerEventsCssClass =
buttonDownInStream || onboardingStatus.current === 'camera' context.store?.buttonDownInStream || onboardingStatus.current === 'camera'
? 'pointer-events-none ' ? 'pointer-events-none '
: 'pointer-events-auto ' : 'pointer-events-auto '
return ( return (

View File

@ -2,7 +2,6 @@ import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { Resizable } from 're-resizable' import { Resizable } from 're-resizable'
import { HTMLAttributes, useCallback, useEffect, useState } from 'react' import { HTMLAttributes, useCallback, useEffect, useState } from 'react'
import { useHotkeys } from 'react-hotkeys-hook' import { useHotkeys } from 'react-hotkeys-hook'
import { useStore } from 'useStore'
import { Tab } from '@headlessui/react' import { Tab } from '@headlessui/react'
import { import {
SidebarPane, SidebarPane,
@ -15,6 +14,7 @@ import { ActionIcon } from 'components/ActionIcon'
import styles from './ModelingSidebar.module.css' import styles from './ModelingSidebar.module.css'
import { ModelingPane } from './ModelingPane' import { ModelingPane } from './ModelingPane'
import { isTauri } from 'lib/isTauri' import { isTauri } from 'lib/isTauri'
import { useModelingContext } from 'hooks/useModelingContext'
interface ModelingSidebarProps { interface ModelingSidebarProps {
paneOpacity: '' | 'opacity-20' | 'opacity-40' paneOpacity: '' | 'opacity-20' | 'opacity-40'
@ -23,14 +23,11 @@ interface ModelingSidebarProps {
export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) { export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
const { settings } = useSettingsAuthContext() const { settings } = useSettingsAuthContext()
const onboardingStatus = settings.context.app.onboardingStatus const onboardingStatus = settings.context.app.onboardingStatus
const { openPanes, buttonDownInStream } = useStore((s) => ({ const { context } = useModelingContext()
buttonDownInStream: s.buttonDownInStream,
openPanes: s.openPanes,
}))
const pointerEventsCssClass = const pointerEventsCssClass =
buttonDownInStream || context.store?.buttonDownInStream ||
onboardingStatus.current === 'camera' || onboardingStatus.current === 'camera' ||
openPanes.length === 0 context.store?.openPanes.length === 0
? 'pointer-events-none ' ? 'pointer-events-none '
: 'pointer-events-auto ' : 'pointer-events-auto '
@ -45,7 +42,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
maxWidth={800} maxWidth={800}
handleClasses={{ handleClasses={{
right: 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 ', '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', left: 'hidden',
top: 'hidden', top: 'hidden',
@ -82,11 +79,10 @@ function ModelingSidebarSection({
const { settings } = useSettingsAuthContext() const { settings } = useSettingsAuthContext()
const showDebugPanel = settings.context.modeling.showDebugPanel const showDebugPanel = settings.context.modeling.showDebugPanel
const paneIds = panes.map((pane) => pane.id) const paneIds = panes.map((pane) => pane.id)
const { openPanes, setOpenPanes } = useStore((s) => ({ const { send, context } = useModelingContext()
openPanes: s.openPanes, const foundOpenPane = context.store?.openPanes.find((pane) =>
setOpenPanes: s.setOpenPanes, paneIds.includes(pane)
})) )
const foundOpenPane = openPanes.find((pane) => paneIds.includes(pane))
const [currentPane, setCurrentPane] = useState( const [currentPane, setCurrentPane] = useState(
foundOpenPane || ('none' as SidebarType | 'none') foundOpenPane || ('none' as SidebarType | 'none')
) )
@ -94,17 +90,37 @@ function ModelingSidebarSection({
const togglePane = useCallback( const togglePane = useCallback(
(newPane: SidebarType | 'none') => { (newPane: SidebarType | 'none') => {
if (newPane === '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') setCurrentPane('none')
} else if (newPane === currentPane) { } else if (newPane === currentPane) {
setCurrentPane('none') setCurrentPane('none')
setOpenPanes(openPanes.filter((p) => p !== newPane)) send({
type: 'Set context',
data: {
openPanes: context.store?.openPanes.filter((p) => p !== newPane),
},
})
} else { } else {
setOpenPanes([...openPanes.filter((p) => p !== currentPane), newPane]) send({
type: 'Set context',
data: {
openPanes: [
...context.store?.openPanes.filter((p) => p !== currentPane),
newPane,
],
},
})
setCurrentPane(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 // Filter out the debug panel if it's not supposed to be shown
@ -122,11 +138,11 @@ function ModelingSidebarSection({
if ( if (
!showDebugPanel.current && !showDebugPanel.current &&
currentPane === 'debug' && currentPane === 'debug' &&
openPanes.includes('debug') context.store?.openPanes.includes('debug')
) { ) {
togglePane('debug') togglePane('debug')
} }
}, [showDebugPanel.current, togglePane, openPanes]) }, [showDebugPanel.current, togglePane, context.store?.openPanes])
return ( return (
<div className={'group contents ' + className} {...props}> <div className={'group contents ' + className} {...props}>
@ -152,7 +168,9 @@ function ModelingSidebarSection({
: ' border-r-0') + : ' border-r-0') +
' p-2 col-start-1 col-span-1 h-fit w-fit flex flex-col items-start gap-2 ' + ' 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 ' + '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"> <Tab key="none" className="sr-only">
@ -172,7 +190,7 @@ function ModelingSidebarSection({
as="article" as="article"
className={ className={
'col-start-2 col-span-1 ' + 'col-start-2 col-span-1 ' +
(openPanes.length === 1 (context.store?.openPanes.length === 1
? currentPane !== 'none' ? currentPane !== 'none'
? `row-start-1 row-end-3` ? `row-start-1 row-end-3`
: `hidden` : `hidden`

View File

@ -2,11 +2,11 @@ import { coreDump } from 'lang/wasm'
import { CoreDumpManager } from 'lib/coredump' import { CoreDumpManager } from 'lib/coredump'
import { CustomIcon } from './CustomIcon' import { CustomIcon } from './CustomIcon'
import { engineCommandManager } from 'lib/singletons' import { engineCommandManager } from 'lib/singletons'
import React from 'react' import React, { useMemo } from 'react'
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
import Tooltip from './Tooltip' import Tooltip from './Tooltip'
import { useStore } from 'useStore'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { useStore } from 'useStore'
export const RefreshButton = ({ children }: React.PropsWithChildren) => { export const RefreshButton = ({ children }: React.PropsWithChildren) => {
const { auth } = useSettingsAuthContext() const { auth } = useSettingsAuthContext()
@ -14,10 +14,9 @@ export const RefreshButton = ({ children }: React.PropsWithChildren) => {
const { htmlRef } = useStore((s) => ({ const { htmlRef } = useStore((s) => ({
htmlRef: s.htmlRef, htmlRef: s.htmlRef,
})) }))
const coreDumpManager = new CoreDumpManager( const coreDumpManager = useMemo(
engineCommandManager, () => new CoreDumpManager(engineCommandManager, htmlRef, token),
htmlRef, []
token
) )
async function refresh() { async function refresh() {

View File

@ -1,5 +1,4 @@
import { MouseEventHandler, useEffect, useRef, useState } from 'react' import { MouseEventHandler, useEffect, useRef, useState } from 'react'
import { useStore } from '../useStore'
import { getNormalisedCoordinates } from '../lib/utils' import { getNormalisedCoordinates } from '../lib/utils'
import Loading from './Loading' import Loading from './Loading'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
@ -10,25 +9,12 @@ import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp'
import { butName } from 'lib/cameraControls' import { butName } from 'lib/cameraControls'
import { sendSelectEventToEngine } from 'lib/selections' import { sendSelectEventToEngine } from 'lib/selections'
export const Stream = ({ className = '' }: { className?: string }) => { export const Stream = () => {
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(true)
const [clickCoords, setClickCoords] = useState<{ x: number; y: number }>() const [clickCoords, setClickCoords] = useState<{ x: number; y: number }>()
const videoRef = useRef<HTMLVideoElement>(null) 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 { settings } = useSettingsAuthContext()
const { state } = useModelingContext() const { state, send, context } = useModelingContext()
const { overallState } = useNetworkContext() const { overallState } = useNetworkContext()
const isNetworkOkay = const isNetworkOkay =
@ -74,9 +60,9 @@ export const Stream = ({ className = '' }: { className?: string }) => {
) )
return return
if (!videoRef.current) return if (!videoRef.current) return
if (!mediaStream) return if (!context.store?.mediaStream) return
videoRef.current.srcObject = mediaStream videoRef.current.srcObject = context.store.mediaStream
}, [mediaStream]) }, [context.store?.mediaStream])
const handleMouseDown: MouseEventHandler<HTMLDivElement> = (e) => { const handleMouseDown: MouseEventHandler<HTMLDivElement> = (e) => {
if (!isNetworkOkay) return if (!isNetworkOkay) return
@ -88,25 +74,44 @@ export const Stream = ({ className = '' }: { className?: string }) => {
clientX: e.clientX, clientX: e.clientX,
clientY: e.clientY, clientY: e.clientY,
el: videoRef.current, el: videoRef.current,
...streamDimensions, ...context.store?.streamDimensions,
}) })
setButtonDownInStream(e.button) send({
type: 'Set context',
data: {
buttonDownInStream: e.button,
},
})
setClickCoords({ x, y }) setClickCoords({ x, y })
} }
const handleMouseUp: MouseEventHandler<HTMLDivElement> = (e) => { const handleMouseUp: MouseEventHandler<HTMLDivElement> = (e) => {
if (!isNetworkOkay) return if (!isNetworkOkay) return
if (!videoRef.current) return if (!videoRef.current) return
setButtonDownInStream(undefined) send({
type: 'Set context',
data: {
buttonDownInStream: undefined,
},
})
if (state.matches('Sketch')) return if (state.matches('Sketch')) return
if (state.matches('Sketch no face')) return if (state.matches('Sketch no face')) return
if (!didDragInStream && butName(e).left) { if (!context.store?.didDragInStream && butName(e).left) {
sendSelectEventToEngine(e, videoRef.current, streamDimensions) sendSelectEventToEngine(
e,
videoRef.current,
context.store?.streamDimensions
)
} }
setDidDragInStream(false) send({
type: 'Set context',
data: {
didDragInStream: false,
},
})
setClickCoords(undefined) setClickCoords(undefined)
} }
@ -120,8 +125,13 @@ export const Stream = ({ className = '' }: { className?: string }) => {
((clickCoords.x - e.clientX) ** 2 + (clickCoords.y - e.clientY) ** 2) ** ((clickCoords.x - e.clientX) ** 2 + (clickCoords.y - e.clientY) ** 2) **
0.5 0.5
if (delta > 5 && !didDragInStream) { if (delta > 5 && !context.store?.didDragInStream) {
setDidDragInStream(true) send({
type: 'Set context',
data: {
didDragInStream: true,
},
})
} }
} }

View File

@ -1,4 +1,4 @@
import { toolTips } from '../../useStore' import { toolTips } from 'lang/langHelpers'
import { Selections } from 'lib/selections' import { Selections } from 'lib/selections'
import { Program, Value, VariableDeclarator } from '../../lang/wasm' import { Program, Value, VariableDeclarator } from '../../lang/wasm'
import { import {

View File

@ -1,4 +1,4 @@
import { toolTips } from '../../useStore' import { toolTips } from 'lang/langHelpers'
import { Selections } from 'lib/selections' import { Selections } from 'lib/selections'
import { Program, Value, VariableDeclarator } from '../../lang/wasm' import { Program, Value, VariableDeclarator } from '../../lang/wasm'
import { import {

View File

@ -1,4 +1,4 @@
import { toolTips } from '../../useStore' import { toolTips } from 'lang/langHelpers'
import { Selections } from 'lib/selections' import { Selections } from 'lib/selections'
import { Program, ProgramMemory, Value } from '../../lang/wasm' import { Program, ProgramMemory, Value } from '../../lang/wasm'
import { import {

View File

@ -1,4 +1,4 @@
import { toolTips } from '../../useStore' import { toolTips } from 'lang/langHelpers'
import { Selections } from 'lib/selections' import { Selections } from 'lib/selections'
import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm' import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm'
import { import {

View File

@ -1,4 +1,4 @@
import { toolTips } from '../../useStore' import { toolTips } from 'lang/langHelpers'
import { Selection, Selections } from 'lib/selections' import { Selection, Selections } from 'lib/selections'
import { PathToNode, Program, Value } from '../../lang/wasm' import { PathToNode, Program, Value } from '../../lang/wasm'
import { import {

View File

@ -1,4 +1,4 @@
import { toolTips } from '../../useStore' import { toolTips } from 'lang/langHelpers'
import { Selections } from 'lib/selections' import { Selections } from 'lib/selections'
import { BinaryPart, Program, Value } from '../../lang/wasm' import { BinaryPart, Program, Value } from '../../lang/wasm'
import { import {

View File

@ -1,4 +1,4 @@
import { toolTips } from '../../useStore' import { toolTips } from 'lang/langHelpers'
import { Selections } from 'lib/selections' import { Selections } from 'lib/selections'
import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm' import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm'
import { import {

View File

@ -1,4 +1,4 @@
import { toolTips } from '../../useStore' import { toolTips } from 'lang/langHelpers'
import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm' import { BinaryPart, Program, Value, VariableDeclarator } from '../../lang/wasm'
import { import {
getNodePathFromSourceRange, getNodePathFromSourceRange,

View File

@ -1,4 +1,4 @@
import { toolTips } from '../../useStore' import { toolTips } from 'lang/langHelpers'
import { Selections } from 'lib/selections' import { Selections } from 'lib/selections'
import { BinaryPart, Program, Value } from '../../lang/wasm' import { BinaryPart, Program, Value } from '../../lang/wasm'
import { import {

View File

@ -1,9 +1,10 @@
import { useLayoutEffect, useEffect, useRef } from 'react' import { useLayoutEffect, useEffect, useRef } from 'react'
import { useStore } from '../useStore'
import { engineCommandManager, kclManager } from 'lib/singletons' import { engineCommandManager, kclManager } from 'lib/singletons'
import { deferExecution } from 'lib/utils' import { deferExecution } from 'lib/utils'
import { Themes } from 'lib/theme' import { Themes } from 'lib/theme'
import { makeDefaultPlanes, modifyGrid } from 'lang/wasm' import { makeDefaultPlanes, modifyGrid } from 'lang/wasm'
import { useModelingContext } from './useModelingContext'
import { useStore } from 'useStore'
export function useSetupEngineManager( export function useSetupEngineManager(
streamRef: React.RefObject<HTMLDivElement>, streamRef: React.RefObject<HTMLDivElement>,
@ -13,25 +14,21 @@ export function useSetupEngineManager(
theme: Themes.System, theme: Themes.System,
highlightEdges: true, highlightEdges: true,
enableSSAO: true, enableSSAO: true,
modelingSend: (() => {}) as any,
modelingContext: {} as any,
showScaleGrid: false, showScaleGrid: false,
} as { } as {
pool: string | null pool: string | null
theme: Themes theme: Themes
highlightEdges: boolean highlightEdges: boolean
enableSSAO: boolean enableSSAO: boolean
modelingSend: ReturnType<typeof useModelingContext>['send']
modelingContext: ReturnType<typeof useModelingContext>['context']
showScaleGrid: boolean showScaleGrid: boolean
} }
) { ) {
const { const { setIsStreamReady } = useStore((s) => ({
setMediaStream,
setIsStreamReady,
setStreamDimensions,
streamDimensions,
} = useStore((s) => ({
setMediaStream: s.setMediaStream,
setIsStreamReady: s.setIsStreamReady, setIsStreamReady: s.setIsStreamReady,
setStreamDimensions: s.setStreamDimensions,
streamDimensions: s.streamDimensions,
})) }))
const streamWidth = streamRef?.current?.offsetWidth const streamWidth = streamRef?.current?.offsetWidth
@ -52,9 +49,18 @@ export function useSetupEngineManager(
streamWidth, streamWidth,
streamHeight streamHeight
) )
if (!hasSetNonZeroDimensions.current && quadHeight && quadWidth) { if (
!hasSetNonZeroDimensions.current &&
quadHeight &&
quadWidth &&
settings.modelingSend
) {
engineCommandManager.start({ engineCommandManager.start({
setMediaStream, setMediaStream: (mediaStream) =>
settings.modelingSend({
type: 'Set context',
data: { mediaStream },
}),
setIsStreamReady, setIsStreamReady,
width: quadWidth, width: quadWidth,
height: quadHeight, height: quadHeight,
@ -72,9 +78,14 @@ export function useSetupEngineManager(
return modifyGrid(kclManager.engineCommandManager, hidden) return modifyGrid(kclManager.engineCommandManager, hidden)
}, },
}) })
setStreamDimensions({ settings.modelingSend({
type: 'Set context',
data: {
streamDimensions: {
streamWidth: quadWidth, streamWidth: quadWidth,
streamHeight: quadHeight, streamHeight: quadHeight,
},
},
}) })
hasSetNonZeroDimensions.current = true hasSetNonZeroDimensions.current = true
} }
@ -83,6 +94,7 @@ export function useSetupEngineManager(
useLayoutEffect(startEngineInstance, [ useLayoutEffect(startEngineInstance, [
streamRef?.current?.offsetWidth, streamRef?.current?.offsetWidth,
streamRef?.current?.offsetHeight, streamRef?.current?.offsetHeight,
settings.modelingSend,
]) ])
useEffect(() => { useEffect(() => {
@ -92,16 +104,21 @@ export function useSetupEngineManager(
streamRef?.current?.offsetHeight streamRef?.current?.offsetHeight
) )
if ( if (
streamDimensions.streamWidth !== width || settings.modelingContext.store.streamDimensions.streamWidth !== width ||
streamDimensions.streamHeight !== height settings.modelingContext.store.streamDimensions.streamHeight !== height
) { ) {
engineCommandManager.handleResize({ engineCommandManager.handleResize({
streamWidth: width, streamWidth: width,
streamHeight: height, streamHeight: height,
}) })
setStreamDimensions({ settings.modelingSend({
type: 'Set context',
data: {
streamDimensions: {
streamWidth: width, streamWidth: width,
streamHeight: height, streamHeight: height,
},
},
}) })
} }
}, 500) }, 500)

View File

@ -8,9 +8,9 @@ import { settingsMachine } from 'machines/settingsMachine'
import { homeMachine } from 'machines/homeMachine' import { homeMachine } from 'machines/homeMachine'
import { Command, CommandSetConfig, CommandSetSchema } from 'lib/commandTypes' import { Command, CommandSetConfig, CommandSetSchema } from 'lib/commandTypes'
import { useKclContext } from 'lang/KclProvider' import { useKclContext } from 'lang/KclProvider'
import { useStore } from 'useStore'
import { useNetworkContext } from 'hooks/useNetworkContext' import { useNetworkContext } from 'hooks/useNetworkContext'
import { NetworkHealthState } from 'hooks/useNetworkStatus' import { NetworkHealthState } from 'hooks/useNetworkStatus'
import { useStore } from 'useStore'
// This might not be necessary, AnyStateMachine from xstate is working // This might not be necessary, AnyStateMachine from xstate is working
export type AllMachines = export type AllMachines =

View File

@ -1,4 +1,4 @@
import { executeAst, lintAst } from 'useStore' import { executeAst, lintAst } from 'lang/langHelpers'
import { Selections } from 'lib/selections' import { Selections } from 'lib/selections'
import { KCLError, kclErrorsToDiagnostics } from './errors' import { KCLError, kclErrorsToDiagnostics } from './errors'
import { uuidv4 } from 'lib/utils' import { uuidv4 } from 'lib/utils'

View File

@ -8,7 +8,7 @@ import toast from 'react-hot-toast'
import { editorManager } from 'lib/singletons' import { editorManager } from 'lib/singletons'
import { Annotation, KeyBinding, Transaction } from '@uiw/react-codemirror' import { Annotation, KeyBinding, Transaction } from '@uiw/react-codemirror'
const PERSIST_CODE_TOKEN = 'persistCode' const PERSIST_CODE_KEY = 'persistCode'
const codeManagerUpdateAnnotation = Annotation.define<null>() const codeManagerUpdateAnnotation = Annotation.define<null>()
export const codeManagerUpdateEvent = codeManagerUpdateAnnotation.of(null) export const codeManagerUpdateEvent = codeManagerUpdateAnnotation.of(null)
@ -25,7 +25,7 @@ export default class CodeManager {
return return
} }
const storedCode = safeLSGetItem(PERSIST_CODE_TOKEN) const storedCode = safeLSGetItem(PERSIST_CODE_KEY)
// TODO #819 remove zustand persistence logic in a few months // TODO #819 remove zustand persistence logic in a few months
// short term migration, shouldn't make a difference for tauri app users // short term migration, shouldn't make a difference for tauri app users
// anyway since that's filesystem based. // anyway since that's filesystem based.
@ -125,7 +125,7 @@ export default class CodeManager {
}) })
}) })
} else { } else {
safeLSSetItem(PERSIST_CODE_TOKEN, this.code) safeLSSetItem(PERSIST_CODE_KEY, this.code)
} }
} }
} }

120
src/lang/langHelpers.ts Normal file
View 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 []
}
}

View File

@ -1,4 +1,4 @@
import { ToolTip } from '../useStore' import { ToolTip } from 'lang/langHelpers'
import { Selection, Selections } from 'lib/selections' import { Selection, Selections } from 'lib/selections'
import { import {
ArrayExpression, ArrayExpression,

View File

@ -23,7 +23,7 @@ import {
isLiteralArrayOrStatic, isLiteralArrayOrStatic,
isNotLiteralArrayOrStatic, isNotLiteralArrayOrStatic,
} from 'lang/std/sketchcombos' } from 'lang/std/sketchcombos'
import { toolTips, ToolTip } from '../../useStore' import { toolTips, ToolTip } from 'lang/langHelpers'
import { createPipeExpression, splitPathAtPipeExpression } from '../modifyAst' import { createPipeExpression, splitPathAtPipeExpression } from '../modifyAst'
import { import {

View File

@ -1,5 +1,5 @@
import { getNodeFromPath } from 'lang/queryAst' import { getNodeFromPath } from 'lang/queryAst'
import { ToolTip, toolTips } from '../../useStore' import { ToolTip, toolTips } from 'lang/langHelpers'
import { import {
Program, Program,
VariableDeclarator, VariableDeclarator,

View File

@ -8,7 +8,7 @@ import {
ConstraintLevel, ConstraintLevel,
getConstraintLevelFromSourceRange, getConstraintLevelFromSourceRange,
} from './sketchcombos' } from './sketchcombos'
import { ToolTip } from '../../useStore' import { ToolTip } from 'lang/langHelpers'
import { Selections } from 'lib/selections' import { Selections } from 'lib/selections'
import { err } from 'lib/trap' import { err } from 'lib/trap'
import { enginelessExecutor } from '../../lib/testHelpers' import { enginelessExecutor } from '../../lib/testHelpers'

View File

@ -1,5 +1,5 @@
import { TransformCallback, VarValues } from './stdTypes' import { TransformCallback, VarValues } from './stdTypes'
import { toolTips, ToolTip } from '../../useStore' import { ToolTip, toolTips } from 'lang/langHelpers'
import { Selections, Selection } from 'lib/selections' import { Selections, Selection } from 'lib/selections'
import { cleanErrs, err } from 'lib/trap' import { cleanErrs, err } from 'lib/trap'
import { import {

View File

@ -1,4 +1,4 @@
import { ToolTip } from 'useStore' import { ToolTip } from 'lang/langHelpers'
import { import {
ProgramMemory, ProgramMemory,
Path, Path,

View File

@ -5,7 +5,7 @@ import { findUniqueName } from 'lang/modifyAst'
import { PrevVariable, findAllPreviousVariables } from 'lang/queryAst' import { PrevVariable, findAllPreviousVariables } from 'lang/queryAst'
import { Value, parse } from 'lang/wasm' import { Value, parse } from 'lang/wasm'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { executeAst } from 'useStore' import { executeAst } from 'lang/langHelpers'
import { trap } from 'lib/trap' import { trap } from 'lib/trap'
const isValidVariableName = (name: string) => const isValidVariableName = (name: string) =>

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,5 @@
import { OnboardingButtons, useDismiss, useNextClick } from '.' import { OnboardingButtons, useDismiss, useNextClick } from '.'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { useStore } from '../../useStore'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { import {
CameraSystem, CameraSystem,
@ -8,11 +7,10 @@ import {
cameraSystems, cameraSystems,
} from 'lib/cameraControls' } from 'lib/cameraControls'
import { SettingsSection } from 'components/Settings/SettingsSection' import { SettingsSection } from 'components/Settings/SettingsSection'
import { useModelingContext } from 'hooks/useModelingContext'
export default function Units() { export default function Units() {
const { buttonDownInStream } = useStore((s) => ({ const { context } = useModelingContext()
buttonDownInStream: s.buttonDownInStream,
}))
const dismiss = useDismiss() const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.STREAMING) const next = useNextClick(onboardingPaths.STREAMING)
const { const {
@ -31,7 +29,7 @@ export default function Units() {
<div <div
className={ 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' + '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 <SettingsSection

View File

@ -1,12 +1,10 @@
import usePlatform from 'hooks/usePlatform' import usePlatform from 'hooks/usePlatform'
import { OnboardingButtons, kbdClasses, useDismiss, useNextClick } from '.' import { OnboardingButtons, kbdClasses, useDismiss, useNextClick } from '.'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { useStore } from '../../useStore' import { useModelingContext } from 'hooks/useModelingContext'
export default function CmdK() { export default function CmdK() {
const { buttonDownInStream } = useStore((s) => ({ const { context } = useModelingContext()
buttonDownInStream: s.buttonDownInStream,
}))
const dismiss = useDismiss() const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.USER_MENU) const next = useNextClick(onboardingPaths.USER_MENU)
const platformName = usePlatform() const platformName = usePlatform()
@ -16,7 +14,7 @@ export default function CmdK() {
<div <div
className={ 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' + '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> <h2 className="text-2xl font-bold">Command Bar</h2>

View File

@ -1,12 +1,10 @@
import { useModelingContext } from 'hooks/useModelingContext'
import { OnboardingButtons, useDemoCode, useDismiss, useNextClick } from '.' import { OnboardingButtons, useDemoCode, useDismiss, useNextClick } from '.'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { useStore } from '../../useStore'
export default function OnboardingCodeEditor() { export default function OnboardingCodeEditor() {
useDemoCode() useDemoCode()
const { buttonDownInStream } = useStore((s) => ({ const { context } = useModelingContext()
buttonDownInStream: s.buttonDownInStream,
}))
const dismiss = useDismiss() const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.PARAMETRIC_MODELING) const next = useNextClick(onboardingPaths.PARAMETRIC_MODELING)
@ -15,7 +13,7 @@ export default function OnboardingCodeEditor() {
<div <div
className={ 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' + '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"> <section className="flex-1 overflow-y-auto">

View File

@ -1,12 +1,10 @@
import { APP_NAME } from 'lib/constants' import { APP_NAME } from 'lib/constants'
import { OnboardingButtons, useDismiss, useNextClick } from '.' import { OnboardingButtons, useDismiss, useNextClick } from '.'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { useStore } from '../../useStore' import { useModelingContext } from 'hooks/useModelingContext'
export default function Export() { export default function Export() {
const { buttonDownInStream } = useStore((s) => ({ const { context } = useModelingContext()
buttonDownInStream: s.buttonDownInStream,
}))
const dismiss = useDismiss() const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.SKETCHING) const next = useNextClick(onboardingPaths.SKETCHING)
@ -15,7 +13,7 @@ export default function Export() {
<div <div
className={ 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' + '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"> <section className="flex-1">

View File

@ -6,14 +6,12 @@ import {
useNextClick, useNextClick,
} from '.' } from '.'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { useStore } from '../../useStore'
import { bracketWidthConstantLine } from 'lib/exampleKcl' import { bracketWidthConstantLine } from 'lib/exampleKcl'
import { useModelingContext } from 'hooks/useModelingContext'
export default function OnboardingInteractiveNumbers() { export default function OnboardingInteractiveNumbers() {
useDemoCode() useDemoCode()
const { buttonDownInStream } = useStore((s) => ({ const { context } = useModelingContext()
buttonDownInStream: s.buttonDownInStream,
}))
const dismiss = useDismiss() const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.COMMAND_K) const next = useNextClick(onboardingPaths.COMMAND_K)
@ -22,7 +20,7 @@ export default function OnboardingInteractiveNumbers() {
<div <div
className={ 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' + '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"> <section className="flex-1 overflow-y-auto mb-6">

View File

@ -1,15 +1,13 @@
import { OnboardingButtons, useDemoCode, useDismiss, useNextClick } from '.' import { OnboardingButtons, useDemoCode, useDismiss, useNextClick } from '.'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { useStore } from '../../useStore'
import { Themes, getSystemTheme } from 'lib/theme' import { Themes, getSystemTheme } from 'lib/theme'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { bracketThicknessCalculationLine } from 'lib/exampleKcl' import { bracketThicknessCalculationLine } from 'lib/exampleKcl'
import { useModelingContext } from 'hooks/useModelingContext'
export default function OnboardingParametricModeling() { export default function OnboardingParametricModeling() {
useDemoCode() useDemoCode()
const { buttonDownInStream } = useStore((s) => ({ const { context } = useModelingContext()
buttonDownInStream: s.buttonDownInStream,
}))
const { const {
settings: { settings: {
context: { context: {
@ -32,7 +30,7 @@ export default function OnboardingParametricModeling() {
<div <div
className={ 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' + '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"> <section className="flex-1 overflow-y-auto mb-6">

View File

@ -1,12 +1,10 @@
import { OnboardingButtons, useDismiss, useNextClick } from '.' import { OnboardingButtons, useDismiss, useNextClick } from '.'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { useStore } from '../../useStore'
import { isTauri } from 'lib/isTauri' import { isTauri } from 'lib/isTauri'
import { useModelingContext } from 'hooks/useModelingContext'
export default function ProjectMenu() { export default function ProjectMenu() {
const { buttonDownInStream } = useStore((s) => ({ const { context } = useModelingContext()
buttonDownInStream: s.buttonDownInStream,
}))
const dismiss = useDismiss() const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.EXPORT) const next = useNextClick(onboardingPaths.EXPORT)
const tauri = isTauri() const tauri = isTauri()
@ -16,7 +14,7 @@ export default function ProjectMenu() {
<div <div
className={ 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' + '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"> <section className="flex-1">

View File

@ -1,11 +1,11 @@
import { OnboardingButtons, useDismiss, useNextClick } from '.' import { OnboardingButtons, useDismiss, useNextClick } from '.'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { useStore } from 'useStore'
import { useEffect } from 'react' import { useEffect } from 'react'
import { codeManager, kclManager } from 'lib/singletons' import { codeManager, kclManager } from 'lib/singletons'
import { useModelingContext } from 'hooks/useModelingContext'
export default function Sketching() { export default function Sketching() {
const buttonDownInStream = useStore((s) => s.buttonDownInStream) const { context } = useModelingContext()
const dismiss = useDismiss() const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.FUTURE_WORK) const next = useNextClick(onboardingPaths.FUTURE_WORK)
@ -23,7 +23,7 @@ export default function Sketching() {
<div <div
className={ 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' + '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> <h1 className="text-2xl font-bold">Sketching</h1>

View File

@ -1,11 +1,9 @@
import { useModelingContext } from 'hooks/useModelingContext'
import { OnboardingButtons, useDismiss, useNextClick } from '.' import { OnboardingButtons, useDismiss, useNextClick } from '.'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { useStore } from '../../useStore'
export default function Streaming() { export default function Streaming() {
const { buttonDownInStream } = useStore((s) => ({ const { context } = useModelingContext()
buttonDownInStream: s.buttonDownInStream,
}))
const dismiss = useDismiss() const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.EDITOR) const next = useNextClick(onboardingPaths.EDITOR)
@ -14,7 +12,7 @@ export default function Streaming() {
<div <div
className={ 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' + '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"> <section className="flex-1 overflow-y-auto">

View File

@ -1,12 +1,10 @@
import { OnboardingButtons, useDismiss, useNextClick } from '.' import { OnboardingButtons, useDismiss, useNextClick } from '.'
import { onboardingPaths } from 'routes/Onboarding/paths' import { onboardingPaths } from 'routes/Onboarding/paths'
import { useStore } from '../../useStore'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useModelingContext } from 'hooks/useModelingContext'
export default function UserMenu() { export default function UserMenu() {
const { buttonDownInStream } = useStore((s) => ({ const { context } = useModelingContext()
buttonDownInStream: s.buttonDownInStream,
}))
const dismiss = useDismiss() const dismiss = useDismiss()
const next = useNextClick(onboardingPaths.PROJECT_MENU) const next = useNextClick(onboardingPaths.PROJECT_MENU)
const [avatarErrored, setAvatarErrored] = useState(false) const [avatarErrored, setAvatarErrored] = useState(false)
@ -33,7 +31,7 @@ export default function UserMenu() {
<div <div
className={ 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' + '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"> <section className="flex-1">

View File

@ -1,202 +1,19 @@
import { create } from 'zustand' 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 { export interface StoreState {
mediaStream?: MediaStream
setMediaStream: (mediaStream: MediaStream) => void
isStreamReady: boolean isStreamReady: boolean
setIsStreamReady: (isStreamReady: boolean) => void 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 setHtmlRef: (ref: React.RefObject<HTMLDivElement>) => void
htmlRef: React.RefObject<HTMLDivElement> | null 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>()( export const useStore = create<StoreState>()((set, get) => {
persist(
(set, get) => {
return { return {
setMediaStream: (mediaStream) => set({ mediaStream }),
isStreamReady: false,
setIsStreamReady: (isStreamReady) => set({ isStreamReady }), setIsStreamReady: (isStreamReady) => set({ isStreamReady }),
buttonDownInStream: undefined,
setButtonDownInStream: (buttonDownInStream) => {
set({ buttonDownInStream })
},
setHtmlRef: (htmlRef) => { setHtmlRef: (htmlRef) => {
set({ htmlRef }) set({ htmlRef })
}, },
htmlRef: null, htmlRef: null,
didDragInStream: false, isStreamReady: 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 }),
} }
}, })
{
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,
},
}
}
}
}
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 []
}
}