diff --git a/MosaicIQ/src/App.tsx b/MosaicIQ/src/App.tsx index 2291413..faaade1 100644 --- a/MosaicIQ/src/App.tsx +++ b/MosaicIQ/src/App.tsx @@ -293,9 +293,9 @@ function App() { onClearPortfolioAction={handleClearPortfolioAction} resetCommandIndex={resetCommandIndex} portfolioWorkflow={activePortfolioWorkflow} - onOpenResearchContext={(intent) => { - void handleOpenResearch(intent); - }} + researchWorkspaces={researchWorkspaces.workspaces} + activeResearchWorkspaceId={researchWorkspaces.activeWorkspaceId} + onCaptureResearchNote={handleCaptureResearchNote} /> )} diff --git a/MosaicIQ/src/components/Research/ResearchMode.test.tsx b/MosaicIQ/src/components/Research/ResearchMode.test.tsx index c57b75d..135fcf6 100644 --- a/MosaicIQ/src/components/Research/ResearchMode.test.tsx +++ b/MosaicIQ/src/components/Research/ResearchMode.test.tsx @@ -1,6 +1,20 @@ import { describe, expect, it } from 'bun:test'; import { renderToStaticMarkup } from 'react-dom/server'; import { ResearchMode } from './ResearchMode'; +import type { ResearchWorkspace } from '../../types/research'; + +const createWorkspace = (id: string, ticker: string): ResearchWorkspace => ({ + id, + name: `${ticker} Research`, + primaryTicker: ticker, + scope: 'single_company', + stage: 'capture', + defaultView: 'canvas', + pinnedNoteIds: [], + archived: false, + createdAt: '2026-04-09T10:00:00Z', + updatedAt: '2026-04-09T10:00:00Z', +}); describe('ResearchMode', () => { it('renders empty state when no workspace is selected', () => { @@ -19,6 +33,25 @@ describe('ResearchMode', () => { ); expect(html).toContain('No research workspace selected'); - expect(html).toContain('Quick capture'); + expect(html).toContain('Select a research workspace'); + }); + + it('renders the selected workspace details from the active workspace id', () => { + const html = renderToStaticMarkup( + {}} + onEnsureWorkspace={async () => null} + onCaptureResearchNote={async () => { + throw new Error('not used'); + }} + onConsumeNavigationIntent={() => {}} + />, + ); + + expect(html).toContain('NVDA Research'); + expect(html).toContain('NVDA'); }); }); diff --git a/MosaicIQ/src/components/Research/ResearchMode.tsx b/MosaicIQ/src/components/Research/ResearchMode.tsx index e9a10c5..adc9dcc 100644 --- a/MosaicIQ/src/components/Research/ResearchMode.tsx +++ b/MosaicIQ/src/components/Research/ResearchMode.tsx @@ -96,7 +96,12 @@ export const ResearchMode: React.FC = ({ const [isLoadingAuditTrail, setIsLoadingAuditTrail] = useState(false); const [isCaptureModalOpen, setIsCaptureModalOpen] = useState(false); const [isCreateWorkspaceModalOpen, setIsCreateWorkspaceModalOpen] = useState(false); - const [terminalNoteSeed, setTerminalNoteSeed] = useState | null>(null); + const [terminalNoteSeed, setTerminalNoteSeed] = useState & { + contextLabel?: string; + } | null>(null); useEffect(() => { if (!activeWorkspaceId) { @@ -137,6 +142,7 @@ export const ResearchMode: React.FC = ({ setTerminalNoteSeed({ rawText: navigationIntent.terminalNoteSeed.rawText ?? '', ticker: navigationIntent.terminalNoteSeed.ticker ?? '', + contextLabel: navigationIntent.terminalNoteSeed.contextLabel, }); setIsCaptureModalOpen(true); } @@ -152,7 +158,14 @@ export const ResearchMode: React.FC = ({ ]); const projection = projectionState.projection; - const currentWorkspace = projection?.workspace ?? workspaceState.workspace; + const selectedWorkspaceRecord = useMemo( + () => workspaces.find((workspace) => workspace.id === activeWorkspaceId) ?? null, + [activeWorkspaceId, workspaces], + ); + const currentWorkspace = + (projection?.workspace.id === activeWorkspaceId ? projection.workspace : null) ?? + (workspaceState.workspace?.id === activeWorkspaceId ? workspaceState.workspace : null) ?? + selectedWorkspaceRecord; const filteredNotes = useMemo(() => { const notes = projection?.notes ?? []; @@ -482,7 +495,10 @@ export const ResearchMode: React.FC = ({ defaultWorkspaceId={activeWorkspaceId} defaultTicker={currentWorkspace?.primaryTicker ?? navigationIntent?.ticker} defaultRawText={terminalNoteSeed?.rawText} - contextLabel={currentWorkspace ? `${currentWorkspace.primaryTicker} workspace` : 'Quick capture'} + contextLabel={ + terminalNoteSeed?.contextLabel ?? + (currentWorkspace ? `${currentWorkspace.primaryTicker} workspace` : 'Quick capture') + } onWorkspaceChange={onSelectWorkspace} onSubmitCapture={(draft) => onCaptureResearchNote({ diff --git a/MosaicIQ/src/components/Research/ResearchSidebar.tsx b/MosaicIQ/src/components/Research/ResearchSidebar.tsx index 7db1087..fda1b0a 100644 --- a/MosaicIQ/src/components/Research/ResearchSidebar.tsx +++ b/MosaicIQ/src/components/Research/ResearchSidebar.tsx @@ -300,7 +300,7 @@ export const ResearchSidebar: React.FC = ({ }`} title="Switch to Terminal (Cmd+T)" > - + {/* */} Terminal diff --git a/MosaicIQ/src/components/Research/ResearchToolbar.tsx b/MosaicIQ/src/components/Research/ResearchToolbar.tsx index 7c2b295..742fb72 100644 --- a/MosaicIQ/src/components/Research/ResearchToolbar.tsx +++ b/MosaicIQ/src/components/Research/ResearchToolbar.tsx @@ -1,6 +1,5 @@ import React from "react"; import { - ChevronLeft, LayoutGrid, Menu, PanelLeft, diff --git a/MosaicIQ/src/components/Sidebar/Sidebar.tsx b/MosaicIQ/src/components/Sidebar/Sidebar.tsx index 78e2e04..12d5900 100644 --- a/MosaicIQ/src/components/Sidebar/Sidebar.tsx +++ b/MosaicIQ/src/components/Sidebar/Sidebar.tsx @@ -1,16 +1,22 @@ -import React from 'react'; -import { Settings, ChevronLeft, Briefcase, TerminalSquare, NotebookPen } from 'lucide-react'; -import { PortfolioSummary } from './PortfolioSummary'; -import { TickerHistory } from './TickerHistory'; -import { Portfolio } from '../../types/financial'; -import { TickerHistoryEntry } from '../../types/terminal'; +import React from "react"; +import { + Settings, + ChevronLeft, + Briefcase, + TerminalSquare, + NotebookPen, +} from "lucide-react"; +import { PortfolioSummary } from "./PortfolioSummary"; +import { TickerHistory } from "./TickerHistory"; +import { Portfolio } from "../../types/financial"; +import { TickerHistoryEntry } from "../../types/terminal"; interface SidebarProps { onCommand: (command: string) => void; onOpenTerminal: () => void; onOpenResearch: () => void; onOpenSettings: () => void; - activeView: 'terminal' | 'research' | 'settings'; + activeView: "terminal" | "research" | "settings"; isOpen: boolean; onToggle: () => void; tickerHistory: TickerHistoryEntry[]; @@ -53,7 +59,7 @@ export const Sidebar: React.FC = ({
{/* Portfolio icon */}
@@ -168,42 +183,46 @@ export const Sidebar: React.FC = ({
- Press Cmd+B to toggle + Press{" "} + + Cmd+B + {" "} + to toggle
diff --git a/MosaicIQ/src/components/Terminal/CommandInput.tsx b/MosaicIQ/src/components/Terminal/CommandInput.tsx index a1c0bbb..efb33cf 100644 --- a/MosaicIQ/src/components/Terminal/CommandInput.tsx +++ b/MosaicIQ/src/components/Terminal/CommandInput.tsx @@ -408,7 +408,7 @@ export const CommandInput = React.forwardRef {!actionComposerActive && shadowState ? ( -
+
Command Shadow diff --git a/MosaicIQ/src/components/Terminal/Terminal.tsx b/MosaicIQ/src/components/Terminal/Terminal.tsx index 3b23e43..9c1c574 100644 --- a/MosaicIQ/src/components/Terminal/Terminal.tsx +++ b/MosaicIQ/src/components/Terminal/Terminal.tsx @@ -1,10 +1,15 @@ import React from 'react'; +import { ResearchCaptureModal } from '../Research/ResearchCaptureModal'; import { PortfolioWorkflowState } from '../../hooks/usePortfolioWorkflow'; -import { extractResearchContext } from '../../lib/researchContext'; -import { buildTerminalResearchNoteSeed } from '../../lib/terminalResearchNote'; import { - ResearchNavigationIntent, + resolveTerminalCaptureWorkspaceId, + resolveTerminalResearchCaptureIntent, +} from '../../lib/terminalResearchCapture'; +import { + NoteCaptureResult, + ResearchWorkspace, } from '../../types/research'; +import type { ResearchComposerState } from '../../hooks/useResearchComposer'; import { PortfolioAction, PortfolioActionDraft, @@ -29,7 +34,14 @@ interface TerminalProps { onClearPortfolioAction: () => void; resetCommandIndex: () => void; portfolioWorkflow: PortfolioWorkflowState; - onOpenResearchContext: (intent: ResearchNavigationIntent) => void; + researchWorkspaces: ResearchWorkspace[]; + activeResearchWorkspaceId: string | null; + onCaptureResearchNote: (args: { + draft: ResearchComposerState; + fallbackTicker?: string; + explicitWorkspaceId?: string | null; + autoCreateFromTicker?: boolean; + }) => Promise; } export const Terminal: React.FC = ({ @@ -44,66 +56,56 @@ export const Terminal: React.FC = ({ onClearPortfolioAction, resetCommandIndex, portfolioWorkflow, - onOpenResearchContext, + researchWorkspaces, + activeResearchWorkspaceId, + onCaptureResearchNote, }) => { - const researchContext = extractResearchContext(history); - const [terminalNoteSeed, setTerminalNoteSeed] = React.useState<{ + const [terminalCapture, setTerminalCapture] = React.useState<{ key: string; rawText?: string; ticker?: string; - contextLabel: string; + contextLabel?: string; } | null>(null); - const focusResearchCapture = React.useCallback(() => { + const defaultWorkspaceId = React.useMemo( + () => + resolveTerminalCaptureWorkspaceId({ + ticker: terminalCapture?.ticker, + workspaces: researchWorkspaces, + activeWorkspaceId: activeResearchWorkspaceId, + }), + [activeResearchWorkspaceId, researchWorkspaces, terminalCapture?.ticker], + ); + + const closeTerminalCapture = React.useCallback(() => { + setTerminalCapture(null); requestAnimationFrame(() => { - const element = document.getElementById('research-capture-input'); - if (element instanceof HTMLElement) { - element.focus(); - } + inputRef.current?.focusWithText(''); }); - }, []); + }, [inputRef]); const handleTerminalSubmit = React.useCallback( (command: string) => { - const normalized = command.trim().toLowerCase(); - if (normalized === '/notes add') { - setTerminalNoteSeed({ - key: `${Date.now()}-note-add`, - ticker: researchContext?.ticker, - contextLabel: researchContext?.label ?? 'Quick note from terminal', - rawText: '', + const researchIntent = resolveTerminalResearchCaptureIntent(command, history); + if (researchIntent?.terminalNoteSeed) { + setTerminalCapture({ + key: `${Date.now()}-${command.trim().toLowerCase()}`, + rawText: researchIntent.terminalNoteSeed.rawText, + ticker: researchIntent.terminalNoteSeed.ticker ?? researchIntent.ticker, + contextLabel: researchIntent.terminalNoteSeed.contextLabel, }); - focusResearchCapture(); - return; - } - - if (normalized === '/notes current') { - const seed = buildTerminalResearchNoteSeed(history); - setTerminalNoteSeed({ - key: `${Date.now()}-note-current`, - ticker: seed?.ticker ?? researchContext?.ticker, - contextLabel: seed?.contextLabel ?? researchContext?.label ?? 'Current terminal context', - rawText: - seed?.rawText ?? - 'Current terminal context is not available yet.\n\nAnalyst note:', - }); - focusResearchCapture(); return; } onSubmit(command); }, - [focusResearchCapture, history, onSubmit, researchContext?.label, researchContext?.ticker], + [history, onSubmit], ); - const captureContextLabel = terminalNoteSeed?.contextLabel ?? researchContext?.label; - const captureTicker = terminalNoteSeed?.ticker ?? researchContext?.ticker; - const showResearchCapture = Boolean(terminalNoteSeed); - return (
{/* Command Input */} -
+
= ({ portfolioDraft={portfolioWorkflow.draft} lastPortfolioCommand={portfolioWorkflow.lastPortfolioCommand} /> - {showResearchCapture ? ( -
-
-
- Research capture ready - click below to open capture modal -
-
- {captureContextLabel} {captureTicker ? `• ${captureTicker}` : ''} -
-
-
- - -
-
- ) : null}
{/* Terminal Output */} @@ -163,6 +128,24 @@ export const Terminal: React.FC = ({ onRunCommand={onRunCommand} onStartPortfolioAction={onStartPortfolioAction} /> + + + onCaptureResearchNote({ + draft, + fallbackTicker: terminalCapture?.ticker, + autoCreateFromTicker: Boolean(terminalCapture?.ticker), + }) + } + />
); }; diff --git a/MosaicIQ/src/hooks/useResearchProjection.ts b/MosaicIQ/src/hooks/useResearchProjection.ts index 879dcba..4b77de8 100644 --- a/MosaicIQ/src/hooks/useResearchProjection.ts +++ b/MosaicIQ/src/hooks/useResearchProjection.ts @@ -2,6 +2,7 @@ import { startTransition, useDeferredValue, useEffect, + useEffectEvent, useReducer, useRef, } from 'react'; @@ -29,6 +30,7 @@ interface ProjectionState { } type ProjectionAction = + | { type: 'reset' } | { type: 'load_started'; refresh: boolean } | { type: 'load_succeeded'; projection: WorkspaceProjection } | { type: 'load_failed'; error: string } @@ -50,6 +52,8 @@ const projectionReducer = ( action: ProjectionAction, ): ProjectionState => { switch (action.type) { + case 'reset': + return createProjectionState(); case 'load_started': return { ...state, @@ -113,31 +117,51 @@ export const useResearchProjection = ( const [state, dispatch] = useReducer(projectionReducer, undefined, createProjectionState); const deferredView = useDeferredValue(view); const refreshTimeoutRef = useRef(null); + const requestIdRef = useRef(0); + const previousWorkspaceIdRef = useRef(workspaceId); - const loadProjection = useRef(async (refresh: boolean) => { - if (!workspaceId) { + const loadProjection = useEffectEvent(async (refresh: boolean) => { + const nextWorkspaceId = workspaceId; + if (!nextWorkspaceId) { return; } + const requestId = ++requestIdRef.current; dispatch({ type: 'load_started', refresh }); try { const projection = await researchBridge.getWorkspaceProjection({ - workspaceId, + workspaceId: nextWorkspaceId, view: deferredView, }); + if (requestId !== requestIdRef.current) { + return; + } dispatch({ type: 'load_succeeded', projection }); } catch (loadError) { + if (requestId !== requestIdRef.current) { + return; + } dispatch({ type: 'load_failed', error: loadError instanceof Error ? loadError.message : String(loadError), }); } - }).current; + }); useEffect(() => { if (!workspaceId) { + previousWorkspaceIdRef.current = null; + requestIdRef.current += 1; + dispatch({ type: 'reset' }); return; } + + if (previousWorkspaceIdRef.current !== workspaceId) { + previousWorkspaceIdRef.current = workspaceId; + requestIdRef.current += 1; + dispatch({ type: 'reset' }); + } + void loadProjection(false); }, [deferredView, loadProjection, workspaceId]); @@ -149,7 +173,7 @@ export const useResearchProjection = ( }; }, []); - const scheduleRefresh = useRef(() => { + const scheduleRefresh = useEffectEvent(() => { if (refreshTimeoutRef.current != null) { window.clearTimeout(refreshTimeoutRef.current); } @@ -158,7 +182,7 @@ export const useResearchProjection = ( void loadProjection(true); refreshTimeoutRef.current = null; }, 150); - }).current; + }); useEffect(() => { if (!workspaceId) { diff --git a/MosaicIQ/src/hooks/useResearchWorkspace.ts b/MosaicIQ/src/hooks/useResearchWorkspace.ts index dd63a28..67d2c4b 100644 --- a/MosaicIQ/src/hooks/useResearchWorkspace.ts +++ b/MosaicIQ/src/hooks/useResearchWorkspace.ts @@ -28,6 +28,7 @@ export const useResearchWorkspace = ({ } let cancelled = false; + setWorkspace(null); setIsLoadingWorkspace(true); void researchBridge.getResearchWorkspace(workspaceId).then((nextWorkspace) => { if (!cancelled) { diff --git a/MosaicIQ/src/hooks/useResearchWorkspaces.ts b/MosaicIQ/src/hooks/useResearchWorkspaces.ts index 0f1d8a9..380e21b 100644 --- a/MosaicIQ/src/hooks/useResearchWorkspaces.ts +++ b/MosaicIQ/src/hooks/useResearchWorkspaces.ts @@ -47,7 +47,11 @@ export const useResearchWorkspaces = ({ const next = await researchBridge.listResearchWorkspaces(); startTransition(() => { setWorkspaces(next); - setActiveWorkspaceId((current) => current ?? next[0]?.id ?? null); + // Always set activeWorkspaceId if it's null, even if it was previously set + setActiveWorkspaceId((current) => { + if (current) return current; + return next[0]?.id ?? null; + }); }); } catch (loadError) { setError(loadError instanceof Error ? loadError.message : String(loadError)); @@ -60,11 +64,28 @@ export const useResearchWorkspaces = ({ void loadWorkspaces(); }, [loadWorkspaces]); + // Ensure activeWorkspaceId always points to a valid workspace + useEffect(() => { + if (activeWorkspaceId) { + const exists = workspaces.find((w) => w.id === activeWorkspaceId); + if (!exists && workspaces.length > 0) { + startTransition(() => { + setActiveWorkspaceId(workspaces[0].id); + }); + } + } + }, [activeWorkspaceId, workspaces]); + useResearchEventSubscriptions({ onWorkspaceUpdate: ({ workspace }) => { startTransition(() => { setWorkspaces((current) => upsertWorkspaceRecord(current, workspace)); - setActiveWorkspaceId((current) => current ?? workspace.id); + setActiveWorkspaceId((current) => { + // If we have a current active workspace, keep it + if (current) return current; + // Otherwise, use the updated workspace's ID + return workspace.id; + }); }); }, }); diff --git a/MosaicIQ/src/lib/terminalResearchCapture.test.ts b/MosaicIQ/src/lib/terminalResearchCapture.test.ts new file mode 100644 index 0000000..5136dd4 --- /dev/null +++ b/MosaicIQ/src/lib/terminalResearchCapture.test.ts @@ -0,0 +1,124 @@ +import { describe, expect, it } from 'bun:test'; +import { + resolveTerminalCaptureWorkspaceId, + resolveTerminalResearchCaptureIntent, +} from './terminalResearchCapture'; +import type { TerminalEntry } from '../types/terminal'; +import type { ResearchWorkspace } from '../types/research'; + +describe('resolveTerminalResearchCaptureIntent', () => { + const history: TerminalEntry[] = [ + { + id: 'command-1', + type: 'command', + content: '/search NVDA', + timestamp: new Date('2026-04-09T10:00:00Z'), + }, + { + id: 'panel-1', + type: 'panel', + content: { + type: 'company', + data: { + symbol: 'NVDA', + name: 'NVIDIA', + price: 900, + change: 12, + changePercent: 1.35, + marketCap: 2_000_000_000_000, + profile: { + sector: 'Semiconductors', + description: 'GPU leader.', + }, + }, + }, + timestamp: new Date('2026-04-09T10:00:01Z'), + }, + ]; + + it('creates a direct-open research intent for quick notes', () => { + const intent = resolveTerminalResearchCaptureIntent('/notes add', history); + + expect(intent).toEqual({ + ticker: 'NVDA', + terminalNoteSeed: { + rawText: '', + ticker: 'NVDA', + contextLabel: 'NVDA company snapshot', + }, + }); + }); + + it('creates a seeded research intent for current terminal context', () => { + const intent = resolveTerminalResearchCaptureIntent('/notes current', history); + + expect(intent?.ticker).toBe('NVDA'); + expect(intent?.terminalNoteSeed?.ticker).toBe('NVDA'); + expect(intent?.terminalNoteSeed?.contextLabel).toBe('NVDA company context'); + expect(intent?.terminalNoteSeed?.rawText).toContain( + 'Current terminal context: company snapshot for NVDA', + ); + expect(intent?.terminalNoteSeed?.rawText).toContain('Analyst note:'); + }); + + it('prefers a workspace whose primary ticker matches the terminal capture', () => { + const workspaces: ResearchWorkspace[] = [ + { + id: 'workspace-aapl', + name: 'AAPL Research', + primaryTicker: 'AAPL', + scope: 'single_company', + stage: 'capture', + defaultView: 'canvas', + pinnedNoteIds: [], + archived: false, + createdAt: '2026-04-09T10:00:00Z', + updatedAt: '2026-04-09T10:00:00Z', + }, + { + id: 'workspace-nvda', + name: 'NVDA Research', + primaryTicker: 'NVDA', + scope: 'single_company', + stage: 'capture', + defaultView: 'canvas', + pinnedNoteIds: [], + archived: false, + createdAt: '2026-04-09T10:00:00Z', + updatedAt: '2026-04-09T10:00:00Z', + }, + ]; + + expect( + resolveTerminalCaptureWorkspaceId({ + ticker: 'nvda', + workspaces, + activeWorkspaceId: 'workspace-aapl', + }), + ).toBe('workspace-nvda'); + }); + + it('falls back to the active workspace when the terminal capture has no ticker', () => { + const workspaces: ResearchWorkspace[] = [ + { + id: 'workspace-aapl', + name: 'AAPL Research', + primaryTicker: 'AAPL', + scope: 'single_company', + stage: 'capture', + defaultView: 'canvas', + pinnedNoteIds: [], + archived: false, + createdAt: '2026-04-09T10:00:00Z', + updatedAt: '2026-04-09T10:00:00Z', + }, + ]; + + expect( + resolveTerminalCaptureWorkspaceId({ + workspaces, + activeWorkspaceId: 'workspace-aapl', + }), + ).toBe('workspace-aapl'); + }); +}); diff --git a/MosaicIQ/src/lib/terminalResearchCapture.ts b/MosaicIQ/src/lib/terminalResearchCapture.ts new file mode 100644 index 0000000..4a641f1 --- /dev/null +++ b/MosaicIQ/src/lib/terminalResearchCapture.ts @@ -0,0 +1,66 @@ +import type { ResearchNavigationIntent, ResearchWorkspace } from '../types/research'; +import type { TerminalEntry } from '../types/terminal'; +import { extractResearchContext } from './researchContext'; +import { buildTerminalResearchNoteSeed } from './terminalResearchNote'; + +const FALLBACK_CURRENT_CONTEXT_TEXT = + 'Current terminal context is not available yet.\n\nAnalyst note:'; + +export const resolveTerminalResearchCaptureIntent = ( + command: string, + history: TerminalEntry[], +): ResearchNavigationIntent | null => { + const normalized = command.trim().toLowerCase(); + const researchContext = extractResearchContext(history); + + if (normalized === '/notes add') { + return { + ticker: researchContext?.ticker, + terminalNoteSeed: { + rawText: '', + ticker: researchContext?.ticker, + contextLabel: researchContext?.label ?? 'Quick note from terminal', + }, + }; + } + + if (normalized === '/notes current') { + const seed = buildTerminalResearchNoteSeed(history); + const ticker = seed?.ticker ?? researchContext?.ticker; + + return { + ticker, + terminalNoteSeed: { + rawText: seed?.rawText ?? FALLBACK_CURRENT_CONTEXT_TEXT, + ticker, + contextLabel: + seed?.contextLabel ?? researchContext?.label ?? 'Current terminal context', + }, + }; + } + + return null; +}; + +export const resolveTerminalCaptureWorkspaceId = ({ + ticker, + workspaces, + activeWorkspaceId, +}: { + ticker?: string; + workspaces: ResearchWorkspace[]; + activeWorkspaceId?: string | null; +}): string | undefined => { + const normalizedTicker = ticker?.trim().toUpperCase(); + if (normalizedTicker) { + return workspaces.find( + (workspace) => workspace.primaryTicker.trim().toUpperCase() === normalizedTicker, + )?.id; + } + + if (activeWorkspaceId) { + return activeWorkspaceId; + } + + return workspaces.length === 1 ? workspaces[0].id : undefined; +}; diff --git a/MosaicIQ/src/types/research.ts b/MosaicIQ/src/types/research.ts index 5d11b2f..5b973ef 100644 --- a/MosaicIQ/src/types/research.ts +++ b/MosaicIQ/src/types/research.ts @@ -596,6 +596,7 @@ export interface ResearchNavigationIntent { terminalNoteSeed?: { rawText?: string; ticker?: string; + contextLabel?: string; }; }