* Only show the Replay Onboarding button in file settings Resolves #351. Eventually we will implement more sophisticated logic for which settings should be shown where. Signed-off-by: Frank Noirot <frank@kittycad.io> * Remove unnecessary console.log Signed-off-by: Frank Noirot <frank@kittycad.io> * Respond to system theme changes in real-time If the user has their "theme" setting to "system". I tried to use the [XState invoked callback approach](https://xstate.js.org/docs/guides/communication.html#invoking-callbacks), but I could not find any way to respond to the latest context/state values within the media listener; I kept receiving stale state. Signed-off-by: Frank Noirot <frank@kittycad.io> --------- Signed-off-by: Frank Noirot <frank@kittycad.io>
125 lines
3.0 KiB
TypeScript
125 lines
3.0 KiB
TypeScript
import { AnyStateMachine, EventFrom, StateFrom } from 'xstate'
|
|
import { isTauri } from './isTauri'
|
|
|
|
type InitialCommandBarMetaArg = {
|
|
name: string
|
|
type: 'string' | 'select'
|
|
description?: string
|
|
defaultValue?: string
|
|
options: string | Array<{ name: string }>
|
|
}
|
|
|
|
type Platform = 'both' | 'web' | 'desktop'
|
|
|
|
export type CommandBarMeta = {
|
|
[key: string]:
|
|
| {
|
|
displayValue: (args: string[]) => string
|
|
args: InitialCommandBarMetaArg[]
|
|
hide?: Platform
|
|
}
|
|
| {
|
|
hide?: Platform
|
|
}
|
|
}
|
|
|
|
export type Command = {
|
|
owner: string
|
|
name: string
|
|
callback: Function
|
|
meta?: {
|
|
displayValue(args: string[]): string | string
|
|
args: SubCommand[]
|
|
}
|
|
}
|
|
|
|
export type SubCommand = {
|
|
name: string
|
|
type: 'select' | 'string'
|
|
description?: string
|
|
options?: Partial<{ name: string }>[]
|
|
}
|
|
|
|
interface CommandBarArgs<T extends AnyStateMachine> {
|
|
type: EventFrom<T>['type']
|
|
state: StateFrom<T>
|
|
commandBarMeta?: CommandBarMeta
|
|
send: Function
|
|
owner: string
|
|
}
|
|
|
|
export function createMachineCommand<T extends AnyStateMachine>({
|
|
type,
|
|
state,
|
|
commandBarMeta,
|
|
send,
|
|
owner,
|
|
}: CommandBarArgs<T>): Command | null {
|
|
const lookedUpMeta = commandBarMeta && commandBarMeta[type]
|
|
if (lookedUpMeta && 'hide' in lookedUpMeta) {
|
|
const { hide } = lookedUpMeta
|
|
if (hide === 'both') return null
|
|
else if (hide === 'desktop' && isTauri()) return null
|
|
else if (hide === 'web' && !isTauri()) return null
|
|
}
|
|
let replacedArgs
|
|
|
|
if (lookedUpMeta && 'args' in lookedUpMeta) {
|
|
replacedArgs = lookedUpMeta.args.map((arg) => {
|
|
const optionsFromContext = state.context[
|
|
arg.options as keyof typeof state.context
|
|
] as { name: string }[] | string | undefined
|
|
const defaultValueFromContext = state.context[
|
|
arg.defaultValue as keyof typeof state.context
|
|
] as string | undefined
|
|
|
|
const options =
|
|
arg.options instanceof Array
|
|
? arg.options.map((o) => ({
|
|
...o,
|
|
description:
|
|
defaultValueFromContext === o.name ? '(current)' : '',
|
|
}))
|
|
: !optionsFromContext || typeof optionsFromContext === 'string'
|
|
? [
|
|
{
|
|
name: optionsFromContext,
|
|
description: arg.description || '',
|
|
},
|
|
]
|
|
: optionsFromContext.map((o) => ({
|
|
name: o.name || '',
|
|
description: arg.description || '',
|
|
}))
|
|
|
|
return {
|
|
...arg,
|
|
options,
|
|
}
|
|
}) as any[]
|
|
}
|
|
|
|
// We have to recreate this object every time,
|
|
// otherwise we'll have stale state in the CommandBar
|
|
// after completing our first action
|
|
const meta = lookedUpMeta
|
|
? {
|
|
...lookedUpMeta,
|
|
args: replacedArgs,
|
|
}
|
|
: undefined
|
|
|
|
return {
|
|
name: type,
|
|
owner,
|
|
callback: (data: EventFrom<T, typeof type>) => {
|
|
if (data !== undefined && data !== null) {
|
|
send(type, { data })
|
|
} else {
|
|
send(type)
|
|
}
|
|
},
|
|
meta: meta as any,
|
|
}
|
|
}
|