Compare commits
1 Commits
main
...
extrude-ma
Author | SHA1 | Date | |
---|---|---|---|
2d350a93ed |
@ -72,55 +72,78 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
|
||||
)}
|
||||
{selectedCommand?.name}
|
||||
</p>
|
||||
{Object.entries(selectedCommand?.args || {}).map(
|
||||
([argName, arg], i) => (
|
||||
<button
|
||||
disabled={!isReviewing && currentArgument?.name === argName}
|
||||
onClick={() => {
|
||||
commandBarSend({
|
||||
type: isReviewing
|
||||
? 'Edit argument'
|
||||
: 'Change current argument',
|
||||
data: { arg: { ...arg, name: argName } },
|
||||
})
|
||||
}}
|
||||
key={argName}
|
||||
className={`relative w-fit px-2 py-1 rounded-sm flex gap-2 items-center border ${
|
||||
argName === currentArgument?.name
|
||||
? 'disabled:bg-energy-10/50 dark:disabled:bg-energy-10/20 disabled:border-energy-10 dark:disabled:border-energy-10 disabled:text-chalkboard-100 dark:disabled:text-chalkboard-10'
|
||||
: 'bg-chalkboard-20/50 dark:bg-chalkboard-80/50 border-chalkboard-20 dark:border-chalkboard-80'
|
||||
}`}
|
||||
>
|
||||
{argumentsToSubmit[argName] ? (
|
||||
arg.inputType === 'selection' ? (
|
||||
getSelectionTypeDisplayText(
|
||||
argumentsToSubmit[argName] as Selections
|
||||
)
|
||||
) : typeof argumentsToSubmit[argName] === 'object' ? (
|
||||
JSON.stringify(argumentsToSubmit[argName])
|
||||
) : (
|
||||
argumentsToSubmit[argName]
|
||||
)
|
||||
) : arg.payload ? (
|
||||
arg.inputType === 'selection' ? (
|
||||
getSelectionTypeDisplayText(arg.payload as Selections)
|
||||
) : typeof arg.payload === 'object' ? (
|
||||
JSON.stringify(arg.payload)
|
||||
) : (
|
||||
arg.payload
|
||||
)
|
||||
) : (
|
||||
<em>{argName}</em>
|
||||
)}
|
||||
{showShortcuts && (
|
||||
<small className="absolute -top-[1px] right-full translate-x-1/2 px-0.5 rounded-sm bg-chalkboard-80 text-chalkboard-10 dark:bg-energy-10 dark:text-chalkboard-100">
|
||||
<span className="sr-only">Hotkey: </span>
|
||||
{i + 1}
|
||||
</small>
|
||||
)}
|
||||
</button>
|
||||
{Object.entries(selectedCommand?.args || {})
|
||||
.filter(([argName, _]) =>
|
||||
selectedCommand?.args
|
||||
? selectedCommand?.args[argName]?.required ||
|
||||
(argName in argumentsToSubmit && argumentsToSubmit[argName])
|
||||
: false
|
||||
)
|
||||
)}
|
||||
.map(([argName, arg], i) => (
|
||||
<div className="relative group" key={argName}>
|
||||
<button
|
||||
disabled={!isReviewing && currentArgument?.name === argName}
|
||||
onClick={() => {
|
||||
commandBarSend({
|
||||
type: isReviewing
|
||||
? 'Edit argument'
|
||||
: 'Change current argument',
|
||||
data: { arg: { ...arg, name: argName } },
|
||||
})
|
||||
}}
|
||||
className={`relative w-fit px-2 py-1 rounded-sm flex gap-2 items-center border ${
|
||||
argName === currentArgument?.name
|
||||
? 'disabled:bg-energy-10/50 dark:disabled:bg-energy-10/20 disabled:border-energy-10 dark:disabled:border-energy-10 disabled:text-chalkboard-100 dark:disabled:text-chalkboard-10'
|
||||
: 'bg-chalkboard-20/50 dark:bg-chalkboard-80/50 border-chalkboard-20 dark:border-chalkboard-80'
|
||||
}`}
|
||||
>
|
||||
{argumentsToSubmit[argName] ? (
|
||||
arg.inputType === 'selection' ? (
|
||||
getSelectionTypeDisplayText(
|
||||
argumentsToSubmit[argName] as Selections
|
||||
)
|
||||
) : typeof argumentsToSubmit[argName] === 'object' ? (
|
||||
JSON.stringify(argumentsToSubmit[argName])
|
||||
) : (
|
||||
argumentsToSubmit[argName]
|
||||
)
|
||||
) : arg.payload ? (
|
||||
arg.inputType === 'selection' ? (
|
||||
getSelectionTypeDisplayText(arg.payload as Selections)
|
||||
) : typeof arg.payload === 'object' ? (
|
||||
JSON.stringify(arg.payload)
|
||||
) : (
|
||||
arg.payload
|
||||
)
|
||||
) : (
|
||||
<em>{argName}</em>
|
||||
)}
|
||||
{showShortcuts && (
|
||||
<small className="absolute -top-[1px] right-full translate-x-1/2 px-0.5 rounded-sm bg-chalkboard-80 text-chalkboard-10 dark:bg-energy-10 dark:text-chalkboard-100">
|
||||
<span className="sr-only">Hotkey: </span>
|
||||
{i + 1}
|
||||
</small>
|
||||
)}
|
||||
</button>
|
||||
{!arg.required && (
|
||||
<button
|
||||
onClick={() => {
|
||||
commandBarSend({
|
||||
type: 'Remove argument',
|
||||
data: { [argName]: { ...arg, name: argName } },
|
||||
})
|
||||
}}
|
||||
className="invisible group-hover:visible absolute top-0 right-0 -translate-y-1/2 !p-0 flex items-center justify-center rounded-sm border-none bg-none"
|
||||
>
|
||||
<CustomIcon
|
||||
name="close"
|
||||
className="w-4 h-4 bg-destroy-80 dark:bg-destroy-30 hover:bg-destroy-70 dark:hover:bg-destroy-0 text-destroy-10 dark:text-destroy-80"
|
||||
/>
|
||||
<span className="sr-only">Remove argument</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{isReviewing ? <ReviewingButton /> : <GatheringArgsButton />}
|
||||
</div>
|
||||
|
@ -1,12 +1,21 @@
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import CommandBarHeader from './CommandBarHeader'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { ActionButton } from 'components/ActionButton'
|
||||
|
||||
function CommandBarReview({ stepBack }: { stepBack: () => void }) {
|
||||
const { commandBarState, commandBarSend } = useCommandsContext()
|
||||
const {
|
||||
context: { argumentsToSubmit, selectedCommand },
|
||||
} = commandBarState
|
||||
const optionalArgsNotAdded = Object.entries(
|
||||
selectedCommand?.args || {}
|
||||
).filter(
|
||||
([key, val]) =>
|
||||
selectedCommand?.args &&
|
||||
!selectedCommand.args[key].required &&
|
||||
!argumentsToSubmit[key]
|
||||
)
|
||||
|
||||
useHotkeys('backspace', stepBack, {
|
||||
enableOnFormTags: true,
|
||||
@ -46,7 +55,7 @@ function CommandBarReview({ stepBack }: { stepBack: () => void }) {
|
||||
|
||||
return (
|
||||
<CommandBarHeader>
|
||||
<p className="px-4">Confirm {selectedCommand?.name}</p>
|
||||
<p className="px-4 py-1">Confirm {selectedCommand?.name}</p>
|
||||
<form
|
||||
id="review-form"
|
||||
className="absolute opacity-0 inset-0 pointer-events-none"
|
||||
@ -74,6 +83,41 @@ function CommandBarReview({ stepBack }: { stepBack: () => void }) {
|
||||
)
|
||||
})}
|
||||
</form>
|
||||
{optionalArgsNotAdded.length > 0 && (
|
||||
<>
|
||||
<div className="block w-full my-2 h-[1px] bg-chalkboard-20 dark:bg-chalkboard-80" />
|
||||
<div className="flex flex-wrap px-4 gap-2 items-center">
|
||||
{optionalArgsNotAdded.map(([key, _]) => {
|
||||
const arg = selectedCommand?.args
|
||||
? selectedCommand?.args[key]
|
||||
: undefined
|
||||
if (!arg) return null
|
||||
|
||||
return (
|
||||
<ActionButton
|
||||
Element="button"
|
||||
key={key}
|
||||
className="text-xs [&:not(:hover)]:border-transparent gap-0.5"
|
||||
onClick={() => {
|
||||
commandBarSend({
|
||||
type: 'Edit argument',
|
||||
data: { arg: { ...arg, name: key } },
|
||||
})
|
||||
}}
|
||||
icon={{
|
||||
icon: 'plus',
|
||||
bgClassName: '!bg-transparent',
|
||||
iconClassName:
|
||||
'text-chalkboard-10 dark:text-chalkboard-100',
|
||||
}}
|
||||
>
|
||||
{key}
|
||||
</ActionButton>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</CommandBarHeader>
|
||||
)
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ export type CustomIconName =
|
||||
| 'line'
|
||||
| 'move'
|
||||
| 'parallel'
|
||||
| 'plus'
|
||||
| 'search'
|
||||
| 'sketch'
|
||||
| 'vertical'
|
||||
@ -297,6 +298,22 @@ export const CustomIcon = ({
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
case 'plus':
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M9.5 9.5V5.5H10.5V9.5H14.5V10.5H10.5V14.5H9.5V10.5H5.5V9.5H9.5Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
case 'search':
|
||||
return (
|
||||
<svg
|
||||
|
@ -15,6 +15,7 @@ export type ModelingCommandSchema = {
|
||||
selection: Selections // & { type: 'face' } would be cool to lock that down
|
||||
// result: (typeof EXTRUSION_RESULTS)[number]
|
||||
distance: number
|
||||
makeVariable?: string
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,6 +53,10 @@ export const modelingMachineConfig: CommandSetConfig<
|
||||
defaultValue: 5,
|
||||
required: true,
|
||||
},
|
||||
makeVariable: {
|
||||
inputType: 'string',
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import { Selections } from 'lib/selections'
|
||||
|
||||
export const commandBarMachine = createMachine(
|
||||
{
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QGED2BbdBDAdhABAEJYBOAxMgDaqxgDaADALqKgAONAlgC6eo6sQAD0QBaAJwA6AGwAmAKwBmBoukAWafIAcDcSoA0IAJ6JZDaZIDs8hgzV6AjA61a1DWQF8PhtJlwFiciowUkYWJBAOWB4+AQiRBFF5CwdpcVkHS1lpVyU5QxNEh1lFGTUsrUUtOQd5SwZLLx8MbDwiUkkqGkgyAHk2MBwwwSiY-kEEswdJNWcNbPFLNMr5AsRFWUtJcVSdRR2VVMUmkF9WgI6u2ggyADFONv98WkowAGNufDeW-2GI0d443iiHESkktUUG1klTsSi0awQDgYWhmqkWjnUslBWhOZyegU61GuZAAghACN8-HhYH92FxAXFQAlRA5FpJ5FjFPYtNDpIpLOkEW4GNs4eJxJoGvJxOVcT82gSrj0AEpgdCoABuYC+8ogNOYI3psQmYlkGUkU2slhc6mkmUUCIyKLc4i0lkU8iUyjUHLlVIuJEkAGUwK8Pg8oDr-WQQ2HPpTzrTIkagUzEDkLHZkQK1CUtKCHAiNlsdjkVAdFEc-ed2oGAOJYbgACzAJAj+FIUAAruhBtxYGQACJwUPveO6pMA43AhA5UqWXOzBjS-nOR3LySqXNODTyZwONTV-GXXXPUcfHqTlOM4TrSublb5-lLewlIVySRaOrmGw-jLSI8FRPf0zzjS8HHCOlogZE1ERUEUSgPa05yQ1ZjEQeZP2-VwDzUN1zEabxTlPAkG2bVt207Hs+wHZAm1wGAvi7EgSD7DsSG7XscG4K9oOnNNEVUeQZGSOxc0qaQ7HhdDBJFeRc35extGkNI+UAgNJDIls2xwSMqK4-tJBJAB3LAYl0-AHjYLtuBjLsACN0B4djOL7XixhvBIDxUT8qj5VIkTwtQHRk8oUQcD15LMXQclcdTa00xttMojjqO42BJAANSwShOAgRsIzICB+DASQHg1VAAGtSo1HK8sbMASVSgz3JgmdMnKGYzRlbIdGXA8EX8zd3RsTY9yyY4iLxID6ySiiLP0misrq-LeF0shWxIVBAzYShGwAM229BJFq3LVsa5q3INf5r1gg9JIfXqqh0TQsiFTYrCyJZRpsXJ4oJVUNU4MBjLsxznITX5rqgjzYMsaZtGXaVNhcZFKgRd0RXUdJ6mUXQXX+jpAeB0GyQIRbuNa-jbwQcVSmhAUeTkWQ3HyGSBVKDQMyRAKAsJwNiZBshVXVLUXLSnjoeTPjUxpnmpFtcVs1cPNBq5T8fR5DrKwaHEppIomwCBoWAFEIGcinJcg6XYZnTRhJsbQJU9VlQTVtQNbqcVZhsSFxH5zoWzeSr2ya1z0qKkqypwCrqpOlaGrDiX9WtqdZYSeSEeXEplCceo1xkvQLGCmUIp0Mwck8fWQMVIOQ4spODIHYqcFK8qqpqhPuAu8P+zoCDDRlzzTCkCVWWlSxrBceTygRFxZHZNJbE2VQ5AFAO6PeevI0bmiNpY7bJF2g6jvjs7E8u9KqfTxAkK2fZYuRvdYUGjQZnqYVxRKdx-ZOHBUAgHAQQ00AyD1tgJUQCwLQMEyHUG0Gh7SOmmDuGB6gOTyVBMkDeRJIBgLahA4oFo8zWlSPUT00o0KFA9NMYKrhKwbH2GaQ81cawEljGOdskM8B4OpgkWY9MV5ZjqLUN6MlqGojoRFEo6QWYb1PC8McuCbpD1gosLYrhkTZCxNIawhYxEfW0HhCKKgzT8lBAHLS809KX37Dwm+iIWZSEinjTRy51BFnvNofM7hGZfgXBYuaOlrG9wyiZMya1IxWRsnY4eDi2TONsK45IaghQbEkO4VIKlsaVE2AE8iQTxZN2WufCJMS7o6JSBKNQLp7CT35EKZw2wnDVDyCvBczDmg10NsbYyZS7aZHBNU58q8sTQgxnud+kVsjyQzHrTprDLh11DjY+AyjwFy00DQ2EaQBSLAPLIDGWRNw2BUiXHkwVCJeCAA */
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QGED2BbdBDAdhABAEJYBOAxMgDaqxgDaADALqKgAONAlgC6eo6sQAD0QBaAJwA6AGwAmAKwBmBoukAWafIAcDcSoA0IAJ6JZDaZIDs8hgzV6AjA61a1DWQF8PhtJlwFiciowUkYWJBAOWB4+AQiRBFF5CwdpcVkHS1lpVyU5QxNEh1lFGTUsrUUtOQd5SwZLLx8MbDwiUkkqGkgyAHk2MBwwwSiY-kEEswdJNWcNbPFLNMr5AsRFWUtJcVSdRR2VVMUmkF9WgI6u2ggyADFONv98WkowAGNufDeW-2GI0d443iiHESkktUUG1klTsSi0awQDgYWhmqkWjnUslBWhOZyegU61GuZAAghACN8-HhYH92FxAXFQAlRA5FpJ5FjFPYtNDpIpLOkEW4GNs4eJxJoGvJxOVcT82gSrj0AEpgdCoABuYC+8ogNOYI3psQmYlkGUkU2slhc6mkmUUCIyKLc4i0lkU8iUyjUHLlVIuJEkAGUwK8Pg8oDr-WQQ2HPpTzrTIkagUzEDkLHZkQK1CUtKCHAiNlsdjkVAdFEc-ed2oG8W0Xu9uD0kwDjcCEJDplUPfn+Ut7CUhXJJFo6uYbBOMtJq-jLrrnqGmy2HOE6dEGSbESoRSUHOVqpV99Zh7JR+PXPu1G7zI1vKcFwSAOJYbgACzAJAj+FIUAAruggzcLAFBvrgMBfH+JAkEBP4kP+gE4NwrYpoywiIA4qjyDIyR2LmlTSHY8LGBhyjsrm-L2No0hpHys4Kh0L7vp+36-gBQEgQAInAS4fFGiYGv8qFbjkpSWLmswMNK-LOI6UmSKouZOBo8jOPu9EBpITEfl+OCRmxiHAZIJIAO5YDEen4A8bB-twMZ-gARugPBwQhQEoRu7ZpoibilJU1SVnaRFqA6JEIAe4IevIua2BKLhqBptZaa+OmsfB7FIbAkgAGpYJQnAQK+EZkBA-BgJIDwaqgADW5UanlBWvmAJLpYZHljGhCSZOUMxmjK2Q6FJ+4Iny3bujYmyqVkxz3vWmnaSxlkGRxOUNYVvB6WQn4kKggZsJQr4AGa7egkj1fl63Na17mCeuHVbvuhEKTyokuBOWRCpsVhZEsE02LkiUEqqGqcGAJn2U5LkJr8t3Jp5qboQgljTNoUnSpsb0uKFhTuiK6jpPUyi6C6gMdMDoPg2SBDLUh7Wbh24qlNCAovWabj5GFAqlBoGZIqkTh2qTgbk2DZCquqWquRlyGw22CNdQwrJlGkehuq4eYjVyo4+jy3WVg0OKzY+ZNgCDosAKIQC5NMy2ucP3R2mjYTY2gSp6rKgpraja3U4qzDYkLiELnQfm81Xfi1bmZSVZUVTgVW1Wda1NZH0v6nbcudYg0Uo1JJTKE49SyWFegWCFMqYToZg5J4Rv+klyCh+Hlmp4ZIGlTg5WVTVdXJ82rccXQq6GvDWcIFi2x2qCizWC40XlAiLhnskejuO6NGbEHdc1oqTcR9d0fbbtkj7UdJ1JxdKcH8BdNeYjx5bPsORjukqmwiNGgzPUwriiU7hb80euz4UqLX0tfEC4tNTahtrfeWpg7DTAlMeQ8XIOSrDCrsdk4gqjImRD6TCIVg4LV0mAqOwExZqigVLNqw8hKjwepoUo7g4SzE0EFQsGDpTbFcG6AhgUlheHvDgVAEA4CCDmrWEeDtvKiAWBaRWVobQaHtI6aYylzDFGUIpSsCVt5zjrESSAUj6YyOKBaPM1pUj1E9NKdBhQPTTBCq4SsGx9hs2DrGJs35oZ4GMXfBIswmabG9FJZGdRZBFlUqiZxmESjpFkLowBO95z10bB8IxdDpGIxntrZE2QsTSGsBw+xX1tDXkwioM0-JQREJASQ6hHE-FwMRAkqQUUiZ5KkuoIslZ2QuBlFaGiWQEm1OYvUm2WVTLmQ2pGaytkmlj2KGydpthOnJDUEKDYkh3CpBovjSomxRmpSWuA1al8ZkLIYUscEEo1AunsJYd0lghTOG2E4aoeRgniUSQ+IBJszYmUuY7TI4I7n9lUHIdIxEcZRPKFFbI0UMyGySfokO7xm6RgHplIF3laivMZgRNm7NsaIACgpV2SxajYMhDNLwQA */
|
||||
context: {
|
||||
commands: [] as Command[],
|
||||
selectedCommand: undefined as Command | undefined,
|
||||
@ -167,6 +167,19 @@ export const commandBarMachine = createMachine(
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
||||
'Remove argument': [
|
||||
{
|
||||
target: 'Review',
|
||||
cond: 'Is current argument',
|
||||
actions: 'Remove argument',
|
||||
},
|
||||
{
|
||||
target: 'Gathering arguments',
|
||||
internal: true,
|
||||
actions: 'Remove argument',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
@ -185,17 +198,7 @@ export const commandBarMachine = createMachine(
|
||||
|
||||
'Remove argument': {
|
||||
target: 'Review',
|
||||
actions: [
|
||||
assign({
|
||||
argumentsToSubmit: (context, event) => {
|
||||
const argName = Object.keys(event.data)[0]
|
||||
const { argumentsToSubmit } = context
|
||||
const newArgumentsToSubmit = { ...argumentsToSubmit }
|
||||
newArgumentsToSubmit[argName] = undefined
|
||||
return newArgumentsToSubmit
|
||||
},
|
||||
}),
|
||||
],
|
||||
actions: ['Remove argument'],
|
||||
},
|
||||
|
||||
'Edit argument': {
|
||||
@ -362,10 +365,25 @@ export const commandBarMachine = createMachine(
|
||||
return args
|
||||
},
|
||||
}),
|
||||
'Remove argument': assign({
|
||||
argumentsToSubmit: (context, event) => {
|
||||
if (event.type !== 'Remove argument') return context.argumentsToSubmit
|
||||
const argName = Object.keys(event.data)[0]
|
||||
const { argumentsToSubmit } = context
|
||||
const newArgumentsToSubmit = { ...argumentsToSubmit }
|
||||
delete newArgumentsToSubmit[argName]
|
||||
return newArgumentsToSubmit
|
||||
},
|
||||
}),
|
||||
},
|
||||
guards: {
|
||||
'Command needs review': (context, _) =>
|
||||
context.selectedCommand?.needsReview || false,
|
||||
'Is current argument': (context, event) => {
|
||||
if (event.type !== 'Remove argument') return false
|
||||
const argName = Object.keys(event.data)[0]
|
||||
return argName === context.currentArgument?.name
|
||||
},
|
||||
},
|
||||
services: {
|
||||
'Validate argument': (context, event) => {
|
||||
@ -381,6 +399,10 @@ export const commandBarMachine = createMachine(
|
||||
return new Promise((resolve, reject) => {
|
||||
for (const [argName, arg] of Object.entries(
|
||||
context.argumentsToSubmit
|
||||
).filter(([argName, _]) =>
|
||||
context.selectedCommand?.args
|
||||
? context.selectedCommand?.args[argName]?.required
|
||||
: false
|
||||
)) {
|
||||
let argConfig = context.selectedCommand!.args![argName]
|
||||
|
||||
|
Reference in New Issue
Block a user