* add first version of DefaultPlanes to FeatureTreePane * fix lint issues * don't show default planes UI in sketch mode * lint * toggling default planes: implementation in xstate * revert malformed modelingMachine.ts * lint * save and restore default plane visibility when returning to modeling mode * fmt * tsc * introduce new cleanup state with actor when exiting sketch mode * temp remove restore default plane visibility - causes error on starting up a project * set selection filter after executeAst - this is a wip hacky fix * remove unused early return: this also caused plane selection to only work with double click * lint * no need to set selection filter to curves only, we want faces to be selectable in modeling mode, even though this means default planes are also selectable * tightening types for visibility map * lint * cleanups * fix border issue when visibility toggle is not active and props.visible === true * ui updates on FeatureTreePane/default planes * no pointer cursor for unselectable default planes * show default planes initially even for non-empty projects * dont show default planes initially when project is not empty * fix test: Only show axis planes when there are no errors * fixes for sketch tests * better initialize for planes * lint * fix uneccessary 'reset camera position' in sketch entry * revert hiding/showing content depending on artifact graph for tests * only show default planes when there are no errors * disable Restore default plane visibility, was causing temporary flashing of default planes when exiting sketch mode * Always show default plane visibility toggles, regardless of being on/off * revert modelingMachine to original idle states to avoid 'zoom_to_fit' test regression - probably racing condition * fmt
This commit is contained in:
@ -19,7 +19,6 @@ import { err, reportRejection, trap } from '@src/lib/trap'
|
||||
import type { IndexLoaderData } from '@src/lib/types'
|
||||
import { uuidv4 } from '@src/lib/utils'
|
||||
import { engineStreamActor, useSettings } from '@src/lib/singletons'
|
||||
import { useCommandBarState } from '@src/lib/singletons'
|
||||
import {
|
||||
EngineStreamState,
|
||||
EngineStreamTransition,
|
||||
@ -58,8 +57,6 @@ export const EngineStream = (props: {
|
||||
const { state: modelingMachineState, send: modelingMachineActorSend } =
|
||||
useModelingContext()
|
||||
|
||||
const commandBarState = useCommandBarState()
|
||||
|
||||
const streamIdleMode = settings.app.streamIdleMode.current
|
||||
|
||||
const startOrReconfigureEngine = () => {
|
||||
@ -332,15 +329,7 @@ export const EngineStream = (props: {
|
||||
if (!engineStreamState.context.videoRef.current) return
|
||||
// If we're in sketch mode, don't send a engine-side select event
|
||||
if (modelingMachineState.matches('Sketch')) return
|
||||
// Only respect default plane selection if we're on a selection command argument
|
||||
if (
|
||||
modelingMachineState.matches({ idle: 'showPlanes' }) &&
|
||||
!(
|
||||
commandBarState.matches('Gathering arguments') &&
|
||||
commandBarState.context.currentArgument?.inputType === 'selection'
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
// If we're mousing up from a camera drag, don't send a select event
|
||||
if (sceneInfra.camControls.wasDragging === true) return
|
||||
|
||||
@ -361,7 +350,6 @@ export const EngineStream = (props: {
|
||||
!isNetworkOkay ||
|
||||
!engineStreamState.context.videoRef.current ||
|
||||
modelingMachineState.matches('Sketch') ||
|
||||
modelingMachineState.matches({ idle: 'showPlanes' }) ||
|
||||
sceneInfra.camControls.wasDragging === true ||
|
||||
!btnName(e.nativeEvent).left
|
||||
) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { Diagnostic } from '@codemirror/lint'
|
||||
import { useMachine, useSelector } from '@xstate/react'
|
||||
import type { ComponentProps } from 'react'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react'
|
||||
import type { Actor, Prop } from 'xstate'
|
||||
|
||||
import type { Operation } from '@rust/kcl-lib/bindings/Operation'
|
||||
@ -23,7 +23,7 @@ import {
|
||||
getOperationLabel,
|
||||
stdLibMap,
|
||||
} from '@src/lib/operations'
|
||||
import { editorManager, kclManager } from '@src/lib/singletons'
|
||||
import { editorManager, kclManager, rustContext } from '@src/lib/singletons'
|
||||
import {
|
||||
featureTreeMachine,
|
||||
featureTreeMachineDefaultContext,
|
||||
@ -160,6 +160,7 @@ export const FeatureTreePane = () => {
|
||||
<Loading className="h-full">Building feature tree...</Loading>
|
||||
) : (
|
||||
<>
|
||||
{!modelingState.matches('Sketch') && <DefaultPlanes />}
|
||||
{parseErrors.length > 0 && (
|
||||
<div
|
||||
className={`absolute inset-0 rounded-lg p-2 ${
|
||||
@ -204,41 +205,27 @@ export const FeatureTreePane = () => {
|
||||
)
|
||||
}
|
||||
|
||||
export const visibilityMap = new Map<string, boolean>()
|
||||
|
||||
interface VisibilityToggleProps {
|
||||
entityId: string
|
||||
initialVisibility: boolean
|
||||
onVisibilityChange?: () => void
|
||||
visible: boolean
|
||||
onVisibilityChange: () => unknown
|
||||
}
|
||||
|
||||
/**
|
||||
* A button that toggles the visibility of an entity
|
||||
* tied to an artifact in the feature tree.
|
||||
* TODO: this is unimplemented and will be used for
|
||||
* default planes after we fix them and add them to the artifact graph / feature tree
|
||||
* For now just used for default planes.
|
||||
*/
|
||||
const VisibilityToggle = (props: VisibilityToggleProps) => {
|
||||
const [visible, setVisible] = useState(props.initialVisibility)
|
||||
|
||||
function handleToggleVisible() {
|
||||
setVisible(!visible)
|
||||
visibilityMap.set(props.entityId, !visible)
|
||||
props.onVisibilityChange?.()
|
||||
}
|
||||
const visible = props.visible
|
||||
const handleToggleVisible = useCallback(() => {
|
||||
props.onVisibilityChange()
|
||||
}, [props.onVisibilityChange])
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={handleToggleVisible}
|
||||
className="border-transparent p-0 m-0"
|
||||
>
|
||||
<button onClick={handleToggleVisible} className="p-0 m-0">
|
||||
<CustomIcon
|
||||
name={visible ? 'eyeOpen' : 'eyeCrossedOut'}
|
||||
className={`w-5 h-5 ${
|
||||
visible
|
||||
? 'hidden group-hover/item:block group-focus-within/item:block'
|
||||
: 'text-chalkboard-50'
|
||||
}`}
|
||||
className="w-5 h-5"
|
||||
/>
|
||||
</button>
|
||||
)
|
||||
@ -256,6 +243,7 @@ const OperationItemWrapper = ({
|
||||
menuItems,
|
||||
errors,
|
||||
className,
|
||||
selectable = true,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLButtonElement> & {
|
||||
icon: CustomIconName
|
||||
@ -263,17 +251,18 @@ const OperationItemWrapper = ({
|
||||
visibilityToggle?: VisibilityToggleProps
|
||||
menuItems?: ComponentProps<typeof ContextMenu>['items']
|
||||
errors?: Diagnostic[]
|
||||
selectable?: boolean
|
||||
}) => {
|
||||
const menuRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={menuRef}
|
||||
className="flex select-none items-center group/item my-0 py-0.5 px-1 focus-within:bg-primary/10 hover:bg-primary/5"
|
||||
className={`flex select-none items-center group/item my-0 py-0.5 px-1 ${selectable ? 'focus-within:bg-primary/10 hover:bg-primary/5' : ''}`}
|
||||
>
|
||||
<button
|
||||
{...props}
|
||||
className={`reset flex-1 flex items-center gap-2 border-transparent dark:border-transparent text-left text-base ${className}`}
|
||||
className={`reset flex-1 flex items-center gap-2 text-left text-base ${selectable ? 'border-transparent dark:border-transparent' : 'border-none cursor-default'} ${className}`}
|
||||
>
|
||||
<CustomIcon name={icon} className="w-5 h-5 block" />
|
||||
{name}
|
||||
@ -514,3 +503,40 @@ const OperationItem = (props: {
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const DefaultPlanes = () => {
|
||||
const { state: modelingState, send } = useModelingContext()
|
||||
|
||||
const defaultPlanes = rustContext.defaultPlanes
|
||||
if (!defaultPlanes) return null
|
||||
|
||||
const planes = [
|
||||
{ name: 'Front plane', id: defaultPlanes.xz, key: 'xz' },
|
||||
{ name: 'Top plane', id: defaultPlanes.xy, key: 'xy' },
|
||||
{ name: 'Side plane', id: defaultPlanes.yz, key: 'yz' },
|
||||
] as const
|
||||
|
||||
return (
|
||||
<div className="mb-2">
|
||||
{planes.map((plane) => (
|
||||
<OperationItemWrapper
|
||||
key={plane.key}
|
||||
icon={'plane'}
|
||||
name={plane.name}
|
||||
selectable={false}
|
||||
visibilityToggle={{
|
||||
visible: modelingState.context.defaultPlaneVisibility[plane.key],
|
||||
onVisibilityChange: () => {
|
||||
send({
|
||||
type: 'Toggle default plane visibility',
|
||||
planeId: plane.id,
|
||||
planeKey: plane.key,
|
||||
})
|
||||
},
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<div className="h-px bg-chalkboard-50/20 my-2" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ import { jsAppSettings } from '@src/lib/settings/settingsUtils'
|
||||
|
||||
import { err, reportRejection } from '@src/lib/trap'
|
||||
import { deferExecution, uuidv4 } from '@src/lib/utils'
|
||||
import type { PlaneVisibilityMap } from '@src/machines/modelingMachine'
|
||||
|
||||
interface ExecuteArgs {
|
||||
ast?: Node<Program>
|
||||
@ -70,7 +71,6 @@ export class KclManager {
|
||||
*/
|
||||
artifactGraph: ArtifactGraph = new Map()
|
||||
artifactIndex: ArtifactIndex = []
|
||||
defaultPlanesShown: boolean = false
|
||||
|
||||
private _ast: Node<Program> = {
|
||||
body: [],
|
||||
@ -354,6 +354,15 @@ export class KclManager {
|
||||
})
|
||||
}, 200)(null)
|
||||
}
|
||||
|
||||
// Send the 'artifact graph initialized' event for modelingMachine, only once, when default planes are also initialized.
|
||||
deferExecution((a?: null) => {
|
||||
if (this.defaultPlanes) {
|
||||
this.engineCommandManager.modelingSend({
|
||||
type: 'Artifact graph initialized',
|
||||
})
|
||||
}
|
||||
}, 200)(null)
|
||||
}
|
||||
|
||||
async safeParse(code: string): Promise<Node<Program> | null> {
|
||||
@ -702,6 +711,19 @@ export class KclManager {
|
||||
}
|
||||
return Promise.all(thePromises)
|
||||
}
|
||||
|
||||
setPlaneVisibilityByKey(
|
||||
planeKey: keyof PlaneVisibilityMap,
|
||||
visible: boolean
|
||||
) {
|
||||
const planeId = this.defaultPlanes?.[planeKey]
|
||||
if (!planeId) {
|
||||
console.warn(`Plane ${planeKey} not found`)
|
||||
return
|
||||
}
|
||||
return this.engineCommandManager.setPlaneHidden(planeId, !visible)
|
||||
}
|
||||
|
||||
/** TODO: this function is hiding unawaited asynchronous work */
|
||||
defaultSelectionFilter(selectionsToRestore?: Selections) {
|
||||
setSelectionFilterToDefault(this.engineCommandManager, selectionsToRestore)
|
||||
|
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user