22
33import { useEffect , useState } from 'react'
44import { resolveFileCategory } from '@/components/file-viewer/file-category'
5- import { MENTION_BROWSE_LIMIT , type MentionFamily } from '@/lib/api/contracts/organization-mentions'
5+ import { MENTION_BROWSE_LIMIT } from '@/lib/api/contracts/organization-mentions'
66import type { MothershipResource } from '@/lib/mothership/resources/types'
7- import { workspacePath } from '@/lib/navigation/paths'
8- import { isWorkspaceResourceKind , workspaceResourcePath } from '@/lib/resources'
97import { requestTerminalFocus } from '@/lib/terminal/focus'
108import { terminalIdFromResourceId } from '@/lib/terminal/resource-id'
119import { getFileExtension , getMimeTypeFromExtension } from '@/lib/uploads/utils/file'
@@ -18,8 +16,18 @@ import type {
1816} from '@/hooks/mothership-chat/use-resource-panel'
1917import { useTerminalTabResources } from '@/hooks/mothership-chat/use-terminal-tab-resources'
2018import { useOrganizationMentions } from '@/hooks/queries/organization-mentions'
21- import { grouped , useDashboardsShown , useMentionBrowse } from '@/platform/lib/data/chat-mentions'
22- import type { ChatContext } from '@/platform/lib/data/chat-types'
19+ import {
20+ grouped ,
21+ useDashboardsShown ,
22+ useMentionBrowse ,
23+ useTestsShown ,
24+ } from '@/platform/lib/data/chat-mentions'
25+ import {
26+ isPanelFamily ,
27+ livesInWorkspace ,
28+ pickedResource ,
29+ resourceHref ,
30+ } from '@/platform/lib/data/chat-panel-model'
2331import type {
2432 ChatSource ,
2533 FileStream ,
@@ -28,38 +36,6 @@ import type {
2836} from '@/platform/lib/data/models/chat'
2937import type { PanelPicker , PanelResources } from '@/platform/lib/data/models/chat-panel'
3038import { useOrganization } from '@/platform/lib/data/organization'
31- import { type SurfaceScope , surfaceHref } from '@/platform/lib/data/organization-model'
32-
33- const RESOURCE_KIND : Partial < Record < string , string > > = {
34- knowledgebase : 'knowledge' ,
35- filefolder : 'folder' ,
36- }
37-
38- /**
39- * A workspace resource opens on its workspace page; any other opens beside the organization's Home.
40- * A dashboard links to the Dashboard tab only while that tab is shown.
41- */
42- function resourceHref (
43- scope : SurfaceScope ,
44- resource : MothershipResource ,
45- dashboardsShown : boolean
46- ) : string | undefined {
47- if ( ! resource . workspaceId ) return undefined
48- const kind = RESOURCE_KIND [ resource . type ] ?? resource . type
49- if ( isWorkspaceResourceKind ( kind ) )
50- return workspaceResourcePath ( resource . workspaceId , kind , resource . id )
51- if ( kind === 'dashboard' )
52- return dashboardsShown ? workspacePath ( resource . workspaceId , 'dashboards' ) : undefined
53- if ( kind === 'log' && resource . executionId )
54- return workspacePath ( resource . workspaceId , `logs/${ encodeURIComponent ( resource . executionId ) } ` )
55- return `${ surfaceHref ( scope , '/home' ) } ?${ new URLSearchParams ( { resource : resource . id } ) } `
56- }
57-
58- /** A kind that belongs to one workspace, which a workspace chat's older tabs leave implied. */
59- function livesInWorkspace ( type : string ) : boolean {
60- const kind = RESOURCE_KIND [ type ] ?? type
61- return isWorkspaceResourceKind ( kind ) || kind === 'dashboard' || kind === 'log'
62- }
6339
6440/** A Sources tab lists what its answer cited; the transcript already holds that evidence. */
6541function citedBy (
@@ -184,6 +160,7 @@ export function usePanelResources({
184160 useTerminalTabResources ( panel . desktopTabResourceOptions )
185161 const scope = useOrganization ( )
186162 const dashboardsShown = useDashboardsShown ( )
163+ const testsShown = useTestsShown ( )
187164 const picker = usePanelPicker ( panel , dashboardsShown )
188165 const locked = useAgentFileEditLock ( chat . previewSession ?. status === 'streaming' , chat . isSending )
189166 const resources : PlatformResource [ ] = chat . resources . map ( ( stored ) => {
@@ -196,7 +173,7 @@ export function usePanelResources({
196173 type : resource . type ,
197174 title : resource . title ,
198175 workspaceId : resource . workspaceId ,
199- href : resourceHref ( scope , resource , dashboardsShown ) ,
176+ href : resourceHref ( scope , resource , { dashboards : dashboardsShown , tests : testsShown } ) ,
200177 executionId : resource . executionId ,
201178 active : resource . id === chat . activeResourceId ,
202179 search : resource . search ,
@@ -231,54 +208,6 @@ export function usePanelResources({
231208 }
232209}
233210
234- /**
235- * The families a panel tab shows, as its + picker offers them. Chats, skills and workflow, table
236- * and knowledge base folders open no tab of their own, as in legacy's picker. Dashboards are offered
237- * only while the Dashboard tab is shown, which `grouped` decides.
238- */
239- const PANEL_FAMILIES : ReadonlySet < MentionFamily > = new Set ( [
240- 'dashboards' ,
241- 'tables' ,
242- 'files' ,
243- 'file_folders' ,
244- 'knowledge_bases' ,
245- 'workflows' ,
246- 'logs' ,
247- ] )
248-
249- /** The tab a picked reference opens, or nothing for a kind no tab shows. */
250- function pickedResource (
251- context : ChatContext ,
252- logIds : ReadonlyMap < string , string >
253- ) : MothershipResource | null {
254- const owned = {
255- title : context . label ,
256- workspaceId : 'workspaceId' in context ? context . workspaceId : undefined ,
257- }
258- switch ( context . kind ) {
259- case 'table' :
260- return { ...owned , type : 'table' , id : context . tableId }
261- case 'file' :
262- return { ...owned , type : 'file' , id : context . fileId }
263- case 'filefolder' :
264- return { ...owned , type : 'filefolder' , id : context . fileFolderId }
265- case 'knowledge' :
266- return context . knowledgeId
267- ? { ...owned , type : 'knowledgebase' , id : context . knowledgeId }
268- : null
269- case 'workflow' :
270- return { ...owned , type : 'workflow' , id : context . workflowId }
271- case 'dashboard' :
272- return { ...owned , type : 'dashboard' , id : context . dashboardId }
273- case 'logs' : {
274- const id = context . executionId && logIds . get ( context . executionId )
275- return id ? { ...owned , type : 'log' , id, executionId : context . executionId } : null
276- }
277- default :
278- return null
279- }
280- }
281-
282211/**
283212 * The panel's + picker over the organization's mention reads: each workspace with its most recently
284213 * updated tables, files, knowledge bases, workflows, logs and (while their tab is shown) dashboards,
@@ -299,10 +228,10 @@ function usePanelPicker(
299228 { enabled : enabled && ! browse . searching }
300229 )
301230 const rows = browse . searching
302- ? browse . rows . filter ( ( row ) => PANEL_FAMILIES . has ( row . family ) )
231+ ? browse . rows . filter ( ( row ) => isPanelFamily ( row . family ) )
303232 : [
304233 ...browse . rows . filter ( ( row ) => row . family === 'workspaces' ) ,
305- ...( recent . data ?. rows ?? [ ] ) . filter ( ( row ) => PANEL_FAMILIES . has ( row . family ) ) ,
234+ ...( recent . data ?. rows ?? [ ] ) . filter ( ( row ) => isPanelFamily ( row . family ) ) ,
306235 ]
307236 const logIds = new Map (
308237 rows . flatMap ( ( row ) =>
0 commit comments