1- import { createSpaceState , fetchAccessibleSpaces , fetchThreads } from './fetch.js' ;
1+ import { createSpaceState , fetchThreads } from './fetch.js' ;
22import { loadConfig , saveConfig } from './config.js' ;
3- import { filterItems } from './list-cli.js' ;
3+ import { searchSpaces , resolveSpaceFromInput } from './spaces.js' ;
4+ import { parseSpaceIdFromInput } from './space-id.js' ;
45import { die , info , promptInput , promptSearch , success } from './ui.js' ;
56
7+ const BROWSE_PAGE = 20 ;
8+
69function spaceLabel ( s ) {
710 const name = s . name || '(unnamed)' ;
811 const maps = s . map_count != null ? ` · ${ s . map_count } map(s)` : '' ;
912 const role = s . role ? ` · ${ s . role } ` : '' ;
1013 return name + maps + role ;
1114}
1215
16+ function spaceChoices ( spaces ) {
17+ return spaces . map ( ( s ) => ( {
18+ name : spaceLabel ( s ) ,
19+ value : s ,
20+ description : s . id ,
21+ } ) ) ;
22+ }
23+
1324export async function requireConfig ( ) {
1425 const cfg = loadConfig ( ) ;
1526 if ( ! cfg . apiKey || ! cfg . apiBaseUrl ) {
@@ -18,17 +29,14 @@ export async function requireConfig() {
1829 return cfg ;
1930}
2031
21- function spaceChoices ( spaces , query ) {
22- const list = filterItems ( spaces , query , [ 'name' , 'id' ] ) ;
23- return list . map ( ( s ) => ( {
24- name : spaceLabel ( s ) ,
25- value : s ,
26- description : s . id ,
27- } ) ) ;
28- }
29-
3032function threadChoices ( threads , query ) {
31- const list = filterItems ( threads , query , [ 'name' , 'id' ] ) ;
33+ const terms = ( query || '' ) . trim ( ) . toLowerCase ( ) ;
34+ const list = terms
35+ ? threads . filter ( ( t ) => {
36+ const hay = `${ t . name } ${ t . id } ` . toLowerCase ( ) ;
37+ return hay . includes ( terms ) ;
38+ } )
39+ : threads ;
3240 const out = [
3341 { name : '➕ Create new thread' , value : '__new__' , description : 'New space state' } ,
3442 ] ;
@@ -43,19 +51,45 @@ function threadChoices(threads, query) {
4351 return out ;
4452}
4553
46- export async function pickSpace ( initialFilter = '' , preloadedSpaces = null ) {
54+ async function pickSpaceByLink ( cfg ) {
55+ const raw = await promptInput ( 'Paste Mantis space link or UUID (Enter to browse)' , {
56+ default : '' ,
57+ } ) ;
58+ if ( ! raw ?. trim ( ) ) return null ;
59+ const space = await resolveSpaceFromInput ( cfg . apiBaseUrl , cfg . apiKey , raw ) ;
60+ if ( ! space ) die ( 'Space not found or you do not have access.' ) ;
61+ return saveSpace ( cfg , space ) ;
62+ }
63+
64+ export async function pickSpace ( ) {
4765 const cfg = await requireConfig ( ) ;
48- const spaces = preloadedSpaces ?? ( await fetchAccessibleSpaces ( cfg . apiBaseUrl , cfg . apiKey ) ) ;
49- if ( ! spaces . length ) die ( 'No spaces found.' ) ;
5066
5167 if ( ! process . stdin . isTTY ) {
52- return saveSpace ( cfg , spaces [ 0 ] ) ;
68+ const page = await searchSpaces ( cfg . apiBaseUrl , cfg . apiKey , { limit : 1 } ) ;
69+ if ( ! page . spaces ?. length ) die ( 'No spaces found.' ) ;
70+ return saveSpace ( cfg , page . spaces [ 0 ] ) ;
5371 }
5472
73+ const fromLink = await pickSpaceByLink ( cfg ) ;
74+ if ( fromLink ) return fromLink ;
75+
5576 const picked = await promptSearch (
56- 'Select space (↑↓ move, type to filter)' ,
57- async ( input ) => spaceChoices ( spaces , input || initialFilter ) ,
77+ 'Select space (↑↓ type to search, paste link in previous step)' ,
78+ async ( input ) => {
79+ const id = parseSpaceIdFromInput ( input ) ;
80+ if ( id ) {
81+ const one = await resolveSpaceFromInput ( cfg . apiBaseUrl , cfg . apiKey , input ) ;
82+ return one ? spaceChoices ( [ one ] ) : [ { name : 'No match for that link/id' , value : null , disabled : true } ] ;
83+ }
84+ const page = await searchSpaces ( cfg . apiBaseUrl , cfg . apiKey , {
85+ q : input || '' ,
86+ limit : BROWSE_PAGE ,
87+ offset : 0 ,
88+ } ) ;
89+ return spaceChoices ( page . spaces || [ ] ) ;
90+ } ,
5891 ) ;
92+ if ( ! picked ) die ( 'No space selected.' ) ;
5993 return saveSpace ( cfg , picked ) ;
6094}
6195
0 commit comments