This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

semble / .agent / logs / 20260304_semble_graph_view.md
5.2 kB

Sigma graph library#

Data Schema / Graph Data Model#

Node Types#

The graph consists of four primary node types:

  1. Users - User DIDs (Decentralized Identifiers)

    • Source: users.id
    • Display: users.handle or DID
  2. URLs - Web resources saved as URL cards

    • Source: cards.url where cards.type = 'URL'
    • Metadata: cards.urlType, cards.contentData
  3. Collections - Curated groups of cards

    • Source: collections.id
    • Display: collections.name
    • Metadata: collections.description, collections.accessType
  4. Note Cards - User annotations and notes

    • Source: cards.id where cards.type = 'NOTE'
    • Metadata: cards.contentData

Edge Types#

The graph relationships are derived from the following sources:

1. Follow Relationships#

User → User (social follows)

SELECT followerId, targetId
FROM follows
WHERE targetType = 'user'

User → Collection (collection follows)

SELECT followerId, targetId
FROM follows
WHERE targetType = 'collection'

2. Authorship (User → URL)#

Inferred from URL card creation:

SELECT authorId as userId, url
FROM cards
WHERE type = 'URL' AND url IS NOT NULL

3. Note Connections (Note → URL)#

Inferred from note cards referencing parent URL cards:

SELECT n.id as noteCardId, p.url
FROM cards n
JOIN cards p ON n.parentCardId = p.id
WHERE n.type = 'NOTE' AND p.type = 'URL'

4. Collection Memberships (Collection ↔ URL)#

Inferred from collection-card relationships:

SELECT cc.collectionId, c.url
FROM collection_cards cc
JOIN cards c ON cc.cardId = c.id
WHERE c.type = 'URL' AND c.url IS NOT NULL

Alternative (include metadata):

SELECT
  cc.collectionId,
  c.url,
  cc.addedBy,
  cc.addedAt
FROM collection_cards cc
JOIN cards c ON cc.cardId = c.id
WHERE c.url IS NOT NULL

Additional Relationship Data#

The connections table provides explicit curator-defined relationships between nodes:

SELECT
  curatorId,
  sourceType,  -- 'URL' or 'CARD'
  sourceValue, -- URL string or Card UUID
  targetType,  -- 'URL' or 'CARD'
  targetValue, -- URL string or Card UUID
  connectionType, -- SUPPORTS, OPPOSES, etc.
  note
FROM connections

These can be used to create typed/weighted edges with semantic meaning (e.g., "supports", "opposes").


Sigma.js + Next.js Setup#

1. Install#

npm install sigma graphology @react-sigma/core @react-sigma/layout-forceatlas2

2. Dynamic Import Component#

Create components/Graph.tsx (no 'use client' needed—dynamic import handles it):

'use client';
import { SigmaContainer, useSigma } from '@react-sigma/core';
import { useEffect, useState } from 'react';
import Graph from 'graphology';

export default function GraphView() {
  const [graph, setGraph] = useState<Graph | null>(null);

  useEffect(() => {
    const g = new Graph();
    g.addNode('a', { x: 0, y: 0, size: 10, label: 'Node A' });
    g.addNode('b', { x: 1, y: 1, size: 15, label: 'Node B' });
    g.addEdge('a', 'b');
    setGraph(g);
  }, []);

  if (!graph) return null;

  return (
    <SigmaContainer style={{ height: '600px', background: '#1e1e1e' }} graph={graph}>
      <GraphEvents />
    </SigmaContainer>
  );
}

3. Events & Camera Controls#

function GraphEvents() {
  const sigma = useSigma();

  const zoomToNode = (nodeId: string) => {
    const node = sigma.getGraph().getNodeAttributes(nodeId);
    sigma.getCamera().animate(
      { x: node.x, y: node.y, ratio: 0.3 },
      { duration: 800 }
    );
  };

  return (
    <Controls zoomToNode={zoomToNode} />
  );
}

4. Page Integration#

import dynamic from 'next/dynamic';

const Graph = dynamic(() => import('@/components/Graph'), { ssr: false });

export default function Page() {
  return <Graph />;
}

5. Force Layout#

Add physics with useLayoutForceAtlas2() hook from @react-sigma/layout-forceatlas2 1:

import { useLayoutForceAtlas2 } from '@react-sigma/layout-forceatlas2';

function GraphWithPhysics({ graph }: { graph: Graph }) {
  const { start, stop } = useLayoutForceAtlas2({
    settings: { gravity: 0.5 },
  });

  useEffect(() => {
    start();
    return () => stop();
  }, []);
  return null;
}

Key notes: Always use ssr: false 2. Sigma uses WebGL 3—canvas rendering, not DOM elements.