11'use client' ;
22
33import React , { useEffect , useMemo , useState } from 'react' ;
4- import type { VCSProviderType } from '@bitcode/vcs-core' ;
4+ import type { VCSBranch , VCSCommit , VCSProviderType } from '@bitcode/vcs-core' ;
55
66import BitcodeChipCloud from '@/components/base/bitcode/execution/BitcodeChipCloud' ;
77import BitcodeInlineExplainer from '@/components/base/bitcode/execution/BitcodeInlineExplainer' ;
@@ -34,14 +34,36 @@ interface TerminalDepositComposerProps {
3434 onRecordActivity ?: ( draft : TerminalActivityRecordDraft ) => Promise < unknown > ;
3535 repositoryAnchor ?: string | null ;
3636 repositoryProvider ?: VCSProviderType | null ;
37+ repositoryBranch ?: string | null ;
38+ repositoryCommit ?: string | null ;
39+ repositoryBranches ?: VCSBranch [ ] ;
40+ repositoryCommits ?: VCSCommit [ ] ;
41+ isLoadingRepositoryBranches ?: boolean ;
42+ isLoadingRepositoryCommits ?: boolean ;
43+ onRepositorySourceBranchChange ?: ( branch : string ) => void ;
44+ onRepositorySourceCommitChange ?: ( commit : string ) => void ;
3745 transactionReadiness : BitcodeTransactionReadiness ;
3846 showDemonstrationDraft ?: boolean ;
3947}
4048
49+ function formatCommitOption ( commit : VCSCommit ) {
50+ const shortSha = commit . sha . slice ( 0 , 7 ) ;
51+ const title = commit . message . split ( '\n' ) [ 0 ] ?. trim ( ) || 'Commit' ;
52+ return `${ shortSha } - ${ title } ` ;
53+ }
54+
4155export default function TerminalDepositComposer ( {
4256 onRecordActivity,
4357 repositoryAnchor,
4458 repositoryProvider,
59+ repositoryBranch,
60+ repositoryCommit,
61+ repositoryBranches = [ ] ,
62+ repositoryCommits = [ ] ,
63+ isLoadingRepositoryBranches = false ,
64+ isLoadingRepositoryCommits = false ,
65+ onRepositorySourceBranchChange,
66+ onRepositorySourceCommitChange,
4567 transactionReadiness,
4668 showDemonstrationDraft = true ,
4769} : TerminalDepositComposerProps ) {
@@ -60,7 +82,11 @@ export default function TerminalDepositComposer({
6082 const [ content , setContent ] = useState ( '' ) ;
6183 const [ submitState , setSubmitState ] = useState < SubmitState > ( { kind : 'idle' } ) ;
6284 const repositoryAnchorValue = String ( repositoryAnchor || '' ) . trim ( ) ;
85+ const repositoryBranchValue = String ( repositoryBranch || '' ) . trim ( ) ;
86+ const repositoryCommitValue = String ( repositoryCommit || '' ) . trim ( ) ;
6387 const usesLiveRepositoryAnchor = Boolean ( repositoryAnchorValue ) ;
88+ const hasSelectedSourceRevision =
89+ ! usesLiveRepositoryAnchor || Boolean ( repositoryBranchValue && repositoryCommitValue ) ;
6490 const composer = useMemo < TerminalDepositComposerState | null > (
6591 ( ) => {
6692 if ( ! showDemonstrationDraft && repositoryAnchorValue ) {
@@ -84,6 +110,7 @@ export default function TerminalDepositComposer({
84110 const selectedSupplyCount = usesLiveRepositoryAnchor ? 1 : composer ?. selectedCount || 0 ;
85111 const selectedInventoryEntryIds = usesLiveRepositoryAnchor ? [ ] : composer ?. selectedInventoryEntryIds || [ ] ;
86112 const displayedSourceRepo = usesLiveRepositoryAnchor ? repositoryAnchorValue : sourceRepo ;
113+ const displayedSourceCommit = usesLiveRepositoryAnchor ? repositoryCommitValue : sourceCommit ;
87114
88115 useEffect ( ( ) => {
89116 setSignerAddress ( ( current ) => current || composer ?. signerAddress || '' ) ;
@@ -105,6 +132,7 @@ export default function TerminalDepositComposer({
105132 ( selectedSupplyCount > 0 || content . trim ( ) ) &&
106133 ( title . trim ( ) || selectedSupplyCount > 0 ) &&
107134 ( author . trim ( ) || effectiveAuthSessionId ) &&
135+ hasSelectedSourceRevision &&
108136 settlementReady ,
109137 ) ;
110138
@@ -133,7 +161,9 @@ export default function TerminalDepositComposer({
133161 repositoryAnchor : repositoryAnchorValue || composer . sourceRepo || undefined ,
134162 repositoryProvider : repositoryProvider || undefined ,
135163 sourceRepo : displayedSourceRepo || undefined ,
136- sourceCommit,
164+ sourceBranch : repositoryBranchValue || undefined ,
165+ sourceRef : repositoryBranchValue || undefined ,
166+ sourceCommit : displayedSourceCommit || undefined ,
137167 workflowRunId,
138168 signerAddress,
139169 visualPreview,
@@ -181,6 +211,8 @@ export default function TerminalDepositComposer({
181211 selectedInventoryEntryIds,
182212 selectedInventoryTitles : selectedEntryLabels ,
183213 repositoryAnchor : repositoryAnchorValue || null ,
214+ sourceBranch : repositoryBranchValue || null ,
215+ sourceCommit : displayedSourceCommit || null ,
184216 candidateAssetId : payload . asset ?. assetId || null ,
185217 } ,
186218 output : {
@@ -322,17 +354,71 @@ export default function TerminalDepositComposer({
322354
323355 < div className = "rounded-[1.5rem] border border-white/8 bg-black/20 px-4 py-4" >
324356 < span className = "flex items-center gap-2 text-[0.66rem] uppercase tracking-[0.24em] text-neutral-400" >
325- < span > Source commit / ref</ span >
326- < BitcodeInlineExplainer explainer = { TERMINAL_INLINE_EXPLAINERS . sourceCommit } />
357+ < span > { usesLiveRepositoryAnchor ? 'Source branch' : 'Source commit / ref' } </ span >
358+ < BitcodeInlineExplainer
359+ explainer = {
360+ usesLiveRepositoryAnchor
361+ ? TERMINAL_INLINE_EXPLAINERS . sourceBranch
362+ : TERMINAL_INLINE_EXPLAINERS . sourceCommit
363+ }
364+ />
327365 </ span >
328- < input
329- value = { sourceCommit }
330- onChange = { ( event ) => setSourceCommit ( event . target . value ) }
331- placeholder = "commit or branch override"
332- className = "mt-3 w-full rounded-xl border border-white/10 bg-[rgba(10,15,30,0.88)] px-3 py-3 text-sm text-white outline-none transition placeholder:text-neutral-500 focus:border-emerald-400/40"
333- />
366+ { usesLiveRepositoryAnchor ? (
367+ < >
368+ < select
369+ aria-label = "Deposit source branch"
370+ value = { repositoryBranchValue }
371+ disabled = { isLoadingRepositoryBranches || repositoryBranches . length === 0 }
372+ onChange = { ( event ) => onRepositorySourceBranchChange ?.( event . target . value ) }
373+ className = "mt-3 w-full rounded-xl border border-white/10 bg-[rgba(10,15,30,0.88)] px-3 py-3 text-sm text-white outline-none transition focus:border-emerald-400/40 disabled:cursor-not-allowed disabled:opacity-60"
374+ >
375+ { repositoryBranches . length ? null : < option value = "" > No branches loaded</ option > }
376+ { repositoryBranches . map ( ( branch ) => (
377+ < option key = { branch . name } value = { branch . name } >
378+ { branch . name }
379+ </ option >
380+ ) ) }
381+ </ select >
382+ < p className = "mt-2 text-[0.68rem] uppercase tracking-[0.18em] text-neutral-500" >
383+ { isLoadingRepositoryBranches ? 'Loading branches…' : 'Changing branch refreshes commit refs' }
384+ </ p >
385+ </ >
386+ ) : (
387+ < input
388+ value = { sourceCommit }
389+ onChange = { ( event ) => setSourceCommit ( event . target . value ) }
390+ placeholder = "commit or branch override"
391+ className = "mt-3 w-full rounded-xl border border-white/10 bg-[rgba(10,15,30,0.88)] px-3 py-3 text-sm text-white outline-none transition placeholder:text-neutral-500 focus:border-emerald-400/40"
392+ />
393+ ) }
334394 </ div >
335395
396+ { usesLiveRepositoryAnchor ? (
397+ < div className = "rounded-[1.5rem] border border-white/8 bg-black/20 px-4 py-4" >
398+ < span className = "flex items-center gap-2 text-[0.66rem] uppercase tracking-[0.24em] text-neutral-400" >
399+ < span > Source commit / ref</ span >
400+ < BitcodeInlineExplainer explainer = { TERMINAL_INLINE_EXPLAINERS . sourceCommit } />
401+ </ span >
402+ < select
403+ aria-label = "Deposit source commit"
404+ value = { repositoryCommitValue }
405+ disabled = { ! repositoryBranchValue || isLoadingRepositoryCommits || repositoryCommits . length === 0 }
406+ onChange = { ( event ) => onRepositorySourceCommitChange ?.( event . target . value ) }
407+ className = "mt-3 w-full rounded-xl border border-white/10 bg-[rgba(10,15,30,0.88)] px-3 py-3 text-sm text-white outline-none transition focus:border-emerald-400/40 disabled:cursor-not-allowed disabled:opacity-60"
408+ >
409+ { repositoryCommits . length ? null : < option value = "" > No commits loaded</ option > }
410+ { repositoryCommits . map ( ( commit ) => (
411+ < option key = { commit . sha } value = { commit . sha } >
412+ { formatCommitOption ( commit ) }
413+ </ option >
414+ ) ) }
415+ </ select >
416+ < p className = "mt-2 text-[0.68rem] uppercase tracking-[0.18em] text-neutral-500" >
417+ { isLoadingRepositoryCommits ? 'Loading commits…' : 'Exact source materialization uses this commit SHA' }
418+ </ p >
419+ </ div >
420+ ) : null }
421+
336422 < label className = "rounded-[1.5rem] border border-white/8 bg-black/20 px-4 py-4" >
337423 < span className = "text-[0.66rem] uppercase tracking-[0.24em] text-neutral-400" > Workflow run override</ span >
338424 < input
@@ -468,10 +554,15 @@ export default function TerminalDepositComposer({
468554 < p className = "mt-3 text-sm leading-6 text-neutral-300" >
469555 { selectedSupplyCount
470556 ? usesLiveRepositoryAnchor
471- ? `Bitcode will bind ${ repositoryAnchorValue } as the selected repository source for this deposit.`
557+ ? `Bitcode will bind ${ repositoryAnchorValue } ${ repositoryBranchValue ? ` on ${ repositoryBranchValue } ` : '' } ${ repositoryCommitValue ? ` at ${ repositoryCommitValue . slice ( 0 , 12 ) } ` : '' } as the selected repository source for this deposit.`
472558 : `Bitcode will bind ${ selectedSupplyCount } selected repo artifact${ selectedSupplyCount === 1 ? '' : 's' } into this deposit.`
473559 : 'No repo artifacts are currently selected. Use raw fallback content or select inventory above.' }
474560 </ p >
561+ { usesLiveRepositoryAnchor && ! hasSelectedSourceRevision ? (
562+ < p className = "mt-3 rounded-[1.1rem] border border-amber-400/20 bg-amber-400/10 px-4 py-3 text-sm leading-6 text-amber-100" >
563+ Select a branch and commit before depositing so source materialization can fetch an exact snapshot.
564+ </ p >
565+ ) : null }
475566 { selectedEntryLabels . length ? (
476567 < BitcodeChipCloud
477568 chips = { selectedEntryLabels }
0 commit comments