From b6f661c6ec7b242f2e4ac9cf1a839adf717e71d4 Mon Sep 17 00:00:00 2001 From: Andy Burke Date: Wed, 24 Sep 2025 13:21:39 -0700 Subject: [PATCH] feature: essays fix: multiple rendering of things when sending --- models/event.ts | 12 +- public/api/users/index.ts | 3 + public/base.css | 3 + public/index.html | 2 + public/{tabs/chat => js}/emojis/en.js | 0 public/js/external/md_to_html.js | 86 ++++++++ public/tabs/blurbs/blurbs.html | 50 ++--- public/tabs/chat/chat.html | 52 +++-- public/tabs/essays/essays.html | 271 +++++++++++++++++++++++++- public/tabs/essays/new_essay.html | 118 +++++++++++ 10 files changed, 537 insertions(+), 60 deletions(-) rename public/{tabs/chat => js}/emojis/en.js (100%) create mode 100644 public/js/external/md_to_html.js create mode 100644 public/tabs/essays/new_essay.html diff --git a/models/event.ts b/models/event.ts index f60d2b4..be904aa 100644 --- a/models/event.ts +++ b/models/event.ts @@ -93,7 +93,17 @@ export function VALIDATE_EVENT(event: EVENT) { } break; case 'essay': - if (event.data?.essay?.length <= 0) { + if (event.data?.title?.length <= 0) { + errors.push({ + cause: 'essay_title_missing', + message: 'An essay must have a title.' + }); + } else if (event.data?.title?.length > 2 ** 7) { + errors.push({ + cause: 'essay_title_length_limit_exceeded', + message: 'An essay title cannot be longer than 128 characters.' + }); + } else if (event.data?.essay?.length <= 0) { errors.push({ cause: 'essay_missing', message: 'An essay cannot be empty.' diff --git a/public/api/users/index.ts b/public/api/users/index.ts index 0af20b5..6ac6b9c 100644 --- a/public/api/users/index.ts +++ b/public/api/users/index.ts @@ -19,6 +19,9 @@ const DEFAULT_USER_PERMISSIONS: string[] = [ 'topics.blurbs.write', 'topics.chat.write', 'topics.chat.read', + 'topics.essays.create', + 'topics.essays.read', + 'topics.essays.write', 'topics.posts.create', 'topics.posts.write', 'topics.posts.read', diff --git a/public/base.css b/public/base.css index e65ea4c..7e49cce 100644 --- a/public/base.css +++ b/public/base.css @@ -313,6 +313,9 @@ body[data-perms*="topics.blurbs.write"] [data-requires-permission="topics.blurbs body[data-perms*="topics.chat.create"] [data-requires-permission="topics.chat.create"], body[data-perms*="topics.chat.read"] [data-requires-permission="topics.chat.read"], body[data-perms*="topics.chat.write"] [data-requires-permission="topics.chat.write"], +body[data-perms*="topics.essays.create"] [data-requires-permission="topics.essays.create"], +body[data-perms*="topics.essays.read"] [data-requires-permission="topics.essays.read"], +body[data-perms*="topics.essays.write"] [data-requires-permission="topics.essays.write"], body[data-perms*="topics.posts.create"] [data-requires-permission="topics.posts.create"], body[data-perms*="topics.posts.read"] [data-requires-permission="topics.posts.read"], body[data-perms*="topics.posts.write"] [data-requires-permission="topics.posts.write"], diff --git a/public/index.html b/public/index.html index 57ea98a..a55c9f2 100644 --- a/public/index.html +++ b/public/index.html @@ -24,6 +24,8 @@ + + diff --git a/public/tabs/chat/emojis/en.js b/public/js/emojis/en.js similarity index 100% rename from public/tabs/chat/emojis/en.js rename to public/js/emojis/en.js diff --git a/public/js/external/md_to_html.js b/public/js/external/md_to_html.js new file mode 100644 index 0000000..c3b7911 --- /dev/null +++ b/public/js/external/md_to_html.js @@ -0,0 +1,86 @@ +// see: https://andyburke.dev/andyburke/serverus/src/branch/dev/handlers/markdown.ts + +/* MARKDOWN TO HTML */ + +/* order of these transforms matters, so we list them here in an array and build type from it after. */ +const MD_TRANSFORM_NAMES = [ + "characters", + "headings", + "horizontal_rules", + "list_items", + "bold", + "italic", + "strikethrough", + "code", + "images", + "links", + "breaks", +]; +const MD_TRANSFORMS = { + characters: [ + [/&/g, "&"], + [//g, ">"], + [/"/g, """], + [/'/g, "'"], + ], + + headings: [ + [/^#\s(.+)$/gm, "

$1

\n"], + [/^##\s(.+)$/gm, "

$1

\n"], + [/^###\s(.+)$/gm, "

$1

\n"], + [/^####\s(.+)$/gm, "

$1

\n"], + [/^#####\s(.+)$/gm, "
$1
\n"], + ], + + horizontal_rules: [[/^----*$/gm, "
\n"]], + + list_items: [ + [/\n\n([ \t]*)([-\*\.].*?)\n\n/gs, "\n\n\n\n\n"], + [/^([ \t]*)[-\*\.](\s+.*)$/gm, "
  • $1$2
  • \n"], + ], + + bold: [[/\*([^\*]+)\*/gm, "$1"]], + + italic: [[/_([^_]+)_/gm, "$1"]], + + strikethrough: [[/~([^~]+)~/gm, "$1"]], + + code: [ + [/```\n([^`]+)\n```/gm, "
    $1
    "], + [/```([^`]+)```/gm, "$1"], + ], + + images: [[/!\[([^\]]+)\]\(([^\)]+)\)/g, '$1']], + + links: [[/\[([^\]]+)\]\(([^\)]+)\)/g, '$1']], + + breaks: [ + [/\s\s\n/g, "\n
    \n"], + [/\n\n/g, "\n
    \n"], + ], +}; + +/** + * Convert markdown to HTML. + * @param markdown The markdown string. + * @param options _(Optional)_ A record of transforms to disable. + * @returns The generated HTML string. + */ +function md_to_html(markdown, transform_config) { + let html = markdown; + for (const transform_name of MD_TRANSFORM_NAMES) { + const enabled = + typeof transform_config === "undefined" || transform_config[transform_name] !== false; + if (!enabled) { + continue; + } + + const transforms = MD_TRANSFORMS[transform_name] ?? []; + for (const markdown_transformer of transforms) { + html = html.replace(...markdown_transformer); + } + } + + return `
    ${html}
    `; +} diff --git a/public/tabs/blurbs/blurbs.html b/public/tabs/blurbs/blurbs.html index 444e547..1ccb4d6 100644 --- a/public/tabs/blurbs/blurbs.html +++ b/public/tabs/blurbs/blurbs.html @@ -148,14 +148,15 @@ + diff --git a/public/tabs/essays/new_essay.html b/public/tabs/essays/new_essay.html new file mode 100644 index 0000000..d116a7d --- /dev/null +++ b/public/tabs/essays/new_essay.html @@ -0,0 +1,118 @@ + +
    + +
    + + + + + + + + + + + + + +
    0 / 128
    + + +
    0 / 65536
    + + + +
    +