Skip to content

Commit

Permalink
Updated to API changes
Browse files Browse the repository at this point in the history
  • Loading branch information
HEmile committed Apr 20, 2021
1 parent 883bec2 commit b391a5d
Show file tree
Hide file tree
Showing 6 changed files with 20 additions and 204 deletions.
3 changes: 2 additions & 1 deletion docs/Juggl.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ Juggl is the next generation of PKM-focused graph views!
- **Code** is on Github: https://github.com/HEmile/juggl
- **Support the development** of Juggl:
- Buy me a kofi: https://ko-fi.com/Emile
- Sponsor me on Github: https://github.com/sponsors/HEmile
- Paypal.me: https://paypal.me/EvanKrieken

# Features
Expand Down Expand Up @@ -51,6 +50,8 @@ Note that Juggl is GPL3 **dual-**licensed. Contact me for details.
# Neo4j Graph View
If you came here looking for the documentation of the deprecated [[Neo4j Graph View Plugin]], please go [[Neo4j Graph View Plugin|here]].

<iframe src="https://github.com/sponsors/HEmile/card" title="Sponsor HEmile" height="225" width="600" style="border: 0;"></iframe>

---
#plugin
- author [[Emile van Krieken]]
Expand Down
117 changes: 3 additions & 114 deletions juggl/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,17 @@ import {
DefaultJugglSettings, LAYOUTS,
genStyleGroups, emptyStyleGroup,
} from './settings';
import {Juggl, MD_VIEW_TYPE} from './viz/visualization';
import {Juggl} from './viz/visualization';
import {ImageServer} from './image-server';
import type {
ICoreDataStore,
IDataStore,
IJugglStores,
ITypedLink,
ITypedLinkProperties,
IJugglPlugin,
IJuggl,
} from 'juggl-api';
import {OBSIDIAN_STORE_NAME, ObsidianStore} from './obsidian-store';
import cytoscape, {NodeSingular} from 'cytoscape';
// import coseBilkent from 'cytoscape-cose-bilkent';
import navigator from 'cytoscape-navigator';
import popper from 'cytoscape-popper';
import cola from 'cytoscape-cola';
Expand Down Expand Up @@ -50,9 +47,7 @@ import {GlobalWarningModal} from './ui/settings/global-graph-modal';
export default class JugglPlugin extends Plugin implements IJugglPlugin {
// Match around [[ and ]], and ensure content isn't a wikilnk closure
// This doesn't explicitly parse aliases.
static wikilinkRegex = '\\[\\[([^\\]\\r\\n]+?)\\]\\]';//
static CAT_DANGLING = 'dangling';
static nameRegex = '[^\\W\\d]\\w*';

settings: IJugglPluginSettings;
path: string;
Expand Down Expand Up @@ -294,6 +289,7 @@ export default class JugglPlugin extends Plugin implements IJugglPlugin {
this.setGlobalIcon();
this.addChild(new ImageServer(this));
}

public setGlobalIcon() {
if (this.ribbonIcon) {
this.ribbonIcon.detach();
Expand All @@ -304,6 +300,7 @@ export default class JugglPlugin extends Plugin implements IJugglPlugin {
});
}
}

public async openFileFromNode(node: NodeSingular, newLeaf= false): Promise<TFile> {
const id = VizId.fromNode(node);
if (!(id.storeId === 'core')) {
Expand Down Expand Up @@ -361,114 +358,6 @@ export default class JugglPlugin extends Plugin implements IJugglPlugin {
// '"}) OPTIONAL MATCH (n)-[r]-(m) RETURN n,r,m';
// }

_parseTags(tags: string[]): string[] {
return [].concat(...tags
.map((tag) => {
tag = tag.slice(1);
const hSplit = tag.split('/');
const tags = [];
for (const i in hSplit) {
const hTag = hSplit.slice(0, parseInt(i) + 1).join('-');
tags.push(`tag-${hTag}`);
}
return tags;
}));
}

public getClasses(file: TFile): string[] {
if (file) {
const classes = [];
if (['png', 'jpg', 'jpeg', 'gif', 'bmp', 'svg', 'tiff'].contains(file.extension)) {
classes.push('image');
} else if (['mp3', 'webm', 'wav', 'm4a', 'ogg', '3gp', 'flac'].contains(file.extension)) {
classes.push('audio');
} else if (['mp4', 'webm', 'ogv'].contains(file.extension)) {
classes.push('video');
} else if (file.extension === 'pdf') {
classes.push('pdf');
}
// This is replaced by the 'path' data attribute.
// if (!(file.parent.name === '/' || file.parent.name === '')) {
// classes.push(`folder-${file.parent.name
// .replace(' ', '_')}`);
// } else {
// classes.push('root');
// }
if (file.extension === 'md') {
classes.push('note');
const cache = this.app.metadataCache.getFileCache(file);
if (cache?.frontmatter) {
if ('image' in cache.frontmatter) {
classes.push('image');
}
if ('tags' in cache.frontmatter) {
const tags = parseFrontMatterTags(cache.frontmatter);
if (tags) {
classes.push(...this._parseTags(tags));
}
}
if ('cssclass' in cache.frontmatter) {
const clazzes = parseFrontMatterStringArray(cache.frontmatter, 'cssclass');
if (clazzes) {
classes.push(...clazzes);
}
}
}
if (cache?.tags) {
classes.push(...this._parseTags(cache.tags.map((t) => t.tag)));
}
} else {
classes.push('file');
}
return classes;
}
return [JugglPlugin.CAT_DANGLING];
}

regexEscape(str: string) {
return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}

public parseTypedLink(link: ReferenceCache, line: string): ITypedLink {
// TODO: This is something specific I use, but shouldn't keep being in this repo.
const regexPublishedIn = new RegExp(
`^${this.regexEscape(this.settings.typedLinkPrefix)} (publishedIn) (\\d\\d\\d\\d) (${JugglPlugin.wikilinkRegex},? *)+$`);
const matchPI = regexPublishedIn.exec(line);
if (!(matchPI === null)) {
return {
class: 'type-publishedIn',
isInline: false,
properties: {
year: matchPI[2],
context: '',
type: 'publishedIn',
} as ITypedLinkProperties,
} as ITypedLink;
}

// Intuition: Start with the typed link prefix. Then a neo4j name (nameRegex).
// Then one or more of the wikilink group: wikilink regex separated by optional comma and multiple spaces
const regex = new RegExp(
`^${this.regexEscape(this.settings.typedLinkPrefix)} (${JugglPlugin.nameRegex}) (${JugglPlugin.wikilinkRegex},? *)+$`);
const match = regex.exec(line);
const splitLink = link.original.split('|');
let alias = null;
if (splitLink.length > 1) {
alias = splitLink.slice(1).join().slice(0, -2);
}
if (!(match === null)) {
return {
class: `type-${match[1]}`,
isInline: false,
properties: {
alias: alias,
context: '',
type: match[1],
} as ITypedLinkProperties,
} as ITypedLink;
}
return null;
}

// executeQuery() {
// // Code taken from https://github.com/mrjackphil/obsidian-text-expand/blob/0.6.4/main.ts
Expand Down
88 changes: 7 additions & 81 deletions juggl/obsidian-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ import type JugglPlugin from './main';
import type {
NodeDefinition,
EdgeDefinition,
NodeDataDefinition,
NodeCollection,
EdgeDataDefinition, Collection,
} from 'cytoscape';
import {CLASS_EXPANDED} from './constants';
import {VizId} from 'juggl-api';
import {nodeDangling, nodeFromFile, parseRefCache, VizId} from 'juggl-api';

export const OBSIDIAN_STORE_NAME = 'Obsidian';

Expand Down Expand Up @@ -54,31 +52,7 @@ export class ObsidianStore extends Component implements ICoreDataStore {
if (toNodes.$id(otherId).length > 0) {
const edgeId = `${srcId}->${otherId}`;
const count = edgeId in edges ? edges[edgeId].length + 1 : 1;
const line = content[ref.position.start.line];
let data = {
id: `${edgeId}${count}`,
source: srcId,
target: otherId,
context: line,
edgeCount: 1,
} as EdgeDataDefinition;
const splitLink = ref.original.split('|');
if (splitLink.length > 1) {
data['alias'] = splitLink.slice(1).join().slice(0, -2);
}
let classes = '';
const typedLink = this.plugin.parseTypedLink(ref, line);
if (typedLink === null) {
classes = `${classes} inline`;
} else {
data = {...typedLink.properties, ...data};
classes = `${classes} ${typedLink.class}`;
}
const edge = {
group: 'edges',
data: data,
classes: classes,
} as EdgeDefinition;
const edge = parseRefCache(ref, content, `${edgeId}${count}`, srcId, otherId, this.plugin.settings.typedLinkPrefix);
if (edgeId in edges) {
edges[edgeId].push(edge);
} else {
Expand Down Expand Up @@ -166,9 +140,9 @@ ${edge.data.context}`;
const path = getLinkpath(link.link);
const file = this.metadata.getFirstLinkpathDest(path, sourcePath);
if (file) {
return await this.nodeFromFile(file);
return await nodeFromFile(file, this.plugin);
} else {
return this.nodeDangling(path);
return nodeDangling(path);
}
}

Expand All @@ -187,61 +161,13 @@ ${edge.data.context}`;
const file = this.vault.getAbstractFileByPath(otherPath) as TFile;
const id = VizId.fromFile(file).toId();
if (!(id in nodes)) {
nodes[id] = await this.nodeFromFile(file);
nodes[id] = await nodeFromFile(file, this.plugin);
}
}
}
}
}

async nodeFromFile(file: TFile) : Promise<NodeDefinition> {
const cache = this.metadata.getFileCache(file);
const name = file.extension === 'md' ? file.basename : file.name;
const classes = this.plugin.getClasses(file).join(' ');
const data = {
id: VizId.toId(file.name, this.storeId()),
name: name,
path: file.path,
resource_url: `http://localhost:${this.plugin.settings.imgServerPort}/${encodeURI(file.path)}`,
} as NodeDataDefinition;
data['content'] = await this.vault.cachedRead(file);
const frontmatter = cache?.frontmatter;
if (frontmatter) {
Object.keys(frontmatter).forEach((k) => {
if (!(k === 'position')) {
if (k === 'image') {
const imageField = frontmatter[k];
try {
// Check if url. throws error otherwise
new URL(imageField);
data[k] = imageField;
} catch {
data[k] = `http://localhost:${this.plugin.settings.imgServerPort}/${encodeURI(imageField)}`;
}
} else {
data[k] = frontmatter[k];
}
}
});
}

return {
group: 'nodes',
data: data,
classes: classes,
};
}

nodeDangling(path: string): NodeDefinition {
return {
group: 'nodes',
data: {
id: VizId.toId(path, this.storeId()),
name: path,
},
classes: 'dangling',
};
}

async getNeighbourhood(nodeIds: VizId[]): Promise<NodeDefinition[]> {
const nodes: Record<string, NodeDefinition> = {};
Expand All @@ -256,7 +182,7 @@ ${edge.data.context}`;
continue;
}
if (!(nodeId.toId() in nodes)) {
nodes[nodeId.toId()] = await this.nodeFromFile(file);
nodes[nodeId.toId()] = await nodeFromFile(file, this.plugin);
}
const promiseNodes: Record<string, Promise<NodeDefinition>> = {};
iterateCacheRefs(cache, (ref) => {
Expand Down Expand Up @@ -290,7 +216,7 @@ ${edge.data.context}`;
console.log('returning empty cache', nodeId);
return null;
}
return Promise.resolve(this.nodeFromFile(file));
return Promise.resolve(nodeFromFile(file, this.plugin));
}

async refreshNode(view: IJuggl, id: VizId) {
Expand Down
12 changes: 6 additions & 6 deletions juggl/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion juggl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"cytoscape-dblclick": "^0.3.1",
"cytoscape-navigator": "^2.0.1",
"cytoscape-popper": "^2.0.0",
"juggl-api": "github:hemile/juggl-api",
"juggl-api": "github:HEmile/juggl-api",
"obsidian": "github:obsidianmd/obsidian-api",
"search-query-parser": "^1.5.5"
}
Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "juggl",
"name": "Juggl",
"version": "1.1.0",
"version": "1.1.1",
"minAppVersion": "0.11.5",
"description": "Adds a completely interactive, stylable and expandable graph view to Obsidian.",
"author": "Emile",
Expand Down

0 comments on commit b391a5d

Please sign in to comment.