Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/app/ontology/ontology-graph-3d.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,8 @@ export function OntologyGraph3D({ schemas, edges, selectedId, onSelect }: Props)
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="12" cy="12" r="10" />
<circle cx="12" cy="12" r="3" />
<path d="M12 17v5" />
<path d="M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z" />
</svg>
</button>
</Html>
Expand All @@ -394,6 +394,7 @@ export function OntologyGraph3D({ schemas, edges, selectedId, onSelect }: Props)
makeDefault
dollySpeed={0.5}
truckSpeed={1}
dollyToCursor
/>
<EffectComposer>
<Bloom
Expand Down
5 changes: 5 additions & 0 deletions src/app/ontology/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ export interface SchemaNode {
color: string
node_key: string
attributes: SchemaAttribute[]
title_key?: string
index?: string
description_key?: string
icon?: string
secondary_color?: string
}

export interface SchemaEdge {
Expand Down
12 changes: 12 additions & 0 deletions src/components/layout/app-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,26 @@ import { AddContentModal } from "@/components/modals/add-content-modal"
import { BudgetModal } from "@/components/modals/budget-modal"
import { useAppStore } from "@/stores/app-store"
import { useGraphStore } from "@/stores/graph-store"
import { useSchemaStore } from "@/stores/schema-store"
import { useMocks } from "@/lib/mock-data"

export function AppLayout() {
const [sourcesOpen, setSourcesOpen] = useState(false)
const searchTerm = useAppStore((s) => s.searchTerm)
const hasResults = useGraphStore((s) => s.nodes.length > 0)
const schemas = useSchemaStore((s) => s.schemas)
const fetchSchemas = useSchemaStore((s) => s.fetchAll)

const searchPanelOpen = !!searchTerm && hasResults

// Schemas power display-name resolution (title_key / index) for search
// results and any other node chrome — load once on mount if not already
// populated (e.g. by the ontology page).
useEffect(() => {
if (useMocks() || schemas.length > 0) return
fetchSchemas()
}, [schemas.length, fetchSchemas])

// Auto-close sources when search results appear
useEffect(() => {
if (searchPanelOpen) setSourcesOpen(false)
Expand Down
32 changes: 26 additions & 6 deletions src/components/layout/search-results-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,33 @@ import { Separator } from "@/components/ui/separator"
import { Badge } from "@/components/ui/badge"
import { useGraphStore } from "@/stores/graph-store"
import { useAppStore } from "@/stores/app-store"
import { useSchemaStore } from "@/stores/schema-store"
import type { GraphNode } from "@/lib/graph-api"
import type { SchemaNode } from "@/app/ontology/page"

function NodeRow({ node }: { node: GraphNode }) {
const name =
(node.properties?.name as string) ??
node.ref_id
const DISPLAY_KEY_FALLBACKS = ["name", "title", "label", "text", "content", "body"] as const

function pickString(props: Record<string, unknown> | undefined, key: string | undefined): string | undefined {
if (!props || !key) return undefined
const v = props[key]
return typeof v === "string" && v.length > 0 ? v : undefined
}

function NodeRow({ node, schemas }: { node: GraphNode; schemas: SchemaNode[] }) {
const nodeType = node.node_type ?? "Unknown"
const schema = schemas.find((s) => s.type === nodeType)
// Priority: title_key → index (sphinx convention) → common display-ish
// property names → ref_id. The last step catches nodes whose schema key
// isn't populated on this particular row.
const props = node.properties
let name = pickString(props, schema?.title_key) ?? pickString(props, schema?.index)
if (!name) {
for (const key of DISPLAY_KEY_FALLBACKS) {
name = pickString(props, key)
if (name) break
}
}
if (!name) name = node.ref_id

return (
<button className="flex items-center gap-3 px-4 py-3 w-full text-left cursor-pointer hover:bg-sidebar-accent transition-colors group">
Expand All @@ -36,6 +55,7 @@ function NodeRow({ node }: { node: GraphNode }) {
export function SearchResultsPanel({ onClose }: { onClose: () => void }) {
const { nodes, edges, loading } = useGraphStore()
const searchTerm = useAppStore((s) => s.searchTerm)
const schemas = useSchemaStore((s) => s.schemas)

if (!searchTerm) return null

Expand Down Expand Up @@ -64,7 +84,7 @@ export function SearchResultsPanel({ onClose }: { onClose: () => void }) {
</p>
</div>

<ScrollArea className="relative z-10 flex-1">
<ScrollArea className="relative z-10 flex-1 min-h-0">
{loading ? (
<div className="flex items-center justify-center py-12">
<div className="h-5 w-5 rounded-full border-2 border-primary/30 border-t-primary animate-spin" />
Expand All @@ -80,7 +100,7 @@ export function SearchResultsPanel({ onClose }: { onClose: () => void }) {
<div className="py-1">
{nodes.map((node, i) => (
<div key={node.ref_id}>
<NodeRow node={node} />
<NodeRow node={node} schemas={schemas} />
{i < nodes.length - 1 && (
<Separator className="bg-sidebar-border/50" />
)}
Expand Down
10 changes: 10 additions & 0 deletions src/stores/schema-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,12 @@ export const useSchemaStore = create<SchemaState>((set) => ({
type: string
parent?: string
primary_color?: string
secondary_color?: string
node_key?: string
title_key?: string
index?: string
description_key?: string
icon?: string
attributes?: Record<string, unknown>
}>
edges: SchemaEdge[]
Expand All @@ -137,7 +142,12 @@ export const useSchemaStore = create<SchemaState>((set) => ({
type: s.type ?? "",
parent: s.parent ?? "",
color: s.primary_color ?? "#64748b",
secondary_color: s.secondary_color,
node_key: s.node_key ?? "name",
title_key: s.title_key,
index: s.index,
description_key: s.description_key,
icon: s.icon,
attributes: parseAttributes(s.attributes),
}))

Expand Down
Loading