From 8ef3054888ad354e916315194e3b83ac4e6df704 Mon Sep 17 00:00:00 2001 From: imperosol Date: Thu, 3 Sep 2026 10:26:01 +0200 Subject: [PATCH 1/4] add `@ae_utbm/aemark` to JS deps --- package-lock.json | 6 ++++++ package.json | 1 + 2 files changed, 7 insertions(+) diff --git a/package-lock.json b/package-lock.json index 323b58bf..a5754b0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "3", "license": "GPL-3.0-only", "dependencies": { + "@ae_utbm/aemark": "^0.1.1", "@alpinejs/sort": "^3.16.2", "@arendjr/text-clipper": "npm:@jsr/arendjr__text-clipper@^3.0.0", "@floating-ui/dom": "^1.8.0", @@ -56,6 +57,11 @@ "vite": "^8.2.2" } }, + "node_modules/@ae_utbm/aemark": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@ae_utbm/aemark/-/aemark-0.1.1.tgz", + "integrity": "sha512-ZH5ebIfTjx02fMJexMG/To+AakmnedG0DH5Q/W3gUqRBimp0k5y/eUpvAmPXDdV9NnWy0IFxEZvKur0x6gidxA==" + }, "node_modules/@alpinejs/sort": { "version": "3.16.2", "resolved": "https://registry.npmjs.org/@alpinejs/sort/-/sort-3.16.2.tgz", diff --git a/package.json b/package.json index 45419643..67b09bce 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "vite": "^8.2.2" }, "dependencies": { + "@ae_utbm/aemark": "^0.1.1", "@alpinejs/sort": "^3.16.2", "@arendjr/text-clipper": "npm:@jsr/arendjr__text-clipper@^3.0.0", "@floating-ui/dom": "^1.8.0", From b5b837498ff94e5ade849e07d46c596886f7fa6b Mon Sep 17 00:00:00 2001 From: imperosol Date: Thu, 3 Sep 2026 10:35:10 +0200 Subject: [PATCH 2/4] Use client-side md parser for easymde --- .../bundled/core/components/easymde-index.ts | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/core/static/bundled/core/components/easymde-index.ts b/core/static/bundled/core/components/easymde-index.ts index 45dded04..adc01bb4 100644 --- a/core/static/bundled/core/components/easymde-index.ts +++ b/core/static/bundled/core/components/easymde-index.ts @@ -3,16 +3,12 @@ import "codemirror/lib/codemirror.css"; // @ts-expect-error 2307 import "easymde/src/css/easymde.css"; +import { markdown } from "@ae_utbm/aemark"; // biome-ignore lint/correctness/noUndeclaredDependencies: Imported by EasyMDE -import type CodeMirror from "codemirror"; -// biome-ignore lint/style/useNamingConvention: This is how they called their namespace +import type CodeMirror from "codemirror"; // biome-ignore lint/style/useNamingConvention: This is how they called their namespace import EasyMDE from "easymde"; import { inheritHtmlElement, registerComponent } from "#core:utils/web-components"; -import { - markdownRenderMarkdown, - type UploadUploadImageErrors, - uploadUploadImage, -} from "#openapi"; +import { type UploadUploadImageErrors, uploadUploadImage } from "#openapi"; const loadEasyMde = (textarea: HTMLTextAreaElement) => { const easymde = new EasyMDE({ @@ -64,19 +60,7 @@ const loadEasyMde = (textarea: HTMLTextAreaElement) => { }); easymde.codemirror.replaceSelection("\n"); }, - previewRender: (plainText, preview) => { - /* This is wrapped this way to allow time for Alpine to be loaded on the page */ - return Alpine.debounce(() => { - const func = async () => { - preview.innerHTML = ( - await markdownRenderMarkdown({ body: { text: plainText } }) - ).data as string; - return null; - }; - func().then(); - return null; - }, 300)(); - }, + previewRender: (plainText) => markdown(plainText), forceSync: true, // Avoid validation error on generic create view imageTexts: { sbInit: gettext("Attach files by drag and dropping or pasting from clipboard."), From ff69741c82cea07fd2189c994612cf0dc68bb8ce Mon Sep 17 00:00:00 2001 From: imperosol Date: Thu, 3 Sep 2026 12:03:52 +0200 Subject: [PATCH 3/4] remove /api/markdown route --- core/api.py | 11 ----------- core/schemas.py | 4 ---- 2 files changed, 15 deletions(-) diff --git a/core/api.py b/core/api.py index 039c7858..d46548f5 100644 --- a/core/api.py +++ b/core/api.py @@ -3,7 +3,6 @@ from typing import Annotated, Literal from annotated_types import Ge, Le, MinLen from django.conf import settings from django.db.models import F -from django.http import HttpResponse from ninja import File, Query from ninja.security import SessionAuth from ninja_extra import ControllerBase, api_controller, paginate, route @@ -18,7 +17,6 @@ from core.models import Group, QuickUploadImage, SithFile, User from core.schemas import ( FamilyGodfatherSchema, GroupSchema, - MarkdownSchema, SithFileSchema, UploadedFileSchema, UploadedImage, @@ -28,18 +26,9 @@ from core.schemas import ( UserSchema, ValidationErrorSchema, ) -from core.templatetags.renderer import markdown from counter.utils import is_logged_in_counter -@api_controller("/markdown") -class MarkdownController(ControllerBase): - @route.post("", url_name="markdown") - def render_markdown(self, body: MarkdownSchema): - """Convert the markdown text into html.""" - return HttpResponse(markdown(body.text), content_type="text/html") - - @api_controller("/upload") class UploadController(ControllerBase): @route.post( diff --git a/core/schemas.py b/core/schemas.py index 7f159363..f6dc4f5b 100644 --- a/core/schemas.py +++ b/core/schemas.py @@ -161,10 +161,6 @@ class UserFilterSchema(FilterSchema): return value -class MarkdownSchema(Schema): - text: str - - class FamilyGodfatherSchema(Schema): godfather: int godchild: int From 549d8c67e74c5a2d440b093fcd788a28ddbe9f38 Mon Sep 17 00:00:00 2001 From: imperosol Date: Thu, 3 Sep 2026 15:00:02 +0200 Subject: [PATCH 4/4] doc: ae markdown --- docs/tutorial/markdown.md | 51 +++++++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 52 insertions(+) create mode 100644 docs/tutorial/markdown.md diff --git a/docs/tutorial/markdown.md b/docs/tutorial/markdown.md new file mode 100644 index 00000000..319b2f43 --- /dev/null +++ b/docs/tutorial/markdown.md @@ -0,0 +1,51 @@ +## syntaxe aemark + +Le site AE utilise markdown pour le rendu de la plupart +des textes saisis par les utilisateurs. +Cependant, la syntaxe utilisée n'est pas celle officielle +telle que définie par John Gruber, +mais est basée sur [CommonMark](https://commonmark.org/), +avec quelques variations pour les usages particuliers du site AE. + +Les deux principaux ajouts sont : + +- Les urls commençant par `page://` sont modifiées pour commencer par `/page/` +- Des modificateurs de taille peuvent être indiqués directement dans la source + d'une image + +Les variations d'aemark sont documentées sur +[le site AE](https://ae.utbm.fr/page/Aide_sur_la_syntaxe/). + +Le code est hébergé sur le dépôt Git [aemark](https://github.com/ae-utbm/ae-markdown). + +## Utiliser aemark + +Le code du parser aemark est écrit en Rust dans une librairie +indépendante, avec des bindings vers différents langages. +Les librairies mises à disposition exposent une seule fonction, +qui prend simplement du markdown en entrée et renvoie l'HTML correspondant. + +Les librairies pour les différents langages sont toutes +basées sur le même code Rust. +La seule différence entre chacune tient uniquement dans la manière +d'intégrer ce code dans les bindings. +De cette manière, le comportement est assuré d'être le même +sur toutes les plateformes disponibles, avec en prime des performances +respectables. + + +=== ":simple-python: Python" + + ```python + from aemark import markdown + + result = markdown("this is some *markdown text* with __formatting__") + ``` + +=== ":simple-javascript: Javascript" + + ```typescript + import { markdown } from "@ae_utbm/aemark" + + const result = markdown("this is some *markdown text* with __formatting__"); + ``` diff --git a/mkdocs.yml b/mkdocs.yml index cd6df0de..48a966f5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -71,6 +71,7 @@ nav: - API: - Développement: tutorial/api/dev.md - Connexion à l'API: tutorial/api/connect.md + - Markdown AE: tutorial/markdown.md - Etransactions: tutorial/etransaction.md - How-to: - L'ORM de Django: howto/querysets.md