mirror of
https://github.com/logseq/logseq.git
synced 2026-04-29 00:24:51 +00:00
@@ -1,65 +1,75 @@
|
||||
(ns frontend.handler.graph
|
||||
(:require [frontend.db :as db]
|
||||
[clojure.string :as string]
|
||||
[frontend.util :as util]
|
||||
[frontend.util :as util :refer [profile]]
|
||||
[frontend.date :as date]
|
||||
[frontend.state :as state]
|
||||
[clojure.set :as set]
|
||||
[medley.core :as medley]))
|
||||
[medley.core :as medley]
|
||||
[frontend.db.default :as default-db]
|
||||
[frontend.text :as text]))
|
||||
|
||||
(defn- build-edges
|
||||
[edges]
|
||||
(defn- build-links
|
||||
[links]
|
||||
(map (fn [[from to]]
|
||||
{:source from
|
||||
:target to})
|
||||
edges))
|
||||
links))
|
||||
|
||||
(defn- get-connections
|
||||
[page edges]
|
||||
[page links]
|
||||
(count (filter (fn [{:keys [source target]}]
|
||||
(or (= source page)
|
||||
(= target page)))
|
||||
edges)))
|
||||
links)))
|
||||
|
||||
(defn- build-nodes
|
||||
[dark? current-page edges tags nodes]
|
||||
(let [pages (->> (set (flatten nodes))
|
||||
[dark? current-page page-links tags nodes namespaces]
|
||||
(let [parents (set (map last namespaces))
|
||||
current-page (or current-page "")
|
||||
pages (->> (set (flatten nodes))
|
||||
(remove nil?))]
|
||||
(->>
|
||||
(mapv (fn [p]
|
||||
(when p
|
||||
(let [p (str p)
|
||||
current-page? (= p current-page)
|
||||
block? (and p (util/uuid-string? p))
|
||||
color (if block?
|
||||
"#1a6376"
|
||||
(case [dark? current-page?] ; FIXME: Put it into CSS
|
||||
[false false] "#222222"
|
||||
[false true] "#045591"
|
||||
[true false] "#8abbbb"
|
||||
[true true] "#ffffff"))
|
||||
color (if (contains? tags (string/lower-case (str p)))
|
||||
color (case [dark? current-page?] ; FIXME: Put it into CSS
|
||||
[false false] "#999"
|
||||
[false true] "#045591"
|
||||
[true false] "#93a1a1"
|
||||
[true true] "#ffffff")
|
||||
color (if (contains? tags p)
|
||||
(if dark? "orange" "green")
|
||||
color)]
|
||||
{:id p
|
||||
:name p
|
||||
:val (get-connections p edges)
|
||||
:autoColorBy "group"
|
||||
:group (js/Math.ceil (* (js/Math.random) 12))
|
||||
:color color})))
|
||||
(let [n (get page-links p 1)
|
||||
size-v (if (> n 2)
|
||||
(js/Math.cbrt n)
|
||||
n)
|
||||
size-v (if (< size-v 1)
|
||||
1
|
||||
(int size-v))
|
||||
size (* size-v 8)]
|
||||
(cond->
|
||||
{:id p
|
||||
:label p
|
||||
:size size
|
||||
:color color}
|
||||
(contains? parents p)
|
||||
(assoc :parent true))))))
|
||||
pages)
|
||||
(remove nil?))))
|
||||
|
||||
;; slow
|
||||
(defn- uuid-or-asset?
|
||||
[id]
|
||||
(let [id (str id)]
|
||||
(or (util/uuid-string? id)
|
||||
(string/starts-with? id "../assets/")
|
||||
(= id "..")
|
||||
(string/starts-with? id "assets/")
|
||||
(string/ends-with? id ".gif")
|
||||
(string/ends-with? id ".jpg")
|
||||
(string/ends-with? id ".png"))))
|
||||
(or (util/uuid-string? id)
|
||||
(string/starts-with? id "../assets/")
|
||||
(= id "..")
|
||||
(string/starts-with? id "assets/")
|
||||
(string/ends-with? id ".gif")
|
||||
(string/ends-with? id ".jpg")
|
||||
(string/ends-with? id ".png")))
|
||||
|
||||
(defn- remove-uuids-and-files!
|
||||
[nodes]
|
||||
@@ -68,73 +78,59 @@
|
||||
nodes))
|
||||
|
||||
(defn- normalize-page-name
|
||||
[{:keys [nodes links] :as g}]
|
||||
(let [all-pages (->> (set (apply concat
|
||||
[(map :id nodes)
|
||||
(map :source links)
|
||||
(map :target links)]))
|
||||
(map string/lower-case))
|
||||
names (db/pull-many '[:block/name :block/original-name] (mapv (fn [page]
|
||||
(if (util/uuid-string? page)
|
||||
[:block/uuid (uuid page)]
|
||||
[:block/name page])) all-pages))
|
||||
names (zipmap (map (fn [x] (get x :block/name)) names)
|
||||
(map (fn [x]
|
||||
(get x :block/original-name (:block/name x))) names))
|
||||
nodes (mapv (fn [node] (assoc node :id (get names (:id node) (:id node)))) nodes)
|
||||
links (->>
|
||||
links
|
||||
(remove (fn [{:keys [source target]}]
|
||||
(or (nil? source) (nil? target))))
|
||||
(mapv (fn [{:keys [source target]}]
|
||||
(when (and (not (uuid-or-asset? source))
|
||||
(not (uuid-or-asset? target)))
|
||||
{:source (get names (string/lower-case source))
|
||||
:target (get names (string/lower-case target))})))
|
||||
(remove nil?)
|
||||
(remove (fn [{:keys [source target]}]
|
||||
(or (nil? source) (nil? target)))))
|
||||
[{:keys [nodes links page-name->original-name]}]
|
||||
(let [links (->>
|
||||
(map
|
||||
(fn [{:keys [source target]}]
|
||||
(let [source (get page-name->original-name source)
|
||||
target (get page-name->original-name target)]
|
||||
(when (and source target)
|
||||
{:source source :target target})))
|
||||
links)
|
||||
(remove nil?))
|
||||
nodes (->> (remove-uuids-and-files! nodes)
|
||||
(util/distinct-by #(string/lower-case (:id %))))]
|
||||
(util/distinct-by (fn [node] (:id node)))
|
||||
(map (fn [node]
|
||||
(if-let [original-name (get page-name->original-name (:id node))]
|
||||
(assoc node :id original-name :label original-name)
|
||||
nil)))
|
||||
(remove nil?))]
|
||||
{:nodes nodes
|
||||
:links links}))
|
||||
|
||||
(defn build-global-graph
|
||||
[theme show-journal?]
|
||||
[theme {:keys [journal? orphan-pages? builtin-pages?] :as settings}]
|
||||
(let [dark? (= "dark" theme)
|
||||
current-page (:block/name (db/get-current-page))]
|
||||
current-page (or (:block/name (db/get-current-page)) "")]
|
||||
(when-let [repo (state/get-current-repo)]
|
||||
(let [relation (db/get-pages-relation repo show-journal?)
|
||||
(let [relation (db/get-pages-relation repo journal?)
|
||||
tagged-pages (db/get-all-tagged-pages repo)
|
||||
namespaces (db/get-all-namespace-relation repo)
|
||||
tags (set (map second tagged-pages))
|
||||
linked-pages (-> (concat
|
||||
relation
|
||||
tagged-pages)
|
||||
flatten
|
||||
set)
|
||||
all-pages (db/get-pages repo)
|
||||
other-pages (->> (remove linked-pages all-pages)
|
||||
(remove nil?))
|
||||
other-pages (if show-journal? other-pages
|
||||
(remove date/valid-journal-title? other-pages))
|
||||
other-pages (if (seq other-pages)
|
||||
(map string/lower-case other-pages)
|
||||
other-pages)
|
||||
nodes (concat (seq relation)
|
||||
full-pages (db/get-all-pages repo)
|
||||
get-original-name (fn [p] (or (:block/original-name p) (:block/name p)))
|
||||
all-pages (map get-original-name full-pages)
|
||||
page-name->original-name (zipmap (map :block/name full-pages) all-pages)
|
||||
pages-after-journal-filter (if-not journal?
|
||||
(remove :block/journal? full-pages)
|
||||
full-pages)
|
||||
links (concat (seq relation)
|
||||
(seq tagged-pages)
|
||||
(if (seq other-pages)
|
||||
(map (fn [page]
|
||||
[page])
|
||||
other-pages)
|
||||
[]))
|
||||
edges (build-edges (remove
|
||||
(fn [[_ to]]
|
||||
(nil? to))
|
||||
nodes))
|
||||
nodes (build-nodes dark? current-page edges tags nodes)]
|
||||
(seq namespaces))
|
||||
linked (set (flatten links))
|
||||
nodes (cond->> (map :block/name pages-after-journal-filter)
|
||||
(not builtin-pages?)
|
||||
(remove (fn [p] (default-db/built-in-pages-names (string/upper-case p))))
|
||||
(not orphan-pages?)
|
||||
(filter #(contains? linked (string/lower-case %))))
|
||||
page-links (reduce (fn [m [k v]] (-> (update m k inc)
|
||||
(update v inc))) {} links)
|
||||
links (build-links (remove (fn [[_ to]] (nil? to)) links))
|
||||
nodes (build-nodes dark? (string/lower-case current-page) page-links tags nodes namespaces)]
|
||||
(normalize-page-name
|
||||
{:nodes nodes
|
||||
:links edges})))))
|
||||
:links links
|
||||
:page-name->original-name page-name->original-name})))))
|
||||
|
||||
(defn build-page-graph
|
||||
[page theme]
|
||||
@@ -147,7 +143,9 @@
|
||||
tags (remove #(= page %) tags)
|
||||
ref-pages (db/get-page-referenced-pages repo page)
|
||||
mentioned-pages (db/get-pages-that-mentioned-page repo page)
|
||||
edges (concat
|
||||
namespaces (db/get-all-namespace-relation repo)
|
||||
links (concat
|
||||
namespaces
|
||||
(map (fn [[p aliases]]
|
||||
[page p]) ref-pages)
|
||||
(map (fn [[p aliases]]
|
||||
@@ -159,7 +157,7 @@
|
||||
(map first mentioned-pages))
|
||||
(remove nil?)
|
||||
(set))
|
||||
other-pages-edges (mapcat
|
||||
other-pages-links (mapcat
|
||||
(fn [page]
|
||||
(let [ref-pages (-> (map first (db/get-page-referenced-pages repo page))
|
||||
(set)
|
||||
@@ -171,21 +169,27 @@
|
||||
(map (fn [p] [page p]) ref-pages)
|
||||
(map (fn [p] [p page]) mentioned-pages))))
|
||||
other-pages)
|
||||
edges (->> (concat edges other-pages-edges)
|
||||
links (->> (concat links other-pages-links)
|
||||
(remove nil?)
|
||||
(distinct)
|
||||
(build-edges))
|
||||
(build-links))
|
||||
nodes (->> (concat
|
||||
[page]
|
||||
(map first ref-pages)
|
||||
(map first mentioned-pages)
|
||||
tags)
|
||||
(remove nil?)
|
||||
(distinct)
|
||||
(build-nodes dark? page edges (set tags)))]
|
||||
(distinct))
|
||||
nodes (build-nodes dark? page links (set tags) nodes namespaces)
|
||||
full-pages (db/get-all-pages repo)
|
||||
get-original-name (fn [p] (or (:block/original-name p)
|
||||
(:block/name p)))
|
||||
all-pages (map get-original-name full-pages)
|
||||
page-name->original-name (zipmap (map :block/name full-pages) all-pages)]
|
||||
(normalize-page-name
|
||||
{:nodes nodes
|
||||
:links edges})))))
|
||||
:links links
|
||||
:page-name->original-name page-name->original-name})))))
|
||||
|
||||
(defn build-block-graph
|
||||
"Builds a citation/reference graph for a given block uuid."
|
||||
@@ -193,13 +197,15 @@
|
||||
(let [dark? (= "dark" theme)]
|
||||
(when-let [repo (state/get-current-repo)]
|
||||
(let [ref-blocks (db/get-block-referenced-blocks block)
|
||||
edges (concat
|
||||
namespaces (db/get-all-namespace-relation repo)
|
||||
links (concat
|
||||
(map (fn [[p aliases]]
|
||||
[block p]) ref-blocks))
|
||||
[block p]) ref-blocks)
|
||||
namespaces)
|
||||
other-blocks (->> (concat (map first ref-blocks))
|
||||
(remove nil?)
|
||||
(set))
|
||||
other-blocks-edges (mapcat
|
||||
other-blocks-links (mapcat
|
||||
(fn [block]
|
||||
(let [ref-blocks (-> (map first (db/get-block-referenced-blocks block))
|
||||
(set)
|
||||
@@ -207,17 +213,40 @@
|
||||
(concat
|
||||
(map (fn [p] [block p]) ref-blocks))))
|
||||
other-blocks)
|
||||
edges (->> (concat edges other-blocks-edges)
|
||||
links (->> (concat links other-blocks-links)
|
||||
(remove nil?)
|
||||
(distinct)
|
||||
(build-edges))
|
||||
(build-links))
|
||||
nodes (->> (concat
|
||||
[block]
|
||||
(map first ref-blocks))
|
||||
(remove nil?)
|
||||
(distinct)
|
||||
;; FIXME: get block tags
|
||||
(build-nodes dark? block edges #{}))]
|
||||
)
|
||||
nodes (build-nodes dark? block links #{} nodes namespaces)]
|
||||
(normalize-page-name
|
||||
{:nodes nodes
|
||||
:links edges})))))
|
||||
:links links})))))
|
||||
|
||||
(defn n-hops
|
||||
"Get all nodes that are n hops from nodes (a collection of node ids)"
|
||||
[{:keys [links] :as graph} nodes level]
|
||||
(let [search-nodes (fn [forward?]
|
||||
(let [links (group-by (if forward? :source :target) links)]
|
||||
(loop [nodes nodes
|
||||
level level]
|
||||
(if (zero? level)
|
||||
nodes
|
||||
(recur (distinct (apply concat nodes
|
||||
(map
|
||||
(fn [id]
|
||||
(->> (get links id) (map (if forward? :target :source))))
|
||||
nodes)))
|
||||
(dec level))))))
|
||||
nodes (concat (search-nodes true) (search-nodes false))
|
||||
nodes (set nodes)]
|
||||
(update graph :nodes
|
||||
(fn [full-nodes]
|
||||
(filter (fn [node] (contains? nodes (:id node)))
|
||||
full-nodes)))))
|
||||
|
||||
Reference in New Issue
Block a user