Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
eaef11e
refactor: update dependencies and improve graph handling
Anchel123 Jun 1, 2026
98fc391
feat: enhance combobox type safety and element menu positioning
Anchel123 Jul 8, 2026
94953fc
feat: update API proxy target and add element menu tests
Anchel123 Jul 8, 2026
b14ed9e
feat: improve graph element handling and enhance toolbar accessibility
Anchel123 Jul 8, 2026
7b0cde0
fix: resolve build and compilation errors
Anchel123 Jul 8, 2026
da01b49
Fix remaining PR #697 code quality issues
Anchel123 Jul 8, 2026
b86d5f9
Improve path zoom handling and remove unnecessary state updates
Anchel123 Jul 8, 2026
bcf5e7a
Reduce link zoom multiplier to 1.1x for better spacing
Anchel123 Jul 8, 2026
bf30fd7
Remove automatic path zoom - just highlight paths without forcing zoom
Anchel123 Jul 8, 2026
d64403e
fix: improve path zoom behavior - zoom all paths on response, then zo…
Anchel123 Jul 8, 2026
babe0ba
fix: add graphId to ForceGraph for incremental data updates
Anchel123 Jul 8, 2026
6745207
fix: update @falkordb/canvas dependency to version 0.2.5
Anchel123 Jul 9, 2026
4807a29
fix: improve element menu removal test for deterministic assertions
Anchel123 Jul 9, 2026
0bb7392
fix: update API proxy target to local development server
Anchel123 Jul 9, 2026
d063625
fix: set viewport in browser context to ensure desktop layout on page…
Anchel123 Jul 9, 2026
d14b29d
Merge branch 'staging' into add-layouts
Anchel123 Jul 12, 2026
e39f528
fix: resolve combobox crash from repo objects and legacy graph name l…
Anchel123 Jul 12, 2026
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
34 changes: 31 additions & 3 deletions api/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,17 @@ def __init__(self, name: str, branch: Optional[str] = None) -> None:
# Normalize empty / None to DEFAULT_BRANCH so the stored
# branch matches the key actually used by compose_graph_name.
self.branch = branch or DEFAULT_BRANCH
self.name = compose_graph_name(self.project, self.branch)
self.db = create_falkordb()
# Legacy (pre-T17) graphs are stored under the bare project
# name. Prefer that existing graph over composing a new,
# empty ``code:{project}:{branch}`` key out from under it.
if branch in (None, "", DEFAULT_BRANCH) and name in self.db.list_graphs():
self.name = name
else:
self.name = compose_graph_name(self.project, self.branch)

self.db = create_falkordb()
if not hasattr(self, "db"):
self.db = create_falkordb()
self.g = self.db.select_graph(self.name)

# Initialize the backlog as disabled by default
Expand Down Expand Up @@ -832,12 +840,32 @@ def __init__(self, name: str, branch: Optional[str] = None) -> None:
else:
self.project = name
self.branch = branch or DEFAULT_BRANCH
self.name = compose_graph_name(self.project, self.branch)
# Composition is deferred until ``graph_exists``/first query so
# we can prefer an existing legacy (pre-T17) bare graph over
# composing a new, empty ``code:{project}:{branch}`` key; see
# ``_resolve_name``.
self.name = name
self._needs_resolution = branch in (None, "", DEFAULT_BRANCH)
self.db = _async_db()
self.g = self.db.select_graph(self.name)

async def _resolve_name(self) -> None:
"""Resolve a bare project name to its actual FalkorDB graph key.

Prefers an existing legacy graph (stored under the bare project
name) over composing a new ``code:{project}:{branch}`` key.
"""
if not getattr(self, "_needs_resolution", False):
return
self._needs_resolution = False
graphs = await self.db.list_graphs()
if self.name not in graphs:
self.name = compose_graph_name(self.project, self.branch)
self.g = self.db.select_graph(self.name)

async def graph_exists(self) -> bool:
"""Check if this graph exists, reusing the current connection."""
await self._resolve_name()
graphs = await self.db.list_graphs()
return self.name in graphs

Expand Down
37 changes: 24 additions & 13 deletions app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"preview": "vite preview"
},
"dependencies": {
"@falkordb/canvas": "^0.0.44",
"@falkordb/canvas": "^0.2.5",
"@radix-ui/react-checkbox": "^1.1.4",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-dropdown-menu": "^2.1.6",
Expand Down
128 changes: 41 additions & 87 deletions app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import { Carousel, CarouselApi, CarouselContent, CarouselItem, CarouselNext, Car
import { Drawer, DrawerContent, DrawerDescription, DrawerTitle, DrawerTrigger } from '@/components/ui/drawer';
import Input from './components/Input';
import { Labels } from './components/labels';
import { Toolbar } from './components/toolbar';
import { cn, GraphRef, Message, Path, PathData, PathNode } from '@/lib/utils';
import { Toolbar, ZoomControls } from './components/toolbar';
import { cn, DEFAULT_BRANCH, GraphRef, Message, Path, PathData, PathNode, RepoOption } from '@/lib/utils';
import type { GraphNode } from '@falkordb/canvas';
import { convertToCanvasData } from './components/ForceGraph';
import { Toaster } from '@/components/ui/toaster';
import GTM from './GTM';
import { Button } from '@/components/ui/button';
Expand Down Expand Up @@ -71,15 +72,16 @@ export default function App() {
const [createURL, setCreateURL] = useState("")
const [createOpen, setCreateOpen] = useState(false)
const [tipOpen, setTipOpen] = useState(false)
const [options, setOptions] = useState<string[]>([]);
const [options, setOptions] = useState<RepoOption[]>([]);
const [path, setPath] = useState<Path | undefined>();
const [isSubmit, setIsSubmit] = useState<boolean>(false);
const desktopChartRef = useRef<GraphRef["current"]>(null)
const mobileChartRef = useRef<GraphRef["current"]>(null)
const [menuOpen, setMenuOpen] = useState(false)
const [chatOpen, setChatOpen] = useState(false)
const [searchNode, setSearchNode] = useState<PathNode>({});
const [cooldownTicks, setCooldownTicks] = useState<number | undefined>(0)
const [animation, setAnimation] = useState(false)
const [manualDimmed, setManualDimmed] = useState<boolean>(true)
Comment thread
Anchel123 marked this conversation as resolved.
const [optionsOpen, setOptionsOpen] = useState(false)
const [messages, setMessages] = useState<Message[]>([]);
const [query, setQuery] = useState('');
Expand Down Expand Up @@ -140,7 +142,7 @@ export default function App() {

const graphName = createURL.split('/').pop()!

setOptions(prev => [...prev, graphName])
setOptions(prev => [...prev, { project: graphName, branch: DEFAULT_BRANCH, graph: graphName }])
setSelectedValue(graphName)
setCreateURL("")
setCreateOpen(false)
Expand Down Expand Up @@ -174,8 +176,6 @@ export default function App() {
const g = Graph.create(json.entities, graphName)
setGraph(g)

if (cooldownTicks === 0) setCooldownTicks(-1)

setIsPathResponse(false)
chatPanel.current?.expand()
// @ts-ignore
Expand Down Expand Up @@ -225,34 +225,10 @@ export default function App() {
if (!chartNode) {
chartNode = graph.extend({ nodes: [node], edges: [] }).nodes[0]

if (cooldownTicks === 0) setCooldownTicks(-1)

setZoomedNodes([chartNode])
graph.visibleLinks(true, [chartNode!.id])

const currentData = canvas.getGraphData()

const { dataToGraphData } = await import('@falkordb/canvas')
const graphNode = dataToGraphData({
nodes: [{
color: chartNode.color,
id: chartNode.id,
labels: [chartNode.category],
visible: chartNode.visible,
data: {
...chartNode.data,
isPath: chartNode.isPath,
isPathSelected: chartNode.isPathSelected,
}
}], links: []
}).nodes[0]

if (graphNode) {
currentData.nodes.push(graphNode)
}

canvas.setGraphData(currentData)

canvas.setGraphData(convertToCanvasData(graph.Elements))

setTimeout(() => {
canvas.zoomToFit(4, (n: GraphNode) => n.id === chartNode!.id)
Expand All @@ -265,22 +241,7 @@ export default function App() {
chartNode.visible = true
graph.visibleLinks(true, [chartNode!.id])

const currentData = canvas.getGraphData()

const graphNode = currentData.nodes.find(n => n.id === chartNode!.id)
if (graphNode) {
graphNode.visible = true
}

currentData.links.forEach(canvasLink => {
const appLink = graph.LinksMap.get(canvasLink.id)

if (appLink) {
canvasLink.visible = appLink.visible
}
})

canvas.setGraphData(currentData)
canvas.setGraphData(convertToCanvasData(graph.Elements))
}

setTimeout(() => {
Expand All @@ -306,27 +267,18 @@ export default function App() {

graph.visibleLinks(show)

const currentData = canvas.getGraphData();

currentData.nodes.forEach(canvasNode => {
const appNode = graph.NodesMap.get(canvasNode.id);

if (appNode) {
canvasNode.visible = appNode.visible;
}
});

currentData.links.forEach(canvasLink => {
const appLink = graph.LinksMap.get(canvasLink.id);

if (appLink) {
canvasLink.visible = appLink.visible;
}
});

canvas.setGraphData(currentData);
// setGraphData doesn't update visible for existing nodes — mutate canvas nodes directly
const canvasData = canvas.getGraphData()
canvasData.nodes.forEach((canvasNode: { id: number, visible: boolean }) => {
const appNode = graph.NodesMap.get(canvasNode.id)
if (appNode) canvasNode.visible = appNode.visible
})
canvasData.links.forEach((canvasLink: { id: number, visible: boolean }) => {
const appLink = graph.LinksMap.get(canvasLink.id)
if (appLink) canvasLink.visible = appLink.visible
})
canvas.refresh()

setCooldownTicks(cooldownTicks === undefined ? undefined : -1);
setHasHiddenElements(graph.getElements().some(element => !element.visible));
}

Expand Down Expand Up @@ -538,8 +490,10 @@ export default function App() {
handleSearchSubmit={(node) => handleSearchSubmit(node, desktopChartRef)}
searchNode={searchNode}
setSearchNode={setSearchNode}
cooldownTicks={cooldownTicks}
setCooldownTicks={setCooldownTicks}
animation={animation}
setAnimation={setAnimation}
manualDimmed={manualDimmed}
setManualDimmed={setManualDimmed}
onCategoryClick={(name, show) => onCategoryClick(name, show, desktopChartRef)}
handleDownloadImage={handleDownloadImage}
zoomedNodes={zoomedNodes}
Expand Down Expand Up @@ -574,7 +528,6 @@ export default function App() {
setIsPathResponse={setIsPathResponse}
paths={paths}
setPaths={setPaths}
setCooldownTicks={setCooldownTicks}
/>
</Panel>
</PanelGroup>
Expand Down Expand Up @@ -665,8 +618,10 @@ export default function App() {
handleSearchSubmit={(node) => handleSearchSubmit(node, mobileChartRef)}
setSearchNode={setSearchNode}
searchNode={searchNode}
cooldownTicks={cooldownTicks}
setCooldownTicks={setCooldownTicks}
animation={animation}
setAnimation={setAnimation}
manualDimmed={manualDimmed}
setManualDimmed={setManualDimmed}
onCategoryClick={(name, show) => onCategoryClick(name, show, mobileChartRef)}
handleDownloadImage={handleDownloadImage}
zoomedNodes={zoomedNodes}
Expand Down Expand Up @@ -705,7 +660,6 @@ export default function App() {
setChatOpen={setChatOpen}
paths={paths}
setPaths={setPaths}
setCooldownTicks={setCooldownTicks}
/>
</DrawerContent>
</Drawer>
Expand All @@ -720,11 +674,10 @@ export default function App() {
<DrawerTitle />
<DrawerDescription />
</VisuallyHidden>
<Toolbar
className='bg-transparent absolute -top-14 left-0 w-full justify-between px-6'
{/* Zoom controls floating above the drawer handle */}
<ZoomControls
className='bg-transparent absolute -top-14 left-0 w-full justify-center px-6'
canvasRef={mobileChartRef}
setCooldownTicks={setCooldownTicks}
cooldownTicks={cooldownTicks}
/>
<Input
className='border-2 border-border'
Expand All @@ -735,15 +688,16 @@ export default function App() {
node={searchNode}
/>
<Labels categories={graph.Categories} onClick={(name, show) => onCategoryClick(name, show, mobileChartRef)} />
<div className='flex flex-col gap-2 items-center'>
<button
className='control-button'
onClick={handleDownloadImage}
>
<Download size={30} />
</button>
<p className=''>Take Screenshot</p>
</div>
{/* Options controls inside the drawer, below search */}
<Toolbar
hideZoom
className='w-full justify-center'
canvasRef={mobileChartRef}
animation={animation}
setAnimation={setAnimation}
manualDimmed={manualDimmed}
setManualDimmed={setManualDimmed}
/>
</DrawerContent>
</Drawer>
</div>
Expand Down
Loading
Loading