mirror of
https://gitlab.com/octtspacc/OcttKB
synced 2025-02-19 12:50:45 +01:00
603 lines
17 KiB
JavaScript
603 lines
17 KiB
JavaScript
|
/* eslint-disable max-lines */
|
|||
|
import type { IParseTreeNode } from 'tiddlywiki';
|
|||
|
import type { IScriptAddon } from '../../scriptAddon';
|
|||
|
|
|||
|
const colors = [
|
|||
|
'#5470c6',
|
|||
|
'#91cc75',
|
|||
|
'#fac858',
|
|||
|
'#ee6666',
|
|||
|
'#73c0de',
|
|||
|
'#3ba272',
|
|||
|
'#fc8452',
|
|||
|
'#9a60b4',
|
|||
|
'#ea7ccc',
|
|||
|
];
|
|||
|
const CategoriesEn = [
|
|||
|
'Focusing',
|
|||
|
'History',
|
|||
|
'Link To',
|
|||
|
'Backlink From',
|
|||
|
'Tag To',
|
|||
|
'Tag By',
|
|||
|
'Transclude',
|
|||
|
].map((name, index) => ({
|
|||
|
name,
|
|||
|
itemStyle: { color: colors[index % colors.length] },
|
|||
|
}));
|
|||
|
const CategoriesZh = [
|
|||
|
'聚焦',
|
|||
|
'历史',
|
|||
|
'链接',
|
|||
|
'反链',
|
|||
|
'标签',
|
|||
|
'作为标签',
|
|||
|
'嵌套',
|
|||
|
].map((name, index) => ({
|
|||
|
name,
|
|||
|
itemStyle: { color: colors[index % colors.length] },
|
|||
|
}));
|
|||
|
const attributes = new Set<string>([
|
|||
|
'focussedTiddler',
|
|||
|
'levels',
|
|||
|
'graphTitle',
|
|||
|
'aliasField',
|
|||
|
'excludeFilter',
|
|||
|
'previewDelay',
|
|||
|
]);
|
|||
|
const getPlatteColor = (name: string) =>
|
|||
|
$tw.wiki.renderText(
|
|||
|
'text/plain',
|
|||
|
'text/vnd.tiddlywiki',
|
|||
|
`<$transclude tiddler={{$:/palette}} index="${name}"><$transclude tiddler="$:/palettes/Vanilla" index="${name}"><$transclude tiddler="$:/config/DefaultColourMappings/${name}"/></$transclude></$transclude>`,
|
|||
|
{},
|
|||
|
);
|
|||
|
|
|||
|
const findIcon = (title: string) => {
|
|||
|
const fields = $tw.wiki.getTiddler(title)?.fields;
|
|||
|
if (!fields?.icon) {
|
|||
|
return undefined;
|
|||
|
}
|
|||
|
const iconFields = $tw.wiki.getTiddler(fields.icon as string)?.fields;
|
|||
|
if (!iconFields) {
|
|||
|
if (/^https?:\/\//.test(fields.icon as string)) {
|
|||
|
return `image://${fields.icon as string}`;
|
|||
|
}
|
|||
|
return undefined;
|
|||
|
}
|
|||
|
if (iconFields._canonical_uri) {
|
|||
|
return `image://${iconFields._canonical_uri}`;
|
|||
|
} else if (iconFields.title.startsWith('$:/core/images/')) {
|
|||
|
return undefined;
|
|||
|
} else {
|
|||
|
return `image://data:${iconFields.type};base64,${iconFields.text}`;
|
|||
|
}
|
|||
|
};
|
|||
|
const getAliasOrTitle = (
|
|||
|
tiddlerTitle: string,
|
|||
|
aliasField: string | undefined,
|
|||
|
): [string, boolean] => {
|
|||
|
if (aliasField === undefined || aliasField === 'title') {
|
|||
|
return [tiddlerTitle, Boolean($tw.wiki.getTiddler(tiddlerTitle))];
|
|||
|
}
|
|||
|
const tiddler = $tw.wiki.getTiddler(tiddlerTitle);
|
|||
|
if (tiddler) {
|
|||
|
const aliasValue = tiddler.fields[aliasField];
|
|||
|
return [
|
|||
|
typeof aliasValue === 'string'
|
|||
|
? $tw.wiki.renderText('text/plain', 'text/vnd.tiddlywiki', aliasValue, {
|
|||
|
variables: { currentTiddler: tiddlerTitle },
|
|||
|
})
|
|||
|
: tiddlerTitle,
|
|||
|
true,
|
|||
|
];
|
|||
|
} else {
|
|||
|
return [tiddlerTitle, false];
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
interface ITheBrainState {
|
|||
|
currentlyFocused?: string;
|
|||
|
historyTiddlers: string[];
|
|||
|
viewingTiddlers: Set<string>;
|
|||
|
focusing?: string;
|
|||
|
}
|
|||
|
|
|||
|
const TheBrainAddon: IScriptAddon<ITheBrainState> = {
|
|||
|
onMount: (myChart, attributes) => {
|
|||
|
myChart.on('click', { dataType: 'node' }, (event: any) => {
|
|||
|
new $tw.Story().navigateTiddler(event.data.name);
|
|||
|
});
|
|||
|
return {
|
|||
|
historyTiddlers: [],
|
|||
|
viewingTiddlers: new Set(),
|
|||
|
focusing: attributes.focussedTiddler,
|
|||
|
};
|
|||
|
},
|
|||
|
shouldUpdate: (
|
|||
|
{ viewingTiddlers, focusing, currentlyFocused },
|
|||
|
changedTiddlers,
|
|||
|
changedAttributes,
|
|||
|
) => {
|
|||
|
return (
|
|||
|
Object.keys(changedTiddlers).some(title => viewingTiddlers.has(title)) ||
|
|||
|
Object.keys(changedAttributes).some(attribute =>
|
|||
|
attributes.has(attribute),
|
|||
|
) ||
|
|||
|
(focusing === undefined &&
|
|||
|
$tw.wiki.getTiddlerText('$:/temp/focussedTiddler') !== currentlyFocused)
|
|||
|
);
|
|||
|
},
|
|||
|
// eslint-disable-next-line complexity
|
|||
|
onUpdate: (
|
|||
|
myCharts,
|
|||
|
state,
|
|||
|
addonAttributes: {
|
|||
|
focussedTiddler?: string;
|
|||
|
levels?: number;
|
|||
|
graphTitle?: string;
|
|||
|
aliasField?: string;
|
|||
|
excludeFilter?: string;
|
|||
|
previewDelay?: string;
|
|||
|
},
|
|||
|
) => {
|
|||
|
/** 参数:focussedTiddler 是图的中央节点 */
|
|||
|
let focussedTiddler =
|
|||
|
addonAttributes.focussedTiddler ||
|
|||
|
$tw.wiki.getTiddlerText('$:/temp/focussedTiddler')!;
|
|||
|
state.viewingTiddlers.clear();
|
|||
|
state.focusing = addonAttributes.focussedTiddler;
|
|||
|
state.currentlyFocused = focussedTiddler;
|
|||
|
if (!focussedTiddler) {
|
|||
|
return;
|
|||
|
}
|
|||
|
state.viewingTiddlers.add(focussedTiddler);
|
|||
|
if ($tw.wiki.getTiddler(focussedTiddler)?.fields['draft.of']) {
|
|||
|
focussedTiddler = $tw.wiki.getTiddler(focussedTiddler)!.fields[
|
|||
|
'draft.of'
|
|||
|
] as string;
|
|||
|
}
|
|||
|
const nodes: any[] = [];
|
|||
|
const edges: any[] = [];
|
|||
|
const ifChinese =
|
|||
|
$tw.wiki.getTiddlerText('$:/language')?.includes('zh') === true;
|
|||
|
/** 参数:levels 指定图向外展开几级 */
|
|||
|
let levels = Number(addonAttributes.levels);
|
|||
|
if (Number.isNaN(levels)) {
|
|||
|
levels = 1;
|
|||
|
}
|
|||
|
levels = Math.max(levels, 0);
|
|||
|
/** 参数:graphTitle 指定右下角显示的标题 */
|
|||
|
const graphTitle =
|
|||
|
addonAttributes.graphTitle || (ifChinese ? '聚焦' : 'Focusing Map');
|
|||
|
/** 参数:aliasField 用于指定展示为节点标题的字段,例如 caption */
|
|||
|
const aliasField =
|
|||
|
addonAttributes.aliasField === ''
|
|||
|
? undefined
|
|||
|
: addonAttributes.aliasField;
|
|||
|
/** 参数:excludeFilter 用于排除部分节点 */
|
|||
|
const excludeFilter =
|
|||
|
addonAttributes.excludeFilter === ''
|
|||
|
? undefined
|
|||
|
: $tw.wiki.compileFilter(
|
|||
|
addonAttributes.excludeFilter ?? '[prefix[$:/]]',
|
|||
|
);
|
|||
|
const nodeMap: Map<string, boolean> = new Map();
|
|||
|
|
|||
|
// 聚焦点
|
|||
|
nodes.push({
|
|||
|
name: focussedTiddler,
|
|||
|
// fixed: true,
|
|||
|
category: 0,
|
|||
|
label: {
|
|||
|
formatter: getAliasOrTitle(focussedTiddler, aliasField)[0],
|
|||
|
fontWeight: 'bold',
|
|||
|
fontSize: '15px',
|
|||
|
},
|
|||
|
symbol: findIcon(focussedTiddler),
|
|||
|
symbolSize: 15,
|
|||
|
select: {
|
|||
|
disabled: true,
|
|||
|
},
|
|||
|
itemStyle: {
|
|||
|
opacity: 1,
|
|||
|
borderColor: `${colors[0]}66`,
|
|||
|
borderWidth: 15,
|
|||
|
},
|
|||
|
isTag: false,
|
|||
|
tooltip: {
|
|||
|
show: false,
|
|||
|
},
|
|||
|
});
|
|||
|
|
|||
|
// 初始化:当前关注的 Tiddler
|
|||
|
let tiddlerQueue = [focussedTiddler];
|
|||
|
if (excludeFilter) {
|
|||
|
const tiddlers = new Set<string>(tiddlerQueue);
|
|||
|
for (const excluded of excludeFilter.call($tw.wiki, tiddlerQueue)) {
|
|||
|
tiddlers.delete(excluded);
|
|||
|
}
|
|||
|
tiddlerQueue = Array.from(tiddlers);
|
|||
|
}
|
|||
|
nodeMap.set(focussedTiddler, true);
|
|||
|
nodeMap.set('', false);
|
|||
|
|
|||
|
const tryPush = (
|
|||
|
title: string,
|
|||
|
node: (label: string, exist: boolean) => any,
|
|||
|
edge: (exist: boolean) => any,
|
|||
|
) => {
|
|||
|
if (excludeFilter && excludeFilter.call($tw.wiki, [title]).length > 0) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
const nodeState = nodeMap.get(title);
|
|||
|
const [label, exist] =
|
|||
|
nodeState === undefined
|
|||
|
? getAliasOrTitle(title, aliasField)
|
|||
|
: ['', nodeState];
|
|||
|
if (nodeState === undefined) {
|
|||
|
nodes.push(node(label, exist));
|
|||
|
nodeMap.set(title, exist);
|
|||
|
if (exist) {
|
|||
|
tiddlerQueue.push(title);
|
|||
|
}
|
|||
|
}
|
|||
|
edges.push(edge(exist));
|
|||
|
return exist;
|
|||
|
};
|
|||
|
|
|||
|
// 广搜 levels 层
|
|||
|
while (tiddlerQueue.length && levels-- > 0) {
|
|||
|
const tiddlers = tiddlerQueue;
|
|||
|
tiddlerQueue = [];
|
|||
|
for (const tiddler of tiddlers) {
|
|||
|
// 链接
|
|||
|
for (const linksTo of $tw.wiki.getTiddlerLinks(tiddler)) {
|
|||
|
tryPush(
|
|||
|
linksTo,
|
|||
|
(label, exist) => ({
|
|||
|
name: linksTo,
|
|||
|
label: { formatter: label },
|
|||
|
itemStyle: { opacity: exist ? 1 : 0.65 },
|
|||
|
symbol: findIcon(linksTo),
|
|||
|
category: 2,
|
|||
|
isTag: false,
|
|||
|
}),
|
|||
|
exist => ({
|
|||
|
source: tiddler,
|
|||
|
target: linksTo,
|
|||
|
lineStyle: {
|
|||
|
color: colors[2],
|
|||
|
type: exist ? 'solid' : 'dashed',
|
|||
|
},
|
|||
|
}),
|
|||
|
);
|
|||
|
}
|
|||
|
// 反链
|
|||
|
for (const backlinksFrom of $tw.wiki.getTiddlerBacklinks(tiddler)) {
|
|||
|
tryPush(
|
|||
|
backlinksFrom,
|
|||
|
(label, exist) => ({
|
|||
|
name: backlinksFrom,
|
|||
|
label: { formatter: label },
|
|||
|
itemStyle: { opacity: exist ? 1 : 0.65 },
|
|||
|
symbol: findIcon(backlinksFrom),
|
|||
|
category: 3,
|
|||
|
isTag: false,
|
|||
|
}),
|
|||
|
exist => ({
|
|||
|
source: backlinksFrom,
|
|||
|
target: tiddler,
|
|||
|
lineStyle: {
|
|||
|
color: colors[3],
|
|||
|
type: exist ? 'solid' : 'dashed',
|
|||
|
},
|
|||
|
}),
|
|||
|
);
|
|||
|
}
|
|||
|
// 标签
|
|||
|
for (const tag of $tw.wiki.getTiddler(focussedTiddler)?.fields?.tags ??
|
|||
|
[]) {
|
|||
|
tryPush(
|
|||
|
tag,
|
|||
|
(label, exist) => ({
|
|||
|
name: tag,
|
|||
|
label: { formatter: label },
|
|||
|
itemStyle: { opacity: exist ? 1 : 0.65 },
|
|||
|
symbol: findIcon(tag),
|
|||
|
category: 4,
|
|||
|
isTag: true,
|
|||
|
}),
|
|||
|
exist => ({
|
|||
|
source: tiddler,
|
|||
|
target: tag,
|
|||
|
lineStyle: {
|
|||
|
color: colors[4],
|
|||
|
type: exist ? 'solid' : 'dashed',
|
|||
|
},
|
|||
|
}),
|
|||
|
);
|
|||
|
}
|
|||
|
// 作为标签
|
|||
|
for (const tagBy of $tw.wiki.getTiddlersWithTag(tiddler)) {
|
|||
|
tryPush(
|
|||
|
tagBy,
|
|||
|
(label, exist) => ({
|
|||
|
name: tagBy,
|
|||
|
label: { formatter: label },
|
|||
|
itemStyle: { opacity: exist ? 1 : 0.65 },
|
|||
|
symbol: findIcon(tagBy),
|
|||
|
category: 5,
|
|||
|
isTag: false,
|
|||
|
}),
|
|||
|
exist => ({
|
|||
|
source: tagBy,
|
|||
|
target: tiddler,
|
|||
|
lineStyle: {
|
|||
|
color: colors[5],
|
|||
|
type: exist ? 'solid' : 'dashed',
|
|||
|
},
|
|||
|
}),
|
|||
|
);
|
|||
|
}
|
|||
|
// 嵌入
|
|||
|
const tiddler_ = $tw.wiki.getTiddler(tiddler);
|
|||
|
if (tiddler_) {
|
|||
|
const type = tiddler_.fields.type || 'text/vnd.tiddlywiki';
|
|||
|
if (type === 'text/vnd.tiddlywiki' || type === 'text/x-markdown') {
|
|||
|
const transcluded: Set<string> = new Set();
|
|||
|
const findTransclude = (children: IParseTreeNode[]) => {
|
|||
|
const { length } = children;
|
|||
|
for (let i = 0; i < length; i++) {
|
|||
|
const node = children[i];
|
|||
|
if (node.type === 'tiddler') {
|
|||
|
const title = node.attributes!.tiddler?.value as
|
|||
|
| string
|
|||
|
| undefined;
|
|||
|
if (title) {
|
|||
|
transcluded.add(title);
|
|||
|
}
|
|||
|
} else if (Array.isArray((node as any).children)) {
|
|||
|
findTransclude((node as any).children);
|
|||
|
}
|
|||
|
}
|
|||
|
};
|
|||
|
findTransclude($tw.wiki.parseTiddler(tiddler).tree);
|
|||
|
// eslint-disable-next-line max-depth
|
|||
|
for (const transcludeTiddler of transcluded) {
|
|||
|
tryPush(
|
|||
|
transcludeTiddler,
|
|||
|
(label, exist) => ({
|
|||
|
name: transcludeTiddler,
|
|||
|
label: { formatter: label },
|
|||
|
itemStyle: { opacity: exist ? 1 : 0.65 },
|
|||
|
symbol: findIcon(transcludeTiddler),
|
|||
|
category: 6,
|
|||
|
isTag: false,
|
|||
|
}),
|
|||
|
exist => ({
|
|||
|
source: tiddler,
|
|||
|
target: transcludeTiddler,
|
|||
|
lineStyle: {
|
|||
|
color: colors[6],
|
|||
|
type: exist ? 'solid' : 'dashed',
|
|||
|
},
|
|||
|
}),
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 历史路径
|
|||
|
let nextTiddler = focussedTiddler;
|
|||
|
const historyMap: Set<string> = new Set();
|
|||
|
for (let index = state.historyTiddlers.length - 2; index >= 0; index--) {
|
|||
|
const tiddlerTitle = state.historyTiddlers[index];
|
|||
|
if (
|
|||
|
historyMap.has(tiddlerTitle) ||
|
|||
|
tiddlerTitle === nextTiddler ||
|
|||
|
tiddlerTitle.startsWith('$:/')
|
|||
|
) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
tryPush(
|
|||
|
tiddlerTitle,
|
|||
|
(label, exist) => ({
|
|||
|
name: tiddlerTitle,
|
|||
|
label: { formatter: label, fontSize: '10px' },
|
|||
|
category: 1,
|
|||
|
symbol: findIcon(tiddlerTitle),
|
|||
|
symbolSize: 3,
|
|||
|
itemStyle: { opacity: exist ? 0.65 : 0.4 },
|
|||
|
isTag: false,
|
|||
|
}),
|
|||
|
// eslint-disable-next-line @typescript-eslint/no-loop-func
|
|||
|
exist => ({
|
|||
|
source: tiddlerTitle,
|
|||
|
target: nextTiddler,
|
|||
|
lineStyle: {
|
|||
|
color: colors[1],
|
|||
|
type: exist ? 'dashed' : 'dotted',
|
|||
|
opacity: 0.5,
|
|||
|
},
|
|||
|
}),
|
|||
|
);
|
|||
|
nextTiddler = tiddlerTitle;
|
|||
|
}
|
|||
|
|
|||
|
// 更新历史
|
|||
|
const historyIndex = state.historyTiddlers.indexOf(focussedTiddler);
|
|||
|
if (historyIndex > -1) {
|
|||
|
state.historyTiddlers.splice(historyIndex, 1);
|
|||
|
}
|
|||
|
state.historyTiddlers.push(focussedTiddler);
|
|||
|
state.historyTiddlers.slice(-10);
|
|||
|
|
|||
|
let lastTitle = '';
|
|||
|
let cache: Element[] | undefined;
|
|||
|
const cachedTooltipFormatter = ({
|
|||
|
data: { name, isTag },
|
|||
|
dataType,
|
|||
|
}: {
|
|||
|
data: { name: string; isTag: boolean };
|
|||
|
dataType: string;
|
|||
|
}) => {
|
|||
|
if (dataType !== 'node') {
|
|||
|
return [];
|
|||
|
}
|
|||
|
if (name !== lastTitle || !cache) {
|
|||
|
const container = $tw.utils.domMaker('div', {
|
|||
|
style: {
|
|||
|
maxWidth: '40vw',
|
|||
|
maxHeight: '50vh',
|
|||
|
overflowY: 'auto',
|
|||
|
whiteSpace: 'normal',
|
|||
|
},
|
|||
|
class: 'gk0wk-echarts-thebrain-popuptiddler-container',
|
|||
|
});
|
|||
|
if (isTag) {
|
|||
|
const ul = $tw.utils.domMaker('ul', {});
|
|||
|
const tiddlers = $tw.wiki.getTiddlersWithTag(name);
|
|||
|
const len = tiddlers.length;
|
|||
|
for (let i = 0; i < len; i++) {
|
|||
|
const tiddler = tiddlers[i];
|
|||
|
const li = $tw.utils.domMaker('li', {});
|
|||
|
const a = $tw.utils.domMaker('a', {
|
|||
|
text: tiddler,
|
|||
|
class:
|
|||
|
'tc-tiddlylink tc-tiddlylink-resolves tc-popup-handle tc-popup-absolute',
|
|||
|
style: {
|
|||
|
cursor: 'pointer',
|
|||
|
},
|
|||
|
});
|
|||
|
// eslint-disable-next-line @typescript-eslint/no-loop-func
|
|||
|
a.addEventListener('click', () =>
|
|||
|
new $tw.Story().navigateTiddler(tiddler),
|
|||
|
);
|
|||
|
li.appendChild(a);
|
|||
|
ul.appendChild(li);
|
|||
|
}
|
|||
|
cache = [ul];
|
|||
|
} else {
|
|||
|
// 不可以直接 renderText, 那种是 headless 渲染
|
|||
|
$tw.wiki
|
|||
|
.makeWidget(
|
|||
|
$tw.wiki.parseTiddler(
|
|||
|
'$:/plugins/Gk0Wk/echarts/addons/TheBrainPopup',
|
|||
|
),
|
|||
|
{
|
|||
|
document,
|
|||
|
parseAsInline: true,
|
|||
|
variables: { currentTiddler: name },
|
|||
|
} as any,
|
|||
|
)
|
|||
|
.render(container, null);
|
|||
|
cache = [
|
|||
|
container,
|
|||
|
$tw.utils.domMaker('style', {
|
|||
|
innerHTML: `.gk0wk-echarts-thebrain-popuptiddler-container::-webkit-scrollbar {display: none;} .gk0wk-echarts-thebrain-popuptiddler-container .tc-tiddler-controls { display: none; }`,
|
|||
|
}),
|
|||
|
];
|
|||
|
}
|
|||
|
lastTitle = name;
|
|||
|
}
|
|||
|
return cache;
|
|||
|
};
|
|||
|
|
|||
|
let previewDelay = Number(addonAttributes.previewDelay || '1000');
|
|||
|
if (!Number.isSafeInteger(previewDelay)) {
|
|||
|
previewDelay = -1;
|
|||
|
}
|
|||
|
myCharts.setOption({
|
|||
|
backgroundColor: 'transparent',
|
|||
|
legend: [
|
|||
|
{
|
|||
|
data: (ifChinese ? CategoriesZh : CategoriesEn).map(a => {
|
|||
|
return a.name;
|
|||
|
}),
|
|||
|
icon: 'circle',
|
|||
|
},
|
|||
|
],
|
|||
|
title: {
|
|||
|
text: graphTitle,
|
|||
|
show: true,
|
|||
|
top: 'bottom',
|
|||
|
left: 'right',
|
|||
|
},
|
|||
|
toolbox: {
|
|||
|
show: true,
|
|||
|
left: 0,
|
|||
|
bottom: 0,
|
|||
|
feature: {
|
|||
|
restore: {},
|
|||
|
saveAsImage: {},
|
|||
|
},
|
|||
|
},
|
|||
|
tooltip: {
|
|||
|
position: 'top',
|
|||
|
formatter: cachedTooltipFormatter,
|
|||
|
triggerOn: previewDelay >= 0 ? 'mousemove' : 'none',
|
|||
|
enterable: true,
|
|||
|
showDelay: Math.max(0, previewDelay),
|
|||
|
hideDelay: 800,
|
|||
|
confine: true,
|
|||
|
textStyle: {
|
|||
|
color: 'inherit',
|
|||
|
fontFamily: 'inherit',
|
|||
|
fontSize: 'inherit',
|
|||
|
},
|
|||
|
appendToBody: true,
|
|||
|
backgroundColor: getPlatteColor('page-background'),
|
|||
|
borderColor: getPlatteColor('very-muted-foreground'),
|
|||
|
},
|
|||
|
series: [
|
|||
|
{
|
|||
|
name: graphTitle,
|
|||
|
type: 'graph',
|
|||
|
layout: 'force',
|
|||
|
top: 0,
|
|||
|
bottom: 0,
|
|||
|
left: 0,
|
|||
|
right: 0,
|
|||
|
height: '100%',
|
|||
|
width: '100%',
|
|||
|
nodes,
|
|||
|
edges,
|
|||
|
categories: ifChinese ? CategoriesZh : CategoriesEn,
|
|||
|
roam: true,
|
|||
|
draggable: true,
|
|||
|
zoom: 4,
|
|||
|
label: {
|
|||
|
position: 'right',
|
|||
|
show: true,
|
|||
|
backgroundColor: 'transparent',
|
|||
|
},
|
|||
|
labelLayout: {
|
|||
|
moveOverlap: true,
|
|||
|
},
|
|||
|
force: {
|
|||
|
repulsion: 50,
|
|||
|
},
|
|||
|
cursor: 'pointer',
|
|||
|
symbolSize: 6,
|
|||
|
edgeSymbol: ['none', 'arrow'],
|
|||
|
edgeSymbolSize: [0, 5],
|
|||
|
lineStyle: {
|
|||
|
width: 1,
|
|||
|
opacity: 0.75,
|
|||
|
curveness: 0.15,
|
|||
|
},
|
|||
|
itemStyle: {
|
|||
|
opacity: 0.9,
|
|||
|
},
|
|||
|
},
|
|||
|
],
|
|||
|
} as any);
|
|||
|
},
|
|||
|
};
|
|||
|
|
|||
|
export default TheBrainAddon;
|
|||
|
/* eslint-enable max-lines */
|