Skip to content

Commit e7c4fe4

Browse files
committed
Text color is now chosen based on the --text-normal attribute of .theme-light or .theme-dark from the theme being used by obsidian.
1 parent cd44cb6 commit e7c4fe4

File tree

2 files changed

+63
-23
lines changed

2 files changed

+63
-23
lines changed

linkManager.ts

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,71 @@ import extractLinks from 'markdown-link-extractor';
99
export class LinkManager {
1010
linksMap: Map<string, GltLink>;
1111
api = getAPI();
12+
currentTheme : string;
13+
textColor : string;
1214

1315
constructor() {
1416
this.linksMap = new Map<string, GltLink>();
17+
18+
// Detect changes to the theme.
19+
this.detectThemeChange();
1520
}
1621

1722
generateKey(sourceId: string, targetId: string): string {
1823
return `${sourceId}-${targetId}`;
1924
}
25+
26+
private detectThemeChange(): void {
27+
let lastTheme = '';
28+
let lastStyleSheetHref = '';
29+
let debounceTimer: number;
30+
31+
const themeObserver = new MutationObserver(() => {
32+
clearTimeout(debounceTimer);
33+
debounceTimer = window.setTimeout(() => {
34+
this.currentTheme = document.body.classList.contains('theme-dark') ? 'theme-dark' : 'theme-light';
35+
const currentStyleSheetHref = document.querySelector('link[rel="stylesheet"][href*="theme"]')?.getAttribute('href');
36+
if ((this.currentTheme && this.currentTheme !== lastTheme) || (currentStyleSheetHref !== lastStyleSheetHref)) {
37+
console.log(`Theme has changed to: ${this.currentTheme}`);
38+
this.textColor = this.getComputedColorFromClass(this.currentTheme, '--text-normal');
39+
console.log(this.textColor);
40+
lastTheme = this.currentTheme;
41+
if (currentStyleSheetHref) {
42+
lastStyleSheetHref = currentStyleSheetHref;
43+
}
44+
}
45+
}, 100); // Debounce delay
46+
});
47+
48+
themeObserver.observe(document.body, { attributes: true, attributeFilter: ['class'] });
49+
themeObserver.observe(document.head, { childList: true, subtree: true, attributes: true, attributeFilter: ['href'] });
50+
}
51+
52+
private getComputedColorFromClass(className : string, cssVariable : string) : string {
53+
// Create a temporary element
54+
const tempElement = document.createElement('div');
55+
56+
// Apply the class to the temporary element
57+
tempElement.classList.add(className);
58+
document.body.appendChild(tempElement);
59+
60+
// Get the computed style of the temporary element
61+
const style = getComputedStyle(tempElement);
62+
const colorValue = style.getPropertyValue(cssVariable).trim();
63+
64+
// Remove the temporary element
65+
document.body.removeChild(tempElement);
66+
67+
// Check if the color is in HSL format
68+
if (colorValue.startsWith('hsl')) {
69+
// Return a default color based on some condition, e.g., current theme
70+
// This is a placeholder condition
71+
return document.body.classList.contains('theme-dark') ? '#b3b3b3' : '#5c5c5c';
72+
} else {
73+
// If it's not HSL, return the color as-is
74+
return colorValue;
75+
}
76+
}
2077

2178
addLink(renderer: ObsidianRenderer, obLink: ObsidianLink): void {
2279
const key = this.generateKey(obLink.source.id, obLink.target.id);
@@ -101,6 +158,7 @@ export class LinkManager {
101158
text.x = x;
102159
text.y = y;
103160
text.scale.set(1 / (3 * renderer.nodeScale));
161+
text.style.fill = this.textColor;
104162
}
105163
}
106164

@@ -129,7 +187,7 @@ export class LinkManager {
129187
const textStyle: TextStyle = new TextStyle({
130188
fontFamily: 'Arial',
131189
fontSize: 36,
132-
fill: this.determineTextColor()
190+
fill: this.textColor
133191
});
134192
// Create new text node
135193
const text: Text = new Text(linkString, textStyle);
@@ -149,22 +207,6 @@ export class LinkManager {
149207
return links.length > 0 ? links[0] : '';
150208
}
151209

152-
private determineTextColor(): string {
153-
// Get the computed style of the document body
154-
const style = getComputedStyle(document.body);
155-
156-
// This is a basic check. You might need to adjust the logic based on the themes you support.
157-
// Here, we assume that dark themes have a background color with a low brightness value.
158-
let textColor = '#FF0000';
159-
if (style && style.backgroundColor && style.backgroundColor) {
160-
// @ts-ignore
161-
const isDarkTheme = style.backgroundColor.match(/\d+/g)?.map(Number).slice(0, 3).reduce((a, b) => a + b, 0) < 382.5;
162-
isDarkTheme ? textColor = '#FFFFFF' : textColor = '#000000'; // White text for dark themes, black for light themes)
163-
}
164-
165-
return textColor
166-
}
167-
168210
// Method to determine the type of a value, now a class method
169211
private determineDataviewLinkType(value: any): DataviewLinkType {
170212
if (typeof value === 'object' && value !== null && 'path' in value) {
@@ -237,6 +279,5 @@ export class LinkManager {
237279
// Apply scaling and panning to calculate the actual position.
238280
return { x: linkX * scale + panX, y: linkY * scale + panY };
239281
}
240-
241282

242283
}

main.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Plugin, Notice} from 'obsidian';
22
import { getAPI } from 'obsidian-dataview';
3-
import { ObsidianRenderer, ObsidianLink, LinkPair} from 'types';
3+
import { ObsidianRenderer, ObsidianLink} from 'types';
44
import { LinkManager } from 'linkManager';
55

66

@@ -108,7 +108,7 @@ export default class GraphLinkTypesPlugin extends Plugin {
108108
const renderer : ObsidianRenderer = this.currentRenderer;
109109
// Remove existing text from the graph.
110110
this.linkManager.destroyMap(renderer);
111-
111+
112112
// Call the function to update positions in the next animation frame.
113113
requestAnimationFrame(this.updatePositions.bind(this));
114114
}
@@ -122,13 +122,11 @@ export default class GraphLinkTypesPlugin extends Plugin {
122122
return;
123123
}
124124

125-
126-
127125
const renderer: ObsidianRenderer = this.currentRenderer;
128126

129127
let updateMap = false;
130128

131-
if (this.animationFrameId && this.animationFrameId % 20 == 0) {
129+
if (this.animationFrameId && this.animationFrameId % 10 == 0) {
132130
updateMap = true;
133131
// Update link manager with the current frame's links
134132
this.linkManager.removeLinks(renderer, renderer.links);
@@ -159,5 +157,6 @@ export default class GraphLinkTypesPlugin extends Plugin {
159157
&& typeof renderer.px.stage.removeChild === 'function'
160158
&& Array.isArray(renderer.links);
161159
}
160+
162161
}
163162

0 commit comments

Comments
 (0)