Skip to content
Open
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
155 changes: 120 additions & 35 deletions packages/client/src/components/graph/GraphDrawer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ const keys = [
['refs', 'references'],
['deps', 'dependencies'],
] as const

// Pathfinding mode
const pathfindingMode = graphPathfindingMode
const pathfindingResults = graphPathfindingResults
const pathfindingStart = graphPathfindingStart
const pathfindingEnd = graphPathfindingEnd

function handleFilterToThisModule() {
filterId.value = data.value!.path
pathfindingMode.value = false
}
</script>

<template>
Expand All @@ -51,43 +62,117 @@ const keys = [
mount-to=".graph-body"
>
<div class="w-300px" h-full of-auto>
<div text-md h-80px border-b border-base p3 flex="~ col gap1">
<span text-lg flex="~ gap2 items-center">
{{ data?.name }}
<span v-if="copied" i-material-symbols-check-small text-primary-500 />
<span
v-else-if="data"
hover="op-100" i-carbon-copy cursor-pointer text-sm op-50 :class="{
'text-gray-200': !isSupported,
}" @click="copy(data.name)"
/>
</span>
<button hover="underline" truncate text-left text-gray-500 :title="data?.displayPath" @click="_openInEditor(data!.path)">
{{ data?.displayPath }}
</button>
</div>
<div
v-for="([key, keyDisplay]) in keys" :key="key"
max-h-60 of-auto border-b border-base p3 text-sm
>
<div pb2 text-gray-500>
<span text-primary-500>{{ data?.[key].length }}</span>
{{ keyDisplay }}
</div>
<div flex="~ col gap2 items-start">
<button
v-for="item in data?.[key]" :key="item.path" dark="text-gray-200"
of-hidden truncate ws-nowrap pr-3 text-gray-800 hover="underline" @click="_openInEditor(item.path)"
>
{{ item.displayPath }}
<!-- Show Selected Node Info -->
<template v-if="data">
<div text-md h-80px border-b border-base p3 flex="~ col gap1">
<span text-lg flex="~ gap2 items-center">
{{ data?.name }}
<span v-if="copied" i-material-symbols-check-small text-primary-500 />
<span
v-else-if="data"
hover="op-100" i-carbon-copy cursor-pointer text-sm op-50 :class="{
'text-gray-200': !isSupported,
}" @click="copy(data.name)"
/>
</span>
<button hover="underline" truncate text-left text-gray-500 :title="data?.displayPath" @click="_openInEditor(data!.path)">
{{ data?.displayPath }}
</button>
</div>
</div>
<div p3>
<VueButton type="primary" @click="filterId = data!.path">
Filter to this module
</VueButton>
</div>
<div
v-for="([key, keyDisplay]) in keys" :key="key"
max-h-60 of-auto border-b border-base p3 text-sm
>
<div pb2 text-gray-500>
<span text-primary-500>{{ data?.[key].length }}</span>
{{ keyDisplay }}
</div>
<div flex="~ col gap2 items-start">
<button
v-for="item in data?.[key]" :key="item.path" dark="text-gray-200"
of-hidden truncate ws-nowrap pr-3 text-gray-800 hover="underline" @click="_openInEditor(item.path)"
>
{{ item.displayPath }}
</button>
</div>
</div>
<div p3>
<VueButton type="primary" @click="handleFilterToThisModule">
Filter to this module
</VueButton>
</div>
</template>

<!-- Show Found Paths -->
<template v-if="pathfindingMode && pathfindingStart && pathfindingEnd">
<div v-if="data" border-b border-base p3 />

<div text-md h-auto border-b border-base p3 flex="~ col gap2">
<span text-lg font-bold>Path Results</span>
<div text-sm text-gray-500>
<span text-primary-500>{{ pathfindingResults.length }}</span>
{{ pathfindingResults.length === 1 ? 'path' : 'paths' }} found
</div>
<div text-xs text-gray-400>
From: {{ pathfindingStart }}
</div>
<div text-xs text-gray-400>
To: {{ pathfindingEnd }}
</div>
</div>

<div v-if="pathfindingResults.length === 0" p3 text-sm text-gray-500>
<div mb2>
No paths found between these modules.
</div>
<div text-xs>
Tip: Try using partial file names like "App.vue" or "main.ts"
</div>
</div>

<div
v-for="(pathInfo, index) in pathfindingResults"
:key="index"
border-b border-base p3 text-sm
>
<div pb2 text-gray-500 font-bold>
Path {{ index + 1 }}
<span text-xs>({{ pathInfo.path.length }} steps)</span>
</div>
<div flex="~ col gap1">
<div
v-for="(nodeName, nodeIndex) in pathInfo.displayPath"
:key="nodeIndex"
flex="~ items-center gap-2"
>
<div
h-4px w-4px rounded-full
:class="[
nodeIndex === 0 ? 'bg-green-500'
: nodeIndex === pathInfo.displayPath.length - 1 ? 'bg-red-500'
: 'bg-orange-500',
]"
/>
<button
truncate text-left hover="underline"
:class="[
nodeIndex === 0 ? 'text-green-600 dark:text-green-400 font-bold'
: nodeIndex === pathInfo.displayPath.length - 1 ? 'text-red-600 dark:text-red-400 font-bold'
: 'text-gray-800 dark:text-gray-200',
]"
:title="pathInfo.path[nodeIndex]"
@click="_openInEditor(pathInfo.path[nodeIndex])"
>
{{ nodeName }}
</button>
<div
v-if="nodeIndex < pathInfo.displayPath.length - 1"
i-carbon-arrow-down text-xs op50
/>
</div>
</div>
</div>
</template>
</div>
</VueDrawer>
</template>
43 changes: 41 additions & 2 deletions packages/client/src/components/graph/GraphNavbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,56 @@ const selectableItems = [
] as const

const filterId = graphFilterNodeId

// Pathfinding mode
const pathfindingMode = graphPathfindingMode
const pathfindingStart = graphPathfindingStart
const pathfindingEnd = graphPathfindingEnd

function togglePathfindingMode() {
pathfindingMode.value = !pathfindingMode.value
if (!pathfindingStart.value && text.value) {
pathfindingStart.value = text.value
}
}

function swapStartAndEnd() {
const start = pathfindingStart.value
pathfindingStart.value = pathfindingEnd.value
pathfindingEnd.value = start
}
</script>

<template>
<div flex="~ items-center gap-4 nowrap" class="[&_>*]:flex-[0_0_auto]" absolute left-0 top-0 z-10 navbar-base w-full overflow-x-auto glass-effect px4 text-sm>
<VueInput v-model="text" placeholder="Search modules..." />
<!-- Toggle Pathfinding Mode Button -->
<button
rounded-full px3 py1 text-xs hover:op100
:class="pathfindingMode ? 'bg-primary-500 text-white op100' : 'bg-gray:20 op50'"
@click="togglePathfindingMode"
>
<div flex="~ items-center gap-1">
<div i-carbon-tree-view-alt />
<span>Pathfinding</span>
</div>
</button>

<!-- Pathfinding Mode Inputs -->
<template v-if="pathfindingMode">
<VueInput v-model="pathfindingStart" placeholder="Start module..." />
<button i-carbon-arrow-right rounded-full op50 hover:text-primary-500 hover:op100 @click="swapStartAndEnd" />
<VueInput v-model="pathfindingEnd" placeholder="End module..." />
</template>

<!-- Normal Search Mode Input -->
<VueInput v-else v-model="text" placeholder="Search modules..." />

<div v-for="item in selectableItems" :key="item[0]" flex="~ gap-2 items-center">
<VueCheckbox v-model="settings[item[0]]" />
<span :class="{ 'text-gray-400 dark:text-gray-600': !settings[item[0]] }">Show {{ item[1] ?? item[0] }}</span>
</div>
<div flex-auto />
<button v-if="filterId" rounded-full bg-gray:20 py1 pl3 pr2 text-xs op50 hover:op100 @click="filterId = ''">
<button v-if="!pathfindingMode && filterId" rounded-full bg-gray:20 py1 pl3 pr2 text-xs op50 hover:op100 @click="filterId = ''">
Clear filter
<div i-carbon-close mb2px />
</button>
Expand Down
Loading
Loading