@@ -31,6 +31,7 @@ import {
3131} from "../../hooks/useInputHandler.js" ;
3232import { useNavigation } from "../../store/navigationStore.js" ;
3333import { ConfirmationPrompt } from "../../components/ConfirmationPrompt.js" ;
34+ import { listBlueprints as listBlueprintsService } from "../../services/blueprintService.js" ;
3435
3536const DEFAULT_PAGE_SIZE = 10 ;
3637
@@ -42,10 +43,17 @@ type OperationType =
4243 | null ;
4344
4445// Local interface for blueprint data used in this component
46+ type BlueprintStatus =
47+ | "queued"
48+ | "provisioning"
49+ | "building"
50+ | "failed"
51+ | "build_complete" ;
52+
4553interface BlueprintListItem {
4654 id : string ;
4755 name ?: string ;
48- status ?: string ;
56+ status ?: BlueprintStatus ;
4957 create_time_ms ?: number ;
5058 [ key : string ] : unknown ;
5159}
@@ -78,6 +86,7 @@ const ListBlueprintsUI = ({
7886 const [ showDeleteConfirm , setShowDeleteConfirm ] = React . useState ( false ) ;
7987 const [ selectedIndex , setSelectedIndex ] = React . useState ( 0 ) ;
8088 const [ showPopup , setShowPopup ] = React . useState ( false ) ;
89+ const [ showPublic , setShowPublic ] = React . useState ( false ) ;
8190 const { navigate } = useNavigation ( ) ;
8291
8392 // Search state
@@ -86,8 +95,8 @@ const ListBlueprintsUI = ({
8695 onSearchClear : ( ) => setSelectedIndex ( 0 ) ,
8796 } ) ;
8897
89- // Calculate overhead for viewport height
90- const overhead = 13 + search . getSearchOverhead ( ) ;
98+ // Calculate overhead for viewport height (14 = breadcrumb + tab bar + search + table header + stats + nav tips + borders)
99+ const overhead = 14 + search . getSearchOverhead ( ) ;
91100 const { viewportHeight, terminalWidth } = useViewportHeight ( {
92101 overhead,
93102 minHeight : 5 ,
@@ -118,50 +127,26 @@ const ListBlueprintsUI = ({
118127 startingAt ?: string ;
119128 includeTotalCount ?: boolean ;
120129 } ) => {
121- const client = getClient ( ) ;
122- const pageBlueprints : BlueprintListItem [ ] = [ ] ;
123-
124- // Build query params
125- const queryParams : Record < string , unknown > = {
130+ const result = await listBlueprintsService ( {
126131 limit : params . limit ,
127- // Only request total_count on first page (expensive for backend)
128- include_total_count : params . includeTotalCount === true ,
129- } ;
130- if ( params . startingAt ) {
131- queryParams . starting_after = params . startingAt ;
132- }
133- if ( search . submittedSearchQuery ) {
134- queryParams . search = search . submittedSearchQuery ;
135- }
136-
137- // Fetch ONE page only
138- const page = ( await client . blueprints . list (
139- queryParams ,
140- ) ) as unknown as BlueprintsCursorIDPage < BlueprintListItem > & {
141- total_count ?: number ;
142- } ;
143-
144- // Extract data and create defensive copies
145- if ( page . blueprints && Array . isArray ( page . blueprints ) ) {
146- page . blueprints . forEach ( ( b : BlueprintListItem ) => {
147- pageBlueprints . push ( {
148- id : b . id ,
149- name : b . name ,
150- status : b . status ,
151- create_time_ms : b . create_time_ms ,
152- } ) ;
153- } ) ;
154- }
132+ startingAfter : params . startingAt ,
133+ search : search . submittedSearchQuery || undefined ,
134+ publicOnly : showPublic ,
135+ includeTotalCount : params . includeTotalCount ,
136+ } ) ;
155137
156- const result = {
157- items : pageBlueprints ,
158- hasMore : page . has_more || false ,
159- totalCount : page . total_count ,
138+ return {
139+ items : result . blueprints . map ( ( b ) => ( {
140+ id : b . id ,
141+ name : b . name ,
142+ status : b . status ,
143+ create_time_ms : b . create_time_ms ,
144+ } ) ) ,
145+ hasMore : result . hasMore ,
146+ totalCount : result . totalCount ,
160147 } ;
161-
162- return result ;
163148 } ,
164- [ search . submittedSearchQuery ] ,
149+ [ showPublic , search . submittedSearchQuery ] ,
165150 ) ;
166151
167152 // Use the shared pagination hook
@@ -187,7 +172,7 @@ const ListBlueprintsUI = ({
187172 ! executingOperation &&
188173 ! showDeleteConfirm &&
189174 ! search . searchMode ,
190- deps : [ search . submittedSearchQuery ] ,
175+ deps : [ search . submittedSearchQuery , showPublic ] ,
191176 } ) ;
192177
193178 // Memoize columns array
@@ -325,11 +310,7 @@ const ListBlueprintsUI = ({
325310 icon : figures . info ,
326311 } ) ;
327312
328- if (
329- blueprint &&
330- ( blueprint . status === "build_complete" ||
331- blueprint . status === "building_complete" )
332- ) {
313+ if ( blueprint && blueprint . status === "build_complete" ) {
333314 operations . push ( {
334315 key : "create_devbox" ,
335316 label : "Create Devbox from Blueprint" ,
@@ -602,8 +583,7 @@ const ListBlueprintsUI = ({
602583 c : ( ) => {
603584 if (
604585 selectedBlueprintItem &&
605- ( selectedBlueprintItem . status === "build_complete" ||
606- selectedBlueprintItem . status === "building_complete" )
586+ selectedBlueprintItem . status === "build_complete"
607587 ) {
608588 setShowPopup ( false ) ;
609589 setSelectedBlueprint ( selectedBlueprintItem ) ;
@@ -657,6 +637,10 @@ const ListBlueprintsUI = ({
657637 right : goToNextPage ,
658638 p : goToPrevPage ,
659639 left : goToPrevPage ,
640+ tab : ( ) => {
641+ setShowPublic ( ( prev ) => ! prev ) ;
642+ setSelectedIndex ( 0 ) ;
643+ } ,
660644 enter : ( ) => {
661645 if ( selectedBlueprintItem ) {
662646 navigate ( "blueprint-detail" , {
@@ -882,6 +866,27 @@ const ListBlueprintsUI = ({
882866 < >
883867 < Breadcrumb items = { [ { label : "Blueprints" , active : true } ] } />
884868
869+ { /* Tab bar */ }
870+ < Box paddingX = { 2 } marginBottom = { 0 } >
871+ < Text
872+ color = { ! showPublic ? colors . primary : colors . textDim }
873+ bold = { ! showPublic }
874+ >
875+ { ! showPublic ? figures . pointer : " " } Custom
876+ </ Text >
877+ < Text color = { colors . textDim } > │ </ Text >
878+ < Text
879+ color = { showPublic ? colors . primary : colors . textDim }
880+ bold = { showPublic }
881+ >
882+ { showPublic ? figures . pointer : " " } Public
883+ </ Text >
884+ < Text color = { colors . textDim } dimColor >
885+ { " " }
886+ (Tab to switch)
887+ </ Text >
888+ </ Box >
889+
885890 { /* Search bar */ }
886891 < SearchBar
887892 searchMode = { search . searchMode }
@@ -899,11 +904,12 @@ const ListBlueprintsUI = ({
899904 data = { blueprints }
900905 keyExtractor = { ( blueprint : BlueprintListItem ) => blueprint . id }
901906 selectedIndex = { selectedIndex }
902- title = { `blueprints[${ totalCount } ]` }
907+ title = { `blueprints[${ totalCount } ] ${ showPublic ? "(public)" : "(custom)" } ` }
903908 columns = { blueprintColumns }
904909 emptyState = {
905910 < Text color = { colors . textDim } >
906- { figures . info } No blueprints found. Try: rli blueprint create
911+ { figures . info } No { showPublic ? "public " : "" } blueprints found
912+ { ! showPublic ? ". Try: rli blueprint create" : "" }
907913 </ Text >
908914 }
909915 />
@@ -1003,6 +1009,7 @@ const ListBlueprintsUI = ({
10031009 } ,
10041010 { key : "Enter" , label : "Details" } ,
10051011 { key : "a" , label : "Actions" } ,
1012+ { key : "Tab" , label : "Switch tab" } ,
10061013 { key : "o" , label : "Browser" } ,
10071014 { key : "/" , label : "Search" } ,
10081015 { key : "Esc" , label : "Back" } ,
0 commit comments