@@ -30,6 +30,12 @@ import {
3030 X ,
3131} from ' lucide-vue-next'
3232import { api , isProjectAPIInitializingError } from ' ./api'
33+ import {
34+ canSubmitCreatePrompt ,
35+ createPromptBlockedMessage ,
36+ gitConnectionReady ,
37+ type ProjectCreateReadiness ,
38+ } from ' ./createReadiness'
3339import ConfirmDialog from ' @/components/ConfirmDialog.vue'
3440import StatusBadge from ' @/components/StatusBadge.vue'
3541import { useEscapeKey } from ' @/composables/useEscapeKey'
@@ -313,6 +319,9 @@ const followUpAnswers = ref<Record<string, string>>({})
313319const followUpBusy = ref <Record <string , boolean >>({})
314320const followUpErrors = ref <Record <string , string >>({})
315321const toolState = ref <' idle' | ' loading' | ' ready' | ' error' >(' idle' )
322+ const createReadiness = ref <ProjectCreateReadiness | null >(null )
323+ const createReadinessLoading = ref (false )
324+ const createReadinessError = ref <string | null >(null )
316325const workbench = ref (createDefaultWorkbenchState ())
317326const draggedWorkbenchTabID = ref <string | null >(null )
318327const dragOverWorkbenchTabID = ref <string | null >(null )
@@ -359,7 +368,7 @@ const isBuilderVisible = computed(() => !isAppStudioLandingRoute.value || select
359368const showNewProjectComposer = computed (() => isCreateRoute .value )
360369const chatPaneStyle = computed (() => ({ flexBasis: ` ${splitWidth .value }% ` }))
361370const assistantResumeBusy = computed (() => Object .keys (permissionBusy .value ).length > 0 || Object .keys (followUpBusy .value ).length > 0 )
362- const canStartProjectFromPrompt = computed (() => prompt .value . trim (). length > 0 )
371+ const canStartProjectFromPrompt = computed (() => canSubmitCreatePrompt ( prompt .value , createReadiness . value ) )
363372const canSendPrompt = computed (() => (llmSettings .value ?.configured ?? false ) && prompt .value .trim ().length > 0 && ! messageStreaming .value && ! assistantResumeBusy .value )
364373const settingsProject = computed (() => (isAppStudioLandingRoute .value ? null : selected .value ))
365374const settingsTitle = computed (() => (settingsProject .value ? ' Project settings' : ' LLM settings' ))
@@ -375,6 +384,28 @@ const conversationWorkingLabel = computed(() => {
375384 if (lastAssistant ?.content .trim ()) return ' Working'
376385 return ' Working'
377386})
387+ const gitConnectionCreateReady = computed (() => gitConnectionReady (createReadiness .value ))
388+ const createReadinessChecking = computed (() => createReadinessLoading .value || (!! props .ctx ?.token && createReadiness .value === null && ! createReadinessError .value ))
389+ const createReadinessBlockMessage = computed (() => {
390+ if (createReadinessError .value ) return createReadinessError .value
391+ if (createReadinessChecking .value ) return ' Checking Git connection...'
392+ return createPromptBlockedMessage (createReadiness .value )
393+ })
394+ const createPromptSubmitTitle = computed (() => {
395+ if (! gitConnectionCreateReady .value ) return ' Connect Git before creating a durable project'
396+ return llmSettings .value ?.configured ? ' Create project and send prompt' : ' Configure LLM settings before creating a project'
397+ })
398+ const createPromptSubmitLabel = computed (() => {
399+ if (! gitConnectionCreateReady .value ) return createReadinessChecking .value ? ' Checking Git' : ' Connect Git'
400+ return llmSettings .value ?.configured ? ' Create and send' : ' Set up and send'
401+ })
402+ const llmConfigured = computed (() => llmSettings .value ?.configured ?? false )
403+ const workspaceSetupReady = computed (() => gitConnectionCreateReady .value && llmConfigured .value )
404+ const workspaceSetupLabel = computed (() => {
405+ if (! gitConnectionCreateReady .value ) return createReadinessChecking .value ? ' Checking Git' : ' Git setup needed'
406+ if (! llmConfigured .value ) return ' LLM setup needed'
407+ return ' Workspace ready'
408+ })
378409const deleteProjectMessage = computed (() => {
379410 const project = deleteProjectTarget .value
380411 if (! project ) return ' '
@@ -723,6 +754,7 @@ const developmentPreviewUnavailableMessage = computed(() => {
723754onMounted (() => {
724755 void load ()
725756 void loadProviders ()
757+ void loadCreateReadiness ()
726758 void loadLLMSettings ()
727759 startLandingPlaceholderRotation ()
728760 window .addEventListener (' focus' , handleDevelopmentPreviewAuthorizationWake )
@@ -742,6 +774,7 @@ watch(
742774 () => props .ctx ?.token ,
743775 () => {
744776 void loadProviders ()
777+ void loadCreateReadiness ()
745778 void loadLLMSettings ()
746779 },
747780)
@@ -903,6 +936,7 @@ function handleProjectAPIInitializing(err: unknown): boolean {
903936 initializationRetryTimer = window .setTimeout (() => {
904937 initializationRetryTimer = undefined
905938 void load ()
939+ void loadCreateReadiness ()
906940 void loadLLMSettings ()
907941 }, 2000 )
908942 return true
@@ -938,6 +972,21 @@ async function loadProviders() {
938972 }
939973}
940974
975+ async function loadCreateReadiness() {
976+ if (! props .ctx ?.token ) return
977+ createReadinessLoading .value = true
978+ createReadinessError .value = null
979+ try {
980+ createReadiness .value = await api .getProjectCreateReadiness (props .ctx )
981+ } catch (e ) {
982+ if (handleProjectAPIInitializing (e )) return
983+ createReadiness .value = null
984+ createReadinessError .value = e instanceof Error ? e .message : String (e )
985+ } finally {
986+ createReadinessLoading .value = false
987+ }
988+ }
989+
941990async function loadLLMSettings() {
942991 if (! props .ctx ?.token ) return
943992 try {
@@ -1211,13 +1260,23 @@ async function clearLLMKey() {
12111260async function createProjectFromPrompt() {
12121261 const content = prompt .value .trim ()
12131262 if (! content ) return
1263+ if (! await ensureGitConnectionReady ()) return
12141264 if (! llmSettings .value ?.configured ) {
12151265 openSettings ()
12161266 return
12171267 }
12181268 await createProjectAndStartConversation (content )
12191269}
12201270
1271+ async function ensureGitConnectionReady(): Promise <boolean > {
1272+ if (! gitConnectionCreateReady .value && ! createReadinessLoading .value ) {
1273+ await loadCreateReadiness ()
1274+ }
1275+ if (gitConnectionCreateReady .value ) return true
1276+ error .value = createReadinessError .value || createPromptBlockedMessage (createReadiness .value )
1277+ return false
1278+ }
1279+
12211280async function createProjectAndStartConversation(content : string ) {
12221281 const now = new Date ().toISOString ()
12231282 const draftName = ` draft-${Date .now ()} `
@@ -2881,16 +2940,16 @@ function repositoryCommitFilesLabel(commit: ProjectRepositoryCommit): string {
28812940 <main class =" flex min-h-0 flex-1 items-center justify-center py-4" >
28822941 <section class =" w-full max-w-[1060px]" >
28832942 <div class =" mx-auto flex max-w-[760px] flex-col items-center text-center" >
2884- <span
2885- class =" inline-flex items-center gap-1.5 rounded-full border px-3 py-1.5 text-[12px] font-medium"
2886- :class =" llmSettings?.configured
2887- ? 'border-success/30 bg-success-subtle text-success'
2888- : 'border-warning/30 bg-warning-subtle text-warning'"
2889- >
2890- <Check v-if =" llmSettings ?. configured " class="h-3.5 w-3.5 " :stroke-width =" 2 " />
2891- <Settings2 v-else class="h-3.5 w-3.5 " :stroke-width =" 1.75 " />
2892- {{ llmSettings?.configured ? 'Workspace ready' : 'LLM setup needed' }}
2893- </span >
2943+ <span
2944+ class =" inline-flex items-center gap-1.5 rounded-full border px-3 py-1.5 text-[12px] font-medium"
2945+ :class =" workspaceSetupReady
2946+ ? 'border-success/30 bg-success-subtle text-success'
2947+ : 'border-warning/30 bg-warning-subtle text-warning'"
2948+ >
2949+ <Check v-if =" workspaceSetupReady " class="h-3.5 w-3.5 " :stroke-width =" 2 " />
2950+ <Settings2 v-else class="h-3.5 w-3.5 " :stroke-width =" 1.75 " />
2951+ {{ workspaceSetupLabel }}
2952+ </span >
28942953 <h2 class =" mt-5 text-[44px] font-semibold leading-[1.05] text-text-primary md:text-[56px]" >
28952954 What do you want to build?
28962955 </h2 >
@@ -2938,16 +2997,29 @@ function repositoryCommitFilesLabel(commit: ProjectRepositoryCommit): string {
29382997 <span v-else class =" text-[12px] text-text-muted" >
29392998 The first message will create the project and start the conversation.
29402999 </span >
3000+ <span
3001+ v-if =" createReadinessBlockMessage"
3002+ class =" inline-flex items-center gap-1.5 rounded-md border border-warning/30 bg-warning-subtle px-2.5 py-1.5 text-[12px] font-medium text-warning"
3003+ >
3004+ <GitBranch class="h-3.5 w-3.5 " :stroke-width =" 1.75 " />
3005+ <template v-if =" isMissingCodeConnectionError (createReadinessBlockMessage )" >
3006+ <a :href =" CODE_CONNECTIONS_URL" class =" underline underline-offset-2 hover:text-warning/80" >
3007+ Connect Git
3008+ </a >
3009+ <span >to create a durable project.</span >
3010+ </template >
3011+ <template v-else >{{ createReadinessBlockMessage }}</template >
3012+ </span >
29413013 </div >
29423014 <button
29433015 class =" inline-flex h-9 items-center justify-center gap-2 rounded-md border border-accent/30 bg-accent/10 px-3 text-[13px] font-medium text-accent transition hover:bg-accent/20 disabled:cursor-not-allowed disabled:opacity-60"
29443016 type =" submit"
29453017 :disabled =" busy || !canStartProjectFromPrompt"
2946- :title =" llmSettings?.configured ? 'Create project and send prompt' : 'Configure LLM settings before creating a project' "
3018+ :title =" createPromptSubmitTitle "
29473019 >
2948- <Plus v-if =" llmSettings ?.configured " class="h-4 w-4" :stroke-width =" 2 " />
3020+ <Plus v-if =" gitConnectionCreateReady && llmSettings ?.configured " class="h-4 w-4" :stroke-width =" 2 " />
29493021 <Settings2 v-else class="h-4 w-4" :stroke-width =" 1.75 " />
2950- {{ llmSettings?.configured ? 'Create and send' : 'Set up and send' }}
3022+ {{ createPromptSubmitLabel }}
29513023 </button >
29523024 </div >
29533025 </div >
0 commit comments