AUXteam commited on
Commit
d27a461
·
verified ·
1 Parent(s): 463943e

Upload folder using huggingface_hub

Browse files
.github/workflows/sync-to-hf.yml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Sync to Hugging Face hub
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ workflow_dispatch:
6
+
7
+ jobs:
8
+ sync-to-hub:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v3
12
+ with:
13
+ fetch-depth: 0
14
+ lfs: true
15
+ - name: Push to hub
16
+ env:
17
+ HF_TOKEN: ${{ secrets.HF_TOKEN }}
18
+ run: |
19
+ git push --force https://AUXteam:$HF_TOKEN@huggingface.co/spaces/AUXteam/UserSyncInterface main
.gitignore ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+
15
+ # Editor directories and files
16
+ .vscode/*
17
+ !.vscode/extensions.json
18
+ .idea
19
+ .DS_Store
20
+ *.suo
21
+ *.ntvs*
22
+ *.njsproj
23
+ *.sln
24
+ *.sw?
App.tsx CHANGED
@@ -19,8 +19,21 @@ import ChatPage from './components/ChatPage';
19
  function App() {
20
  const [currentView, setCurrentView] = useState<'landing' | 'simulation' | 'conversation' | 'chat'>('simulation');
21
  const [user, setUser] = useState<any>(null);
 
 
22
 
23
  useEffect(() => {
 
 
 
 
 
 
 
 
 
 
 
24
  const handleAuth = async () => {
25
  try {
26
  const oauthResult = await oauthHandleRedirectIfPresent();
@@ -36,7 +49,22 @@ function App() {
36
  }, []);
37
 
38
  const loginWithHF = async () => {
39
- window.location.href = await oauthLoginUrl();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  };
41
 
42
  const startSimulation = () => {
@@ -68,6 +96,8 @@ function App() {
68
  onOpenChat={openChat}
69
  user={user}
70
  onLogin={loginWithHF}
 
 
71
  />
72
  );
73
  }
@@ -77,7 +107,13 @@ function App() {
77
  }
78
 
79
  if (currentView === 'chat') {
80
- return <ChatPage onBack={goBackToSimulation} />;
 
 
 
 
 
 
81
  }
82
 
83
  return (
 
19
  function App() {
20
  const [currentView, setCurrentView] = useState<'landing' | 'simulation' | 'conversation' | 'chat'>('simulation');
21
  const [user, setUser] = useState<any>(null);
22
+ const [config, setConfig] = useState<{ clientId?: string; scopes?: string }>({});
23
+ const [simulationResult, setSimulationResult] = useState<any>(null);
24
 
25
  useEffect(() => {
26
+ const fetchConfig = async () => {
27
+ try {
28
+ const response = await fetch('/api/config');
29
+ const data = await response.json();
30
+ setConfig(data);
31
+ } catch (error) {
32
+ console.error("Failed to fetch config:", error);
33
+ }
34
+ };
35
+ fetchConfig();
36
+
37
  const handleAuth = async () => {
38
  try {
39
  const oauthResult = await oauthHandleRedirectIfPresent();
 
49
  }, []);
50
 
51
  const loginWithHF = async () => {
52
+ try {
53
+ const url = await oauthLoginUrl({
54
+ clientId: config.clientId,
55
+ scopes: config.scopes,
56
+ redirectUrl: window.location.origin + "/"
57
+ });
58
+ // Use window.top for redirecting out of the Space iframe
59
+ if (window.top) {
60
+ window.top.location.href = url;
61
+ } else {
62
+ window.location.href = url;
63
+ }
64
+ } catch (error) {
65
+ console.error("Login error:", error);
66
+ alert("Failed to initiate login. See console for details.");
67
+ }
68
  };
69
 
70
  const startSimulation = () => {
 
96
  onOpenChat={openChat}
97
  user={user}
98
  onLogin={loginWithHF}
99
+ simulationResult={simulationResult}
100
+ setSimulationResult={setSimulationResult}
101
  />
102
  );
103
  }
 
107
  }
108
 
109
  if (currentView === 'chat') {
110
+ return (
111
+ <ChatPage
112
+ onBack={goBackToSimulation}
113
+ simulationResult={simulationResult}
114
+ setSimulationResult={setSimulationResult}
115
+ />
116
+ );
117
  }
118
 
119
  return (
components/ChatPage.tsx CHANGED
@@ -1,11 +1,13 @@
1
 
2
- import React, { useState, useRef } from 'react';
3
- import { X, ClipboardList, Linkedin, Instagram, Mail, Layout, Edit3, MonitorPlay, Lightbulb, Image, Plus, Sparkles, Zap, AlertCircle, Video, Megaphone, Link as LinkIcon, Loader2, RefreshCw } from 'lucide-react';
4
  import { GradioService } from '../services/gradioService';
5
 
6
  // --- Types ---
7
  interface ChatPageProps {
8
  onBack: () => void;
 
 
9
  }
10
 
11
  // --- Sub-components (Modular Structure) ---
@@ -43,6 +45,7 @@ const CategoryCard: React.FC<{ title: string; options: { label: string; icon: Re
43
 
44
  const ChatInput: React.FC<{ onSimulate: (msg: string) => void; onHelpMeCraft: (msg: string) => void; isSimulating: boolean }> = ({ onSimulate, onHelpMeCraft, isSimulating }) => {
45
  const [message, setMessage] = useState('');
 
46
  const fileInputRef = useRef<HTMLInputElement>(null);
47
 
48
  const handleUploadClick = () => {
@@ -50,16 +53,34 @@ const ChatInput: React.FC<{ onSimulate: (msg: string) => void; onHelpMeCraft: (m
50
  };
51
 
52
  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
53
- const file = e.target.files?.[0];
54
- if (file) {
55
- console.log("Selected file:", file.name);
56
- // In a real app, you'd handle the upload here
 
57
  }
58
  };
59
 
 
 
 
 
60
  return (
61
  <div className="border-t border-gray-800 pt-6 mt-4 bg-[#0a0a0a] px-6 pb-8 md:pb-10 absolute bottom-0 left-0 right-0 z-20 shadow-[0_-20px_50px_rgba(0,0,0,0.8)]">
62
  <div className="max-w-5xl mx-auto space-y-4">
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  <textarea
64
  className="w-full h-24 bg-black border border-gray-800 text-gray-200 placeholder-gray-600 p-4 rounded-2xl resize-none focus:outline-none focus:border-gray-600 focus:ring-1 focus:ring-gray-600 transition-all text-sm leading-relaxed"
65
  placeholder="Paste your brand narrative or campaign copy here"
@@ -77,10 +98,11 @@ const ChatInput: React.FC<{ onSimulate: (msg: string) => void; onHelpMeCraft: (m
77
  onChange={handleFileChange}
78
  className="hidden"
79
  accept="image/*"
 
80
  />
81
  <ChatButton
82
- label="Upload Images"
83
- icon={<Image size={14} />}
84
  className="h-fit"
85
  onClick={handleUploadClick}
86
  />
@@ -107,39 +129,83 @@ const ChatInput: React.FC<{ onSimulate: (msg: string) => void; onHelpMeCraft: (m
107
 
108
  // --- Main Page Component ---
109
 
110
- const ChatPage: React.FC<ChatPageProps> = ({ onBack }) => {
111
  const [showNotification, setShowNotification] = useState(false);
112
  const [isSimulating, setIsSimulating] = useState(false);
113
- const [simulationResult, setSimulationResult] = useState<string | null>(null);
114
 
115
- const handleSimulate = (msg: string) => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  setIsSimulating(true);
117
  setShowNotification(true);
 
118
 
119
- // Simulate API call
120
- setTimeout(() => {
121
  setIsSimulating(false);
122
- setSimulationResult("Please wait, the results will show here. Click Refresh to gather results from the API.");
123
- }, 2000);
 
 
 
 
 
 
 
 
 
 
124
  };
125
 
126
  const handleRefresh = async () => {
127
  setIsSimulating(true);
128
  try {
129
- // In a real scenario, we would poll the Gradio API for results
130
- // result = await GradioService.simulate(...)
131
- setTimeout(() => {
132
- setIsSimulating(false);
133
- setSimulationResult("Final Simulation Results: The content resonated well with 85% of the target audience. Key feedback: 'Clearer value proposition needed in the header'.");
134
- }, 1500);
 
135
  } catch (error) {
136
  setIsSimulating(false);
 
 
 
 
137
  }
138
  };
139
 
140
  const handleHelpMeCraft = async (msg: string) => {
141
- const crafted = await GradioService.helpMeCraft(msg);
142
- alert("Help Me Craft suggestion: " + crafted);
 
 
 
 
 
 
 
 
143
  };
144
 
145
  const categories = {
@@ -175,9 +241,12 @@ const ChatPage: React.FC<ChatPageProps> = ({ onBack }) => {
175
  <div className="w-8 h-8 flex items-center justify-center bg-gray-800 rounded-lg text-white font-bold">Λ</div>
176
  <h2 className="text-xl font-medium tracking-tight">New Simulation</h2>
177
  </div>
178
- <button onClick={onBack} className="p-2 text-gray-500 hover:text-white hover:bg-gray-900 rounded-full transition-colors">
179
- <X size={24} />
180
- </button>
 
 
 
181
  </div>
182
 
183
  {/* Notification Banner */}
@@ -188,13 +257,21 @@ const ChatPage: React.FC<ChatPageProps> = ({ onBack }) => {
188
  <AlertCircle size={18} className="text-green-400" />
189
  </div>
190
  <div className="flex-1">
191
- <h4 className="font-semibold text-green-300 mb-1">Simulation Initiated</h4>
192
  <p className="text-sm text-green-200/70 leading-relaxed">
193
- The simulation has started. This process can take up to <strong className="text-white">30 minutes</strong>. The results will show here when ready.
194
  </p>
195
  {simulationResult && (
196
  <div className="mt-4 p-3 bg-black/40 rounded-xl border border-green-500/20 text-xs text-green-200 flex flex-col gap-3">
197
- <p>{simulationResult}</p>
 
 
 
 
 
 
 
 
198
  <button
199
  onClick={handleRefresh}
200
  disabled={isSimulating}
@@ -214,7 +291,7 @@ const ChatPage: React.FC<ChatPageProps> = ({ onBack }) => {
214
  )}
215
 
216
  {/* Scrollable Content Area */}
217
- <div className="flex-1 overflow-y-auto custom-scrollbar p-6 md:p-8 pb-80"> {/* Large bottom padding for fixed input */}
218
  <div className="max-w-5xl mx-auto">
219
  <h1 className="text-2xl md:text-3xl font-semibold text-center mb-12 mt-4 md:mt-8">What would you like to simulate?</h1>
220
 
 
1
 
2
+ import React, { useState, useRef, useEffect } from 'react';
3
+ import { X, ClipboardList, Linkedin, Instagram, Mail, Layout, Edit3, MonitorPlay, Lightbulb, Image, Plus, Sparkles, Zap, AlertCircle, Video, Megaphone, Link as LinkIcon, Loader2, RefreshCw, CheckCircle2 } from 'lucide-react';
4
  import { GradioService } from '../services/gradioService';
5
 
6
  // --- Types ---
7
  interface ChatPageProps {
8
  onBack: () => void;
9
+ simulationResult: any;
10
+ setSimulationResult: (res: any) => void;
11
  }
12
 
13
  // --- Sub-components (Modular Structure) ---
 
45
 
46
  const ChatInput: React.FC<{ onSimulate: (msg: string) => void; onHelpMeCraft: (msg: string) => void; isSimulating: boolean }> = ({ onSimulate, onHelpMeCraft, isSimulating }) => {
47
  const [message, setMessage] = useState('');
48
+ const [uploadedFiles, setUploadedFiles] = useState<File[]>([]);
49
  const fileInputRef = useRef<HTMLInputElement>(null);
50
 
51
  const handleUploadClick = () => {
 
53
  };
54
 
55
  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
56
+ const files = e.target.files;
57
+ if (files && files.length > 0) {
58
+ const newFiles = Array.from(files);
59
+ setUploadedFiles(prev => [...prev, ...newFiles]);
60
+ console.log("Selected files:", newFiles.map(f => f.name));
61
  }
62
  };
63
 
64
+ const removeFile = (index: number) => {
65
+ setUploadedFiles(prev => prev.filter((_, i) => i !== index));
66
+ };
67
+
68
  return (
69
  <div className="border-t border-gray-800 pt-6 mt-4 bg-[#0a0a0a] px-6 pb-8 md:pb-10 absolute bottom-0 left-0 right-0 z-20 shadow-[0_-20px_50px_rgba(0,0,0,0.8)]">
70
  <div className="max-w-5xl mx-auto space-y-4">
71
+ {uploadedFiles.length > 0 && (
72
+ <div className="flex flex-wrap gap-2 mb-2">
73
+ {uploadedFiles.map((file, idx) => (
74
+ <div key={idx} className="flex items-center gap-2 bg-gray-800 border border-gray-700 px-3 py-1.5 rounded-full text-[10px] text-gray-300">
75
+ <Image size={10} />
76
+ <span className="truncate max-w-[100px]">{file.name}</span>
77
+ <button onClick={() => removeFile(idx)} className="text-gray-500 hover:text-white">
78
+ <X size={10} />
79
+ </button>
80
+ </div>
81
+ ))}
82
+ </div>
83
+ )}
84
  <textarea
85
  className="w-full h-24 bg-black border border-gray-800 text-gray-200 placeholder-gray-600 p-4 rounded-2xl resize-none focus:outline-none focus:border-gray-600 focus:ring-1 focus:ring-gray-600 transition-all text-sm leading-relaxed"
86
  placeholder="Paste your brand narrative or campaign copy here"
 
98
  onChange={handleFileChange}
99
  className="hidden"
100
  accept="image/*"
101
+ multiple
102
  />
103
  <ChatButton
104
+ label={uploadedFiles.length > 0 ? `${uploadedFiles.length} Images Selected` : "Upload Images"}
105
+ icon={uploadedFiles.length > 0 ? <CheckCircle2 size={14} className="text-green-500" /> : <Image size={14} />}
106
  className="h-fit"
107
  onClick={handleUploadClick}
108
  />
 
129
 
130
  // --- Main Page Component ---
131
 
132
+ const ChatPage: React.FC<ChatPageProps> = ({ onBack, simulationResult, setSimulationResult }) => {
133
  const [showNotification, setShowNotification] = useState(false);
134
  const [isSimulating, setIsSimulating] = useState(false);
135
+ const [simulationId, setSimulationId] = useState<string>('User Group 1');
136
 
137
+ useEffect(() => {
138
+ const fetchSimulations = async () => {
139
+ try {
140
+ const sims = await GradioService.listSimulations();
141
+ if (sims && sims.length > 0) {
142
+ // If sims is an array of objects or strings, pick the first one
143
+ const firstSim = typeof sims[0] === 'string' ? sims[0] : (sims[0].id || sims[0].name || 'User Group 1');
144
+ setSimulationId(firstSim);
145
+ }
146
+ } catch (e) {
147
+ console.error("Failed to fetch simulations:", e);
148
+ }
149
+ };
150
+ fetchSimulations();
151
+ }, []);
152
+
153
+ const handleSimulate = async (msg: string) => {
154
+ if (!msg.trim()) {
155
+ alert("Please enter some content to simulate.");
156
+ return;
157
+ }
158
  setIsSimulating(true);
159
  setShowNotification(true);
160
+ setSimulationResult(null);
161
 
162
+ try {
163
+ const result = await GradioService.startSimulationAsync(simulationId, msg);
164
  setIsSimulating(false);
165
+ setSimulationResult({
166
+ status: "Initiated",
167
+ message: "Simulation started successfully. Please wait for the results.",
168
+ data: result
169
+ });
170
+ } catch (error) {
171
+ setIsSimulating(false);
172
+ setSimulationResult({
173
+ status: "Error",
174
+ message: "Failed to start simulation. Please try again."
175
+ });
176
+ }
177
  };
178
 
179
  const handleRefresh = async () => {
180
  setIsSimulating(true);
181
  try {
182
+ const status = await GradioService.getSimulationStatus(simulationId);
183
+ setIsSimulating(false);
184
+ setSimulationResult({
185
+ status: "Updated",
186
+ message: "Latest status gathered from API.",
187
+ data: status
188
+ });
189
  } catch (error) {
190
  setIsSimulating(false);
191
+ setSimulationResult({
192
+ status: "Error",
193
+ message: "Failed to gather results. The simulation might still be in progress."
194
+ });
195
  }
196
  };
197
 
198
  const handleHelpMeCraft = async (msg: string) => {
199
+ if (!msg.trim()) {
200
+ alert("Please enter some content first.");
201
+ return;
202
+ }
203
+ const result = await GradioService.generateVariants(msg);
204
+ if (Array.isArray(result)) {
205
+ alert("Crafted variants:\n\n" + result.join("\n\n"));
206
+ } else {
207
+ alert("Crafted suggestion: " + JSON.stringify(result));
208
+ }
209
  };
210
 
211
  const categories = {
 
241
  <div className="w-8 h-8 flex items-center justify-center bg-gray-800 rounded-lg text-white font-bold">Λ</div>
242
  <h2 className="text-xl font-medium tracking-tight">New Simulation</h2>
243
  </div>
244
+ <div className="flex items-center gap-4">
245
+ {simulationId && <span className="text-[10px] text-gray-500 uppercase tracking-widest hidden md:block">Active Group: {simulationId}</span>}
246
+ <button onClick={onBack} className="p-2 text-gray-500 hover:text-white hover:bg-gray-900 rounded-full transition-colors">
247
+ <X size={24} />
248
+ </button>
249
+ </div>
250
  </div>
251
 
252
  {/* Notification Banner */}
 
257
  <AlertCircle size={18} className="text-green-400" />
258
  </div>
259
  <div className="flex-1">
260
+ <h4 className="font-semibold text-green-300 mb-1">Simulation Status</h4>
261
  <p className="text-sm text-green-200/70 leading-relaxed">
262
+ The simulation process can take up to <strong className="text-white">30 minutes</strong>. Click "Gather Results" to fetch the latest state.
263
  </p>
264
  {simulationResult && (
265
  <div className="mt-4 p-3 bg-black/40 rounded-xl border border-green-500/20 text-xs text-green-200 flex flex-col gap-3">
266
+ <div className="font-medium flex items-center gap-2">
267
+ <div className={`w-1.5 h-1.5 rounded-full ${simulationResult.status === 'Error' ? 'bg-red-500' : 'bg-green-500 animate-pulse'}`}></div>
268
+ {simulationResult.message}
269
+ </div>
270
+ {simulationResult.data && (
271
+ <pre className="text-[10px] bg-black/50 p-2 rounded max-h-32 overflow-y-auto custom-scrollbar whitespace-pre-wrap">
272
+ {JSON.stringify(simulationResult.data, null, 2)}
273
+ </pre>
274
+ )}
275
  <button
276
  onClick={handleRefresh}
277
  disabled={isSimulating}
 
291
  )}
292
 
293
  {/* Scrollable Content Area */}
294
+ <div className="flex-1 overflow-y-auto custom-scrollbar p-6 md:p-8 pb-80">
295
  <div className="max-w-5xl mx-auto">
296
  <h1 className="text-2xl md:text-3xl font-semibold text-center mb-12 mt-4 md:mt-8">What would you like to simulate?</h1>
297
 
components/SimulationPage.tsx CHANGED
@@ -1,6 +1,7 @@
1
  import React, { useState, useEffect } from 'react';
2
- import { ChevronDown, Plus, Info, MessageSquare, BookOpen, LogOut, PanelLeftClose, MessageCircle, AlertTriangle, Menu, PanelRightClose } from 'lucide-react';
3
  import SimulationGraph from './SimulationGraph';
 
4
 
5
  interface SimulationPageProps {
6
  onBack: () => void;
@@ -8,6 +9,8 @@ interface SimulationPageProps {
8
  onOpenChat: () => void;
9
  user?: any;
10
  onLogin?: () => void;
 
 
11
  }
12
 
13
  // Define the data structure for filters
@@ -40,9 +43,12 @@ const VIEW_FILTERS: Record<string, Array<{ label: string; color: string }>> = {
40
  ]
41
  };
42
 
43
- const SimulationPage: React.FC<SimulationPageProps> = ({ onBack, onOpenConversation, onOpenChat, user, onLogin }) => {
 
 
44
  const [society, setSociety] = useState('User Group 1');
45
  const [viewMode, setViewMode] = useState('Job Title');
 
46
  const [isBuilding, setIsBuilding] = useState(false);
47
  const [isRightPanelOpen, setIsRightPanelOpen] = useState(window.innerWidth > 1200);
48
  const [isLeftPanelOpen, setIsLeftPanelOpen] = useState(window.innerWidth > 768);
@@ -81,7 +87,7 @@ const SimulationPage: React.FC<SimulationPageProps> = ({ onBack, onOpenConversat
81
  <div className="p-4 h-16 border-b border-gray-800 flex items-center justify-between">
82
  <div className="flex items-center gap-2 cursor-pointer" onClick={onBack}>
83
  <div className="w-6 h-6 flex items-center justify-center font-bold text-white">Λ</div>
84
- <span className="font-semibold tracking-tight">SyncUsers</span>
85
  </div>
86
  <button
87
  onClick={() => setIsLeftPanelOpen(false)}
@@ -258,10 +264,54 @@ const SimulationPage: React.FC<SimulationPageProps> = ({ onBack, onOpenConversat
258
  </div>
259
  <div className="flex-1 overflow-y-auto p-4 space-y-4">
260
  <div className="bg-gray-900/50 border border-gray-800 rounded-xl p-4">
261
- <p className="text-xs text-gray-500 mb-2">Simulation Results</p>
262
- <div className="text-sm text-gray-400 italic text-center py-8">
263
- Results will appear here after running a simulation.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
  </div>
266
  </div>
267
  </aside>
 
1
  import React, { useState, useEffect } from 'react';
2
+ import { ChevronDown, Plus, Info, MessageSquare, BookOpen, LogOut, PanelLeftClose, MessageCircle, AlertTriangle, Menu, PanelRightClose, RefreshCw } from 'lucide-react';
3
  import SimulationGraph from './SimulationGraph';
4
+ import { GradioService } from '../services/gradioService';
5
 
6
  interface SimulationPageProps {
7
  onBack: () => void;
 
9
  onOpenChat: () => void;
10
  user?: any;
11
  onLogin?: () => void;
12
+ simulationResult: any;
13
+ setSimulationResult: (res: any) => void;
14
  }
15
 
16
  // Define the data structure for filters
 
43
  ]
44
  };
45
 
46
+ const SimulationPage: React.FC<SimulationPageProps> = ({
47
+ onBack, onOpenConversation, onOpenChat, user, onLogin, simulationResult, setSimulationResult
48
+ }) => {
49
  const [society, setSociety] = useState('User Group 1');
50
  const [viewMode, setViewMode] = useState('Job Title');
51
+ const [isRefreshing, setIsRefreshing] = useState(false);
52
  const [isBuilding, setIsBuilding] = useState(false);
53
  const [isRightPanelOpen, setIsRightPanelOpen] = useState(window.innerWidth > 1200);
54
  const [isLeftPanelOpen, setIsLeftPanelOpen] = useState(window.innerWidth > 768);
 
87
  <div className="p-4 h-16 border-b border-gray-800 flex items-center justify-between">
88
  <div className="flex items-center gap-2 cursor-pointer" onClick={onBack}>
89
  <div className="w-6 h-6 flex items-center justify-center font-bold text-white">Λ</div>
90
+ <span className="font-semibold tracking-tight text-xs">Branding Content Testing</span>
91
  </div>
92
  <button
93
  onClick={() => setIsLeftPanelOpen(false)}
 
264
  </div>
265
  <div className="flex-1 overflow-y-auto p-4 space-y-4">
266
  <div className="bg-gray-900/50 border border-gray-800 rounded-xl p-4">
267
+ <div className="flex items-center justify-between mb-2">
268
+ <p className="text-xs text-gray-500">Simulation Results</p>
269
+ {simulationResult && (
270
+ <button
271
+ onClick={async () => {
272
+ setIsRefreshing(true);
273
+ try {
274
+ const status = await GradioService.getSimulationStatus(society);
275
+ setSimulationResult({
276
+ status: "Updated",
277
+ message: "Latest status gathered from API.",
278
+ data: status
279
+ });
280
+ } catch (e) {
281
+ console.error(e);
282
+ } finally {
283
+ setIsRefreshing(false);
284
+ }
285
+ }}
286
+ className="p-1 hover:bg-gray-800 rounded text-gray-500 hover:text-white"
287
+ title="Refresh Status"
288
+ >
289
+ <RefreshCw size={12} className={isRefreshing ? "animate-spin" : ""} />
290
+ </button>
291
+ )}
292
  </div>
293
+
294
+ {!simulationResult ? (
295
+ <div className="text-sm text-gray-400 italic text-center py-8">
296
+ Results will appear here after running a simulation.
297
+ </div>
298
+ ) : (
299
+ <div className="space-y-3">
300
+ <div className="flex items-center gap-2 text-xs font-medium text-green-400">
301
+ <div className="w-1.5 h-1.5 rounded-full bg-green-500 animate-pulse"></div>
302
+ {simulationResult.status}
303
+ </div>
304
+ <p className="text-[11px] text-gray-400">{simulationResult.message}</p>
305
+ {simulationResult.data && (
306
+ <pre className="text-[10px] bg-black/50 p-2 rounded max-h-96 overflow-y-auto custom-scrollbar whitespace-pre-wrap text-gray-300 border border-gray-800">
307
+ {JSON.stringify(simulationResult.data, null, 2)}
308
+ </pre>
309
+ )}
310
+ <p className="text-[10px] text-gray-600 mt-4 italic">
311
+ Note: Complete simulation results can take up to 30 minutes to be fully processed by the API.
312
+ </p>
313
+ </div>
314
+ )}
315
  </div>
316
  </div>
317
  </aside>
inspect_gradio.cjs ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ const { Client } = require("@gradio/client");
3
+
4
+ async function inspect() {
5
+ try {
6
+ const client = await Client.connect("AUXteam/tiny_factory");
7
+ console.log("Keys:", Object.keys(client));
8
+ if (client.view_api) {
9
+ const api = await client.view_api();
10
+ console.log(JSON.stringify(api, null, 2));
11
+ }
12
+ } catch (e) {
13
+ console.error(e);
14
+ }
15
+ }
16
+
17
+ inspect();
inspect_gradio.ts ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import { Client } from "@gradio/client";
3
+
4
+ async function inspect() {
5
+ const client = await Client.connect("AUXteam/tiny_factory");
6
+ console.log(JSON.stringify(client.config.endpoints, null, 2));
7
+ }
8
+
9
+ inspect();
server.cjs CHANGED
@@ -5,6 +5,13 @@ const port = 7860;
5
 
6
  app.use(express.static(path.join(__dirname, 'dist')));
7
 
 
 
 
 
 
 
 
8
  app.get('/health', (req, res) => {
9
  res.status(200).send('OK');
10
  });
 
5
 
6
  app.use(express.static(path.join(__dirname, 'dist')));
7
 
8
+ app.get('/api/config', (req, res) => {
9
+ res.json({
10
+ clientId: process.env.OAUTH_CLIENT_ID,
11
+ scopes: process.env.OAUTH_SCOPES || "openid profile",
12
+ });
13
+ });
14
+
15
  app.get('/health', (req, res) => {
16
  res.status(200).send('OK');
17
  });
services/gradioService.ts CHANGED
@@ -23,25 +23,47 @@ export class GradioService {
23
  }
24
  }
25
 
26
- static async simulate(persona: string, message: string) {
27
  try {
28
  const client = await this.getClient();
29
- const result = await client.predict("/simulate", [persona, message]);
30
  return result.data;
31
  } catch (error) {
32
- console.error("Error in simulation:", error);
33
  throw error;
34
  }
35
  }
36
 
37
- static async helpMeCraft(content: string) {
38
  try {
39
  const client = await this.getClient();
40
- const result = await client.predict("/help_me_craft", [content]);
41
  return result.data;
42
  } catch (error) {
43
- console.error("Error helping to craft content:", error);
44
- return "Unable to craft content at this time.";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  }
46
  }
47
  }
 
23
  }
24
  }
25
 
26
+ static async startSimulationAsync(simulationId: string, contentText: string, format: string = "text") {
27
  try {
28
  const client = await this.getClient();
29
+ const result = await client.predict("/start_simulation_async", [simulationId, contentText, format]);
30
  return result.data;
31
  } catch (error) {
32
+ console.error("Error starting simulation:", error);
33
  throw error;
34
  }
35
  }
36
 
37
+ static async getSimulationStatus(simulationId: string) {
38
  try {
39
  const client = await this.getClient();
40
+ const result = await client.predict("/get_simulation_status", [simulationId]);
41
  return result.data;
42
  } catch (error) {
43
+ console.error("Error getting simulation status:", error);
44
+ throw error;
45
+ }
46
+ }
47
+
48
+ static async generateVariants(contentText: string, numVariants: number = 3) {
49
+ try {
50
+ const client = await this.getClient();
51
+ const result = await client.predict("/generate_variants", [contentText, numVariants]);
52
+ return result.data;
53
+ } catch (error) {
54
+ console.error("Error generating variants:", error);
55
+ return ["Unable to generate variants at this time."];
56
+ }
57
+ }
58
+
59
+ static async listSimulations() {
60
+ try {
61
+ const client = await this.getClient();
62
+ const result = await client.predict("/list_simulations", []);
63
+ return result.data;
64
+ } catch (error) {
65
+ console.error("Error listing simulations:", error);
66
+ return [];
67
  }
68
  }
69
  }