mirror of
https://github.com/thiloho/archtika.git
synced 2025-11-22 02:41:35 +01:00
Wrap headings in section
This commit is contained in:
@@ -25,7 +25,7 @@
|
|||||||
api = pkgs.mkShell {
|
api = pkgs.mkShell {
|
||||||
packages = with pkgs; [ postgresql_16 ];
|
packages = with pkgs; [ postgresql_16 ];
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
alias dbmate="${pkgs.dbmate}/bin/dbmate --url postgres://postgres@localhost:15432/archtika?sslmode=disable"
|
alias dbmate="${pkgs.dbmate}/bin/dbmate --no-dump-schema --url postgres://postgres@localhost:15432/archtika?sslmode=disable"
|
||||||
alias formatsql="${pkgs.pgformatter}/bin/pg_format -s 2 -f 2 -U 2 -i db/migrations/*.sql"
|
alias formatsql="${pkgs.pgformatter}/bin/pg_format -s 2 -f 2 -U 2 -i db/migrations/*.sql"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|||||||
7
web-app/package-lock.json
generated
7
web-app/package-lock.json
generated
@@ -8,6 +8,7 @@
|
|||||||
"name": "web-app",
|
"name": "web-app",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"github-slugger": "2.0.0",
|
||||||
"highlight.js": "11.10.0",
|
"highlight.js": "11.10.0",
|
||||||
"marked": "14.0.0",
|
"marked": "14.0.0",
|
||||||
"marked-highlight": "2.1.4"
|
"marked-highlight": "2.1.4"
|
||||||
@@ -1429,6 +1430,12 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/github-slugger": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/glob": {
|
"node_modules/glob": {
|
||||||
"version": "10.4.5",
|
"version": "10.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"github-slugger": "2.0.0",
|
||||||
"highlight.js": "11.10.0",
|
"highlight.js": "11.10.0",
|
||||||
"marked": "14.0.0",
|
"marked": "14.0.0",
|
||||||
"marked-highlight": "2.1.4"
|
"marked-highlight": "2.1.4"
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { Marked } from "marked";
|
import { Marked } from "marked";
|
||||||
|
import type { Renderer, Token } from "marked";
|
||||||
import { markedHighlight } from "marked-highlight";
|
import { markedHighlight } from "marked-highlight";
|
||||||
import hljs from "highlight.js";
|
import hljs from "highlight.js";
|
||||||
|
import GithubSlugger from "github-slugger";
|
||||||
|
|
||||||
export const sortOptions = [
|
export const sortOptions = [
|
||||||
{ value: "creation-time", text: "Creation time" },
|
{ value: "creation-time", text: "Creation time" },
|
||||||
@@ -31,6 +33,98 @@ const createMarkdownParser = () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi;
|
||||||
|
|
||||||
|
function unescape(html: string) {
|
||||||
|
return html.replace(unescapeTest, (_, n) => {
|
||||||
|
n = n.toLowerCase();
|
||||||
|
if (n === "colon") return ":";
|
||||||
|
if (n.charAt(0) === "#") {
|
||||||
|
return n.charAt(1) === "x"
|
||||||
|
? String.fromCharCode(parseInt(n.substring(2), 16))
|
||||||
|
: String.fromCharCode(+n.substring(1));
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let slugger = new GithubSlugger();
|
||||||
|
let headings: { text: string; raw: string; level: number; id: string }[] = [];
|
||||||
|
let sectionStack: { level: number; id: string }[] = [];
|
||||||
|
|
||||||
|
function gfmHeadingId({ prefix = "" } = {}) {
|
||||||
|
return {
|
||||||
|
renderer: {
|
||||||
|
heading(this: Renderer, { tokens, depth }: { tokens: Token[]; depth: number }) {
|
||||||
|
const text = this.parser.parseInline(tokens);
|
||||||
|
const raw = unescape(this.parser.parseInline(tokens, this.parser.textRenderer))
|
||||||
|
.trim()
|
||||||
|
.replace(/<[!\/a-z].*?>/gi, "");
|
||||||
|
const level = depth;
|
||||||
|
const id = `${prefix}${slugger.slug(raw.toLowerCase())}`;
|
||||||
|
const heading = { level, text, id, raw };
|
||||||
|
headings.push(heading);
|
||||||
|
|
||||||
|
// Close any sections that are at a higher level than the current heading
|
||||||
|
let closingSections = "";
|
||||||
|
while (sectionStack.length > 0 && sectionStack[sectionStack.length - 1].level >= level) {
|
||||||
|
sectionStack.pop();
|
||||||
|
closingSections += "</section>";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open a new section for this heading
|
||||||
|
sectionStack.push({ level, id });
|
||||||
|
const openingSection = `<section id="${id}">`;
|
||||||
|
|
||||||
|
return `
|
||||||
|
${closingSections}
|
||||||
|
${openingSection}
|
||||||
|
<h${level}>
|
||||||
|
<a href="#${id}">${text}</a>
|
||||||
|
</h${level}>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hooks: {
|
||||||
|
preprocess(src: string) {
|
||||||
|
headings = [];
|
||||||
|
sectionStack = [];
|
||||||
|
slugger = new GithubSlugger();
|
||||||
|
|
||||||
|
return src;
|
||||||
|
},
|
||||||
|
postprocess(html: string) {
|
||||||
|
// Close any remaining open sections
|
||||||
|
const closingRemainingSection = "</section>".repeat(sectionStack.length);
|
||||||
|
|
||||||
|
// Generate table of contents
|
||||||
|
const tableOfContents =
|
||||||
|
headings.length > 0
|
||||||
|
? `<details>
|
||||||
|
<summary>Table of contents</summary>
|
||||||
|
<ul>
|
||||||
|
${headings
|
||||||
|
.map(
|
||||||
|
({ id, text, level }) => `
|
||||||
|
<li><a href="#${id}" class="h${level}">${text}</a></li>`
|
||||||
|
)
|
||||||
|
.join("")}
|
||||||
|
</ul>
|
||||||
|
</details>`
|
||||||
|
: "";
|
||||||
|
|
||||||
|
return `
|
||||||
|
${tableOfContents}
|
||||||
|
${html}
|
||||||
|
${closingRemainingSection}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
marked.use(gfmHeadingId());
|
||||||
|
|
||||||
return marked;
|
return marked;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -42,8 +136,6 @@ export const md = async (markdownContent: string) => {
|
|||||||
return html;
|
return html;
|
||||||
};
|
};
|
||||||
|
|
||||||
// test
|
|
||||||
|
|
||||||
export const handleImagePaste = async (event: ClipboardEvent, API_BASE_PREFIX: string) => {
|
export const handleImagePaste = async (event: ClipboardEvent, API_BASE_PREFIX: string) => {
|
||||||
const clipboardItems = Array.from(event.clipboardData?.items || []);
|
const clipboardItems = Array.from(event.clipboardData?.items || []);
|
||||||
const file = clipboardItems.find((item) => item.type.startsWith("image/"));
|
const file = clipboardItems.find((item) => item.type.startsWith("image/"));
|
||||||
|
|||||||
Reference in New Issue
Block a user