138 lines
4.4 KiB
TypeScript
138 lines
4.4 KiB
TypeScript
import { Plugin } from 'obsidian';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
const { shell } = require('electron');
|
|
|
|
// Simple YAML parser for color properties
|
|
function parseYAML(text: string) {
|
|
const result: Record<string, string> = {};
|
|
const lines = text.split('\n');
|
|
for (const line of lines) {
|
|
const trimmed = line.trim();
|
|
if (trimmed && trimmed.includes(':')) {
|
|
const [key, ...valueParts] = trimmed.split(':');
|
|
const value = valueParts.join(':').trim();
|
|
if (key && value) {
|
|
result[key.trim()] = value;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
export default class MenuPlugin extends Plugin {
|
|
async onload() {
|
|
this.registerMarkdownCodeBlockProcessor('menu', (source, el, ctx) => {
|
|
const lines = source.trim().split('\n');
|
|
let layout = '';
|
|
let colors: Record<string, string> = {};
|
|
const links: string[] = [];
|
|
|
|
// Parse YAML-like properties and links
|
|
for (const line of lines) {
|
|
const trimmed = line.trim();
|
|
if (trimmed.startsWith('layout:') || trimmed.startsWith('class:')) {
|
|
const colonIndex = trimmed.indexOf(':');
|
|
layout = trimmed.substring(colonIndex + 1).trim();
|
|
} else if (trimmed.includes(':') && !trimmed.startsWith('[') && !trimmed.startsWith('[[')) {
|
|
// Parse color properties
|
|
const [key, ...valueParts] = trimmed.split(':');
|
|
const value = valueParts.join(':').trim();
|
|
if (key && value && !key.includes('//') && !key.includes('http')) {
|
|
colors[key.trim()] = value;
|
|
}
|
|
} else if (trimmed && !trimmed.includes(':')) {
|
|
links.push(trimmed);
|
|
} else if (trimmed.startsWith('[')) {
|
|
links.push(trimmed);
|
|
}
|
|
}
|
|
|
|
const finalLayout = layout || 'default';
|
|
const container = el.createEl('div', { cls: `menu-container ${finalLayout}` });
|
|
|
|
// Apply custom colors as CSS variables
|
|
if (Object.keys(colors).length > 0) {
|
|
for (const [key, value] of Object.entries(colors)) {
|
|
// Replace 'accent' with 'hover-text' for consistency
|
|
let cssKey = key.replace(/accent/g, 'hover-text');
|
|
container.style.setProperty(`--${cssKey}`, value);
|
|
}
|
|
// Example: ensure hover-text is set and add a comment for usage
|
|
if (colors['hover-text']) {
|
|
container.style.setProperty('--hover-text', colors['hover-text']);
|
|
// To use in CSS: a:hover { color: var(--hover-text); }
|
|
}
|
|
}
|
|
|
|
// Process each link
|
|
for (const link of links) {
|
|
if (link.startsWith('[[') && link.endsWith(']]')) {
|
|
// Internal link
|
|
const linkContent = link.slice(2, -2);
|
|
let href = linkContent;
|
|
let text = linkContent;
|
|
if (linkContent.includes('|')) {
|
|
[href, text] = linkContent.split('|');
|
|
}
|
|
const a = container.createEl('a', {
|
|
text: text,
|
|
attr: { 'data-href': href }
|
|
});
|
|
a.addClass('menu-internal-link');
|
|
a.style.cursor = 'pointer';
|
|
a.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
const vaultName = this.app.vault.getName();
|
|
const encodedFile = encodeURIComponent(href);
|
|
const uri = `obsidian://open?vault=${encodeURIComponent(vaultName)}&file=${encodedFile}`;
|
|
window.open(uri);
|
|
});
|
|
} else if (link.match(/^\[.*\]\(.*\)$/)) {
|
|
// External link
|
|
const match = link.match(/^\[(.*)\]\((.*)\)$/);
|
|
if (match) {
|
|
const text = match[1];
|
|
const url = match[2];
|
|
const a = container.createEl('a', {
|
|
text: text,
|
|
attr: url.startsWith('file://') ? {} : { href: url, target: '_blank', rel: 'noopener noreferrer' }
|
|
});
|
|
a.style.cursor = 'pointer';
|
|
|
|
// Add appropriate class based on link type
|
|
if (url.startsWith('file://')) {
|
|
a.addClass('menu-file-link');
|
|
} else {
|
|
a.addClass('menu-external-link');
|
|
}
|
|
a.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
if (url.startsWith('file://')) {
|
|
try {
|
|
// Convert file URL to path and handle Windows paths
|
|
let filePath = decodeURIComponent(url.substring(7)); // Remove 'file://'
|
|
// Handle Windows paths that start with /C:
|
|
if (filePath.startsWith('/') && filePath.charAt(2) === ':') {
|
|
filePath = filePath.substring(1);
|
|
}
|
|
console.log('Opening file path:', filePath);
|
|
shell.openPath(filePath);
|
|
} catch (error) {
|
|
console.error('Failed to open file:', error);
|
|
}
|
|
} else {
|
|
window.open(url, '_blank', 'noopener,noreferrer');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
onunload() {
|
|
// Cleanup if needed
|
|
}
|
|
}
|