summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorThomas F. K. Jorna <[email protected]>2022-09-27 16:32:13 +0200
committerGitHub <[email protected]>2022-09-27 16:32:13 +0200
commit6f783297f37e95d083b32a7160afcb55e309e883 (patch)
treeaf9fdc3db50ecbc61036d88c81eeea3703ff17cc /util
parent16a8da9e5107833032893bc4c0680b368ac423ac (diff)
parent1936250b99b8747d841edd83002ed20ec12aa793 (diff)
feat: add ability to add/remove nodes to/from the local graph from emacs
Adds the commands `org-roam-ui-add-to-local-graph` and `org-roam-ui-remove-from-local-graph`
Diffstat (limited to 'util')
-rw-r--r--util/findNthNeighbour.ts42
-rw-r--r--util/getLinkColor.ts82
-rw-r--r--util/getLinkNodeColor.ts23
-rw-r--r--util/getNodeColor.ts75
-rw-r--r--util/getNodeColorById.ts25
-rw-r--r--util/getThemeColor.ts3
-rw-r--r--util/hexToRGBA.ts11
-rw-r--r--util/isLinkRelatedToNode.ts7
-rw-r--r--util/nodeSize.ts34
-rw-r--r--util/normalizeLinkEnds.ts12
-rw-r--r--util/numberWithinRange.ts3
-rw-r--r--util/processOrg.tsx4
-rw-r--r--util/uniorg.tsx1
13 files changed, 319 insertions, 3 deletions
diff --git a/util/findNthNeighbour.ts b/util/findNthNeighbour.ts
new file mode 100644
index 0000000..d48adbc
--- /dev/null
+++ b/util/findNthNeighbour.ts
@@ -0,0 +1,42 @@
+import { LinksByNodeId } from '../pages'
+import { normalizeLinkEnds } from './normalizeLinkEnds'
+
+export const findNthNeighbors = ({
+ ids,
+ excludedIds,
+ n,
+ linksByNodeId,
+}: {
+ ids: string[]
+ excludedIds: string[]
+ n: number
+ linksByNodeId: LinksByNodeId
+}) => {
+ let queue = [ids[0]]
+ let todo: string[] = []
+ const completed = [ids[0]]
+ Array.from({ length: n }, () => {
+ queue.forEach((node) => {
+ const links = linksByNodeId[node as string] ?? []
+ links.forEach((link) => {
+ const [sourceId, targetId] = normalizeLinkEnds(link)
+ if (excludedIds.some((id) => [sourceId, targetId].includes(id))) {
+ return
+ }
+ if (!completed.includes(sourceId)) {
+ todo.push(sourceId)
+ return
+ }
+ if (!completed.includes(targetId)) {
+ todo.push(targetId)
+ return
+ }
+ return
+ })
+ })
+ queue = todo
+ todo.forEach((neighbor) => neighbor && completed.push(neighbor))
+ todo = []
+ })
+ return completed
+}
diff --git a/util/getLinkColor.ts b/util/getLinkColor.ts
new file mode 100644
index 0000000..33f7094
--- /dev/null
+++ b/util/getLinkColor.ts
@@ -0,0 +1,82 @@
+import { initialColoring, initialVisuals } from '../components/config'
+import { LinksByNodeId } from '../pages'
+import { getLinkNodeColor } from './getLinkNodeColor'
+import { getThemeColor } from './getThemeColor'
+
+export const getLinkColor = ({
+ sourceId,
+ targetId,
+ needsHighlighting,
+ theme,
+ visuals,
+ highlightColors,
+ opacity,
+ linksByNodeId,
+ coloring,
+ cluster,
+}: {
+ sourceId: string
+ targetId: string
+ needsHighlighting: boolean
+ theme: any
+ visuals: typeof initialVisuals
+ highlightColors: Record<string, any>
+ opacity: number
+ linksByNodeId: LinksByNodeId
+ coloring: typeof initialColoring
+ cluster: any
+}) => {
+ if (!visuals.linkHighlight && !visuals.linkColorScheme && !needsHighlighting) {
+ const nodeColor = getLinkNodeColor({
+ sourceId,
+ targetId,
+ linksByNodeId,
+ visuals,
+ coloring,
+ cluster,
+ })
+ return getThemeColor(nodeColor, theme)
+ }
+
+ if (!needsHighlighting && !visuals.linkColorScheme) {
+ const nodeColor = getLinkNodeColor({
+ sourceId,
+ targetId,
+ linksByNodeId,
+ visuals,
+ coloring,
+ cluster,
+ })
+ return highlightColors[nodeColor][visuals.backgroundColor](visuals.highlightFade * opacity)
+ }
+
+ if (!needsHighlighting) {
+ return highlightColors[visuals.linkColorScheme][visuals.backgroundColor](
+ visuals.highlightFade * opacity,
+ )
+ }
+
+ if (!visuals.linkHighlight && !visuals.linkColorScheme) {
+ const nodeColor = getLinkNodeColor({
+ sourceId,
+ targetId,
+ linksByNodeId,
+ visuals,
+ coloring,
+ cluster,
+ })
+ return getThemeColor(nodeColor, theme)
+ }
+
+ if (!visuals.linkHighlight) {
+ return getThemeColor(visuals.linkColorScheme, theme)
+ }
+
+ if (!visuals.linkColorScheme) {
+ return highlightColors[
+ getLinkNodeColor({ sourceId, targetId, linksByNodeId, visuals, coloring, cluster })
+ ][visuals.linkHighlight](opacity)
+ }
+
+ return highlightColors[visuals.linkColorScheme][visuals.linkHighlight](opacity)
+}
diff --git a/util/getLinkNodeColor.ts b/util/getLinkNodeColor.ts
new file mode 100644
index 0000000..86cfa4b
--- /dev/null
+++ b/util/getLinkNodeColor.ts
@@ -0,0 +1,23 @@
+import { initialColoring, initialVisuals } from '../components/config'
+import { LinksByNodeId } from '../pages'
+import { getNodeColorById } from './getNodeColorById'
+
+export const getLinkNodeColor = ({
+ sourceId,
+ targetId,
+ linksByNodeId,
+ visuals,
+ coloring,
+ cluster,
+}: {
+ sourceId: string
+ targetId: string
+ linksByNodeId: LinksByNodeId
+ visuals: typeof initialVisuals
+ coloring: typeof initialColoring
+ cluster: any
+}) => {
+ return linksByNodeId[sourceId]!.length > linksByNodeId[targetId]!.length
+ ? getNodeColorById({ id: sourceId, linksByNodeId, visuals, cluster, coloring })
+ : getNodeColorById({ id: targetId, visuals, linksByNodeId, cluster, coloring })
+}
diff --git a/util/getNodeColor.ts b/util/getNodeColor.ts
new file mode 100644
index 0000000..f25a15b
--- /dev/null
+++ b/util/getNodeColor.ts
@@ -0,0 +1,75 @@
+import { OrgRoamNode } from '../api'
+import { initialColoring, initialVisuals } from '../components/config'
+import { LinksByNodeId } from '../pages'
+import { getNodeColorById } from './getNodeColorById'
+import { getThemeColor } from './getThemeColor'
+
+export const getNodeColor = ({
+ node,
+ theme,
+ highlightedNodes,
+ previouslyHighlightedNodes,
+ visuals,
+ tagColors,
+ highlightColors,
+ opacity,
+ emacsNodeId,
+ linksByNodeId,
+ cluster,
+ coloring,
+}: {
+ node: OrgRoamNode
+ theme: any
+ visuals: typeof initialVisuals
+ highlightedNodes: Record<string, any>
+ previouslyHighlightedNodes: Record<string, any>
+ tagColors: Record<string, any>
+ highlightColors: Record<string, any>
+ opacity: number
+ emacsNodeId: string | null
+ linksByNodeId: LinksByNodeId
+ cluster: any
+ coloring: typeof initialColoring
+}) => {
+ const needsHighlighting = highlightedNodes[node.id!] || previouslyHighlightedNodes[node.id!]
+ //const needsHighlighting = hoverNode?.id === node.id! || lastHoverNode?.current?.id === node.id
+ // if we are matching the node color and don't have a highlight color
+ // or we don't have our own scheme and we're not being highlighted
+ if (visuals.emacsNodeColor && node.id === emacsNodeId) {
+ return getThemeColor(visuals.emacsNodeColor, theme)
+ }
+ if (tagColors && node?.tags.some((tag) => tagColors[tag])) {
+ const tagColor = tagColors[node?.tags.filter((tag) => tagColors[tag])[0]]
+ return needsHighlighting
+ ? highlightColors[tagColor][tagColor](visuals.highlightFade * opacity)
+ : highlightColors[tagColor][visuals.backgroundColor](visuals.highlightFade * opacity)
+ }
+ if (visuals.citeNodeColor && node?.properties?.ROAM_REFS && node?.properties?.FILELESS) {
+ return needsHighlighting
+ ? getThemeColor(visuals.citeNodeColor, theme)
+ : highlightColors[visuals.citeNodeColor][visuals.backgroundColor](
+ visuals.highlightFade * opacity,
+ )
+ }
+ if (visuals.refNodeColor && node.properties.ROAM_REFS) {
+ return needsHighlighting
+ ? getThemeColor(visuals.refNodeColor, theme)
+ : highlightColors[visuals.refNodeColor][visuals.backgroundColor](
+ visuals.highlightFade * opacity,
+ )
+ }
+ if (!needsHighlighting) {
+ return highlightColors[
+ getNodeColorById({ id: node.id as string, cluster, coloring, linksByNodeId, visuals })
+ ][visuals.backgroundColor](visuals.highlightFade * opacity)
+ }
+ if (!visuals.nodeHighlight) {
+ return getThemeColor(
+ getNodeColorById({ id: node.id as string, cluster, coloring, linksByNodeId, visuals }),
+ theme,
+ )
+ }
+ return highlightColors[
+ getNodeColorById({ id: node.id as string, cluster, coloring, linksByNodeId, visuals })
+ ][visuals.nodeHighlight](opacity)
+}
diff --git a/util/getNodeColorById.ts b/util/getNodeColorById.ts
new file mode 100644
index 0000000..d2c198b
--- /dev/null
+++ b/util/getNodeColorById.ts
@@ -0,0 +1,25 @@
+import { initialColoring, initialVisuals } from '../components/config'
+import { LinksByNodeId } from '../pages'
+import { numberWithinRange } from './numberWithinRange'
+
+export const getNodeColorById = ({
+ id,
+ linksByNodeId,
+ visuals,
+ coloring,
+ cluster,
+}: {
+ id: string
+ linksByNodeId: LinksByNodeId
+ visuals: typeof initialVisuals
+ cluster: any
+ coloring: typeof initialColoring
+}) => {
+ const linklen = linksByNodeId[id!]?.length ?? 0
+ if (coloring.method === 'degree') {
+ return visuals.nodeColorScheme[
+ numberWithinRange(linklen, 0, visuals.nodeColorScheme.length - 1)
+ ]
+ }
+ return visuals.nodeColorScheme[linklen && cluster[id] % visuals.nodeColorScheme.length]
+}
diff --git a/util/getThemeColor.ts b/util/getThemeColor.ts
new file mode 100644
index 0000000..1175a52
--- /dev/null
+++ b/util/getThemeColor.ts
@@ -0,0 +1,3 @@
+export const getThemeColor = (name: string, theme: any) => {
+ return name.split('.').reduce((o, i) => o[i], theme.colors)
+}
diff --git a/util/hexToRGBA.ts b/util/hexToRGBA.ts
new file mode 100644
index 0000000..bacb601
--- /dev/null
+++ b/util/hexToRGBA.ts
@@ -0,0 +1,11 @@
+export function hexToRGBA(hex: string, opacity: number) {
+ return (
+ 'rgba(' +
+ (hex = hex.replace('#', ''))
+ .match(new RegExp('(.{' + hex.length / 3 + '})', 'g'))!
+ .map((l) => parseInt(hex.length % 2 ? l + l : l, 16))
+ .concat(isFinite(opacity) ? opacity : 1)
+ .join(',') +
+ ')'
+ )
+}
diff --git a/util/isLinkRelatedToNode.ts b/util/isLinkRelatedToNode.ts
new file mode 100644
index 0000000..eeacab7
--- /dev/null
+++ b/util/isLinkRelatedToNode.ts
@@ -0,0 +1,7 @@
+import { NodeObject, LinkObject } from 'force-graph'
+
+export const isLinkRelatedToNode = (link: LinkObject, node: NodeObject | null) => {
+ return (
+ (link.source as NodeObject)?.id! === node?.id! || (link.target as NodeObject)?.id! === node?.id!
+ )
+}
diff --git a/util/nodeSize.ts b/util/nodeSize.ts
new file mode 100644
index 0000000..3601c1e
--- /dev/null
+++ b/util/nodeSize.ts
@@ -0,0 +1,34 @@
+import { filter } from '@chakra-ui/react'
+import { initialVisuals } from '../components/config'
+import { LinksByNodeId } from '../pages'
+import { NodeObject } from 'force-graph'
+
+export const nodeSize = ({
+ linksByNodeId,
+ visuals,
+ highlightedNodes,
+ previouslyHighlightedNodes,
+ opacity,
+ node,
+}: {
+ node: NodeObject
+ visuals: typeof initialVisuals
+
+ highlightedNodes: Record<string, any>
+ previouslyHighlightedNodes: Record<string, any>
+ opacity: number
+ linksByNodeId: LinksByNodeId
+}) => {
+ const links = linksByNodeId[node.id!] ?? []
+ const parentNeighbors = links.length ? links.filter((link) => link.type === 'parent').length : 0
+ const basicSize =
+ 3 + links.length * visuals.nodeSizeLinks - (!filter.parent ? parentNeighbors : 0)
+ if (visuals.highlightNodeSize === 1) {
+ return basicSize
+ }
+ const highlightSize =
+ highlightedNodes[node.id!] || previouslyHighlightedNodes[node.id!]
+ ? 1 + opacity * (visuals.highlightNodeSize - 1)
+ : 1
+ return basicSize * highlightSize
+}
diff --git a/util/normalizeLinkEnds.ts b/util/normalizeLinkEnds.ts
new file mode 100644
index 0000000..43eee9c
--- /dev/null
+++ b/util/normalizeLinkEnds.ts
@@ -0,0 +1,12 @@
+import { OrgRoamLink } from '../api'
+import { LinkObject } from 'force-graph'
+
+export function normalizeLinkEnds(link: OrgRoamLink | LinkObject): [string, string] {
+ // we need to cover both because force-graph modifies the original data
+ // but if we supply the original data on each render, the graph will re-render sporadically
+ const sourceId =
+ typeof link.source === 'object' ? (link.source.id! as string) : (link.source as string)
+ const targetId =
+ typeof link.target === 'object' ? (link.target.id! as string) : (link.target as string)
+ return [sourceId, targetId]
+}
diff --git a/util/numberWithinRange.ts b/util/numberWithinRange.ts
new file mode 100644
index 0000000..ae435ce
--- /dev/null
+++ b/util/numberWithinRange.ts
@@ -0,0 +1,3 @@
+export const numberWithinRange = (num: number, min: number, max: number) => {
+ return Math.min(Math.max(num, min), max)
+}
diff --git a/util/processOrg.tsx b/util/processOrg.tsx
index ea868ee..2abc256 100644
--- a/util/processOrg.tsx
+++ b/util/processOrg.tsx
@@ -26,7 +26,7 @@ import remarkSectionize from 'remark-sectionize'
import remarkRehype from 'remark-rehype'
import { PreviewLink } from '../components/Sidebar/Link'
-import { LinksByNodeId, NodeByCite, NodeById, normalizeLinkEnds } from '../pages'
+import { LinksByNodeId, NodeByCite, NodeById } from '../pages'
import React, { createContext, ReactNode, useMemo } from 'react'
import { OrgImage } from '../components/Sidebar/OrgImage'
import { Section } from '../components/Sidebar/Section'
@@ -36,6 +36,7 @@ import { OrgRoamLink, OrgRoamNode } from '../api'
// @ts-expect-error non-ESM unified means no types
import { toString } from 'hast-util-to-string'
import { Box, chakra } from '@chakra-ui/react'
+import { normalizeLinkEnds } from './normalizeLinkEnds'
export interface ProcessedOrgProps {
nodeById: NodeById
@@ -126,7 +127,6 @@ export const ProcessedOrg = (props: ProcessedOrgProps) => {
const isMarkdown = previewNode?.file?.slice(-3) === '.md'
const baseProcessor = isMarkdown ? mdProcessor : orgProcessor
- console.log(macros)
const processor = useMemo(
() =>
baseProcessor
diff --git a/util/uniorg.tsx b/util/uniorg.tsx
index 4172d7a..8e285e6 100644
--- a/util/uniorg.tsx
+++ b/util/uniorg.tsx
@@ -1,5 +1,4 @@
import React, { useEffect, useMemo, useState } from 'react'
-import { OrgRoamLink, OrgRoamNode } from '../api'
import { LinksByNodeId, NodeByCite, NodeById } from '../pages/index'
import { ProcessedOrg } from './processOrg'