wip
This commit is contained in:
@ -11,11 +11,13 @@ export type HomeItem = Project | Prompt
|
||||
export type HomeItems = Project[] | Prompt[]
|
||||
|
||||
export const areHomeItemsProjects = (items: HomeItems): items is Project[] => {
|
||||
if (items.length === 0) return true
|
||||
const item = items[0]
|
||||
return item !== undefined && 'path' in item
|
||||
}
|
||||
|
||||
export const areHomeItemsPrompts = (items: HomeItems): items is Prompt[] => {
|
||||
if (items.length === 0) return true
|
||||
const item = items[0]
|
||||
return item !== undefined && 'prompt' in item
|
||||
}
|
||||
|
@ -1,30 +1,40 @@
|
||||
import ms from 'ms'
|
||||
import type { Prompt } from '@src/lib/prompt'
|
||||
|
||||
interface PromptCardProps extends Prompt {
|
||||
onAction?: (id: Prompt['id']) => void
|
||||
export interface PromptCardProps extends Prompt {
|
||||
disabled?: boolean
|
||||
onAction?: (id: Prompt['id'], prompt: Prompt['prompt']) => void
|
||||
onDelete?: (id: Prompt['id']) => void
|
||||
onFeedback?: (id: string, feedback: Prompt['feedback']) => void
|
||||
}
|
||||
|
||||
export const PromptFeedback = (props: {
|
||||
id: Prompt['id']
|
||||
selected: Prompt['feedback']
|
||||
selected?: Prompt['feedback']
|
||||
disabled?: boolean
|
||||
onFeedback: (id: Prompt['id'], feedback: Prompt['feedback']) => void
|
||||
}) => {
|
||||
const cssUp = 'border-green-300'
|
||||
const cssDown = 'border-red-300'
|
||||
const cssUp =
|
||||
props.selected === undefined || props.selected === 'thumbs_up'
|
||||
? 'border-green-300'
|
||||
: 'border-green-100 text-chalkboard-60'
|
||||
const cssDown =
|
||||
props.selected === undefined || props.selected === 'thumbs_down'
|
||||
? 'border-red-300'
|
||||
: 'border-red-100 text-chalkboard-60'
|
||||
|
||||
return (
|
||||
<div className="flex flex-row gap-2">
|
||||
<button
|
||||
onClick={() => props.onFeedback(props.id, 'thumbs_up')}
|
||||
disabled={props.disabled}
|
||||
className={cssUp}
|
||||
>
|
||||
Good
|
||||
</button>
|
||||
<button
|
||||
onClick={() => props.onFeedback(props.id, 'thumbs_down')}
|
||||
disabled={props.disabled}
|
||||
className={cssDown}
|
||||
>
|
||||
Bad
|
||||
@ -35,13 +45,18 @@ export const PromptFeedback = (props: {
|
||||
|
||||
export const PromptCardActionButton = (props: {
|
||||
status: Prompt['status']
|
||||
disabled?: boolean
|
||||
onClick: () => void
|
||||
}) => {
|
||||
return (
|
||||
<button
|
||||
className="rounded-full bg-gray-100"
|
||||
onClick={props.onClick}
|
||||
disabled={props.status === 'queued' || props.status === 'in_progress'}
|
||||
disabled={
|
||||
props.disabled ||
|
||||
props.status === 'queued' ||
|
||||
props.status === 'in_progress'
|
||||
}
|
||||
>
|
||||
{props.status === 'completed' ? 'Create' : 'Pending'}
|
||||
</button>
|
||||
@ -49,15 +64,19 @@ export const PromptCardActionButton = (props: {
|
||||
}
|
||||
|
||||
export const PromptCard = (props: PromptCardProps) => {
|
||||
const cssCard = 'flex flex-col border rounded-md p-2 gap-2 justify-between'
|
||||
|
||||
return (
|
||||
<div className="flex flex-col border rounded-md p-2 gap-2 justify-between">
|
||||
<div className={`${cssCard} ${props.disabled ? 'text-chalkboard-60' : ''}`}>
|
||||
<div className="flex flex-row justify-between gap-2">
|
||||
<div>{props.prompt}</div>
|
||||
<div className="w-fit flex flex-col items-end">
|
||||
<button onClick={() => props.onDelete?.(props.id)}>Delete</button>
|
||||
{/* TODO: */}
|
||||
{/* <button disabled={props.disabled} onClick={() => props.onDelete?.(props.id)}>Delete</button> */}
|
||||
<PromptFeedback
|
||||
id={props.id}
|
||||
selected={props.feedback}
|
||||
disabled={props.disabled}
|
||||
onFeedback={(...args) => props.onFeedback?.(...args)}
|
||||
/>
|
||||
</div>
|
||||
@ -65,7 +84,8 @@ export const PromptCard = (props: PromptCardProps) => {
|
||||
<div className="flex flex-row justify-between">
|
||||
<PromptCardActionButton
|
||||
status={props.status}
|
||||
onClick={() => props.onAction?.(props.id)}
|
||||
disabled={props.disabled}
|
||||
onClick={() => props.onAction?.(props.id, props.prompt)}
|
||||
/>
|
||||
<div>
|
||||
{ms(new Date(props.created_at).getTime(), { long: true })} ago
|
||||
|
@ -26,6 +26,10 @@ import {
|
||||
engineStreamContextCreate,
|
||||
engineStreamMachine,
|
||||
} from '@src/machines/engineStreamMachine'
|
||||
import {
|
||||
mlEphantDefaultContext,
|
||||
mlEphantManagerMachine,
|
||||
} from '@src/machines/mlEphantManagerMachine'
|
||||
import { ACTOR_IDS } from '@src/machines/machineConstants'
|
||||
import { settingsMachine } from '@src/machines/settingsMachine'
|
||||
import { systemIOMachineDesktop } from '@src/machines/systemIO/systemIOMachineDesktop'
|
||||
@ -117,13 +121,21 @@ if (typeof window !== 'undefined') {
|
||||
},
|
||||
})
|
||||
}
|
||||
const { AUTH, SETTINGS, SYSTEM_IO, ENGINE_STREAM, COMMAND_BAR, BILLING } =
|
||||
ACTOR_IDS
|
||||
const {
|
||||
AUTH,
|
||||
SETTINGS,
|
||||
SYSTEM_IO,
|
||||
ENGINE_STREAM,
|
||||
MLEPHANT_MANAGER,
|
||||
COMMAND_BAR,
|
||||
BILLING,
|
||||
} = ACTOR_IDS
|
||||
const appMachineActors = {
|
||||
[AUTH]: authMachine,
|
||||
[SETTINGS]: settingsMachine,
|
||||
[SYSTEM_IO]: isDesktop() ? systemIOMachineDesktop : systemIOMachineWeb,
|
||||
[ENGINE_STREAM]: engineStreamMachine,
|
||||
[MLEPHANT_MANAGER]: mlEphantManagerMachine,
|
||||
[COMMAND_BAR]: commandBarMachine,
|
||||
[BILLING]: billingMachine,
|
||||
} as const
|
||||
@ -157,6 +169,10 @@ const appMachine = setup({
|
||||
systemId: ENGINE_STREAM,
|
||||
input: engineStreamContextCreate(),
|
||||
}),
|
||||
spawnChild(appMachineActors[MLEPHANT_MANAGER], {
|
||||
systemId: MLEPHANT_MANAGER,
|
||||
input: mlEphantDefaultContext(),
|
||||
}),
|
||||
spawnChild(appMachineActors[SYSTEM_IO], {
|
||||
systemId: SYSTEM_IO,
|
||||
}),
|
||||
@ -226,6 +242,10 @@ export const engineStreamActor = appActor.system.get(
|
||||
ENGINE_STREAM
|
||||
) as ActorRefFrom<(typeof appMachineActors)[typeof ENGINE_STREAM]>
|
||||
|
||||
export const mlEphantManagerActor = appActor.system.get(
|
||||
MLEPHANT_MANAGER
|
||||
) as ActorRefFrom<(typeof appMachineActors)[typeof MLEPHANT_MANAGER]>
|
||||
|
||||
export const commandBarActor = appActor.system.get(COMMAND_BAR) as ActorRefFrom<
|
||||
(typeof appMachineActors)[typeof COMMAND_BAR]
|
||||
>
|
||||
|
@ -68,8 +68,10 @@ export function getPromptSortFunction(sortBy: string) {
|
||||
|
||||
const sortByModified = (a: Prompt, b: Prompt) => {
|
||||
if (a.created_at && b.created_at) {
|
||||
const aDate = new Date(a.created_at)
|
||||
const bDate = new Date(b.created_at)
|
||||
// INTENTIONALLY REVERSED
|
||||
// Will not show properly otherwise.
|
||||
const aDate = new Date(b.created_at)
|
||||
const bDate = new Date(a.created_at)
|
||||
return !sortBy || sortBy.includes('desc')
|
||||
? bDate.getTime() - aDate.getTime()
|
||||
: aDate.getTime() - bDate.getTime()
|
||||
|
@ -3,6 +3,7 @@ export const ACTOR_IDS = {
|
||||
SETTINGS: 'settings',
|
||||
SYSTEM_IO: 'systemIO',
|
||||
ENGINE_STREAM: 'engine_stream',
|
||||
MLEPHANT_MANAGER: 'mlephant_manager',
|
||||
COMMAND_BAR: 'command_bar',
|
||||
BILLING: 'billing',
|
||||
} as const
|
||||
|
@ -1,7 +1,14 @@
|
||||
import { setup, fromPromise } from 'xstate'
|
||||
import { assign, setup, fromPromise } from 'xstate'
|
||||
import type { ActorRefFrom } from 'xstate'
|
||||
import type { Prompt } from '@src/lib/prompt'
|
||||
import { generateFakeSubmittedPrompt } from '@src/lib/prompt'
|
||||
|
||||
export enum MlEphantManagerStates {
|
||||
Setup = 'setup',
|
||||
Idle = 'idle',
|
||||
Pending = 'pending',
|
||||
}
|
||||
|
||||
export enum MlEphantManagerTransitionStates {
|
||||
GetPromptsThatCreatedProjects = 'get-prompts-that-created-projects',
|
||||
GetPromptsBelongingToProject = 'get-prompts-belonging-to-project',
|
||||
@ -23,12 +30,14 @@ export type MlEphantManagerEvents =
|
||||
}
|
||||
| {
|
||||
type: MlEphantManagerTransitionStates.PromptCreateModel
|
||||
// May or may not belong to a project.
|
||||
projectId?: string
|
||||
// For now we fake this, using project_name.
|
||||
projectId: string
|
||||
prompt: string
|
||||
}
|
||||
| {
|
||||
type: MlEphantManagerTransitionStates.PromptEditModel
|
||||
projectId: string
|
||||
prompt: string
|
||||
}
|
||||
| {
|
||||
type: MlEphantManagerTransitionStates.PromptRate
|
||||
@ -43,10 +52,8 @@ export type MlEphantManagerEvents =
|
||||
promptId: string
|
||||
}
|
||||
|
||||
export enum MlEphantManagerStates {
|
||||
Idle = 'idle',
|
||||
Pending = 'pending',
|
||||
}
|
||||
// Used to specify a specific event in input properties
|
||||
type XSEvent<T> = Extract<MlEphantManagerEvents, { type: T }>
|
||||
|
||||
export interface MlEphantManagerContext {
|
||||
promptsThatCreatedProjects: Map<Prompt['id'], Prompt>
|
||||
@ -54,7 +61,7 @@ export interface MlEphantManagerContext {
|
||||
promptsBelongingToProject?: Map<Prompt['id'], Prompt>
|
||||
}
|
||||
|
||||
export const mlEphantDefaultContext = Object.freeze({
|
||||
export const mlEphantDefaultContext = () => ({
|
||||
promptsThatCreatedProjects: new Map(),
|
||||
promptsBelongingToProject: undefined,
|
||||
hasPendingPrompts: false,
|
||||
@ -67,37 +74,146 @@ export const mlEphantManagerMachine = setup({
|
||||
},
|
||||
actors: {
|
||||
[MlEphantManagerTransitionStates.GetPromptsThatCreatedProjects]:
|
||||
fromPromise(async function (args) {
|
||||
console.log(arguments)
|
||||
return {
|
||||
promptsThatCreatedProjects: new Array(13)
|
||||
.fill(undefined)
|
||||
.map(generateFakeSubmittedPrompt),
|
||||
fromPromise(async function (args: {
|
||||
input: {
|
||||
context: MlEphantManagerContext
|
||||
}
|
||||
}): Promise<Partial<MlEphantManagerContext>> {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
const results = new Array(13)
|
||||
.fill(undefined)
|
||||
.map(generateFakeSubmittedPrompt)
|
||||
|
||||
const promptsThatCreatedProjects = new Map(
|
||||
args.input.context.promptsThatCreatedProjects
|
||||
)
|
||||
results.forEach((result) => {
|
||||
promptsThatCreatedProjects.set(result.id, result)
|
||||
})
|
||||
resolve({
|
||||
promptsThatCreatedProjects,
|
||||
})
|
||||
}, 2000)
|
||||
})
|
||||
}),
|
||||
[MlEphantManagerTransitionStates.PromptCreateModel]: fromPromise(
|
||||
async function (args: {
|
||||
input: {
|
||||
event: XSEvent<MlEphantManagerTransitionStates.PromptCreateModel>
|
||||
context: MlEphantManagerContext
|
||||
}
|
||||
}): Promise<Partial<MlEphantManagerContext>> {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
const promptsThatCreatedProjects = new Map(
|
||||
args.input.context.promptsThatCreatedProjects
|
||||
)
|
||||
|
||||
const result = generateFakeSubmittedPrompt()
|
||||
promptsThatCreatedProjects.set(result.id, result)
|
||||
|
||||
resolve({
|
||||
promptsThatCreatedProjects,
|
||||
})
|
||||
}, 5000)
|
||||
})
|
||||
}
|
||||
),
|
||||
[MlEphantManagerTransitionStates.PromptPollStatus]: fromPromise(
|
||||
async function (args: {
|
||||
input: {
|
||||
context: MlEphantManagerContext
|
||||
}
|
||||
}): Promise<Partial<MlEphantManagerContext>> {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
const promptsThatCreatedProjects = new Map(
|
||||
args.input.context.promptsThatCreatedProjects
|
||||
)
|
||||
// Do the same for prompts of a project
|
||||
promptsThatCreatedProjects.values().forEach((prompt) => {
|
||||
if (prompt.status !== 'pending') return
|
||||
prompt.status = 'completed'
|
||||
})
|
||||
resolve({
|
||||
promptsThatCreatedProjects,
|
||||
})
|
||||
}, 3000)
|
||||
})
|
||||
}
|
||||
),
|
||||
},
|
||||
}).createMachine({
|
||||
initial: MlEphantManagerStates.Idle,
|
||||
context: mlEphantDefaultContext,
|
||||
initial: MlEphantManagerStates.Setup,
|
||||
context: mlEphantDefaultContext(),
|
||||
states: {
|
||||
[MlEphantManagerStates.Setup]: {
|
||||
invoke: {
|
||||
input: (args: { context: MlEphantManagerContext }) => args,
|
||||
src: MlEphantManagerTransitionStates.GetPromptsThatCreatedProjects,
|
||||
onDone: {
|
||||
target: MlEphantManagerStates.Idle,
|
||||
actions: assign(({ event }) => event.output),
|
||||
},
|
||||
onError: { target: MlEphantManagerStates.Idle },
|
||||
},
|
||||
},
|
||||
[MlEphantManagerStates.Idle]: {
|
||||
on: {
|
||||
[MlEphantManagerTransitionStates.GetPromptsThatCreatedProjects]: {
|
||||
target: MlEphantManagerTransitionStates.GetPromptsThatCreatedProjects,
|
||||
target:
|
||||
MlEphantManagerStates.Pending +
|
||||
'.' +
|
||||
MlEphantManagerTransitionStates.GetPromptsThatCreatedProjects,
|
||||
},
|
||||
},
|
||||
},
|
||||
[MlEphantManagerStates.Pending]: {
|
||||
initial: MlEphantManagerStates.Idle,
|
||||
states: {
|
||||
// Pop back out to Idle.
|
||||
[MlEphantManagerStates.Idle]: {
|
||||
type: 'final',
|
||||
target: MlEphantManagerStates.Idle,
|
||||
},
|
||||
[MlEphantManagerTransitionStates.GetPromptsThatCreatedProjects]: {
|
||||
invoke: {
|
||||
input: (args: any) => args,
|
||||
input: (args) => ({
|
||||
context: args.context,
|
||||
}),
|
||||
src: MlEphantManagerTransitionStates.GetPromptsThatCreatedProjects,
|
||||
onDone: { target: MlEphantManagerStates.Idle },
|
||||
onError: { target: MlEphantManagerStates.Idle },
|
||||
},
|
||||
},
|
||||
[MlEphantManagerTransitionStates.PromptCreateModel]: {
|
||||
invoke: {
|
||||
input: (args) => ({
|
||||
event:
|
||||
args.event as XSEvent<MlEphantManagerTransitionStates.PromptCreateModel>,
|
||||
context: args.context,
|
||||
}),
|
||||
src: MlEphantManagerTransitionStates.PromptCreateModel,
|
||||
onDone: { target: MlEphantManagerStates.PromptPollStatus },
|
||||
onError: { target: MlEphantManagerStates.PromptPollStatus },
|
||||
},
|
||||
},
|
||||
[MlEphantManagerTransitionStates.PromptPollStatus]: {
|
||||
invoke: {
|
||||
input: (args) => ({
|
||||
event:
|
||||
args.event as XSEvent<MlEphantManagerTransitionStates.PromptPollStatus>,
|
||||
context: args.context,
|
||||
}),
|
||||
src: MlEphantManagerTransitionStates.PromptPollStatus,
|
||||
onDone: { target: MlEphantManagerStates.Idle },
|
||||
onError: { target: MlEphantManagerStates.Idle },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export type MlEphantManagerActor = ActorRefFrom<typeof mlEphantManagerMachine>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import type { FormEvent, HTMLProps } from 'react'
|
||||
import { useSelector } from '@xstate/react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { toast } from 'react-hot-toast'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
@ -29,7 +30,6 @@ import { PATHS } from '@src/lib/paths'
|
||||
import { markOnce } from '@src/lib/performance'
|
||||
import type { Project } from '@src/lib/project'
|
||||
import type { Prompt } from '@src/lib/prompt'
|
||||
import { generateFakeSubmittedPrompt } from '@src/lib/prompt'
|
||||
import {
|
||||
getNextSearchParams,
|
||||
getProjectSortFunction,
|
||||
@ -44,6 +44,7 @@ import {
|
||||
kclManager,
|
||||
authActor,
|
||||
billingActor,
|
||||
mlEphantManagerActor,
|
||||
systemIOActor,
|
||||
useSettings,
|
||||
} from '@src/lib/singletons'
|
||||
@ -73,6 +74,7 @@ import {
|
||||
defaultLocalStatusBarItems,
|
||||
defaultGlobalStatusBarItems,
|
||||
} from '@src/components/StatusBar/defaultStatusBarItems'
|
||||
import { MlEphantManagerStates } from '@src/machines/mlEphantManagerMachine'
|
||||
|
||||
type ReadWriteProjectState = {
|
||||
value: boolean
|
||||
@ -217,15 +219,10 @@ const Home = () => {
|
||||
}
|
||||
)
|
||||
const projects = useFolders()
|
||||
const prompts: Prompt[] = [
|
||||
generateFakeSubmittedPrompt(),
|
||||
generateFakeSubmittedPrompt(),
|
||||
generateFakeSubmittedPrompt(),
|
||||
generateFakeSubmittedPrompt(),
|
||||
generateFakeSubmittedPrompt(),
|
||||
generateFakeSubmittedPrompt(),
|
||||
generateFakeSubmittedPrompt(),
|
||||
]
|
||||
const prompts = useSelector(mlEphantManagerActor, (actor) => {
|
||||
return actor.context.promptsThatCreatedProjects
|
||||
})
|
||||
|
||||
const [tabSelected, setTabSelected] = useState<HomeTabKeys>(
|
||||
HomeTabKeys.Projects
|
||||
)
|
||||
@ -236,17 +233,23 @@ const Home = () => {
|
||||
|
||||
const onChangeTab = (key: HomeTabKeys) => {
|
||||
setTabSelected(key)
|
||||
switch (key) {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
switch (tabSelected) {
|
||||
case HomeTabKeys.Projects:
|
||||
setItems(projects)
|
||||
break
|
||||
case HomeTabKeys.Prompts:
|
||||
setItems(prompts)
|
||||
// Lessons hard learned: VERY important to do this here, and not within
|
||||
// the useSelector. React will think it's a new value every time, and
|
||||
// cause this useEffect to fire indefinitely.
|
||||
setItems(Array.from(prompts.values()))
|
||||
break
|
||||
default:
|
||||
const _ex: never = key
|
||||
const _ex: never = tabSelected
|
||||
}
|
||||
}
|
||||
}, [tabSelected, prompts, projects])
|
||||
|
||||
useEffect(() => {
|
||||
searchAgainst(items)('')
|
||||
@ -439,6 +442,7 @@ const Home = () => {
|
||||
searchResults={searchResults}
|
||||
sortBy={sortBy}
|
||||
query={query}
|
||||
settings={settings}
|
||||
/>
|
||||
</div>
|
||||
<StatusBar
|
||||
@ -483,6 +487,7 @@ function HomeTab(props: HomeTabProps) {
|
||||
<div className="flex flex-row">
|
||||
{tabs.map((el) => (
|
||||
<div
|
||||
key={el.key}
|
||||
className={el.key === selected ? cssActive : cssInactive}
|
||||
onClick={onClickTab(el.key)}
|
||||
role="tab"
|
||||
@ -617,11 +622,14 @@ interface HomeItemsAreaProps {
|
||||
searchResults: HomeItems
|
||||
sortBy: string
|
||||
query: string
|
||||
settings: ReturnType<typeof useSettings>
|
||||
}
|
||||
|
||||
function HomeItemsArea(props: HomeItemsAreaProps) {
|
||||
let grid = null
|
||||
|
||||
console.log('home items area', props.tabSelected, props.searchResults)
|
||||
|
||||
switch (props.tabSelected) {
|
||||
case HomeTabKeys.Projects:
|
||||
grid = areHomeItemsProjects(props.searchResults) ? (
|
||||
@ -640,6 +648,7 @@ function HomeItemsArea(props: HomeItemsAreaProps) {
|
||||
searchResults={props.searchResults}
|
||||
query={props.query}
|
||||
sortBy={props.sortBy}
|
||||
settings={props.settings}
|
||||
/>
|
||||
) : (
|
||||
<NoResults />
|
||||
@ -660,13 +669,26 @@ interface ResultGridPromptsProps {
|
||||
searchResults: Prompt[]
|
||||
query: string
|
||||
sortBy: string
|
||||
settings: ReturnType<typeof useSettings>
|
||||
}
|
||||
|
||||
function ResultGridPrompts(props: ResultGridPromptsProps) {
|
||||
// Maybe consider lifting this higher but I see no reason at the moment
|
||||
const onAction = (...args: any) => {
|
||||
console.log(args)
|
||||
const onAction = (_id: Prompt['id'], prompt: Prompt['prompt']) => {
|
||||
commandBarActor.send({
|
||||
type: 'Find and select command',
|
||||
data: {
|
||||
groupId: 'application',
|
||||
name: 'Text-to-CAD',
|
||||
argDefaultValues: {
|
||||
method: 'newProject',
|
||||
prompt,
|
||||
newProjectName: props.settings.projects.defaultProjectName.current,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
// no-op for now
|
||||
const onDelete = (...args: any) => {
|
||||
console.log(args)
|
||||
}
|
||||
@ -674,6 +696,16 @@ function ResultGridPrompts(props: ResultGridPromptsProps) {
|
||||
console.log(args)
|
||||
}
|
||||
|
||||
const mlEphantManagerSnapshot = mlEphantManagerActor.getSnapshot()
|
||||
|
||||
if (mlEphantManagerSnapshot.matches(MlEphantManagerStates.Setup)) {
|
||||
return (
|
||||
<div className="col-start-2 -col-end-1 w-full flex flex-col justify-center items-center">
|
||||
<Loading isDummy={true}>Loading your prompts...</Loading>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid w-full sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2 gap-4">
|
||||
{props.searchResults
|
||||
@ -682,6 +714,10 @@ function ResultGridPrompts(props: ResultGridPromptsProps) {
|
||||
<PromptCard
|
||||
key={prompt.id}
|
||||
{...prompt}
|
||||
disabled={
|
||||
mlEphantManagerSnapshot.matches(MlEphantManagerStates.Pending) ||
|
||||
prompt.status !== 'completed'
|
||||
}
|
||||
onAction={onAction}
|
||||
onDelete={onDelete}
|
||||
onFeedback={onFeedback}
|
||||
|
Reference in New Issue
Block a user