]> source.dussan.org Git - gitea.git/commitdiff
Use a generic markup class to display externally rendered files and diffs (#15735)
author6543 <6543@obermui.de>
Fri, 7 May 2021 08:43:41 +0000 (10:43 +0200)
committerGitHub <noreply@github.com>
Fri, 7 May 2021 08:43:41 +0000 (10:43 +0200)
* creates and implements generic markup less class

* How to give custom CSS to externally rendered html

* Clarifies sources of CSS styling of markup

* further clarification of sources of markup styling

* rename _markdown to _markup

* remove defunct import

* fix orphaned reference

* Update docs/content/doc/advanced/external-renderers.en-us.md

* more renames markdown -> markup

* do not suggest less customization

* add back tokens

* fix class whitespace, remove useless if-clause

* remove unused csv-data rules

* use named exports and rename functions

* sort imports

Co-authored-by: HarvsG <11440490+HarvsG@users.noreply.github.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: silverwind <me@silverwind.io>
35 files changed:
docs/content/doc/advanced/external-renderers.en-us.md
templates/org/home.tmpl
templates/repo/diff/box.tmpl
templates/repo/diff/comment_form.tmpl
templates/repo/diff/comments.tmpl
templates/repo/editor/edit.tmpl
templates/repo/empty.tmpl
templates/repo/issue/comment_tab.tmpl
templates/repo/issue/milestone_issues.tmpl
templates/repo/issue/milestones.tmpl
templates/repo/issue/view_content.tmpl
templates/repo/issue/view_content/comments.tmpl
templates/repo/release/list.tmpl
templates/repo/release/new.tmpl
templates/repo/settings/lfs_file.tmpl
templates/repo/view_file.tmpl
templates/repo/wiki/view.tmpl
templates/user/profile.tmpl
web_src/js/index.js
web_src/js/markdown/anchors.js [deleted file]
web_src/js/markdown/content.js [deleted file]
web_src/js/markdown/mermaid.js [deleted file]
web_src/js/markup/anchors.js [new file with mode: 0644]
web_src/js/markup/content.js [new file with mode: 0644]
web_src/js/markup/mermaid.js [new file with mode: 0644]
web_src/less/_base.less
web_src/less/_markdown.less [deleted file]
web_src/less/_repository.less
web_src/less/_review.less
web_src/less/features/animations.less
web_src/less/index.less
web_src/less/markdown/mermaid.less [deleted file]
web_src/less/markup/content.less [new file with mode: 0644]
web_src/less/markup/mermaid.less [new file with mode: 0644]
web_src/less/themes/theme-arc-green.less

index 6b283ca2e1349be387ebd71e9ad7b014e4b934a5..71fabc529d7b826a3bbd8abd3a2b9481890b6f89 100644 (file)
@@ -98,3 +98,36 @@ Once your configuration changes have been made, restart Gitea to have changes ta
 
 **Note**: Prior to Gitea 1.12 there was a single `markup.sanitiser` section with keys that were redefined for multiple rules, however,
 there were significant problems with this method of configuration necessitating configuration through multiple sections.
+
+## Customizing CSS
+The external renderer is specified in the .ini in the format `[markup.XXXXX]` and the HTML supplied by your external renderer will be wrapped in a `<div>` with classes `markup` and `XXXXX`. The `markup` class provides out of the box styling (as does `markdown` if `XXXXX` is `markdown`). Otherwise you can use these classes to specifically target the contents of your rendered HTML. 
+
+And so you could write some CSS:
+```css
+.markup.XXXXX html {
+  font-size: 100%;
+  overflow-y: scroll;
+  -webkit-text-size-adjust: 100%;
+  -ms-text-size-adjust: 100%;
+}
+
+.markup.XXXXX body {
+  color: #444;
+  font-family: Georgia, Palatino, 'Palatino Linotype', Times, 'Times New Roman', serif;
+  font-size: 12px;
+  line-height: 1.7;
+  padding: 1em;
+  margin: auto;
+  max-width: 42em;
+  background: #fefefe;
+}
+
+.markup.XXXXX p {
+  color: orangered;
+}
+```
+
+Add your stylesheet to your custom directory e.g `custom/public/css/my-style-XXXXX.css` and import it using a custom header file `custom/templates/custom/header.tmpl`:
+```html
+<link type="text/css" href="{{AppSubUrl}}/css/my-style-XXXXX.css" />
+```
index 9b77ae6b3f2ab82a708b88e8d25beaf6386a541d..5e0a53aa56843354609eb32f55f218ec09c91595 100644 (file)
@@ -11,7 +11,7 @@
                                </span>
                                {{if .IsOrganizationOwner}}<a class="middle text grey" href="{{.OrgLink}}/settings">{{svg "octicon-gear" 16 "mb-3"}}</a>{{end}}
                        </div>
-                       {{if $.RenderedDescription}}<p class="render-content markdown">{{$.RenderedDescription|Str2html}}</p>{{end}}
+                       {{if $.RenderedDescription}}<p class="render-content markup">{{$.RenderedDescription|Str2html}}</p>{{end}}
                        <div class="text grey meta">
                                {{if .Org.Location}}<div class="item">{{svg "octicon-location"}} <span>{{.Org.Location}}</span></div>{{end}}
                                {{if .Org.Website}}<div class="item">{{svg "octicon-link"}} <a target="_blank" rel="noopener noreferrer" href="{{.Org.Website}}">{{.Org.Website}}</a></div>{{end}}
index 9a2f7bdd7fed089c952272e2186f2d429f38e4da..582b66d5db6e52dfd08c6b218e459df49f11d892 100644 (file)
                                        <div class="ui bottom attached active write tab segment">
                                                <textarea class="review-textarea" tabindex="1" name="content"></textarea>
                                        </div>
-                                       <div class="ui bottom attached tab preview segment markdown">
+                                       <div class="ui bottom attached tab preview segment markup">
                                        {{$.i18n.Tr "loading"}}
                                        </div>
                                        <div class="text right edit buttons">
index c82d32c214d8db31b8a2b145d0883aa25900bfe0..628cd52dc25d4d32e6c417d67ce27a64e0d92931 100644 (file)
                        <div class="ui active tab" data-tab="write">
                                <textarea name="content" placeholder="{{$.root.i18n.Tr "repo.diff.comment.placeholder"}}"></textarea>
                        </div>
-                       <div class="ui tab markdown" data-tab="preview">
+                       <div class="ui tab markup" data-tab="preview">
                        {{.i18n.Tr "loading"}}
                        </div>
                </div>
                <div class="field footer">
-                       <span class="markdown-info">{{svg "octicon-markdown"}} {{$.root.i18n.Tr "repo.diff.comment.markdown_info"}}</span>
+                       <span class="markup-info">{{svg "octicon-markup"}} {{$.root.i18n.Tr "repo.diff.comment.markup_info"}}</span>
                        <div class="ui right">
                                {{if $.reply}}
                                        <button class="ui submit green tiny button btn-reply" type="submit">{{$.root.i18n.Tr "repo.diff.comment.reply"}}</button>
index b566ffce9a57247cf1ab152e446a59a511df6871..6e39fbe854991999b64eb8eca40abde9fcd25a69 100644 (file)
@@ -51,7 +51,7 @@
                        </div>
                </div>
                <div class="ui attached segment comment-body">
-                       <div class="render-content markdown">
+                       <div class="render-content markup">
                        {{if .RenderedContent}}
                                {{.RenderedContent|Str2html}}
                        {{else}}
index 3efde70f50885ccd1340cafc9d3ebae4bc915d31..b7e1589aa14e0a2c9ccdc2d30f296e231e30a337 100644 (file)
@@ -44,7 +44,7 @@
 {{.FileContent}}</textarea>
                                        <div class="editor-loading is-loading"></div>
                                </div>
-                               <div class="ui bottom attached tab segment markdown" data-tab="preview">
+                               <div class="ui bottom attached tab segment markup" data-tab="preview">
                                        {{.i18n.Tr "loading"}}
                                </div>
                                <div class="ui bottom attached tab segment diff edit-diff" data-tab="diff">
index 7dae7c012166051ff5d1088f46d91c3d34db58c4..21c600545639d2d2f45cc204daef01f749d41f13 100644 (file)
@@ -27,7 +27,7 @@
 
                                                        <div class="item">
                                                                <h3>{{.i18n.Tr "repo.create_new_repo_command"}}</h3>
-                                                               <div class="markdown">
+                                                               <div class="markup">
                                                                        <pre><code>touch README.md
 git init
 {{if ne .Repository.DefaultBranch "master"}}git checkout -b {{.Repository.DefaultBranch}}{{end}}
@@ -41,7 +41,7 @@ git push -u origin {{.Repository.DefaultBranch}}</code></pre>
 
                                                        <div class="item">
                                                                <h3>{{.i18n.Tr "repo.push_exist_repo"}}</h3>
-                                                               <div class="markdown">
+                                                               <div class="markup">
                                                                        <pre><code>git remote add origin <span class="clone-url">{{if $.DisableSSH}}{{$.CloneLink.HTTPS}}{{else}}{{$.CloneLink.SSH}}{{end}}</span>
 git push -u origin {{.Repository.DefaultBranch}}</code></pre>
                                                                </div>
index ab874bdd136978839ec3060d9e15208b9afdcd95..77e82930dcf90e933e8184bd15b914f85cf1afcd 100644 (file)
@@ -8,7 +8,7 @@
                        {{- if .BodyQuery}}{{.BodyQuery}}{{else if .IssueTemplate}}{{.IssueTemplate}}{{else if .PullRequestTemplate}}{{.PullRequestTemplate}}{{else}}{{.content}}{{end -}}
                </textarea>
        </div>
-       <div class="ui bottom tab markdown" data-tab="preview">
+       <div class="ui bottom tab markup" data-tab="preview">
                {{.i18n.Tr "loading"}}
        </div>
 </div>
index 8c2f36f04bc92013a6a068eeb1cca1ff67fbb0e8..897d297d37cb5359a7f2ae6837fa1cc12fc8e159 100644 (file)
@@ -5,7 +5,7 @@
                <div class="ui three column stackable grid">
                        <div class="column">
                                <h1>{{.Milestone.Name}}</h1>
-                               <div class="markdown content">
+                               <div class="markup content">
                                        {{.Milestone.RenderedContent|Str2html}}
                                </div>
                        </div>
index c7d3522abcf470d980007119e136e3a095e39144..448d758e3ebfd3a051d51693ffbcc570c18329b5 100644 (file)
@@ -98,7 +98,7 @@
                                                </div>
                                        {{end}}
                                        {{if .Content}}
-                                               <div class="markdown content">
+                                               <div class="markup content">
                                                        {{.RenderedContent|Str2html}}
                                                </div>
                                        {{end}}
index 0482604b70292c319159a695701f360028a2e6d9..e353d71ee969ae976ab6b57c25fba3eb383dc5ac 100644 (file)
@@ -57,7 +57,7 @@
                                                </div>
                                        </div>
                                        <div class="ui attached segment comment-body">
-                                               <div class="render-content markdown">
+                                               <div class="render-content markup">
                                                        {{if .Issue.RenderedContent}}
                                                                {{.Issue.RenderedContent|Str2html}}
                                                        {{else}}
                        <div class="ui bottom active tab write">
                                <textarea tabindex="1" name="content"></textarea>
                        </div>
-                       <div class="ui bottom tab preview markdown">
+                       <div class="ui bottom tab preview markup">
                                {{$.i18n.Tr "loading"}}
                        </div>
                </div>
index 81f0d0434a28cf79ea3f9b7ce2ba592cd346c6f6..4863f7f2f1e0b217277bff6cd197960cbaf56257 100644 (file)
@@ -64,7 +64,7 @@
                                        </div>
                                </div>
                                <div class="ui attached segment comment-body">
-                                       <div class="render-content markdown">
+                                       <div class="render-content markup">
                                                {{if .RenderedContent}}
                                                        {{.RenderedContent|Str2html}}
                                                {{else}}
                                                </span>
                                        </div>
                                        <div class="ui attached segment comment-body">
-                                               <div class="render-content markdown">
+                                               <div class="render-content markup">
                                                        {{if .RenderedContent}}
                                                                {{.RenderedContent|Str2html}}
                                                        {{else}}
                                                                                                                </div>
                                                                                                        </div>
                                                                                                        <div class="text comment-content">
-                                                                                                               <div class="render-content markdown">
+                                                                                                               <div class="render-content markup">
                                                                                                                {{if .RenderedContent}}
                                                                                                                        {{.RenderedContent|Str2html}}
                                                                                                                {{else}}
                                                        </span>
                                                </div>
                                                <div class="ui attached segment">
-                                                       <div class="render-content markdown">
+                                                       <div class="render-content markup">
                                                                {{if .RenderedContent}}
                                                                        {{.RenderedContent|Str2html}}
                                                                {{else}}
index 63f9e26cf124a50b727b8fc5dc1dcd24124e180a..d98e441644ccc5a61a6b7db4326d6f0975aab436 100644 (file)
                                                                        | <span class="ahead"><a href="{{$.RepoLink}}/compare/{{.TagName | EscapePound}}...{{.Target}}">{{$.i18n.Tr "repo.release.ahead.commits" .NumCommitsBehind | Str2html}}</a> {{$.i18n.Tr "repo.release.ahead.target" .Target}}</span>
                                                                {{end}}
                                                        </p>
-                                                       <div class="markdown desc">
+                                                       <div class="markup desc">
                                                                {{Str2html .Note}}
                                                        </div>
                                                        <div class="ui accordion download">
index 8489d8959792e0bd5346d06829fa45e1b19f758a..c4b36597c68fa52c141f3461077420b5ad912c5e 100644 (file)
@@ -53,7 +53,7 @@
                                        <div class="ui bottom active tab" data-tab="write">
                                                <textarea name="content">{{.content}}</textarea>
                                        </div>
-                                       <div class="ui bottom tab markdown" data-tab="preview">
+                                       <div class="ui bottom tab markup" data-tab="preview">
                                                {{$.i18n.Tr "loading"}}
                                        </div>
                                </div>
index 09eeb3f27fe8844289d9ddeb8942001e162e1290..478c034e11074b6ad567c2193adf06cfe6424835 100644 (file)
@@ -12,7 +12,7 @@
                                </div>
                        </h4>
                        <div class="ui attached table unstackable segment">
-                               <div class="file-view {{if .IsMarkup}}markdown{{else if .IsRenderedHTML}}plain-text{{else if .IsTextFile}}code-view{{end}}">
+                               <div class="file-view{{if .IsMarkup}} markup {{.MarkupType}}{{else if .IsRenderedHTML}} plain-text{{else if .IsTextFile}} code-view{{end}}">
                                        {{if .IsMarkup}}
                                                {{if .FileContent}}{{.FileContent | Safe}}{{end}}
                                        {{else if .IsRenderedHTML}}
index d9c1e93eaf95acc0df9bee5fb3b5684288fc5778..5798ec875cbf13210a74bb0440914901b9aa8e04 100644 (file)
@@ -64,7 +64,7 @@
                {{end}}
        </h4>
        <div class="ui attached table unstackable segment">
-               <div class="file-view {{if .IsMarkup}}{{.MarkupType}} {{if ne "csv" .MarkupType}}markdown{{end}}{{else if .IsRenderedHTML}}plain-text{{else if .IsTextSource}}code-view{{end}}">
+               <div class="file-view{{if .IsMarkup}} markup {{.MarkupType}}{{else if .IsRenderedHTML}} plain-text{{else if .IsTextSource}} code-view{{end}}">
                        {{if .IsMarkup}}
                                {{if .FileContent}}{{.FileContent | Safe}}{{end}}
                        {{else if .IsRenderedHTML}}
index 0bc58588633252eb8d3590763a20305d5931b67e..fbb97db4ad3296f0a2f75733f5558ac09c3ed3fc 100644 (file)
@@ -61,7 +61,7 @@
                        </div>
                {{end}}
                <div class="ui {{if .sidebarPresent}}grid equal width{{end}}" style="margin-top: 1rem;">
-                       <div class="ui {{if .sidebarPresent}}eleven wide column{{end}} segment markdown">
+                       <div class="ui {{if .sidebarPresent}}eleven wide column{{end}} segment markup">
                                {{.content | Str2html}}
                        </div>
                        {{if .sidebarPresent}}
index 18f3c9f6ddecd3e038a8d8f61ed8a8b966d315e9..29da9334932f44cfe0ee900028ce93bc76a6d044 100644 (file)
@@ -36,7 +36,7 @@
                                                        {{end}}
                                                        {{if $.RenderedDescription}}
                                                                <li>
-                                                                       <div class="render-content markdown">{{$.RenderedDescription|Str2html}}</div>
+                                                                       <div class="render-content markup">{{$.RenderedDescription|Str2html}}</div>
                                                                </li>
                                                        {{end}}
                                                        {{range .OpenIDs}}
index 2fce21b8d3c359c32d726f8b37bb91c2da7f9cd6..53843a6d22764cd55d271b4b3e7c4214b5be99aa 100644 (file)
@@ -4,26 +4,26 @@ import Vue from 'vue';
 import {htmlEscape} from 'escape-goat';
 import 'jquery.are-you-sure';
 
-import initMigration from './features/migration.js';
+import ActivityTopAuthors from './components/ActivityTopAuthors.vue';
+import attachTribute from './features/tribute.js';
+import createColorPicker from './features/colorpicker.js';
+import createDropzone from './features/dropzone.js';
+import initClipboard from './features/clipboard.js';
 import initContextPopups from './features/contextpopup.js';
 import initGitGraph from './features/gitgraph.js';
-import initClipboard from './features/clipboard.js';
 import initHeatmap from './features/heatmap.js';
+import initImageDiff from './features/imagediff.js';
+import initMigration from './features/migration.js';
 import initProject from './features/projects.js';
 import initServiceWorker from './features/serviceworker.js';
-import initMarkdownAnchors from './markdown/anchors.js';
-import renderMarkdownContent from './markdown/content.js';
-import attachTribute from './features/tribute.js';
-import createColorPicker from './features/colorpicker.js';
-import createDropzone from './features/dropzone.js';
 import initTableSort from './features/tablesort.js';
-import initImageDiff from './features/imagediff.js';
-import ActivityTopAuthors from './components/ActivityTopAuthors.vue';
+import {createCodeEditor, createMonaco} from './features/codeeditor.js';
+import {initMarkupAnchors} from './markup/anchors.js';
 import {initNotificationsTable, initNotificationCount} from './features/notification.js';
 import {initStopwatch} from './features/stopwatch.js';
-import {createCodeEditor, createMonaco} from './features/codeeditor.js';
-import {svg, svgs} from './svg.js';
+import {renderMarkupContent} from './markup/content.js';
 import {stripTags, mqBinarySearch} from './utils.js';
+import {svg, svgs} from './svg.js';
 
 const {AppSubUrl, StaticUrlPrefix, csrf} = window.config;
 
@@ -51,7 +51,7 @@ function initCommentPreviewTab($form) {
     }, (data) => {
       const $previewPanel = $form.find(`.tab[data-tab="${$tabMenu.data('preview')}"]`);
       $previewPanel.html(data);
-      renderMarkdownContent();
+      renderMarkupContent();
     });
   });
 
@@ -81,7 +81,7 @@ function initEditPreviewTab($form) {
       }, (data) => {
         const $previewPanel = $form.find(`.tab[data-tab="${$tabMenu.data('preview')}"]`);
         $previewPanel.html(data);
-        renderMarkdownContent();
+        renderMarkupContent();
       });
     });
   }
@@ -1107,7 +1107,7 @@ async function initRepository() {
               dz.emit('submit');
               dz.emit('reload');
             }
-            renderMarkdownContent();
+            renderMarkupContent();
           });
         });
       } else {
@@ -1473,8 +1473,8 @@ function initWikiForm() {
             text: plainText,
             wiki: true
           }, (data) => {
-            preview.innerHTML = `<div class="markdown ui segment">${data}</div>`;
-            renderMarkdownContent();
+            preview.innerHTML = `<div class="markup ui segment">${data}</div>`;
+            renderMarkupContent();
           });
         };
 
@@ -1553,7 +1553,7 @@ function initWikiForm() {
             const $form = $('.repository.wiki.new .ui.form');
             const $root = $form.find('.field.content');
             const loading = $root.data('loading');
-            $root.append(`<div class="ui bottom tab markdown" data-tab="preview">${loading}</div>`);
+            $root.append(`<div class="ui bottom tab markup" data-tab="preview">${loading}</div>`);
             initCommentPreviewTab($form);
           },
           className: 'fa fa-file',
@@ -2772,7 +2772,7 @@ $(document).ready(async () => {
   searchTeams();
   searchRepositories();
 
-  initMarkdownAnchors();
+  initMarkupAnchors();
   initCommentForm();
   initInstall();
   initArchiveLinks();
@@ -2830,7 +2830,7 @@ $(document).ready(async () => {
     initServiceWorker(),
     initNotificationCount(),
     initStopwatch(),
-    renderMarkdownContent(),
+    renderMarkupContent(),
     initGithook(),
     initImageDiff(),
   ]);
diff --git a/web_src/js/markdown/anchors.js b/web_src/js/markdown/anchors.js
deleted file mode 100644 (file)
index 62bf8c8..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-import {svg} from '../svg.js';
-
-const headingSelector = '.markdown h1, .markdown h2, .markdown h3, .markdown h4, .markdown h5, .markdown h6';
-
-function scrollToAnchor() {
-  if (document.querySelector(':target')) return;
-  if (!window.location.hash || window.location.hash.length <= 1) return;
-  const id = decodeURIComponent(window.location.hash.substring(1));
-  const el = document.getElementById(`user-content-${id}`);
-  if (el) {
-    el.scrollIntoView();
-  } else if (id.startsWith('user-content-')) { // compat for links with old 'user-content-' prefixed hashes
-    const el = document.getElementById(id);
-    if (el) el.scrollIntoView();
-  }
-}
-
-export default function initMarkdownAnchors() {
-  if (!document.querySelector('.markdown')) return;
-
-  for (const heading of document.querySelectorAll(headingSelector)) {
-    const originalId = heading.id.replace(/^user-content-/, '');
-    const a = document.createElement('a');
-    a.classList.add('anchor');
-    a.setAttribute('href', `#${encodeURIComponent(originalId)}`);
-    a.innerHTML = svg('octicon-link');
-    heading.prepend(a);
-  }
-
-  scrollToAnchor();
-  window.addEventListener('hashchange', scrollToAnchor);
-}
diff --git a/web_src/js/markdown/content.js b/web_src/js/markdown/content.js
deleted file mode 100644 (file)
index 918cd6f..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-import {renderMermaid} from './mermaid.js';
-
-export default async function renderMarkdownContent() {
-  await renderMermaid(document.querySelectorAll('code.language-mermaid'));
-}
diff --git a/web_src/js/markdown/mermaid.js b/web_src/js/markdown/mermaid.js
deleted file mode 100644 (file)
index a518bc7..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-const MAX_SOURCE_CHARACTERS = 5000;
-
-function displayError(el, err) {
-  el.closest('pre').classList.remove('is-loading');
-  const errorNode = document.createElement('div');
-  errorNode.setAttribute('class', 'ui message error markdown-block-error mono');
-  errorNode.textContent = err.str || err.message || String(err);
-  el.closest('pre').before(errorNode);
-}
-
-export async function renderMermaid(els) {
-  if (!els || !els.length) return;
-
-  const mermaid = await import(/* webpackChunkName: "mermaid" */'mermaid');
-
-  mermaid.initialize({
-    mermaid: {
-      startOnLoad: false,
-    },
-    flowchart: {
-      useMaxWidth: true,
-      htmlLabels: false,
-    },
-    theme: 'neutral',
-    securityLevel: 'strict',
-  });
-
-  for (const el of els) {
-    if (el.textContent.length > MAX_SOURCE_CHARACTERS) {
-      displayError(el, new Error(`Mermaid source of ${el.textContent.length} characters exceeds the maximum allowed length of ${MAX_SOURCE_CHARACTERS}.`));
-      continue;
-    }
-
-    let valid;
-    try {
-      valid = mermaid.parse(el.textContent);
-    } catch (err) {
-      displayError(el, err);
-    }
-
-    if (!valid) {
-      el.closest('pre').classList.remove('is-loading');
-      continue;
-    }
-
-    try {
-      mermaid.init(undefined, el, (id) => {
-        const svg = document.getElementById(id);
-        svg.classList.add('mermaid-chart');
-        svg.closest('pre').replaceWith(svg);
-      });
-    } catch (err) {
-      displayError(el, err);
-    }
-  }
-}
diff --git a/web_src/js/markup/anchors.js b/web_src/js/markup/anchors.js
new file mode 100644 (file)
index 0000000..cc2ed5d
--- /dev/null
@@ -0,0 +1,32 @@
+import {svg} from '../svg.js';
+
+const headingSelector = '.markup h1, .markup h2, .markup h3, .markup h4, .markup h5, .markup h6';
+
+function scrollToAnchor() {
+  if (document.querySelector(':target')) return;
+  if (!window.location.hash || window.location.hash.length <= 1) return;
+  const id = decodeURIComponent(window.location.hash.substring(1));
+  const el = document.getElementById(`user-content-${id}`);
+  if (el) {
+    el.scrollIntoView();
+  } else if (id.startsWith('user-content-')) { // compat for links with old 'user-content-' prefixed hashes
+    const el = document.getElementById(id);
+    if (el) el.scrollIntoView();
+  }
+}
+
+export function initMarkupAnchors() {
+  if (!document.querySelector('.markup')) return;
+
+  for (const heading of document.querySelectorAll(headingSelector)) {
+    const originalId = heading.id.replace(/^user-content-/, '');
+    const a = document.createElement('a');
+    a.classList.add('anchor');
+    a.setAttribute('href', `#${encodeURIComponent(originalId)}`);
+    a.innerHTML = svg('octicon-link');
+    heading.prepend(a);
+  }
+
+  scrollToAnchor();
+  window.addEventListener('hashchange', scrollToAnchor);
+}
diff --git a/web_src/js/markup/content.js b/web_src/js/markup/content.js
new file mode 100644 (file)
index 0000000..f06c990
--- /dev/null
@@ -0,0 +1,5 @@
+import {renderMermaid} from './mermaid.js';
+
+export async function renderMarkupContent() {
+  await renderMermaid(document.querySelectorAll('code.language-mermaid'));
+}
diff --git a/web_src/js/markup/mermaid.js b/web_src/js/markup/mermaid.js
new file mode 100644 (file)
index 0000000..d0aefd1
--- /dev/null
@@ -0,0 +1,56 @@
+const MAX_SOURCE_CHARACTERS = 5000;
+
+function displayError(el, err) {
+  el.closest('pre').classList.remove('is-loading');
+  const errorNode = document.createElement('div');
+  errorNode.setAttribute('class', 'ui message error markup-block-error mono');
+  errorNode.textContent = err.str || err.message || String(err);
+  el.closest('pre').before(errorNode);
+}
+
+export async function renderMermaid(els) {
+  if (!els || !els.length) return;
+
+  const mermaid = await import(/* webpackChunkName: "mermaid" */'mermaid');
+
+  mermaid.initialize({
+    mermaid: {
+      startOnLoad: false,
+    },
+    flowchart: {
+      useMaxWidth: true,
+      htmlLabels: false,
+    },
+    theme: 'neutral',
+    securityLevel: 'strict',
+  });
+
+  for (const el of els) {
+    if (el.textContent.length > MAX_SOURCE_CHARACTERS) {
+      displayError(el, new Error(`Mermaid source of ${el.textContent.length} characters exceeds the maximum allowed length of ${MAX_SOURCE_CHARACTERS}.`));
+      continue;
+    }
+
+    let valid;
+    try {
+      valid = mermaid.parse(el.textContent);
+    } catch (err) {
+      displayError(el, err);
+    }
+
+    if (!valid) {
+      el.closest('pre').classList.remove('is-loading');
+      continue;
+    }
+
+    try {
+      mermaid.init(undefined, el, (id) => {
+        const svg = document.getElementById(id);
+        svg.classList.add('mermaid-chart');
+        svg.closest('pre').replaceWith(svg);
+      });
+    } catch (err) {
+      displayError(el, err);
+    }
+  }
+}
index 7e563d2f2332b221c80a6b7a603da4999f73d659..d85f13cb33c610dc9bb570e1d11773f6e2019435 100644 (file)
   --color-active: #00000014;
   --color-menu: #ffffff;
   --color-card: #ffffff;
-  --color-markdown-table-row: #00000008;
-  --color-markdown-code-block: #00000010;
+  --color-markup-table-row: #00000008;
+  --color-markup-code-block: #00000010;
   --color-button: #ffffff;
   --color-code-bg: #ffffff;
   --color-shadow: #00000030;
diff --git a/web_src/less/_markdown.less b/web_src/less/_markdown.less
deleted file mode 100644 (file)
index 6627689..0000000
+++ /dev/null
@@ -1,542 +0,0 @@
-.markdown:not(code) {
-  overflow: hidden;
-  font-size: 16px;
-  line-height: 1.5 !important;
-  word-wrap: break-word;
-
-  &.ui.segment {
-    padding: 3em;
-  }
-
-  &.file-view {
-    padding: 2em !important;
-  }
-
-  > *:first-child {
-    margin-top: 0 !important;
-  }
-
-  > *:last-child {
-    margin-bottom: 0 !important;
-  }
-
-  a:not([href]) {
-    color: inherit;
-    text-decoration: none;
-  }
-
-  .absent {
-    color: var(--color-red);
-  }
-
-  .anchor {
-    padding-right: 4px;
-    margin-left: -20px;
-    line-height: 1;
-    color: inherit;
-  }
-
-  .anchor .svg {
-    vertical-align: middle;
-  }
-
-  .anchor:focus {
-    outline: none;
-  }
-
-  h1 .anchor .svg,
-  h2 .anchor .svg,
-  h3 .anchor .svg,
-  h4 .anchor .svg,
-  h5 .anchor .svg,
-  h6 .anchor .svg {
-    visibility: hidden;
-  }
-
-  h1:hover .anchor .svg,
-  h2:hover .anchor .svg,
-  h3:hover .anchor .svg,
-  h4:hover .anchor .svg,
-  h5:hover .anchor .svg,
-  h6:hover .anchor .svg {
-    visibility: visible;
-  }
-
-  h2 .anchor .svg,
-  h3 .anchor .svg,
-  h4 .anchor .svg {
-    position: relative;
-    top: -2px;
-  }
-
-  h1,
-  h2,
-  h3,
-  h4,
-  h5,
-  h6 {
-    margin-top: 24px;
-    margin-bottom: 16px;
-    font-weight: 600;
-    line-height: 1.25;
-  }
-
-  h1 tt,
-  h1 code,
-  h2 tt,
-  h2 code,
-  h3 tt,
-  h3 code,
-  h4 tt,
-  h4 code,
-  h5 tt,
-  h5 code,
-  h6 tt,
-  h6 code {
-    font-size: inherit;
-  }
-
-  h1 {
-    padding-bottom: .3em;
-    font-size: 2em;
-    border-bottom: 1px solid var(--color-secondary);
-  }
-
-  h2 {
-    padding-bottom: .3em;
-    font-size: 1.5em;
-    border-bottom: 1px solid var(--color-secondary);
-  }
-
-  h3 {
-    font-size: 1.25em;
-  }
-
-  h4 {
-    font-size: 1em;
-  }
-
-  h5 {
-    font-size: .875em;
-  }
-
-  h6 {
-    font-size: .85em;
-    color: var(--color-text-light-2);
-  }
-
-  p,
-  blockquote,
-  details,
-  ul,
-  ol,
-  dl,
-  table,
-  pre {
-    margin-top: 0;
-    margin-bottom: 16px;
-  }
-
-  hr {
-    height: 4px;
-    padding: 0;
-    margin: 16px 0;
-    background-color: var(--color-secondary);
-    border: 0;
-  }
-
-  ul,
-  ol {
-    padding-left: 2em;
-  }
-
-  ul.no-list,
-  ol.no-list {
-    padding: 0;
-    list-style-type: none;
-  }
-
-  .task-list-item {
-    list-style-type: none;
-
-    input[type="checkbox"] {
-      margin: 0 6px .25em -1.6em;
-    }
-  }
-
-  .task-list-item + .task-list-item {
-    margin-top: 3px;
-  }
-
-  input[type="checkbox"] {
-    -webkit-appearance: none;
-    -moz-appearance: none;
-    appearance: none;
-    position: relative;
-    border: 1px solid var(--color-secondary);
-    border-radius: 2px;
-    background: var(--color-input-background);
-    height: 14px;
-    width: 14px;
-    opacity: 1 !important; // override fomantic on edit preview
-    pointer-events: auto !important; // override fomantic on edit preview
-    vertical-align: middle !important; // override fomantic on edit preview
-  }
-
-  input[type="checkbox"]:not([disabled]):hover,
-  input[type="checkbox"]:not([disabled]):active {
-    border-color: var(--color-primary);
-  }
-
-  input[type="checkbox"]::after {
-    position: absolute;
-    left: 0;
-    top: 0;
-    bottom: 0;
-    right: 0;
-    pointer-events: none;
-    background: var(--color-text);
-    mask-size: cover;
-    -webkit-mask-size: cover;
-  }
-
-  input[type="checkbox"]:checked::after {
-    content: "";
-    mask-image: var(--checkbox-mask-checked);
-    -webkit-mask-image: var(--checkbox-mask-checked);
-  }
-
-  input[type="checkbox"]:indeterminate::after {
-    content: "";
-    mask-image: var(--checkbox-mask-indeterminate);
-    -webkit-mask-image: var(--checkbox-mask-indeterminate);
-  }
-
-  ul ul,
-  ul ol,
-  ol ol,
-  ol ul {
-    margin-top: 0;
-    margin-bottom: 0;
-  }
-
-  ol ol,
-  ul ol {
-    list-style-type: lower-roman;
-  }
-
-  li > p {
-    margin-top: 16px;
-  }
-
-  li + li {
-    margin-top: .25em;
-  }
-
-  dl {
-    padding: 0;
-  }
-
-  dl dt {
-    padding: 0;
-    margin-top: 16px;
-    font-size: 1em;
-    font-style: italic;
-    font-weight: 600;
-  }
-
-  dl dd {
-    padding: 0 16px;
-    margin-bottom: 16px;
-  }
-
-  blockquote {
-    margin-left: 0;
-    padding: 0 15px;
-    color: var(--color-text-light-2);
-    border-left: 4px solid var(--color-secondary);
-  }
-
-  blockquote > :first-child {
-    margin-top: 0;
-  }
-
-  blockquote > :last-child {
-    margin-bottom: 0;
-  }
-
-  table {
-    display: block;
-    width: 100%;
-    width: -webkit-max-content;
-    width: -moz-max-content;
-    width: max-content;
-    max-width: 100%;
-    overflow: auto;
-  }
-
-  table th {
-    font-weight: 600;
-  }
-
-  table th,
-  table td {
-    padding: 6px 13px !important;
-    border: 1px solid var(--color-secondary) !important;
-  }
-
-  table tr {
-    border-top: 1px solid var(--color-secondary);
-  }
-
-  table tr:nth-child(2n) {
-    background-color: var(--color-markdown-table-row);
-  }
-
-  img {
-    max-width: 100%;
-    box-sizing: initial;
-  }
-
-  img[align="right"] {
-    padding-left: 20px;
-  }
-
-  img[align="left"] {
-    padding-right: 20px;
-  }
-
-  .emoji {
-    max-width: none;
-    vertical-align: text-top;
-  }
-
-  span.frame {
-    display: block;
-    overflow: hidden;
-  }
-
-  span.frame > span {
-    display: block;
-    float: left;
-    width: auto;
-    padding: 7px;
-    margin: 13px 0 0;
-    overflow: hidden;
-    border: 1px solid var(--color-secondary);
-  }
-
-  span.frame span img {
-    display: block;
-    float: left;
-  }
-
-  span.frame span span {
-    display: block;
-    padding: 5px 0 0;
-    clear: both;
-    color: var(--color-text);
-  }
-
-  span.align-center {
-    display: block;
-    overflow: hidden;
-    clear: both;
-  }
-
-  span.align-center > span {
-    display: block;
-    margin: 13px auto 0;
-    overflow: hidden;
-    text-align: center;
-  }
-
-  span.align-center span img {
-    margin: 0 auto;
-    text-align: center;
-  }
-
-  span.align-right {
-    display: block;
-    overflow: hidden;
-    clear: both;
-  }
-
-  span.align-right > span {
-    display: block;
-    margin: 13px 0 0;
-    overflow: hidden;
-    text-align: right;
-  }
-
-  span.align-right span img {
-    margin: 0;
-    text-align: right;
-  }
-
-  span.float-left {
-    display: block;
-    float: left;
-    margin-right: 13px;
-    overflow: hidden;
-  }
-
-  span.float-left span {
-    margin: 13px 0 0;
-  }
-
-  span.float-right {
-    display: block;
-    float: right;
-    margin-left: 13px;
-    overflow: hidden;
-  }
-
-  span.float-right > span {
-    display: block;
-    margin: 13px auto 0;
-    overflow: hidden;
-    text-align: right;
-  }
-
-  code,
-  tt {
-    padding: .2em .4em;
-    margin: 0;
-    font-size: 85%;
-    background-color: var(--color-markdown-code-block);
-    border-radius: 4px;
-  }
-
-  code br,
-  tt br {
-    display: none;
-  }
-
-  del code {
-    text-decoration: inherit;
-  }
-
-  pre > code {
-    padding: 0;
-    margin: 0;
-    font-size: 100%;
-    white-space: pre-wrap;
-    word-break: break-all;
-    overflow-wrap: break-word;
-    background: transparent;
-    border: 0;
-  }
-
-  .highlight {
-    margin-bottom: 16px;
-  }
-
-  .highlight pre,
-  pre {
-    padding: 16px;
-    font-size: 85%;
-    line-height: 1.45;
-    background-color: var(--color-markdown-code-block);
-    border-radius: 4px;
-  }
-
-  .highlight pre {
-    margin-bottom: 0;
-    word-break: normal;
-  }
-
-  pre {
-    word-wrap: normal;
-  }
-
-  pre code,
-  pre tt {
-    display: inline;
-    max-width: initial;
-    padding: 0;
-    margin: 0;
-    overflow: initial;
-    line-height: inherit;
-    word-wrap: normal;
-    background-color: transparent;
-    border: 0;
-  }
-
-  pre code::before,
-  pre code::after,
-  pre tt::before,
-  pre tt::after {
-    content: normal;
-  }
-
-  kbd {
-    display: inline-block;
-    padding: 3px 5px;
-    font-size: 11px;
-    line-height: 10px;
-    color: var(--color-text-light);
-    vertical-align: middle;
-    background-color: var(--color-markdown-code-block);
-    border: 1px solid var(--color-secondary);
-    border-radius: 3px;
-    box-shadow: inset 0 -1px 0 var(--color-secondary);
-  }
-
-  .ui.list .list,
-  ol.ui.list ol,
-  ul.ui.list ul {
-    padding-left: 2em;
-  }
-}
-
-.repository.wiki.revisions {
-  .ui.container > .ui.stackable.grid {
-    -ms-flex-direction: row-reverse;
-    flex-direction: row-reverse;
-
-    > .header {
-      margin-top: 0;
-
-      .sub.header {
-        padding-left: 52px;
-        word-break: break-word;
-      }
-    }
-  }
-}
-
-.file-revisions-btn {
-  display: block;
-  float: left;
-  margin-bottom: 2px !important;
-  padding: 11px !important;
-  margin-right: 10px !important;
-
-  i {
-    -webkit-touch-callout: none;
-    -webkit-user-select: none;
-    -khtml-user-select: none;
-    -moz-user-select: none;
-    -ms-user-select: none;
-    user-select: none;
-  }
-}
-
-.markdown-block-error {
-  margin-bottom: 0 !important;
-  border-bottom-left-radius: 0 !important;
-  border-bottom-right-radius: 0 !important;
-  box-shadow: none !important;
-  font-size: 85% !important;
-  white-space: pre !important;
-  padding: .5rem 1rem !important;
-  text-align: left !important;
-}
-
-.markdown-block-error + pre {
-  border-top: none !important;
-  margin-top: 0 !important;
-  border-top-left-radius: 0 !important;
-  border-top-right-radius: 0 !important;
-}
index 5ff51b1d6df6b4f9b2fc1c8bb296dad4085770c7..549bd4d7e5324756425ed9c10d1611c4bc5171cb 100644 (file)
           border-right-color: var(--color-box-body);
         }
 
-        .markdown {
+        .markup {
           font-size: 14px;
         }
       }
             }
           }
 
-          .markdown {
+          .markup {
             font-size: 14px;
           }
 
               overflow: hidden;
             }
 
-            .tab.markdown {
+            .tab.markup {
               min-height: 5rem;
             }
           }
       margin-bottom: 1.5rem;
     }
 
-    .markdown {
+    .markup {
       font-size: 14px;
     }
   }
         margin-top: -5px;
       }
 
-      > .markdown {
+      > .markup {
         padding: 15px 30px;
 
         h1,
@@ -2991,7 +2991,7 @@ td.blob-excerpt {
 .webhook-info {
   padding: 7px 12px;
   margin: 10px 0;
-  background-color: var(--color-markdown-code-block);
+  background-color: var(--color-markup-code-block);
   border: 1px solid var(--color-secondary);
   border-radius: 3px;
   font-size: 13px;
index f6f5df20a21e219969f2f5e364e1e64177bf6aee..2b8700a745487267b62a95733b55d07033e8478c 100644 (file)
@@ -81,7 +81,7 @@
   .ui.active.tab {
     padding: .5em;
 
-    &.markdown {
+    &.markup {
       padding: 1em;
       min-height: 168px;
     }
@@ -95,7 +95,7 @@
     border-top: 1px solid var(--color-secondary);
     padding: 10px 0;
 
-    .markdown-info {
+    .markup-info {
       display: inline-block;
       margin: 5px 0;
       font-size: 12px;
index 0b50440bf377e91b357706299b76d80bef6773eb..f3491155cd9b233acca1fa50115718754a13cd6f 100644 (file)
@@ -28,7 +28,7 @@
   border-radius: 100%;
 }
 
-.markdown pre.is-loading,
+.markup pre.is-loading,
 .editor-loading.is-loading {
   height: 12rem;
 }
index c125ccac318d8b07c0073a75e64254f9178a8eeb..f52953f2a4874ed00d6667ec1f57a667ab06f748 100644 (file)
@@ -9,7 +9,8 @@
 @import "./features/imagediff.less";
 @import "./features/codeeditor.less";
 @import "./features/projects.less";
-@import "./markdown/mermaid.less";
+@import "./markup/content.less";
+@import "./markup/mermaid.less";
 
 @import "./chroma/base.less";
 @import "./chroma/light.less";
@@ -18,7 +19,6 @@
 @import "_tribute";
 @import "_font_i18n";
 @import "_base";
-@import "_markdown";
 @import "_home";
 @import "_install";
 @import "_form";
diff --git a/web_src/less/markdown/mermaid.less b/web_src/less/markdown/mermaid.less
deleted file mode 100644 (file)
index f68b577..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-.mermaid-chart {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  padding: 1rem;
-  margin: 1rem auto;
-  height: auto;
-}
-
-/* mermaid's errorRenderer seems to unavoidably spew stuff into <body>, hide it */
-body > div[id*="mermaid-"] {
-  display: none !important;
-}
diff --git a/web_src/less/markup/content.less b/web_src/less/markup/content.less
new file mode 100644 (file)
index 0000000..df87c21
--- /dev/null
@@ -0,0 +1,542 @@
+.markup {
+  overflow: hidden;
+  font-size: 16px;
+  line-height: 1.5 !important;
+  word-wrap: break-word;
+
+  &.ui.segment {
+    padding: 3em;
+  }
+
+  &.file-view {
+    padding: 2em !important;
+  }
+
+  > *:first-child {
+    margin-top: 0 !important;
+  }
+
+  > *:last-child {
+    margin-bottom: 0 !important;
+  }
+
+  a:not([href]) {
+    color: inherit;
+    text-decoration: none;
+  }
+
+  .absent {
+    color: var(--color-red);
+  }
+
+  .anchor {
+    padding-right: 4px;
+    margin-left: -20px;
+    line-height: 1;
+    color: inherit;
+  }
+
+  .anchor .svg {
+    vertical-align: middle;
+  }
+
+  .anchor:focus {
+    outline: none;
+  }
+
+  h1 .anchor .svg,
+  h2 .anchor .svg,
+  h3 .anchor .svg,
+  h4 .anchor .svg,
+  h5 .anchor .svg,
+  h6 .anchor .svg {
+    visibility: hidden;
+  }
+
+  h1:hover .anchor .svg,
+  h2:hover .anchor .svg,
+  h3:hover .anchor .svg,
+  h4:hover .anchor .svg,
+  h5:hover .anchor .svg,
+  h6:hover .anchor .svg {
+    visibility: visible;
+  }
+
+  h2 .anchor .svg,
+  h3 .anchor .svg,
+  h4 .anchor .svg {
+    position: relative;
+    top: -2px;
+  }
+
+  h1,
+  h2,
+  h3,
+  h4,
+  h5,
+  h6 {
+    margin-top: 24px;
+    margin-bottom: 16px;
+    font-weight: 600;
+    line-height: 1.25;
+  }
+
+  h1 tt,
+  h1 code,
+  h2 tt,
+  h2 code,
+  h3 tt,
+  h3 code,
+  h4 tt,
+  h4 code,
+  h5 tt,
+  h5 code,
+  h6 tt,
+  h6 code {
+    font-size: inherit;
+  }
+
+  h1 {
+    padding-bottom: .3em;
+    font-size: 2em;
+    border-bottom: 1px solid var(--color-secondary);
+  }
+
+  h2 {
+    padding-bottom: .3em;
+    font-size: 1.5em;
+    border-bottom: 1px solid var(--color-secondary);
+  }
+
+  h3 {
+    font-size: 1.25em;
+  }
+
+  h4 {
+    font-size: 1em;
+  }
+
+  h5 {
+    font-size: .875em;
+  }
+
+  h6 {
+    font-size: .85em;
+    color: var(--color-text-light-2);
+  }
+
+  p,
+  blockquote,
+  details,
+  ul,
+  ol,
+  dl,
+  table,
+  pre {
+    margin-top: 0;
+    margin-bottom: 16px;
+  }
+
+  hr {
+    height: 4px;
+    padding: 0;
+    margin: 16px 0;
+    background-color: var(--color-secondary);
+    border: 0;
+  }
+
+  ul,
+  ol {
+    padding-left: 2em;
+  }
+
+  ul.no-list,
+  ol.no-list {
+    padding: 0;
+    list-style-type: none;
+  }
+
+  .task-list-item {
+    list-style-type: none;
+
+    input[type="checkbox"] {
+      margin: 0 6px .25em -1.6em;
+    }
+  }
+
+  .task-list-item + .task-list-item {
+    margin-top: 3px;
+  }
+
+  input[type="checkbox"] {
+    -webkit-appearance: none;
+    -moz-appearance: none;
+    appearance: none;
+    position: relative;
+    border: 1px solid var(--color-secondary);
+    border-radius: 2px;
+    background: var(--color-input-background);
+    height: 14px;
+    width: 14px;
+    opacity: 1 !important; // override fomantic on edit preview
+    pointer-events: auto !important; // override fomantic on edit preview
+    vertical-align: middle !important; // override fomantic on edit preview
+  }
+
+  input[type="checkbox"]:not([disabled]):hover,
+  input[type="checkbox"]:not([disabled]):active {
+    border-color: var(--color-primary);
+  }
+
+  input[type="checkbox"]::after {
+    position: absolute;
+    left: 0;
+    top: 0;
+    bottom: 0;
+    right: 0;
+    pointer-events: none;
+    background: var(--color-text);
+    mask-size: cover;
+    -webkit-mask-size: cover;
+  }
+
+  input[type="checkbox"]:checked::after {
+    content: "";
+    mask-image: var(--checkbox-mask-checked);
+    -webkit-mask-image: var(--checkbox-mask-checked);
+  }
+
+  input[type="checkbox"]:indeterminate::after {
+    content: "";
+    mask-image: var(--checkbox-mask-indeterminate);
+    -webkit-mask-image: var(--checkbox-mask-indeterminate);
+  }
+
+  ul ul,
+  ul ol,
+  ol ol,
+  ol ul {
+    margin-top: 0;
+    margin-bottom: 0;
+  }
+
+  ol ol,
+  ul ol {
+    list-style-type: lower-roman;
+  }
+
+  li > p {
+    margin-top: 16px;
+  }
+
+  li + li {
+    margin-top: .25em;
+  }
+
+  dl {
+    padding: 0;
+  }
+
+  dl dt {
+    padding: 0;
+    margin-top: 16px;
+    font-size: 1em;
+    font-style: italic;
+    font-weight: 600;
+  }
+
+  dl dd {
+    padding: 0 16px;
+    margin-bottom: 16px;
+  }
+
+  blockquote {
+    margin-left: 0;
+    padding: 0 15px;
+    color: var(--color-text-light-2);
+    border-left: 4px solid var(--color-secondary);
+  }
+
+  blockquote > :first-child {
+    margin-top: 0;
+  }
+
+  blockquote > :last-child {
+    margin-bottom: 0;
+  }
+
+  table {
+    display: block;
+    width: 100%;
+    width: -webkit-max-content;
+    width: -moz-max-content;
+    width: max-content;
+    max-width: 100%;
+    overflow: auto;
+  }
+
+  table th {
+    font-weight: 600;
+  }
+
+  table th,
+  table td {
+    padding: 6px 13px !important;
+    border: 1px solid var(--color-secondary) !important;
+  }
+
+  table tr {
+    border-top: 1px solid var(--color-secondary);
+  }
+
+  table tr:nth-child(2n) {
+    background-color: var(--color-markup-table-row);
+  }
+
+  img {
+    max-width: 100%;
+    box-sizing: initial;
+  }
+
+  img[align="right"] {
+    padding-left: 20px;
+  }
+
+  img[align="left"] {
+    padding-right: 20px;
+  }
+
+  .emoji {
+    max-width: none;
+    vertical-align: text-top;
+  }
+
+  span.frame {
+    display: block;
+    overflow: hidden;
+  }
+
+  span.frame > span {
+    display: block;
+    float: left;
+    width: auto;
+    padding: 7px;
+    margin: 13px 0 0;
+    overflow: hidden;
+    border: 1px solid var(--color-secondary);
+  }
+
+  span.frame span img {
+    display: block;
+    float: left;
+  }
+
+  span.frame span span {
+    display: block;
+    padding: 5px 0 0;
+    clear: both;
+    color: var(--color-text);
+  }
+
+  span.align-center {
+    display: block;
+    overflow: hidden;
+    clear: both;
+  }
+
+  span.align-center > span {
+    display: block;
+    margin: 13px auto 0;
+    overflow: hidden;
+    text-align: center;
+  }
+
+  span.align-center span img {
+    margin: 0 auto;
+    text-align: center;
+  }
+
+  span.align-right {
+    display: block;
+    overflow: hidden;
+    clear: both;
+  }
+
+  span.align-right > span {
+    display: block;
+    margin: 13px 0 0;
+    overflow: hidden;
+    text-align: right;
+  }
+
+  span.align-right span img {
+    margin: 0;
+    text-align: right;
+  }
+
+  span.float-left {
+    display: block;
+    float: left;
+    margin-right: 13px;
+    overflow: hidden;
+  }
+
+  span.float-left span {
+    margin: 13px 0 0;
+  }
+
+  span.float-right {
+    display: block;
+    float: right;
+    margin-left: 13px;
+    overflow: hidden;
+  }
+
+  span.float-right > span {
+    display: block;
+    margin: 13px auto 0;
+    overflow: hidden;
+    text-align: right;
+  }
+
+  code,
+  tt {
+    padding: .2em .4em;
+    margin: 0;
+    font-size: 85%;
+    background-color: var(--color-markup-code-block);
+    border-radius: 4px;
+  }
+
+  code br,
+  tt br {
+    display: none;
+  }
+
+  del code {
+    text-decoration: inherit;
+  }
+
+  pre > code {
+    padding: 0;
+    margin: 0;
+    font-size: 100%;
+    white-space: pre-wrap;
+    word-break: break-all;
+    overflow-wrap: break-word;
+    background: transparent;
+    border: 0;
+  }
+
+  .highlight {
+    margin-bottom: 16px;
+  }
+
+  .highlight pre,
+  pre {
+    padding: 16px;
+    font-size: 85%;
+    line-height: 1.45;
+    background-color: var(--color-markup-code-block);
+    border-radius: 4px;
+  }
+
+  .highlight pre {
+    margin-bottom: 0;
+    word-break: normal;
+  }
+
+  pre {
+    word-wrap: normal;
+  }
+
+  pre code,
+  pre tt {
+    display: inline;
+    max-width: initial;
+    padding: 0;
+    margin: 0;
+    overflow: initial;
+    line-height: inherit;
+    word-wrap: normal;
+    background-color: transparent;
+    border: 0;
+  }
+
+  pre code::before,
+  pre code::after,
+  pre tt::before,
+  pre tt::after {
+    content: normal;
+  }
+
+  kbd {
+    display: inline-block;
+    padding: 3px 5px;
+    font-size: 11px;
+    line-height: 10px;
+    color: var(--color-text-light);
+    vertical-align: middle;
+    background-color: var(--color-markup-code-block);
+    border: 1px solid var(--color-secondary);
+    border-radius: 3px;
+    box-shadow: inset 0 -1px 0 var(--color-secondary);
+  }
+
+  .ui.list .list,
+  ol.ui.list ol,
+  ul.ui.list ul {
+    padding-left: 2em;
+  }
+}
+
+.repository.wiki.revisions {
+  .ui.container > .ui.stackable.grid {
+    -ms-flex-direction: row-reverse;
+    flex-direction: row-reverse;
+
+    > .header {
+      margin-top: 0;
+
+      .sub.header {
+        padding-left: 52px;
+        word-break: break-word;
+      }
+    }
+  }
+}
+
+.file-revisions-btn {
+  display: block;
+  float: left;
+  margin-bottom: 2px !important;
+  padding: 11px !important;
+  margin-right: 10px !important;
+
+  i {
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+  }
+}
+
+.markup-block-error {
+  margin-bottom: 0 !important;
+  border-bottom-left-radius: 0 !important;
+  border-bottom-right-radius: 0 !important;
+  box-shadow: none !important;
+  font-size: 85% !important;
+  white-space: pre !important;
+  padding: .5rem 1rem !important;
+  text-align: left !important;
+}
+
+.markup-block-error + pre {
+  border-top: none !important;
+  margin-top: 0 !important;
+  border-top-left-radius: 0 !important;
+  border-top-right-radius: 0 !important;
+}
diff --git a/web_src/less/markup/mermaid.less b/web_src/less/markup/mermaid.less
new file mode 100644 (file)
index 0000000..f68b577
--- /dev/null
@@ -0,0 +1,13 @@
+.mermaid-chart {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 1rem;
+  margin: 1rem auto;
+  height: auto;
+}
+
+/* mermaid's errorRenderer seems to unavoidably spew stuff into <body>, hide it */
+body > div[id*="mermaid-"] {
+  display: none !important;
+}
index 346a6e85ea3e6fb2114c7a0750622e440b06a594..9e39c4bec4ec575ad64e123887a5100ec4f7eb23 100644 (file)
@@ -97,8 +97,8 @@
   --color-active: #ffffff16;
   --color-menu: #2e323e;
   --color-card: #2e323e;
-  --color-markdown-table-row: #ffffff06;
-  --color-markdown-code-block: #2a2e3a;
+  --color-markup-table-row: #ffffff06;
+  --color-markup-code-block: #2a2e3a;
   --color-button: #353846;
   --color-code-bg: #2a2e3a;
   --color-shadow: #00000060;
@@ -301,7 +301,7 @@ a.ui.basic.green.label:hover {
   & + .editor-preview-side {
     background: #353945;
 
-    .markdown:not(code).ui.segment {
+    .markup.ui.segment {
       border-width: 0;
     }
   }
@@ -754,7 +754,7 @@ img[src$="/img/matrix.svg"] {
   border-color: #4a4c58 #4a4c58 #d7d7da #d7d7da;
 }
 
-.markdown-block-error {
+.markup-block-error {
   border: 1px solid rgba(121, 71, 66, .5) !important;
   border-bottom: none !important;
 }