diff --git a/template/app/components/PageTree.vue b/template/app/components/PageTree.vue
index 33ae740..9d82a2c 100644
--- a/template/app/components/PageTree.vue
+++ b/template/app/components/PageTree.vue
@@ -7,7 +7,7 @@ const { data: pages } = await useAsyncData('nav', () =>
diff --git a/template/app/pages/[...slug].vue b/template/app/pages/[...slug].vue
index 99d36e2..18016f4 100644
--- a/template/app/pages/[...slug].vue
+++ b/template/app/pages/[...slug].vue
@@ -48,6 +48,7 @@ if (!page.value) {
width: 100%;
max-width: 800px;
padding: 0 1rem;
+ margin-bottom: 300px;
}
.page-tree-sidebar {
diff --git a/template/content/index.md b/template/content/index.md
index 08a564b..6b3ccb0 100644
--- a/template/content/index.md
+++ b/template/content/index.md
@@ -6,4 +6,6 @@ visible: true
Hola que tal! Hauria de funcionar
-Test
+Test
+
+Razas
diff --git a/template/convert.js b/template/convert.js
index 64c0985..1462cff 100644
--- a/template/convert.js
+++ b/template/convert.js
@@ -19,7 +19,9 @@
*
* VISIBILITY GATE:
* Only notes whose frontmatter contains `visible: true` are converted.
- * Any note missing the field, or with `visible: false`, is silently skipped.
+ * Any note missing the field, or with `visible: false`, is skipped.
+ * Wiki links that point to a hidden (or missing) note are rendered as dead
+ * links: instead of a normal link.
*
* Usage:
* node obsidian-to-nuxt.js --vault ./my-vault --out ./content --base-path /notes
@@ -108,6 +110,11 @@ function collectMarkdownFiles(dir) {
return results;
}
+/**
+ * Build a lookup map of stem/title/slug → URL for every **visible** note.
+ * Hidden notes (visible !== true) are intentionally excluded so that wiki
+ * links pointing at them resolve to nothing and are rendered as dead links.
+ */
function buildNoteIndex(files) {
const candidates = new Map();
@@ -118,27 +125,31 @@ function buildNoteIndex(files) {
}
for (const file of files) {
- const stem = path.basename(file, ".md");
- const rel = path.relative(VAULT_DIR, file).replace(/\.md$/, "");
- const url = BASE_PATH + "/" + rel.split(path.sep).map(slugify).join("/");
- const depth = rel.split(path.sep).length;
const content = fs.readFileSync(file, "utf8");
+ // Skip hidden notes — links to them must become dead links.
+ const { frontmatter, body } = parseFrontmatter(content);
+ if (!isVisible(frontmatter)) continue;
+
+ const stem = path.basename(file, ".md");
+ const rel = path.relative(VAULT_DIR, file).replace(/\.md$/, "");
+ const url = BASE_PATH + "/" + rel.split(path.sep).map(slugify).join("/");
+ const depth = rel.split(path.sep).length;
+
// Key 1: exact lowercase filename stem e.g. "note2"
addCandidate(stem.toLowerCase(), url, depth);
// Key 2: slugified filename stem e.g. "note-two" from "Note Two.md"
addCandidate(slugify(stem), url, depth);
- // Key 3: frontmatter title
- const fmMatch = content.match(/^---[\s\S]*?^title:\s*(.+)/m);
- if (fmMatch) {
- const title = fmMatch[1].trim().replace(/^["']|["']$/g, "");
+ // Key 3: frontmatter title (already parsed above — no second regex needed)
+ if (frontmatter.title) {
+ const title = String(frontmatter.title);
addCandidate(title.toLowerCase(), url, depth);
addCandidate(slugify(title), url, depth);
}
// Key 4: first H1 heading
- const h1Match = content.match(/^#\s+(.+)/m);
+ const h1Match = body.match(/^#\s+(.+)/m);
if (h1Match) {
const h1 = h1Match[1].trim();
addCandidate(h1.toLowerCase(), url, depth);
@@ -151,6 +162,24 @@ function buildNoteIndex(files) {
return result;
}
+/**
+ * Collect the lowercase stems (and slugified stems) of every note that exists
+ * in the vault but is NOT visible. Used to distinguish "hidden" dead links
+ * from "truly missing" dead links when rendering wiki links.
+ */
+function buildHiddenStems(files) {
+ const hidden = new Set();
+ for (const file of files) {
+ const content = fs.readFileSync(file, "utf8");
+ const { frontmatter } = parseFrontmatter(content);
+ if (isVisible(frontmatter)) continue;
+ const stem = path.basename(file, ".md");
+ hidden.add(stem.toLowerCase());
+ hidden.add(slugify(stem));
+ }
+ return hidden;
+}
+
// ─── Frontmatter ─────────────────────────────────────────────────────────────
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---/;
@@ -299,7 +328,12 @@ function convertEmbeds(body, noteIndex) {
// ─── Wiki links [[...]] ──────────────────────────────────────────────────────
-function convertWikiLinks(body, noteIndex) {
+/**
+ * @param {string} body
+ * @param {Map} noteIndex - visible notes only (stem/title → url)
+ * @param {Set} hiddenStems - stems of notes that exist but are not visible
+ */
+function convertWikiLinks(body, noteIndex, hiddenStems) {
return body.replace(/\[\[([^\]]+?)\]\]/g, function(_, ref) {
const pipeIdx = ref.indexOf("|");
let target, alias;
@@ -334,7 +368,12 @@ function convertWikiLinks(body, noteIndex) {
const label = alias || (anchor ? stem + " \u203a " + anchor : stem);
if (dead) {
- return '' + label + '';
+ // Distinguish between a note that is hidden vs one that doesn't exist at all.
+ const isHidden = hiddenStems.has(stem.toLowerCase()) || hiddenStems.has(slugify(stem));
+ const extraAttr = isHidden
+ ? ' data-hidden="' + stem + '"'
+ : ' data-missing="' + stem + '"';
+ return '' + label + '';
}
return "[" + label + "](" + url + ")";
});
@@ -342,7 +381,7 @@ function convertWikiLinks(body, noteIndex) {
// ─── Convert one file ────────────────────────────────────────────────────────
-function convertFile(content, noteIndex, filename) {
+function convertFile(content, noteIndex, hiddenStems, filename) {
let { frontmatter, body } = parseFrontmatter(content);
const inlineTags = extractInlineTags(body);
@@ -358,16 +397,14 @@ function convertFile(content, noteIndex, filename) {
body = convertCallouts(body);
body = convertBlockIds(body);
body = convertEmbeds(body, noteIndex);
- body = convertWikiLinks(body, noteIndex);
+ body = convertWikiLinks(body, noteIndex, hiddenStems);
// Prepend a title if the body doesn't already start with an H1 heading
const trimmed = body.trimStart();
const hasTitle = /^#[^#]/.test(trimmed);
if (!hasTitle) {
const title = frontmatter.title || path.basename(filename, ".md");
- if (title != "index") {
- body = "# " + title + "\n\n" + trimmed;
- }
+ body = "# " + title + "\n\n" + trimmed;
}
const fmString = Object.keys(frontmatter).length
@@ -385,8 +422,9 @@ function main() {
process.exit(1);
}
- const files = collectMarkdownFiles(VAULT_DIR);
- const noteIndex = buildNoteIndex(files);
+ const files = collectMarkdownFiles(VAULT_DIR);
+ const noteIndex = buildNoteIndex(files);
+ const hiddenStems = buildHiddenStems(files);
console.log("Found " + files.length + " markdown file(s) in " + VAULT_DIR);
@@ -416,7 +454,7 @@ function main() {
let result;
try {
- result = convertFile(content, noteIndex, path.basename(file));
+ result = convertFile(content, noteIndex, hiddenStems, path.basename(file));
} catch (err) {
console.warn(" \u26A0 Skipping " + rel + ": " + err.message);
skipCount++;
diff --git a/template/public/favicon.ico b/template/public/favicon.ico
index 18993ad..f39f2db 100644
Binary files a/template/public/favicon.ico and b/template/public/favicon.ico differ