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 { 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()

View File

@ -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,11 +44,8 @@ export function App() {
}, [projectName, projectPath])
useHotKeyListener()
const { buttonDownInStream, didDragInStream, streamDimensions, setHtmlRef } =
useStore((s) => ({
buttonDownInStream: s.buttonDownInStream,
didDragInStream: s.didDragInStream,
streamDimensions: s.streamDimensions,
const { context } = useModelingContext()
const { setHtmlRef } = useStore((s) => ({
setHtmlRef: s.setHtmlRef,
}))
@ -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}

View File

@ -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
}

View File

@ -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 = '',

View File

@ -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,

View File

@ -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

View File

@ -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 = () => {}

View File

@ -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 = ({

View File

@ -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' })

View File

@ -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 (

View File

@ -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`

View File

@ -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() {

View File

@ -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,
},
})
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

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

View File

@ -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 {

View File

@ -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({
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({
settings.modelingSend({
type: 'Set context',
data: {
streamDimensions: {
streamWidth: width,
streamHeight: height,
},
},
})
}
}, 500)

View File

@ -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 =

View File

@ -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'

View File

@ -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
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 {
ArrayExpression,

View File

@ -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 {

View File

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

View File

@ -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'

View File

@ -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 {

View File

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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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>

View File

@ -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">

View File

@ -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">

View File

@ -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) => {
export const useStore = create<StoreState>()((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 }),
isStreamReady: false,
}
},
{
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 []
}
}
})