This commit is contained in:
2026-07-18 01:32:38 +02:00
parent 270b292f6d
commit fcd25d4239
32 changed files with 865 additions and 236 deletions

3
template/.gitignore vendored
View File

@@ -23,4 +23,5 @@ logs
.env.*
!.env.example
content
content
vault

View File

@@ -1,66 +1,68 @@
/* ── Light Theme (Default) ────────────────────────────────── */
:root {
--bg-body: #faf6f0;
--bg-elevated: #fffdf8;
--text-primary: #2d2418;
--text-heading: #1a1408;
--text-secondary: #5c4a37;
--text-muted: #7a6b56;
--border-light: #f0e5d7;
--border-medium: #e8dbcc;
--border-accent: #d4c4b0;
--link-color: #b45309;
--link-hover: #92400e;
--link-border: rgba(180, 83, 9, 0.25);
--link-border-hover: rgba(180, 83, 9, 0.6);
--strong-color: #1a1408;
--marker-color: #5c4a37;
--blockquote-bg: #fdf9f3;
--blockquote-border: #d4c4b0;
--blockquote-text: #5c4a37;
--code-inline-bg: #fef3e2;
--code-inline-color: #9a4710;
--code-block-bg: #1f1910;
--code-block-color: #e8dbcc;
--table-border: #e8dbcc;
--table-header-bg: #fdf9f3;
--table-header-text: #4a3b2c;
--table-row-border: #f5efe6;
--table-cell-text: #5c4a37;
--hr-color: #e8dbcc;
--bg-body: #f4f1ee;
--bg-elevated: #faf8f5;
--bg-sidebar: #f4f1ee;
--text-primary: #1e1a2e;
--text-heading: #0f0c1a;
--text-secondary: #3d3457;
--text-muted: #6b5f82;
--border-light: #e2dde8;
--border-medium: #cec6d8;
--border-accent: #b8aac8;
--link-color: #9b2335;
--link-hover: #6e1522;
--link-border: rgba(155, 35, 53, 0.25);
--link-border-hover: rgba(155, 35, 53, 0.6);
--strong-color: #0f0c1a;
--marker-color: #6b3d7a;
--blockquote-bg: #f7f4f9;
--blockquote-border: #9b2335;
--blockquote-text: #3d3457;
--code-inline-bg: #f0ebf5;
--code-inline-color: #7c2d8c;
--code-block-bg: #12101e;
--code-block-color: #d4ccdf;
--table-border: #cec6d8;
--table-header-bg: #f0ecf5;
--table-header-text: #2a2040;
--table-row-border: #e8e3ef;
--table-cell-text: #3d3457;
--hr-color: #c8b850;
}
/* ── Dark Theme ───────────────────────────────────────────── */
@media (prefers-color-scheme: dark) {
:root {
--bg-body: #17120c;
--bg-elevated: #1f1810;
--text-primary: #d4c4b0;
--text-heading: #e8dbcc;
--text-secondary: #a89580;
--text-muted: #7a6b56;
--border-light: #2e2419;
--border-medium: #3d3125;
--border-accent: #4f4030;
--link-color: #fb923c;
--link-hover: #fdba74;
--link-border: rgba(251, 146, 60, 0.3);
--link-border-hover: rgba(251, 146, 60, 0.6);
--strong-color: #e8dbcc;
--marker-color: #a89580;
--blockquote-bg: #1f1810;
--blockquote-border: #4f4030;
--blockquote-text: #a89580;
--code-inline-bg: #2a2015;
--code-inline-color: #fb923c;
--code-block-bg: #0e0a06;
--code-block-color: #d4c4b0;
--table-border: #3d3125;
--table-header-bg: #1f1810;
--table-header-text: #cbd5c4;
--table-row-border: #2e2419;
--table-cell-text: #a89580;
--hr-color: #3d3125;
--bg-body: #0e0c18;
--bg-elevated: #15121f;
--bg-sidebar: #0d0d0f;
--text-primary: #cfc8dc;
--text-heading: #e4dff0;
--text-secondary: #9d93b5;
--text-muted: #6b5f82;
--border-light: #201c30;
--border-medium: #2e2844;
--border-accent: #3d3557;
--link-color: #e05567;
--link-hover: #f07d8a;
--link-border: rgba(224, 85, 103, 0.3);
--link-border-hover: rgba(224, 85, 103, 0.6);
--strong-color: #e4dff0;
--marker-color: #a07ab5;
--blockquote-bg: #15121f;
--blockquote-border: #c0293d;
--blockquote-text: #9d93b5;
--code-inline-bg: #1f1930;
--code-inline-color: #c084d8;
--code-block-bg: #080710;
--code-block-color: #cfc8dc;
--table-border: #2e2844;
--table-header-bg: #15121f;
--table-header-text: #c8c0d8;
--table-row-border: #201c30;
--table-cell-text: #9d93b5;
--hr-color: #8c7a20;
}
}
@@ -78,11 +80,26 @@ html {
tab-size: 4;
}
@font-face {
font-family: "Bookinsanity";
src: url("/fonts/Bookinsanity.otf") format("opentype");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Nodesto Caps Condensed";
src: url("/fonts/Nodesto Caps Condensed.otf") format("opentype");
font-weight: normal;
}
body {
margin: 0 auto;
padding: 0;
line-height: 1.7;
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
font-family: "Bookinsanity", system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
color: var(--text-primary);
background-color: var(--bg-body);
word-break: break-word;
@@ -102,24 +119,25 @@ body {
/* ── Typography ───────────────────────────────────────────── */
h1, h2, h3, h4, h5, h6 {
margin-top: 2em;
margin-top: 60px;
margin-bottom: 0.6em;
line-height: 1.3;
color: var(--text-heading);
font-weight: 700;
font-family: "Nodesto Caps Condensed", system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}
h1 {
font-size: 2.25rem;
padding-bottom: 0.5em;
border-bottom: 2px solid var(--border-medium);
border-bottom: 2px solid var(--hr-color);
letter-spacing: -0.02em;
}
h2 {
font-size: 1.75rem;
padding-bottom: 0.35em;
border-bottom: 1px solid var(--border-light);
border-bottom: 1px solid var(--border-medium);
}
h3 {
@@ -150,11 +168,21 @@ a {
text-decoration: none;
border-bottom: 1px solid var(--link-border);
transition: color 0.2s, border-color 0.2s;
&.dead-link {
color: var(--text-muted);
border-bottom-color: var(--border-light);
pointer-events: none;
}
}
a:hover {
color: var(--link-hover);
border-bottom-color: var(--link-border-hover);
&.dead-link {
color: var(--text-muted);
border-bottom-color: var(--border-light);
}
}
/* ── Emphasis & Strong ────────────────────────────────────── */
@@ -265,11 +293,11 @@ hr {
/* ── Images ───────────────────────────────────────────────── */
img {
max-width: 100%;
max-width: 600px;
height: auto;
border-radius: 8px;
display: block;
margin: 0.5em 0;
margin: 0.5em auto;
}
/* ── Forms ────────────────────────────────────────────────── */
@@ -302,4 +330,4 @@ textarea {
h3 {
font-size: 1.2rem;
}
}
}

View File

@@ -0,0 +1,21 @@
<!-- components/PageTree.vue -->
<script setup>
const { data: pages } = await useAsyncData('nav', () =>
queryCollectionNavigation('content')
)
</script>
<template>
<nav>
<h2>Notes</h2>
<TreeNode :nodes="pages" />
</nav>
</template>
<style lang="scss" scoped>
nav {
padding: 40px;
box-shadow: var(--shadow-elevation-medium);
background-color: var(--bg-sidebar);
}
</style>

View File

@@ -0,0 +1,51 @@
<!-- components/TreeNode.vue -->
<script setup>
defineProps({ nodes: Array, depth: { type: Number, default: 0 } })
const router = useRouter()
function getTitle(node) {
if (node.title == "index") return "Index"
return node.title || node._path?.split('/').pop() || 'Home';
}
function navigate(path) {
if (!path) return
router.push(path)
}
</script>
<template>
<ul :style="{ paddingLeft: depth ? '1rem' : '0' }">
<li v-for="node in nodes" :key="node.path ?? node.title">
<template v-if="!node.children?.length">
<a v-if="node.path" :href="node.path" @click.prevent="navigate(node.path)">
{{ getTitle(node) }}
</a>
<span v-else>{{ getTitle(node) }}</span>
</template>
<template v-else>
<span>📁 {{ getTitle(node) }}</span>
<TreeNode :nodes="node.children" :depth="depth + 1" />
</template>
</li>
</ul>
</template>
<style lang="scss" scoped>
ul {
list-style: none;
padding-left: 0;
}
li {
margin-bottom: 0;
}
a {
color: var(--text-link);
}
</style>

View File

@@ -11,8 +11,56 @@ if (!page.value) {
</script>
<template>
<div class="prose-wrapper" v-if="page">
<ContentRenderer :value="page" />
<div v-if="page" class="page-layout">
<aside class="page-tree-sidebar">
<PageTree />
</aside>
<div class="docs-container">
<div class="docs-layout">
<ContentRenderer :value="page" />
</div>
</div>
</div>
<NotFound />
<NotFound v-if="!page" />
</template>
<style lang="scss">
.page-layout {
display: grid;
grid-template-columns: minmax(0, 400px) minmax(0, 1fr);
justify-content: left;
column-gap: 2rem;
}
.docs-container {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.docs-layout {
h1:first-of-type {
margin-top: 80px;
}
width: 100%;
max-width: 800px;
padding: 0 1rem;
}
.page-tree-sidebar {
position: sticky;
top: 0;
height: 100vh;
overflow-y: auto;
/* Firefox */
scrollbar-width: none;
/* IE and old Edge */
-ms-overflow-style: none;
background-color: var(--bg-sidebar);
}
</style>

View File

@@ -1,3 +0,0 @@
# About Content Version 3
[Back home](/)

View File

@@ -1 +1,5 @@
Hola que tal!
# index
Hola que tal! Hauria de funcionar
Test

View File

@@ -8,7 +8,7 @@
* Transformations applied:
* 1. [[Wiki links]] → [Wiki links](/slug)
* 2. [[Wiki links|Alias]] → [Alias](/slug)
* 3. ![[image.png]] → <img> / NuxtImg tag
* 3. ![[image.png]] → <img> / <NuxtImg> tag
* 4. ![[note]] → MDC ::include component
* 5. Callouts > [!TYPE] Title → MDC ::callout{type} blocks
* 6. Block IDs ^block-id → <span id="block-id"> anchors
@@ -21,43 +21,61 @@
* node obsidian-to-nuxt.js --vault ./my-vault --out ./content --base-path /notes
*
* Options:
* --vault Path to Obsidian vault root (default: ./vault)
* --out Output directory for converted files (default: ./content)
* --base-path URL prefix for all generated links (default: "")
* --img-prefix URL prefix for embedded images (default: /images)
* --nuxt-img Use <NuxtImg> instead of <img> (flag)
* --dry-run Print conversions without writing files (flag)
* --vault Path to Obsidian vault root (default: ./vault)
* --out Output directory for converted files (default: ./content)
* --base-path URL prefix for all generated links (default: "")
* --img-prefix URL prefix for embedded images (default: /images)
* --nuxt-img Use <NuxtImg> instead of <img> (flag)
* --dry-run Print conversions without writing files (flag)
*/
import fs from "fs";
import fs from "fs";
import path from "path";
import { parseArgs } from "util";
// ─── CLI args ────────────────────────────────────────────────────────────────
// ─── CLI args (manual parser, no dependencies) ───────────────────────────────
const { values: args } = parseArgs({
options: {
vault: { type: "string", default: "./vault" },
out: { type: "string", default: "./content" },
"base-path":{ type: "string", default: "" },
"img-prefix":{ type: "string", default: "/images" },
"nuxt-img": { type: "boolean", default: false },
"dry-run": { type: "boolean", default: false },
},
});
function parseArgs() {
const argv = process.argv.slice(2);
const args = {
vault: "./vault",
out: "./content",
"base-path": "",
"img-prefix": "/images",
"nuxt-img": false,
"dry-run": false,
};
const VAULT_DIR = path.resolve(args["vault"]);
const OUT_DIR = path.resolve(args["out"]);
const BASE_PATH = args["base-path"].replace(/\/$/, "");
const IMG_PREFIX = args["img-prefix"].replace(/\/$/, "");
const USE_NUXT_IMG = args["nuxt-img"];
const DRY_RUN = args["dry-run"];
for (let i = 0; i < argv.length; i++) {
const key = argv[i].replace(/^--/, "");
if (argv[i].startsWith("--")) {
const next = argv[i + 1];
if (!next || next.startsWith("--")) {
// boolean flag
args[key] = true;
} else {
args[key] = next;
i++;
}
}
}
return args;
}
const args = parseArgs();
const VAULT_DIR = path.resolve(args["vault"]);
const OUT_DIR = path.resolve(args["out"]);
const BASE_PATH = (args["base-path"] || "").replace(/\/$/, "");
const IMG_PREFIX = (args["img-prefix"] || "/images").replace(/\/$/, "");
const USE_NUXT_IMG = args["nuxt-img"] === true || args["nuxt-img"] === "true";
const DRY_RUN = args["dry-run"] === true || args["dry-run"] === "true";
const IMAGE_EXTS = new Set([".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp", ".avif"]);
// ─── Utilities ───────────────────────────────────────────────────────────────
/** Turn a note name / file stem into a URL-safe slug */
function slugify(name) {
return name
.toLowerCase()
@@ -66,7 +84,6 @@ function slugify(name) {
.replace(/--+/g, "-");
}
/** Turn a heading string into a GitHub-style anchor slug */
function headingSlug(heading) {
return heading
.toLowerCase()
@@ -74,7 +91,6 @@ function headingSlug(heading) {
.replace(/[^\w\-]/g, "");
}
/** Recursively collect all .md files in a directory */
function collectMarkdownFiles(dir) {
const results = [];
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
@@ -88,25 +104,50 @@ function collectMarkdownFiles(dir) {
return results;
}
/**
* Build a lookup map: note stem (lowercase) → relative URL path
* Handles duplicate names by preferring the shortest path (vault-root wins).
*/
function buildNoteIndex(files) {
const index = new Map();
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 key = stem.toLowerCase();
if (!index.has(key) || rel.split(path.sep).length < index.get(key).depth) {
index.set(key, { url, depth: rel.split(path.sep).length });
const candidates = new Map();
function addCandidate(key, url, depth) {
if (key && (!candidates.has(key) || depth < candidates.get(key).depth)) {
candidates.set(key, { url, depth });
}
}
return new Map([...index.entries()].map(([k, v]) => [k, v.url]));
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");
// 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, "");
addCandidate(title.toLowerCase(), url, depth);
addCandidate(slugify(title), url, depth);
}
// Key 4: first H1 heading
const h1Match = content.match(/^#\s+(.+)/m);
if (h1Match) {
const h1 = h1Match[1].trim();
addCandidate(h1.toLowerCase(), url, depth);
addCandidate(slugify(h1), url, depth);
}
}
const result = new Map();
for (const [k, v] of candidates.entries()) result.set(k, v.url);
return result;
}
// ─── Frontmatter helpers ─────────────────────────────────────────────────────
// ─── Frontmatter ─────────────────────────────────────────────────────────────
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---/;
@@ -114,16 +155,15 @@ function parseFrontmatter(content) {
const match = content.match(FRONTMATTER_RE);
if (!match) return { frontmatter: {}, body: content };
const raw = match[1];
const raw = match[1];
const body = content.slice(match[0].length);
// Minimal YAML parser (handles string scalars and simple arrays)
const fm = {};
const fm = {};
let currentKey = null;
for (const line of raw.split("\n")) {
const listItem = line.match(/^\s+-\s+(.+)/);
if (listItem && currentKey) {
fm[currentKey] = Array.isArray(fm[currentKey]) ? fm[currentKey] : [];
if (!Array.isArray(fm[currentKey])) fm[currentKey] = [];
fm[currentKey].push(listItem[1].trim());
continue;
}
@@ -141,58 +181,45 @@ function serializeFrontmatter(fm) {
const lines = ["---"];
for (const [key, val] of Object.entries(fm)) {
if (Array.isArray(val)) {
lines.push(`${key}:`);
for (const v of val) lines.push(` - ${v}`);
lines.push(key + ":");
for (const v of val) lines.push(" - " + v);
} else {
lines.push(`${key}: ${val}`);
lines.push(key + ": " + val);
}
}
lines.push("---");
return lines.join("\n");
}
// ─── Inline-tag extractor ────────────────────────────────────────────────────
// ─── Inline tags ─────────────────────────────────────────────────────────────
/** Pull #tags from body text (skip # inside code fences or headings) */
function extractInlineTags(body) {
const tags = new Set();
// Skip fenced code blocks
const stripped = body.replace(/```[\s\S]*?```/g, "");
const tagRe = /(?<![#\w])#([a-zA-Z][a-zA-Z0-9_/-]*)/g;
for (const m of stripped.matchAll(tagRe)) {
tags.add(m[1]);
}
let m;
while ((m = tagRe.exec(stripped)) !== null) tags.add(m[1]);
return [...tags];
}
/** Remove inline #tags from body (but not ATX headings) */
function stripInlineTags(body) {
return body.replace(/(?<![#\w])#([a-zA-Z][a-zA-Z0-9_/-]*)/g, "").replace(/ {2,}/g, " ");
return body
.replace(/(?<![#\w])#([a-zA-Z][a-zA-Z0-9_/-]*)/g, "")
.replace(/ {2,}/g, " ");
}
// ─── Block IDs ───────────────────────────────────────────────────────────────
/**
* Replace trailing `^block-id` markers with an HTML anchor span.
* Obsidian places them at the end of a paragraph line.
*/
function convertBlockIds(body) {
return body.replace(/\s+\^([a-zA-Z0-9-]+)\s*$/gm, (_, id) => {
return ` <span id="${id}"></span>`;
return body.replace(/[ \t]+\^([a-zA-Z0-9-]+)\s*$/gm, function(_, id) {
return ' <span id="' + id + '"></span>';
});
}
// ─── Callouts ────────────────────────────────────────────────────────────────
const CALLOUT_START_RE = /^> \[!([A-Z]+)\]([+-]?)\s*(.*)/i;
const CALLOUT_START_RE = /^> \[!([A-Za-z]+)\][+-]?\s*(.*)/;
/**
* Convert Obsidian callout blocks to MDC (Nuxt Content) component syntax:
*
* > [!NOTE] Title ::callout{type="note" title="Title"}
* > content content
* ::
*/
function convertCallouts(body) {
const lines = body.split("\n");
const out = [];
@@ -200,122 +227,132 @@ function convertCallouts(body) {
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (!inCallout) {
const m = line.match(CALLOUT_START_RE);
if (m) {
const type = m[1].toLowerCase();
const title = m[3].trim();
const titleAttr = title ? ` title="${title}"` : "";
out.push(`::callout{type="${type}"${titleAttr}}`);
const title = m[2].trim();
const titleAttr = title ? ' title="' + title + '"' : "";
out.push('::callout{type="' + type + '"' + titleAttr + "}");
inCallout = true;
continue;
}
out.push(line);
} else {
// Continuation lines start with "> " or are blank (closing the block)
if (line.startsWith("> ")) {
out.push(line.slice(2)); // strip "> "
} else {
out.push("::"); // close MDC block
out.push(line);
}
} else {
if (line.startsWith("> ")) {
out.push(line.slice(2));
} else {
out.push("::");
out.push(line);
inCallout = false;
}
}
}
if (inCallout) out.push("::");
return out.join("\n");
}
// ─── Embedded content ![[...]] ──────────────────────────────────────────────
// ─── Embeds ![[...]] ─────────────────────────────────────────────────────────
function convertEmbeds(body, noteIndex) {
return body.replace(/!\[\[([^\]]+?)\]\]/g, (_, ref) => {
const [rawName, size] = ref.split("|");
const name = rawName.trim();
const ext = path.extname(name).toLowerCase();
return body.replace(/!\[\[([^\]]+?)\]\]/g, function(_, ref) {
const parts = ref.split("|");
const name = parts[0].trim();
const size = parts[1] ? parts[1].trim() : null;
const ext = path.extname(name).toLowerCase();
if (IMAGE_EXTS.has(ext)) {
// Image embed
const src = `${IMG_PREFIX}/${name}`;
const attrs = size ? ` width="${size.split("x")[0]}"` : "";
const src = IMG_PREFIX + "/" + name;
const altStr = path.basename(name, ext);
const wAttr = size ? ' width="' + size.split("x")[0] + '"' : "";
if (USE_NUXT_IMG) {
return `<NuxtImg src="${src}"${attrs} alt="${path.basename(name, ext)}" />`;
return '<NuxtImg src="' + src + '"' + wAttr + ' alt="' + altStr + '" />';
}
return `<img src="${src}"${attrs} alt="${path.basename(name, ext)}" />`;
return '<img src="' + src + '"' + wAttr + ' alt="' + altStr + '" />';
}
if (ext === ".mp4" || ext === ".webm") {
const src = `${IMG_PREFIX}/${name}`;
return `<video src="${src}" controls></video>`;
return '<video src="' + IMG_PREFIX + "/" + name + '" controls></video>';
}
// Note embed → MDC include component
// Note embed
const stem = path.basename(name, ext || ".md");
const slug = noteIndex.get(stem.toLowerCase()) ?? `${BASE_PATH}/${slugify(stem)}`;
return `::include{path="${slug}"}`;
const slug = noteIndex.get(stem.toLowerCase()) || (BASE_PATH + "/" + slugify(stem));
return '::include{path="' + slug + '"}';
});
}
// ─── Wiki links [[...]] ─────────────────────────────────────────────────────
// ─── Wiki links [[...]] ─────────────────────────────────────────────────────
function convertWikiLinks(body, noteIndex) {
return body.replace(/\[\[([^\]]+?)\]\]/g, (_, ref) => {
// Split off alias: [[Note|Alias]]
const [target, alias] = ref.split("|").map(s => s.trim());
return body.replace(/\[\[([^\]]+?)\]\]/g, function(_, ref) {
const pipeIdx = ref.indexOf("|");
let target, alias;
if (pipeIdx !== -1) {
target = ref.slice(0, pipeIdx).trim();
alias = ref.slice(pipeIdx + 1).trim();
} else {
target = ref.trim();
alias = null;
}
// Split off heading or block ref: [[Note#Heading]] [[Note#^block]]
const [notePart, anchor] = target.split("#").map(s => s.trim());
const hashIdx = target.indexOf("#");
let notePart, anchor;
if (hashIdx !== -1) {
notePart = target.slice(0, hashIdx).trim();
anchor = target.slice(hashIdx + 1).trim();
} else {
notePart = target;
anchor = null;
}
const stem = path.basename(notePart, ".md");
const baseUrl = noteIndex.get(stem.toLowerCase()) ?? `${BASE_PATH}/${slugify(stem)}`;
const stem = path.basename(notePart, ".md");
const resolved = noteIndex.get(stem.toLowerCase()) || noteIndex.get(slugify(stem));
const dead = !resolved;
const baseUrl = resolved || (BASE_PATH + "/" + slugify(stem));
let url = baseUrl;
if (anchor) {
if (anchor.startsWith("^")) {
// Block reference
url += "#" + anchor.slice(1);
} else {
// Heading reference
url += "#" + headingSlug(anchor);
}
url += "#" + (anchor.startsWith("^") ? anchor.slice(1) : headingSlug(anchor));
}
const label = alias || (anchor ? `${stem} ${anchor}` : stem);
return `[${label}](${url})`;
const label = alias || (anchor ? stem + " \u203a " + anchor : stem);
if (dead) {
return '<a href="' + url + '" class="dead-link" data-missing="' + stem + '">' + label + '</a>';
}
return "[" + label + "](" + url + ")";
});
}
// ─── Main conversion ──────────────────────────────────────────────────────────
// ─── Convert one file ────────────────────────────────────────────────────────
function convertFile(content, noteIndex) {
function convertFile(content, noteIndex, filename) {
let { frontmatter, body } = parseFrontmatter(content);
// 1. Extract and merge inline tags into frontmatter
const inlineTags = extractInlineTags(body);
if (inlineTags.length) {
const existing = Array.isArray(frontmatter.tags)
? frontmatter.tags
: frontmatter.tags
? [frontmatter.tags]
: [];
frontmatter.tags = [...new Set([...existing, ...inlineTags])];
: frontmatter.tags ? [frontmatter.tags] : [];
const merged = Array.from(new Set(existing.concat(inlineTags)));
frontmatter.tags = merged;
body = stripInlineTags(body);
}
// 2. Callout blocks (before wiki-link pass so "> [[link]]" still works)
body = convertCallouts(body);
// 3. Block IDs
body = convertBlockIds(body);
// 4. Embedded files ![[...]]
body = convertEmbeds(body, noteIndex);
// 5. Wiki links [[...]]
body = convertWikiLinks(body, noteIndex);
// 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");
body = "# " + title + "\n\n" + trimmed;
}
const fmString = Object.keys(frontmatter).length
? serializeFrontmatter(frontmatter) + "\n\n"
: "";
@@ -323,54 +360,55 @@ function convertFile(content, noteIndex) {
return fmString + body.trim() + "\n";
}
// ─── Entry point ──────────────────────────────────────────────────────────────
// ─── Main ────────────────────────────────────────────────────────────────────
function main() {
if (!fs.existsSync(VAULT_DIR)) {
console.error(`Vault directory not found: ${VAULT_DIR}`);
console.error("Vault directory not found: " + VAULT_DIR);
process.exit(1);
}
const files = collectMarkdownFiles(VAULT_DIR);
const noteIndex = buildNoteIndex(files);
const files = collectMarkdownFiles(VAULT_DIR);
const noteIndex = buildNoteIndex(files);
console.log(`Found ${files.length} markdown files in ${VAULT_DIR}`);
console.log("Found " + files.length + " markdown file(s) in " + VAULT_DIR);
let converted = 0, skipped = 0;
let doneCount = 0, skipCount = 0;
for (const file of files) {
const rel = path.relative(VAULT_DIR, file);
const outPath = path.join(
OUT_DIR,
rel
.split(path.sep)
.map((seg, i, arr) => i === arr.length - 1 ? seg : slugify(seg))
rel.split(path.sep)
.map(function(seg, i, arr) { return i === arr.length - 1 ? seg : slugify(seg); })
.join(path.sep)
);
const content = fs.readFileSync(file, "utf8");
let converted_content;
const content = fs.readFileSync(file, "utf8");
let result;
try {
converted_content = convertFile(content, noteIndex);
result = convertFile(content, noteIndex, path.basename(file));
} catch (err) {
console.warn(` Skipping ${rel}: ${err.message}`);
skipped++;
console.warn(" \u26A0 Skipping " + rel + ": " + err.message);
skipCount++;
continue;
}
if (DRY_RUN) {
console.log(`\n${"─".repeat(60)}\n[DRY-RUN] ${rel}${path.relative(process.cwd(), outPath)}\n`);
console.log(converted_content.slice(0, 500) + (converted_content.length > 500 ? "\n…" : ""));
console.log("\n" + "─".repeat(60));
console.log("[DRY-RUN] " + rel + " \u2192 " + path.relative(process.cwd(), outPath));
console.log("");
console.log(result.slice(0, 800) + (result.length > 800 ? "\n\u2026" : ""));
} else {
fs.mkdirSync(path.dirname(outPath), { recursive: true });
fs.writeFileSync(outPath, converted_content, "utf8");
console.log(`${rel}${path.relative(process.cwd(), outPath)}`);
fs.writeFileSync(outPath, result, "utf8");
console.log(" \u2713 " + rel + " \u2192 " + path.relative(process.cwd(), outPath));
}
converted++;
doneCount++;
}
console.log(`\nDone. ${converted} file(s) converted, ${skipped} skipped.`);
console.log("\nDone. " + doneCount + " file(s) converted, " + skipCount + " skipped.");
}
main();

View File

@@ -12,6 +12,9 @@
"nuxt": "^4.4.8",
"vue": "^3.5.35",
"vue-router": "^5.1.0"
},
"devDependencies": {
"sass-embedded": "^1.100.0"
}
},
"node_modules/@apidevtools/json-schema-ref-parser": {
@@ -454,6 +457,13 @@
}
}
},
"node_modules/@bufbuild/protobuf": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.12.1.tgz",
"integrity": "sha512-BvAMfS6LrgZiryOAZ4pBYucu4wG/Ei/9o9DZ9akbREnMLbPJiom2i8b9C8IsKErQoiKqVhrerzt3kOT/RrzLHg==",
"devOptional": true,
"license": "(Apache-2.0 AND BSD-3-Clause)"
},
"node_modules/@clack/core": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.3.tgz",
@@ -6066,6 +6076,13 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
"node_modules/colorjs.io": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz",
"integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==",
"devOptional": true,
"license": "MIT"
},
"node_modules/comma-separated-tokens": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
@@ -6076,17 +6093,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/commander": {
"version": "13.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
"integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=18"
}
},
"node_modules/commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
@@ -7516,6 +7522,16 @@
"uncrypto": "^0.1.3"
}
},
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
@@ -8014,6 +8030,13 @@
"integrity": "sha512-3MOLanc3sb3LNGWQl1RlQlNWURE5g32aUphrDyFeCsxBTk08iE3VNe4CwsUZ0Qs1X+EfX0+r29Sxdpza4B+yRA==",
"license": "MIT"
},
"node_modules/immutable": {
"version": "5.1.9",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz",
"integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==",
"devOptional": true,
"license": "MIT"
},
"node_modules/impound": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/impound/-/impound-1.1.5.tgz",
@@ -12093,6 +12116,16 @@
"queue-microtask": "^1.2.2"
}
},
"node_modules/rxjs": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -12113,6 +12146,381 @@
],
"license": "MIT"
},
"node_modules/sass": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.100.0.tgz",
"integrity": "sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==",
"license": "MIT",
"optional": true,
"dependencies": {
"chokidar": "^5.0.0",
"immutable": "^5.1.5",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=20.19.0"
},
"optionalDependencies": {
"@parcel/watcher": "^2.4.1"
}
},
"node_modules/sass-embedded": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.100.0.tgz",
"integrity": "sha512-Ut8wlQSk19tm7jMK6mz6cF1+e+E7tUnW2tM02zQDPnOTcVbV8qCQG8UWxZkkNlY50+hV3hqP24OOkUlMz8xBpw==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@bufbuild/protobuf": "^2.5.0",
"colorjs.io": "^0.5.0",
"immutable": "^5.1.5",
"rxjs": "^7.4.0",
"supports-color": "^8.1.1",
"sync-child-process": "^1.0.2",
"varint": "^6.0.0"
},
"bin": {
"sass": "dist/bin/sass.js"
},
"engines": {
"node": ">=16.0.0"
},
"optionalDependencies": {
"sass-embedded-all-unknown": "1.100.0",
"sass-embedded-android-arm": "1.100.0",
"sass-embedded-android-arm64": "1.100.0",
"sass-embedded-android-riscv64": "1.100.0",
"sass-embedded-android-x64": "1.100.0",
"sass-embedded-darwin-arm64": "1.100.0",
"sass-embedded-darwin-x64": "1.100.0",
"sass-embedded-linux-arm": "1.100.0",
"sass-embedded-linux-arm64": "1.100.0",
"sass-embedded-linux-musl-arm": "1.100.0",
"sass-embedded-linux-musl-arm64": "1.100.0",
"sass-embedded-linux-musl-riscv64": "1.100.0",
"sass-embedded-linux-musl-x64": "1.100.0",
"sass-embedded-linux-riscv64": "1.100.0",
"sass-embedded-linux-x64": "1.100.0",
"sass-embedded-unknown-all": "1.100.0",
"sass-embedded-win32-arm64": "1.100.0",
"sass-embedded-win32-x64": "1.100.0"
}
},
"node_modules/sass-embedded-all-unknown": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-all-unknown/-/sass-embedded-all-unknown-1.100.0.tgz",
"integrity": "sha512-auFtXY/kwYILmSVjtBDwyj0axcLbYYiffOKWoaXHnI5bsYwiRbBh3EneR1rpbX2ZIZCrwX93i5pxKLTZF/662Q==",
"cpu": [
"!arm",
"!arm64",
"!riscv64",
"!x64"
],
"license": "MIT",
"optional": true,
"dependencies": {
"sass": "1.100.0"
}
},
"node_modules/sass-embedded-android-arm": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.100.0.tgz",
"integrity": "sha512-70f3HgX2pFNmzpGQ86n5e6QfWn2fP4QUQGfFQK0P1XH73ZLIzLo2YqygrGKGKeeqtc5eU2Wl1/xQzhzuKnO4kw==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/sass-embedded-android-arm64": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.100.0.tgz",
"integrity": "sha512-W+Ru9JwTnfU0UX3jSZcbqFdtKFMcYdfFwytc57h2DgnqCOIiAqI2E06mABZBZC+r3LwXCBuS5GbXAGeVgvVDkA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/sass-embedded-android-riscv64": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.100.0.tgz",
"integrity": "sha512-icU3o0V/uCSytSpf+tX5Lf51BvyQEbLzDUJfUi9etSauYBGHpPKkdtdZH0si4v98phq11Kl8rSV1SggksxF1Hg==",
"cpu": [
"riscv64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/sass-embedded-android-x64": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.100.0.tgz",
"integrity": "sha512-mevF9VQk6gEYByy8+jusaHGmd7Usb2ytX/DsEOd0JtOGCtcf1kh575xJ6OUBDIcJ15uLnbau/0iy1eP6WVBvWA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/sass-embedded-darwin-arm64": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.100.0.tgz",
"integrity": "sha512-1PVlYi61POo93IT/FfrG1mc1tAHxeSTyUALF2aOFmXGWjVXr3bQzEQiBGCOvQbj/ix+5hNyXFXcEMEyKvtUJJA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/sass-embedded-darwin-x64": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.100.0.tgz",
"integrity": "sha512-x97o3JnGyImZNCIVs9wQHJUE5QCvmVIKaH1cwrz/5dK7OT1FpeNiW+u9TUomP9hG6Ekjd8EL8NBHpxTfIhdjmg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/sass-embedded-linux-arm": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.100.0.tgz",
"integrity": "sha512-9Ul7O1eKrc5YlhwWjkp8tZPSe3UEwSZ1uwUZOQom1HL0pRlBA6F/IlGZYFTLwnHMIP1fc77MMNaBRfc05mKMpw==",
"cpu": [
"arm"
],
"libc": "glibc",
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/sass-embedded-linux-arm64": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.100.0.tgz",
"integrity": "sha512-Dwjmj8Z6VRy7rAi53JAdEwIyUjpfl7PhpSc2/LpQPQx+aO5Dp7Spaipkax0ufJl1SoDUdchCsM4y/88YaluorQ==",
"cpu": [
"arm64"
],
"libc": "glibc",
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/sass-embedded-linux-musl-arm": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.100.0.tgz",
"integrity": "sha512-sl0JgbGloPyJg66XXx5UDSDScZ0oU85DpMQU4JU/sCUCFj1Z8zZ69SJWKTCNE4/jwnce7WI2zPCV5AG+RHOZJw==",
"cpu": [
"arm"
],
"libc": "musl",
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/sass-embedded-linux-musl-arm64": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.100.0.tgz",
"integrity": "sha512-XpACJB2KjSLjf2e9uuvGVdOURsoNrFqgRiihhXyUHK9W0t3LIHb7z5MA/7XGPIT9bWSOO2zyw+rH/FHtDV/Yrg==",
"cpu": [
"arm64"
],
"libc": "musl",
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/sass-embedded-linux-musl-riscv64": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.100.0.tgz",
"integrity": "sha512-ShvI0Kx04mwoCARwZ0UjiT97isQvzO80tAt91zmFyHLN9kelc/IrQi940farSm2xQVPCKdeVyeG0ekBsokSpYQ==",
"cpu": [
"riscv64"
],
"libc": "musl",
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/sass-embedded-linux-musl-x64": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.100.0.tgz",
"integrity": "sha512-TDBCRWNuS4RDLQXvRc1gjZlWiWTWaWGp0Bwu/IKwJxov81lsvrCs3TihTyNXtW7V5aoN4Ky3r0QOkNb3mwmBnA==",
"cpu": [
"x64"
],
"libc": "musl",
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/sass-embedded-linux-riscv64": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.100.0.tgz",
"integrity": "sha512-j4ENJGOheO+fm3j/yorLxCjBP6/XskrZx7dTLlT+lXYwN/qqCqoA/gsNLI0McS3DFM6GBwPiffzWsdWS8t6sEQ==",
"cpu": [
"riscv64"
],
"libc": "glibc",
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/sass-embedded-linux-x64": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.100.0.tgz",
"integrity": "sha512-0vUSN8j0WGtCJIOPh//EmUvYGHW0QOe5iul8qyhPk50MAcw49MA0r34AhftjDdx94ILPF6vApFs0gwHPQRlpVA==",
"cpu": [
"x64"
],
"libc": "glibc",
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/sass-embedded-unknown-all": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-unknown-all/-/sass-embedded-unknown-all-1.100.0.tgz",
"integrity": "sha512-c+naBgWId4MIpToXcI0DgqetjdAkwTTAxFAuOaBz7HUXLdyG1oZRrEvSsbe41nEdQOKH0vgofVFCeSQgoXOG9A==",
"license": "MIT",
"optional": true,
"os": [
"!android",
"!darwin",
"!linux",
"!win32"
],
"dependencies": {
"sass": "1.100.0"
}
},
"node_modules/sass-embedded-win32-arm64": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.100.0.tgz",
"integrity": "sha512-iE+yxj+hUXwwbqpHkXxgAWTzeRfcWxJ7SSTQEPMk48lwq3oCrWLlz5sQuWHbuTK/i0GKQfROdP+hOmPi89yjUg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/sass-embedded-win32-x64": {
"version": "1.100.0",
"resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.100.0.tgz",
"integrity": "sha512-qI4F8MI7/KYoy9NdjJfhSspG42WPkADSNDvwEV7qWvCSFC83koJssRsKO2/PfY+niZz6BG65Ic/D+A11h959hw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/sass-embedded/node_modules/supports-color": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/sax": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
@@ -12845,6 +13253,29 @@
"node": ">=16"
}
},
"node_modules/sync-child-process": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz",
"integrity": "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"sync-message-port": "^1.0.0"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/sync-message-port": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/sync-message-port/-/sync-message-port-1.2.0.tgz",
"integrity": "sha512-gAQ9qrUN/UCypHtGFbbe7Rc/f9bzO88IwrG8TDo/aMKAApKyD6E3W4Cm0EfhfBb6Z6SKt59tTCTfD+n1xmAvMg==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/tagged-tag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz",
@@ -13137,8 +13568,8 @@
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD",
"optional": true
"devOptional": true,
"license": "0BSD"
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
@@ -13805,6 +14236,13 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
"node_modules/varint": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz",
"integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==",
"devOptional": true,
"license": "MIT"
},
"node_modules/vfile": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",

View File

@@ -15,5 +15,8 @@
"nuxt": "^4.4.8",
"vue": "^3.5.35",
"vue-router": "^5.1.0"
},
"devDependencies": {
"sass-embedded": "^1.100.0"
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.