File size: 12,830 Bytes
4fdaf19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
import React, { Suspense, lazy, useEffect, useMemo, useState } from 'react';
import { Activity, Bot, Braces, Code2, Database, GitBranch, Globe2, KeyRound, Network, ShieldCheck, Sparkles, TerminalSquare } from 'lucide-react';
import LandingTab from './LandingTab';
import SimulationPage from './SimulationPage';
import ChatPage from './ChatPage';
import PersonaBuilderTab from './PersonaBuilderTab';
import ContentCraftTab from './ContentCraftTab';
import BrowserAutomationTab from './BrowserAutomationTab';
import QaTestingTab from './QaTestingTab';
import DataExtractionTab from './DataExtractionTab';
import UiVerificationTab from './UiVerificationTab';
import DeploymentTab from './DeploymentTab';
// Lazy — pulls the heavy plotly chunk only when a graph view is opened.
const RealNetworkGraph = lazy(() => import('./RealNetworkGraph'));
import SubViewSlider, { SubView } from './SubViewSlider';
import { getApiSpec, publishTabEvent } from '../services/tabBus';
import MindwalkGraphView from './MindwalkGraphView';
import PersonaHubTab from './PersonaHubTab';
import UxMentorChain from './UxMentorChain';
import JourneyConsole from './JourneyConsole';
import RenderFlow from './RenderFlow';
import DevSteeringConsole from './DevSteeringConsole';
import SocialMirror from './SocialMirror';
import AnalysisGraph from './AnalysisGraph';
import AccountPanel from './AccountPanel';
import { TAB_PROMISES } from '../branding';

export type MainTabId = 'usersync' | 'nova-act' | 'datahub' | 'oasis' | 'graph' | 'dev';

interface MainTabViewsProps {
  tab: MainTabId;
  routeView?: string;
  onNavigate?: (tab: string, view?: string) => void;
  onOpenGuide: () => void;
  user: any;
  onLogin: () => void;
  onLogout: () => void;
  simulationResult: any;
  setSimulationResult: (result: any) => void;
}

const viewsByTab: Record<MainTabId, SubView[]> = {
  usersync: [
    { id: 'overview', label: 'Overview', description: 'Landing, use cases, pricing, and product proof.' },
    { id: 'simulation', label: 'Simulation', description: 'Run audience sync and inspect results.' },
    { id: 'personas', label: 'Personas', description: 'Sort and build focus-group personas.' },
    { id: 'content', label: 'Content Craft', description: 'Generate and evaluate variants.' },
  ],
  'nova-act': [
    { id: 'console', label: 'Nova Console', description: 'Run persona-steered journeys, watch steps and thinking live.' },
    { id: 'studio', label: 'Nova Studio', description: 'Amazon Nova-inspired command surface.' },
    { id: 'browser', label: 'Browser Act', description: 'Headful web action session.' },
    { id: 'qa', label: 'QA Flows', description: 'Reusable SDK test paths.' },
    { id: 'verify', label: 'UI Verify', description: 'MCP style visual and DOM checks.' },
    { id: 'ux-chain', label: 'UX Chain', description: 'Screenshot + heatmap, problem, and re-rendered solution.' },
    { id: 'mindwalk', label: 'Mindwalk + OmniParser', description: 'Use OmniParser for UI-to-LLM prompts and Mindwalk for navigation memory.' },
  ],
  datahub: [
    { id: 'render', label: 'Render Flow', description: 'Verify sources, render personas one by one, read their steering.' },
    { id: 'extract', label: 'Extract', description: 'Collect structured records.' },
    { id: 'warehouse', label: 'Warehouse', description: 'Saved artifacts and tab handoff.' },
    { id: 'deploy', label: 'Deploy', description: 'Production export and integrations.' },
  ],
  oasis: [
    { id: 'mirror', label: 'Social Mirror', description: 'Living network of personas; scrub the animation.' },
    { id: 'network', label: 'Oasis Network', description: 'Community, identity, and auth mesh.' },
    { id: 'trust', label: 'Trust Layer', description: 'HF login is shared across every tab.' },
  ],
  graph: [
    { id: 'analysis', label: 'Action Trace', description: 'Similarity blend, decisions, and graph answers.' },
    { id: 'live', label: 'Live Graph', description: 'Network topology view.' },
    { id: 'signals', label: 'Signals', description: 'Sorted persona and datahub events.' },
    { id: 'mindwalk', label: 'Mindwalk + OmniParser', description: 'Graph tabs while OmniParser describes UI controls for LLM action.' },
  ],
  dev: [
    { id: 'steering', label: 'Steering Console', description: 'Layers 1/2, derivation rulesets, and audited corrections.' },
    { id: 'account', label: 'Account', description: 'Credits, capabilities, and jobs.' },
    { id: 'api', label: 'API Docs', description: 'OpenAPI/Swagger-compatible documentation.' },
    { id: 'events', label: 'Tab Bus', description: 'FastAPI-compatible tab communication contract.' },
    { id: 'status', label: 'Status', description: 'Delivery checklist and runtime health.' },
  ],
};

const NovaStudio = () => (
  <div className="mx-auto max-w-7xl px-6 py-12">
    <div className="overflow-hidden rounded-[2rem] border border-violet-500/20 bg-[radial-gradient(circle_at_top_left,_rgba(20,184,166,0.25),_transparent_35%),radial-gradient(circle_at_top_right,_rgba(124,58,237,0.3),_transparent_30%),#050505] p-8 shadow-2xl">
      <div className="flex flex-col gap-8 lg:flex-row lg:items-center lg:justify-between">
        <div className="max-w-3xl">
          <div className="mb-5 inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-4 py-2 text-xs font-bold uppercase tracking-[0.24em] text-teal-200">
            <Sparkles size={14} /> Nova Act command plane
          </div>
          <h1 className="text-4xl font-semibold tracking-tight md:text-6xl">Act, reason, browse, and verify from one tab.</h1>
          <p className="mt-5 text-lg text-gray-300">A Nova-inspired orchestration surface that bundles browser automation, QA, visual verification, content tools, and backend app actions behind one signed-in HF session.</p>
        </div>
        <div className="grid min-w-[280px] gap-3 rounded-3xl border border-white/10 bg-black/50 p-4 font-mono text-xs text-gray-300">
          {['/api/nova-act/session', '/api/omniparser/parse', '/api/nova-act/limitations', '/api/openapi.json'].map((endpoint) => (
            <div key={endpoint} className="flex items-center gap-3 rounded-2xl bg-white/5 p-3"><TerminalSquare size={14} className="text-teal-300" />{endpoint}</div>
          ))}
        </div>
      </div>
      <div className="mt-10 grid gap-4 md:grid-cols-4">
        {[
          ['Multimodal actions', Bot], ['Browser runtime', Globe2], ['Shared auth', KeyRound], ['Verified delivery', ShieldCheck],
        ].map(([label, Icon]: any) => <div key={label} className="rounded-2xl border border-white/10 bg-white/[0.04] p-5"><Icon className="mb-4 text-teal-300" /><div className="font-bold">{label}</div></div>)}
      </div>
    </div>
  </div>
);

const SimplePanel = ({ title, icon: Icon, children }: { title: string; icon: any; children: React.ReactNode }) => (
  <div className="mx-auto max-w-7xl px-6 py-12">
    <div className="rounded-3xl border border-gray-800 bg-[#0b0b0b] p-8">
      <h2 className="mb-4 flex items-center gap-3 text-3xl font-bold"><Icon className="text-teal-400" />{title}</h2>
      <div className="text-gray-300">{children}</div>
    </div>
  </div>
);

const GraphPanel = () => <div className="mx-auto h-[680px] max-w-7xl px-6 py-12"><div className="h-full rounded-3xl border border-gray-800 bg-[#050505] p-4"><Suspense fallback={<div className="skeleton h-full w-full rounded-2xl" />}><RealNetworkGraph /></Suspense></div></div>;

const DevApiDocs = () => {
  const [spec, setSpec] = useState<any>(null);
  useEffect(() => { getApiSpec().then((result) => result.ok && setSpec(result.data)); }, []);
  return <SimplePanel title="Developer API Documentation" icon={Code2}><p className="mb-4">FastAPI-compatible OpenAPI is exposed in-app so every tab can discover backend actions.</p><pre className="max-h-[520px] overflow-auto rounded-2xl border border-gray-800 bg-black p-4 text-xs text-teal-100">{JSON.stringify(spec || { loading: '/api/openapi.json' }, null, 2)}</pre></SimplePanel>;
};

const DEFAULT_VIEW: Record<MainTabId, string> = {
  usersync: 'overview', 'nova-act': 'console', datahub: 'render', oasis: 'mirror', graph: 'analysis', dev: 'steering',
};

const MainTabViews: React.FC<MainTabViewsProps> = (props) => {
  const tabViews = viewsByTab[props.tab];
  // URL is the source of truth for the view; fall back to the tab default.
  const routed = props.routeView && tabViews.some((v) => v.id === props.routeView) ? props.routeView : DEFAULT_VIEW[props.tab];
  const [activeView, setLocalView] = useState<string>(routed);
  const [direction, setDirection] = useState<1 | -1>(1);

  // Sync from the URL when it changes (back/forward, deep link, tab switch).
  useEffect(() => {
    setLocalView(routed);
  }, [routed]);

  const setActiveView = (view: string) => {
    const from = tabViews.findIndex((v) => v.id === activeView);
    const to = tabViews.findIndex((v) => v.id === view);
    setDirection(to >= from ? 1 : -1);
    setLocalView(view);
    props.onNavigate?.(props.tab, view); // deep-linkable
    publishTabEvent({ source: 'frontend', target: props.tab, action: 'view.changed', payload: { view } });
  };
  const content = useMemo(() => {
    if (props.tab === 'usersync') {
      if (activeView === 'simulation') return <SimulationPage onBack={() => setActiveView('overview')} onOpenChat={() => setActiveView('content')} onOpenGuide={props.onOpenGuide} user={props.user} onLogin={props.onLogin} onLogout={props.onLogout} simulationResult={props.simulationResult} setSimulationResult={props.setSimulationResult} />;
      if (activeView === 'personas') return <><PersonaHubTab /><PersonaBuilderTab /></>;
      if (activeView === 'content') return <><ChatPage onBack={() => setActiveView('simulation')} simulationResult={props.simulationResult} setSimulationResult={props.setSimulationResult} /><ContentCraftTab /></>;
      return <LandingTab onTabChange={() => setActiveView('simulation')} />;
    }
    if (props.tab === 'nova-act') return activeView === 'console' ? <JourneyConsole /> : activeView === 'studio' ? <NovaStudio /> : activeView === 'browser' ? <BrowserAutomationTab /> : activeView === 'qa' ? <QaTestingTab /> : activeView === 'ux-chain' ? <UxMentorChain /> : activeView === 'mindwalk' ? <MindwalkGraphView activeTab="nova-act" activeView="mindwalk" /> : <UiVerificationTab />;
    if (props.tab === 'datahub') return activeView === 'render' ? <RenderFlow /> : activeView === 'extract' ? <DataExtractionTab /> : activeView === 'deploy' ? <DeploymentTab /> : <SimplePanel title="DataHub Warehouse" icon={Database}>Saved records, simulation outputs, and browser traces are sorted here before being published to downstream tabs over <code>/api/tabs/events</code>.</SimplePanel>;
    if (props.tab === 'oasis') return activeView === 'mirror' ? <SocialMirror /> : <SimplePanel title={activeView === 'trust' ? 'Shared HF Trust Layer' : 'Oasis Network'} icon={activeView === 'trust' ? KeyRound : Network}>One Hugging Face login cookie is valid across UserSync, Nova Act, DataHub, Oasis, Graph, and Dev. Backend routes resolve the same user with <code>/api/user</code>.</SimplePanel>;
    if (props.tab === 'graph') return activeView === 'analysis' ? <AnalysisGraph /> : activeView === 'live' ? <GraphPanel /> : activeView === 'mindwalk' ? <MindwalkGraphView activeTab="graph" activeView="mindwalk" /> : <SimplePanel title="Graph Signals" icon={GitBranch}>Subview events, persona cohorts, and DataHub artifacts are sorted by source tab and action type for graph exploration.</SimplePanel>;
    if (props.tab === 'dev') return activeView === 'steering' ? <DevSteeringConsole /> : activeView === 'account' ? <AccountPanel /> : activeView === 'api' ? <DevApiDocs /> : activeView === 'events' ? <SimplePanel title="Tab Bus Contract" icon={Braces}>Publish tab communication with <code>POST /api/tabs/events</code>. Read the queue with <code>GET /api/tabs/events</code>. The schema is visible in API docs.</SimplePanel> : <SimplePanel title="Runtime Status" icon={Activity}>Frontend build, Express compatibility API, and FastAPI reference backend are included for delivery.</SimplePanel>;
  }, [activeView, props]);

  return (
    <>
      {/* Per-tab service promise (branding.ts). */}
      {TAB_PROMISES[props.tab] && (
        <div className="mx-auto max-w-[1600px] px-4 pt-4">
          <p className="text-xs font-medium text-gray-500">{TAB_PROMISES[props.tab]}</p>
        </div>
      )}
      <SubViewSlider views={viewsByTab[props.tab]} activeView={activeView} onViewChange={setActiveView} />
      {/* Keyed on tab:view so each switch re-mounts with a directional slide. */}
      <div key={`${props.tab}:${activeView}`} className={direction === 1 ? 'animate-view-right' : 'animate-view-left'}>
        {content}
      </div>
    </>
  );
};

export default MainTabViews;