Skip to content

Commit 06f5cba

Browse files
authored
Watch for a query parameter for the prompt within the text-to-cad UI (#8676)
* Watch for a query parameter for the prompt within the text-to-cad UI All of the "try in browser" links in the website for TTC have been broken since we migrated from the command-based implementation to the pane-based implementation. This provides a way for URLs to include a prompt that will be picked up by the pane UI and then deleted from the search parameters. A separate PR is being made to allow URLs to set the layout, so that we can guarantee that the TTC pane is open in a new iteration of these "try in browser" links. But it's worth noting that this implementation does not rely on the pane being open initially: the param will stick around until the pane is opened. * Hoist the state up and out of the textarea component * Fix cursor-at-end problem * fix: tsc errors
1 parent 8b3f4bb commit 06f5cba

File tree

2 files changed

+30
-1
lines changed

2 files changed

+30
-1
lines changed

src/components/MlEphantConversation2.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export interface MlEphantConversationProps {
2929
disabled?: boolean
3030
hasPromptCompleted: boolean
3131
userAvatarSrc?: string
32+
defaultPrompt?: string
3233
}
3334

3435
const ML_COPILOT_TOOLS: Readonly<MlCopilotTool[]> = Object.freeze([
@@ -252,6 +253,7 @@ interface MlEphantConversationInputProps {
252253
billingContext: BillingContext
253254
onProcess: MlEphantConversationProps['onProcess']
254255
disabled?: boolean
256+
defaultPrompt?: string
255257
}
256258

257259
function BillingStatusBarItem(props: { billingContext: BillingContext }) {
@@ -326,6 +328,9 @@ export const MlEphantConversationInput = (
326328
setExcludedTools(new Set(excludedTools))
327329
}
328330

331+
// Without this the cursor ends up at the start of the text
332+
useEffect(() => setValue(props.defaultPrompt || ''), [props.defaultPrompt])
333+
329334
useEffect(() => {
330335
if (
331336
value.length === 0 &&
@@ -514,6 +519,7 @@ export const MlEphantConversation2 = (props: MlEphantConversationProps) => {
514519
disabled={props.disabled || props.isLoading}
515520
onProcess={onProcess}
516521
billingContext={props.billingContext}
522+
defaultPrompt={props.defaultPrompt}
517523
/>
518524
</div>
519525
</div>

src/components/ModelingSidebar/ModelingPanes/MlEphantConversationPane2.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { type settings } from '@src/lib/settings/initialSettings'
44
import type CodeManager from '@src/lang/codeManager'
55
import type { KclManager } from '@src/lang/KclSingleton'
66
import type { SystemIOActor } from '@src/lib/singletons'
7-
import { useEffect } from 'react'
7+
import { useEffect, useState } from 'react'
88
import { SystemIOMachineEvents } from '@src/machines/systemIO/utils'
99
import { MlEphantConversation2 } from '@src/components/MlEphantConversation2'
1010
import type { MlEphantManagerActor2 } from '@src/machines/mlEphantManagerMachine2'
@@ -19,6 +19,12 @@ import type { FileEntry, Project } from '@src/lib/project'
1919
import type { BillingActor } from '@src/machines/billingMachine'
2020
import { useSelector } from '@xstate/react'
2121
import type { User, MlCopilotServerMessage, MlCopilotTool } from '@kittycad/lib'
22+
import { useSearchParams } from 'react-router-dom'
23+
/** URL query param key we watch for prompt input
24+
* we should never set this search param from the app,
25+
* only read and delete.
26+
*/
27+
const SEARCH_PARAM_PROMPT_KEY = 'ttc-prompt'
2228

2329
export const MlEphantConversationPane2 = (props: {
2430
mlEphantManagerActor: MlEphantManagerActor2
@@ -32,6 +38,8 @@ export const MlEphantConversationPane2 = (props: {
3238
settings: typeof settings
3339
user?: User
3440
}) => {
41+
const [defaultPrompt, setDefaultPrompt] = useState('')
42+
const [searchParams, setSearchParams] = useSearchParams()
3543
const conversation = useSelector(props.mlEphantManagerActor, (actor) => {
3644
return actor.context.conversation
3745
})
@@ -178,6 +186,20 @@ export const MlEphantConversationPane2 = (props: {
178186
// eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: blanket-ignored fix me!
179187
}, [props.settings.meta.id.current])
180188

189+
// We watch the URL for a query parameter to set the defaultPrompt
190+
// for the conversation.
191+
useEffect(() => {
192+
const ttcPromptParam = searchParams.get(SEARCH_PARAM_PROMPT_KEY)
193+
if (ttcPromptParam) {
194+
setDefaultPrompt(ttcPromptParam)
195+
196+
// Now clear that param
197+
const newSearchParams = new URLSearchParams(searchParams)
198+
newSearchParams.delete(SEARCH_PARAM_PROMPT_KEY)
199+
setSearchParams(newSearchParams, { replace: true })
200+
}
201+
}, [searchParams, setSearchParams])
202+
181203
return (
182204
<MlEphantConversation2
183205
isLoading={conversation === undefined}
@@ -192,6 +214,7 @@ export const MlEphantConversationPane2 = (props: {
192214
disabled={isProcessing}
193215
hasPromptCompleted={isProcessing}
194216
userAvatarSrc={props.user?.image}
217+
defaultPrompt={defaultPrompt}
195218
/>
196219
)
197220
}

0 commit comments

Comments
 (0)