1
1
'use server' ;
2
2
3
- import { RevisionPage , SearchAIAnswer , SearchPageResult , SiteSpace , Space } from '@gitbook/api' ;
3
+ import {
4
+ RevisionPage ,
5
+ SearchAIAnswer ,
6
+ SearchPageResult ,
7
+ SiteSpace ,
8
+ SiteStructure ,
9
+ Space ,
10
+ } from '@gitbook/api' ;
4
11
import * as React from 'react' ;
5
12
import { assert } from 'ts-essentials' ;
6
13
@@ -75,22 +82,7 @@ async function searchSiteContent(args: {
75
82
] ) ;
76
83
const siteStructure = siteData ?. structure ;
77
84
78
- const siteSpaces = siteStructure
79
- ? siteStructure . type === 'siteSpaces'
80
- ? siteStructure . structure
81
- : getSiteStructureSections ( siteStructure ) . reduce < SiteSpace [ ] > ( ( prev , section ) => {
82
- const sectionSiteSpaces = section . siteSpaces . map ( ( siteSpace ) => ( {
83
- ...siteSpace ,
84
- space : {
85
- ...siteSpace . space ,
86
- title : section . title || siteSpace . space . title ,
87
- } ,
88
- } ) ) ;
89
-
90
- prev . push ( ...sectionSiteSpaces ) ;
91
- return prev ;
92
- } , [ ] )
93
- : null ;
85
+ const siteSpaces = siteStructure ? extractSiteStructureSiteSpaces ( siteStructure ) : null ;
94
86
95
87
if ( siteSpaces ) {
96
88
// We are searching all of this Site's content
@@ -159,13 +151,19 @@ export async function searchSiteSpaceContent(
159
151
/**
160
152
* Server action to ask a question in a space.
161
153
*/
162
- export const streamAskQuestion = streamResponse ( async function * (
163
- organizationId : string ,
164
- siteId : string ,
165
- siteSpaceId : string | null ,
166
- question : string ,
167
- ) {
168
- const apiCtx = await api . api ( ) ;
154
+ export const streamAskQuestion = streamResponse ( async function * ( {
155
+ pointer,
156
+ question,
157
+ } : {
158
+ pointer : api . SiteContentPointer ;
159
+ question : string ;
160
+ } ) {
161
+ const { organizationId, siteId, siteSpaceId } = pointer ;
162
+ const [ apiCtx , siteData ] = await Promise . all ( [ api . api ( ) , api . getSiteData ( pointer ) ] ) ;
163
+ const siteSpaces = siteData ?. structure
164
+ ? extractSiteStructureSiteSpaces ( siteData . structure )
165
+ : null ;
166
+
169
167
const stream = apiCtx . client . orgs . streamAskInSite (
170
168
organizationId ,
171
169
siteId ,
@@ -222,7 +220,7 @@ export const streamAskQuestion = streamResponse(async function* (
222
220
return map ;
223
221
} , new Map < string , RevisionPage [ ] > ( ) ) ;
224
222
} ) ;
225
- yield await transformAnswer ( chunk . answer , pages ) ;
223
+ yield await transformAnswer ( { answer : chunk . answer , spacePages : pages , siteSpaces } ) ;
226
224
}
227
225
} ) ;
228
226
@@ -237,15 +235,19 @@ export const streamRecommendedQuestions = streamResponse(async function* (
237
235
const stream = apiCtx . client . orgs . streamRecommendedQuestionsInSite ( organizationId , siteId ) ;
238
236
239
237
for await ( const chunk of stream ) {
240
- console . log ( 'got question' , chunk ) ;
241
238
yield chunk ;
242
239
}
243
240
} ) ;
244
241
245
- async function transformAnswer (
246
- answer : SearchAIAnswer ,
247
- spacePages : Map < string , RevisionPage [ ] > ,
248
- ) : Promise < AskAnswerResult > {
242
+ async function transformAnswer ( {
243
+ answer,
244
+ spacePages,
245
+ siteSpaces,
246
+ } : {
247
+ answer : SearchAIAnswer ;
248
+ spacePages : Map < string , RevisionPage [ ] > ;
249
+ siteSpaces : SiteSpace [ ] | null ;
250
+ } ) : Promise < AskAnswerResult > {
249
251
const sources = (
250
252
await Promise . all (
251
253
answer . sources . map ( async ( source ) => {
@@ -264,10 +266,19 @@ async function transformAnswer(
264
266
return null ;
265
267
}
266
268
269
+ // Find the siteSpace in case it is nested in a site section so we can resolve the URL appropriately
270
+ const spaceURL = siteSpaces ?. find (
271
+ ( siteSpace ) => siteSpace . space . id === source . space ,
272
+ ) ?. urls . published ;
273
+
274
+ const href = spaceURL
275
+ ? await getURLWithSections ( page . page . path , spaceURL )
276
+ : await getPageHref ( pages , page . page ) ;
277
+
267
278
return {
268
279
id : source . page ,
269
280
title : page . page . title ,
270
- href : await getPageHref ( pages , page . page ) ,
281
+ href,
271
282
} ;
272
283
} ) ,
273
284
)
@@ -299,28 +310,12 @@ async function transformSectionsAndPage(args: {
299
310
} ) : Promise < [ ComputedPageResult , ComputedSectionResult [ ] ] > {
300
311
const { item, space, spaceURL } = args ;
301
312
302
- // Resolve a relative path to an absolute URL
303
- // if the search result is relative to another space, we use the space URL
304
- const getURL = async ( path : string , spaceURL ?: string ) => {
305
- if ( spaceURL ) {
306
- if ( ! spaceURL . endsWith ( '/' ) ) {
307
- spaceURL += '/' ;
308
- }
309
- if ( path . startsWith ( '/' ) ) {
310
- path = path . slice ( 1 ) ;
311
- }
312
- return spaceURL + path ;
313
- } else {
314
- return getAbsoluteHref ( path ) ;
315
- }
316
- } ;
317
-
318
313
const sections = await Promise . all (
319
314
item . sections ?. map < Promise < ComputedSectionResult > > ( async ( section ) => ( {
320
315
type : 'section' ,
321
316
id : item . id + '/' + section . id ,
322
317
title : section . title ,
323
- href : await getURL ( section . path , spaceURL ) ,
318
+ href : await getURLWithSections ( section . path , spaceURL ) ,
324
319
body : section . body ,
325
320
} ) ) ?? [ ] ,
326
321
) ;
@@ -329,7 +324,7 @@ async function transformSectionsAndPage(args: {
329
324
type : 'page' ,
330
325
id : item . id ,
331
326
title : item . title ,
332
- href : await getURL ( item . path , spaceURL ) ,
327
+ href : await getURLWithSections ( item . path , spaceURL ) ,
333
328
spaceTitle : space ?. title ,
334
329
} ;
335
330
@@ -355,3 +350,39 @@ async function transformPageResult(item: SearchPageResult, space?: Space) {
355
350
356
351
return [ page , ...sections ] ;
357
352
}
353
+
354
+ // Resolve a relative path to an absolute URL
355
+ // if the search result is relative to another space, we use the space URL
356
+ async function getURLWithSections ( path : string , spaceURL ?: string ) {
357
+ if ( spaceURL ) {
358
+ if ( ! spaceURL . endsWith ( '/' ) ) {
359
+ spaceURL += '/' ;
360
+ }
361
+ if ( path . startsWith ( '/' ) ) {
362
+ path = path . slice ( 1 ) ;
363
+ }
364
+ return spaceURL + path ;
365
+ } else {
366
+ return getAbsoluteHref ( path ) ;
367
+ }
368
+ }
369
+
370
+ /*
371
+ * Gets all site spaces, in a site structure and overrides the title
372
+ */
373
+ function extractSiteStructureSiteSpaces ( siteStructure : SiteStructure ) {
374
+ return siteStructure . type === 'siteSpaces'
375
+ ? siteStructure . structure
376
+ : getSiteStructureSections ( siteStructure ) . reduce < SiteSpace [ ] > ( ( prev , section ) => {
377
+ const sectionSiteSpaces = section . siteSpaces . map ( ( siteSpace ) => ( {
378
+ ...siteSpace ,
379
+ space : {
380
+ ...siteSpace . space ,
381
+ title : section . title || siteSpace . space . title ,
382
+ } ,
383
+ } ) ) ;
384
+
385
+ prev . push ( ...sectionSiteSpaces ) ;
386
+ return prev ;
387
+ } , [ ] ) ;
388
+ }
0 commit comments