mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-30 04:40:16 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
baa74c6a8c |
+28
-3
@@ -15,7 +15,7 @@ const EntityResolutionTab = lazy(() => import('./workspaces/EnrichWorkspace/Enti
|
|||||||
const KGOverviewTab = lazy(() => import('./workspaces/ManageWorkspace/KGOverviewTab').then((module) => ({ default: module.KGOverviewTab })));
|
const KGOverviewTab = lazy(() => import('./workspaces/ManageWorkspace/KGOverviewTab').then((module) => ({ default: module.KGOverviewTab })));
|
||||||
const OntologySummaryTab = lazy(() => import('./workspaces/ManageWorkspace/OntologySummaryTab').then((module) => ({ default: module.OntologySummaryTab })));
|
const OntologySummaryTab = lazy(() => import('./workspaces/ManageWorkspace/OntologySummaryTab').then((module) => ({ default: module.OntologySummaryTab })));
|
||||||
|
|
||||||
type WorkspaceId = 'explore' | 'analyze' | 'decisions' | 'enrich' | 'manage';
|
type WorkspaceId = 'welcome' | 'explore' | 'analyze' | 'decisions' | 'enrich' | 'manage';
|
||||||
type ExploreView = 'graph' | 'vocabulary';
|
type ExploreView = 'graph' | 'vocabulary';
|
||||||
type AnalyzeView = 'sparql' | 'reasoning';
|
type AnalyzeView = 'sparql' | 'reasoning';
|
||||||
type EnrichView = 'import' | 'merge' | 'registry' | 'resolve';
|
type EnrichView = 'import' | 'merge' | 'registry' | 'resolve';
|
||||||
@@ -311,14 +311,39 @@ function WorkspaceFallback() {
|
|||||||
return <div className="workspace-loading">Loading workspace…</div>;
|
return <div className="workspace-loading">Loading workspace…</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function WelcomeScreen() {
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
flex: 1,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: 12,
|
||||||
|
color: 'var(--text-muted)',
|
||||||
|
}}>
|
||||||
|
<h1 style={{ margin: 0, fontSize: 28, fontWeight: 700, color: 'var(--text-main)', letterSpacing: '-0.03em' }}>
|
||||||
|
Welcome to Semantica
|
||||||
|
</h1>
|
||||||
|
<p style={{ margin: 0, fontSize: 14 }}>
|
||||||
|
Select a workspace from the sidebar to get started.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [activeWorkspace, setActiveWorkspace] = useState<WorkspaceId>('explore');
|
const [activeWorkspace, setActiveWorkspace] = useState<WorkspaceId>('welcome');
|
||||||
const [exploreView, setExploreView] = useState<ExploreView>('graph');
|
const [exploreView, setExploreView] = useState<ExploreView>('graph');
|
||||||
const [analyzeView, setAnalyzeView] = useState<AnalyzeView>('reasoning');
|
const [analyzeView, setAnalyzeView] = useState<AnalyzeView>('reasoning');
|
||||||
const [enrichView, setEnrichView] = useState<EnrichView>('import');
|
const [enrichView, setEnrichView] = useState<EnrichView>('import');
|
||||||
const [manageView, setManageView] = useState<ManageView>('lineage');
|
const [manageView, setManageView] = useState<ManageView>('lineage');
|
||||||
|
|
||||||
const renderWorkspace = () => {
|
const renderWorkspace = () => {
|
||||||
|
if (activeWorkspace === 'welcome') {
|
||||||
|
return <WelcomeScreen />;
|
||||||
|
}
|
||||||
|
|
||||||
if (activeWorkspace === 'explore') {
|
if (activeWorkspace === 'explore') {
|
||||||
return (
|
return (
|
||||||
<WorkspaceShell
|
<WorkspaceShell
|
||||||
@@ -451,7 +476,7 @@ export default function App() {
|
|||||||
<style>{shellStyles}</style>
|
<style>{shellStyles}</style>
|
||||||
<div className="app-shell">
|
<div className="app-shell">
|
||||||
<aside className="app-rail">
|
<aside className="app-rail">
|
||||||
<div className="brand-pill" title="Semantica Knowledge Explorer">SKE</div>
|
<button className="brand-pill" title="Semantica Knowledge Explorer" onClick={() => setActiveWorkspace('welcome')} style={{ cursor: 'pointer', border: '1px solid rgba(127,208,255,0.18)' }}>SKE</button>
|
||||||
{navItems.map(({ id, label, hint, icon: Icon }) => (
|
{navItems.map(({ id, label, hint, icon: Icon }) => (
|
||||||
<button
|
<button
|
||||||
key={id}
|
key={id}
|
||||||
|
|||||||
+15
-3
@@ -188,10 +188,22 @@ async def serve_spa(full_path: str):
|
|||||||
if full_path.startswith("api/"):
|
if full_path.startswith("api/"):
|
||||||
raise HTTPException(status_code=404, detail="API route not found")
|
raise HTTPException(status_code=404, detail="API route not found")
|
||||||
|
|
||||||
|
# Root path — serve index.html if built, otherwise a welcome JSON response
|
||||||
|
if full_path in ("", "/"):
|
||||||
|
index_file = STATIC_DIR / "index.html"
|
||||||
|
if index_file.is_file():
|
||||||
|
return FileResponse(index_file)
|
||||||
|
return JSONResponse({
|
||||||
|
"name": "Semantica Knowledge Explorer",
|
||||||
|
"version": __version__,
|
||||||
|
"message": "Welcome to Semantica. The frontend is not built yet — run `npm run build` inside the explorer/ directory, or open the Vite dev server at http://localhost:5173.",
|
||||||
|
"docs": "/docs",
|
||||||
|
"health": "/health",
|
||||||
|
})
|
||||||
|
|
||||||
normalized_path = os.path.normpath(full_path)
|
normalized_path = os.path.normpath(full_path)
|
||||||
if (
|
if (
|
||||||
normalized_path in ("", ".")
|
os.path.isabs(normalized_path)
|
||||||
or os.path.isabs(normalized_path)
|
|
||||||
or normalized_path == ".."
|
or normalized_path == ".."
|
||||||
or normalized_path.startswith(".." + os.sep)
|
or normalized_path.startswith(".." + os.sep)
|
||||||
):
|
):
|
||||||
@@ -200,7 +212,7 @@ async def serve_spa(full_path: str):
|
|||||||
# Ensure join remains relative to STATIC_DIR even if input includes leading separators
|
# Ensure join remains relative to STATIC_DIR even if input includes leading separators
|
||||||
safe_rel_path = normalized_path.lstrip("/\\")
|
safe_rel_path = normalized_path.lstrip("/\\")
|
||||||
rel_parts = Path(safe_rel_path).parts
|
rel_parts = Path(safe_rel_path).parts
|
||||||
if any(part in ("", ".", "..") for part in rel_parts):
|
if any(part in (".", "..") for part in rel_parts):
|
||||||
raise HTTPException(status_code=400, detail="Invalid path")
|
raise HTTPException(status_code=400, detail="Invalid path")
|
||||||
|
|
||||||
static_dir_resolved = STATIC_DIR.resolve()
|
static_dir_resolved = STATIC_DIR.resolve()
|
||||||
|
|||||||
Reference in New Issue
Block a user