Add plugin platform with modular backend and frontend registry.

Split graph/import/meta into Django apps, add API v1 with OpenAPI and pytest, and introduce plugin registry with the tags reference plugin on both FE and BE.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-25 09:35:51 +03:00
co-authored by Cursor
parent b694a596b8
commit cafc7335ad
77 changed files with 1801 additions and 490 deletions
+24 -1
View File
@@ -21,6 +21,17 @@
:active-values="activeFilters"
@toggle="toggleFilter"
/>
<div v-if="graphToolbarActions.length" class="graph-plugin-actions">
<button
v-for="action in graphToolbarActions"
:key="action.id"
type="button"
class="btn btn-secondary btn-sm"
@click="runGraphToolbarAction(action)"
>
{{ action.label }}
</button>
</div>
</div>
<div class="graph-area" ref="graphArea">
@@ -150,7 +161,7 @@
defineOptions({ name: 'Graph' })
import { ref, computed, onMounted, onUnmounted, onActivated, onDeactivated, nextTick, watch } from 'vue'
import { RouterLink } from 'vue-router'
import { RouterLink, useRouter } from 'vue-router'
import { Network, DataSet } from 'vis-network/standalone'
import { useContactsStore } from '../stores/contacts'
import { useCtrlLinkSelection } from '../composables/useCtrlLinkSelection'
@@ -168,6 +179,7 @@ import GraphNodeContextMenu from '../components/GraphNodeContextMenu.vue'
import GraphEdgeContextMenu from '../components/GraphEdgeContextMenu.vue'
import EditRelationModal from '../components/EditRelationModal.vue'
import { useGraphNodeContextMenu } from '../composables/useGraphNodeContextMenu.js'
import { getGraphToolbarActions } from '../core/pluginRegistry'
let themeObserver = null
let detachContextHandler = null
@@ -191,6 +203,8 @@ function openNodeInfo(node) {
}
const store = useContactsStore()
const router = useRouter()
const graphToolbarActions = getGraphToolbarActions()
const graphArea = ref(null)
const graphContainer = ref(null)
const loading = ref(true)
@@ -774,6 +788,10 @@ function toggleChrome() {
nextTick(() => network.value?.redraw())
}
function runGraphToolbarAction(action) {
action.onClick?.({ router })
}
watch(() => store.dataRevision, async (revision) => {
if (!network.value || revision === syncedRevision) return
await applyGraphDataFromStore()
@@ -832,6 +850,11 @@ onUnmounted(() => {
flex-shrink: 0;
padding: 0 28px 10px;
}
.graph-plugin-actions {
display: flex;
gap: 8px;
margin-top: 8px;
}
.graph-chrome-bar {
display: flex;
justify-content: center;