update
This commit is contained in:
@@ -7,8 +7,6 @@ import {
|
||||
normalizePath,
|
||||
} from "obsidian";
|
||||
import cytoscape, { Core, NodeSingular } from "cytoscape";
|
||||
// @ts-ignore — no types for cytoscape-fcose
|
||||
import fcose from "cytoscape-fcose";
|
||||
import {
|
||||
VIEW_TYPE_PROJECT_DETAILS_VIEW,
|
||||
VIEW_TYPE_PROJECT_VIEW,
|
||||
@@ -30,14 +28,12 @@ import {
|
||||
import { menu, breadcrumb, emptyState, openMarkdown } from "../ui";
|
||||
import { NameModal } from "../modals/NameModal";
|
||||
|
||||
cytoscape.use(fcose);
|
||||
|
||||
export interface ProjectDetailsState extends Record<string, unknown> {
|
||||
project: string;
|
||||
}
|
||||
|
||||
const NAME_RX = /^[^\\/:*?"<>|]+$/;
|
||||
const ROOT_ID = ":root:";
|
||||
const ROOT_ID = "__pk_root__";
|
||||
|
||||
function validateName(name: string, taken: string[], current?: string): string | null {
|
||||
if (!name) return "Name darf nicht leer sein.";
|
||||
@@ -155,6 +151,7 @@ export class ProjectDetailsView extends ItemView {
|
||||
const styles = getComputedStyle(this.containerEl);
|
||||
const textColor = styles.getPropertyValue("--text-normal").trim() || "#222";
|
||||
const borderColor = styles.getPropertyValue("--background-modifier-border").trim() || "#ccc";
|
||||
const edgeColor = styles.getPropertyValue("--text-muted").trim() || "#888";
|
||||
const accent = styles.getPropertyValue("--interactive-accent").trim() || "#7b6cd9";
|
||||
const bgNode = styles.getPropertyValue("--background-primary").trim() || "#fff";
|
||||
|
||||
@@ -201,28 +198,25 @@ export class ProjectDetailsView extends ItemView {
|
||||
{
|
||||
selector: "edge",
|
||||
style: {
|
||||
width: 1.5,
|
||||
"line-color": borderColor,
|
||||
"target-arrow-color": borderColor,
|
||||
width: 2,
|
||||
"line-color": edgeColor,
|
||||
"target-arrow-color": edgeColor,
|
||||
"target-arrow-shape": "triangle",
|
||||
"curve-style": "bezier",
|
||||
},
|
||||
},
|
||||
],
|
||||
layout: {
|
||||
name: "fcose",
|
||||
// @ts-ignore — fcose options not in cytoscape types
|
||||
quality: "default",
|
||||
randomize: true,
|
||||
animate: false,
|
||||
name: "concentric",
|
||||
concentric: (node) => -((node.data("depth") as number) ?? 0),
|
||||
levelWidth: () => 1,
|
||||
minNodeSpacing: 60,
|
||||
// @ts-ignore — spacingFactor exists in concentric layout
|
||||
spacingFactor: 1.4,
|
||||
fit: true,
|
||||
padding: 30,
|
||||
nodeRepulsion: 8000,
|
||||
idealEdgeLength: 100,
|
||||
gravity: 0.25,
|
||||
fixedNodeConstraint: [
|
||||
{ nodeId: ROOT_ID, position: { x: 0, y: 0 } },
|
||||
],
|
||||
startAngle: -Math.PI / 2,
|
||||
clockwise: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -231,15 +225,17 @@ export class ProjectDetailsView extends ItemView {
|
||||
|
||||
private treeToElements(tree: NodeTree): cytoscape.ElementDefinition[] {
|
||||
const els: cytoscape.ElementDefinition[] = [];
|
||||
els.push({ data: { id: ROOT_ID, label: tree.name } });
|
||||
const walk = (node: NodeTree, parentId: string) => {
|
||||
for (const child of node.children) {
|
||||
els.push({ data: { id: child.path, label: child.name } });
|
||||
els.push({ data: { id: `${parentId}>${child.path}`, source: parentId, target: child.path } });
|
||||
walk(child, child.path);
|
||||
els.push({ data: { id: ROOT_ID, label: tree.name, path: tree.path, depth: 0 } });
|
||||
let counter = 0;
|
||||
const walk = (children: NodeTree[], parentId: string, depth: number) => {
|
||||
for (const child of children) {
|
||||
const id = `n${counter++}`;
|
||||
els.push({ data: { id, label: child.name, path: child.path, depth } });
|
||||
els.push({ data: { source: parentId, target: id } });
|
||||
walk(child.children, id, depth + 1);
|
||||
}
|
||||
};
|
||||
walk(tree, ROOT_ID);
|
||||
walk(tree.children, ROOT_ID, 1);
|
||||
return els;
|
||||
}
|
||||
|
||||
@@ -249,28 +245,27 @@ export class ProjectDetailsView extends ItemView {
|
||||
|
||||
cy.on("tap", "node", (ev) => {
|
||||
const node = ev.target as NodeSingular;
|
||||
const id = node.id();
|
||||
if (id === ROOT_ID) {
|
||||
if (node.id() === ROOT_ID) {
|
||||
const corePath = normalizePath(`${projectPath(this.project)}/${CORE_FILE}`);
|
||||
void openMarkdown(this.app, corePath, this.leaf);
|
||||
return;
|
||||
}
|
||||
void openMarkdown(this.app, nodeMdPath(id), this.leaf);
|
||||
const path = node.data("path") as string;
|
||||
void openMarkdown(this.app, nodeMdPath(path), this.leaf);
|
||||
});
|
||||
|
||||
cy.on("cxttap", "node", (ev) => {
|
||||
const node = ev.target as NodeSingular;
|
||||
const id = node.id();
|
||||
const originalEvent = ev.originalEvent as MouseEvent;
|
||||
originalEvent.preventDefault();
|
||||
if (id === ROOT_ID) {
|
||||
if (node.id() === ROOT_ID) {
|
||||
const parent = projectPath(this.project);
|
||||
menu(originalEvent, [
|
||||
{ title: "Neuer Child-Node", icon: "plus", onClick: () => this.openCreateChild(parent) },
|
||||
]);
|
||||
return;
|
||||
}
|
||||
const folder = id;
|
||||
const folder = node.data("path") as string;
|
||||
const parentPath = folder.substring(0, folder.lastIndexOf("/"));
|
||||
const currentName = folder.split("/").pop() ?? "";
|
||||
menu(originalEvent, [
|
||||
@@ -320,8 +315,10 @@ export class ProjectDetailsView extends ItemView {
|
||||
if (start) node.position(start);
|
||||
return;
|
||||
}
|
||||
const sourcePath = node.id();
|
||||
const targetPath = target.id() === ROOT_ID ? projectPath(this.project) : target.id();
|
||||
const sourcePath = node.data("path") as string;
|
||||
const targetPath = target.id() === ROOT_ID
|
||||
? projectPath(this.project)
|
||||
: (target.data("path") as string);
|
||||
const currentParent = sourcePath.substring(0, sourcePath.lastIndexOf("/"));
|
||||
if (currentParent === targetPath) {
|
||||
if (start) node.position(start);
|
||||
|
||||
Reference in New Issue
Block a user