diff --git a/assets/css/chroma/dark.css b/assets/css/chroma/dark.css new file mode 100644 index 0000000..8f662bd --- /dev/null +++ b/assets/css/chroma/dark.css @@ -0,0 +1,89 @@ +.dark .highlight { + /* Background .bg { color: #c9d1d9; background-color: #0d1117; } + /* PreWrapper .chroma { color: #c9d1d9; background-color: #0d1117; } */ + /* Other */ .chroma .x { } + /* Error */ .chroma .err { color: #f85149 } + /* CodeLine */ .chroma .cl { } + /* LineLink */ .chroma .lnlinks { outline: none; text-decoration: none; color: inherit } + /* LineTableTD .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } */ + /* LineTable .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; } */ + /* LineHighlight .chroma .hl { background-color: #ffffcc } */ + /* LineNumbersTable .chroma .lnt { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #64686c } */ + /* LineNumbers .chroma .ln { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #6e7681 } */ + /* Line */ .chroma .line { display: flex; } + /* Keyword */ .chroma .k { color: #ff7b72 } + /* KeywordConstant */ .chroma .kc { color: #79c0ff } + /* KeywordDeclaration */ .chroma .kd { color: #ff7b72 } + /* KeywordNamespace */ .chroma .kn { color: #ff7b72 } + /* KeywordPseudo */ .chroma .kp { color: #79c0ff } + /* KeywordReserved */ .chroma .kr { color: #ff7b72 } + /* KeywordType */ .chroma .kt { color: #ff7b72 } + /* Name */ .chroma .n { } + /* NameAttribute */ .chroma .na { } + /* NameBuiltin */ .chroma .nb { } + /* NameBuiltinPseudo */ .chroma .bp { } + /* NameClass */ .chroma .nc { color: #f0883e; font-weight: bold } + /* NameConstant */ .chroma .no { color: #79c0ff; font-weight: bold } + /* NameDecorator */ .chroma .nd { color: #d2a8ff; font-weight: bold } + /* NameEntity */ .chroma .ni { color: #ffa657 } + /* NameException */ .chroma .ne { color: #f0883e; font-weight: bold } + /* NameFunction */ .chroma .nf { color: #d2a8ff; font-weight: bold } + /* NameFunctionMagic */ .chroma .fm { } + /* NameLabel */ .chroma .nl { color: #79c0ff; font-weight: bold } + /* NameNamespace */ .chroma .nn { color: #ff7b72 } + /* NameOther */ .chroma .nx { } + /* NameProperty */ .chroma .py { color: #79c0ff } + /* NameTag */ .chroma .nt { color: #7ee787 } + /* NameVariable */ .chroma .nv { color: #79c0ff } + /* NameVariableClass */ .chroma .vc { } + /* NameVariableGlobal */ .chroma .vg { } + /* NameVariableInstance */ .chroma .vi { } + /* NameVariableMagic */ .chroma .vm { } + /* Literal */ .chroma .l { color: #a5d6ff } + /* LiteralDate */ .chroma .ld { color: #79c0ff } + /* LiteralString */ .chroma .s { color: #a5d6ff } + /* LiteralStringAffix */ .chroma .sa { color: #79c0ff } + /* LiteralStringBacktick */ .chroma .sb { color: #a5d6ff } + /* LiteralStringChar */ .chroma .sc { color: #a5d6ff } + /* LiteralStringDelimiter */ .chroma .dl { color: #79c0ff } + /* LiteralStringDoc */ .chroma .sd { color: #a5d6ff } + /* LiteralStringDouble */ .chroma .s2 { color: #a5d6ff } + /* LiteralStringEscape */ .chroma .se { color: #79c0ff } + /* LiteralStringHeredoc */ .chroma .sh { color: #79c0ff } + /* LiteralStringInterpol */ .chroma .si { color: #a5d6ff } + /* LiteralStringOther */ .chroma .sx { color: #a5d6ff } + /* LiteralStringRegex */ .chroma .sr { color: #79c0ff } + /* LiteralStringSingle */ .chroma .s1 { color: #a5d6ff } + /* LiteralStringSymbol */ .chroma .ss { color: #a5d6ff } + /* LiteralNumber */ .chroma .m { color: #a5d6ff } + /* LiteralNumberBin */ .chroma .mb { color: #a5d6ff } + /* LiteralNumberFloat */ .chroma .mf { color: #a5d6ff } + /* LiteralNumberHex */ .chroma .mh { color: #a5d6ff } + /* LiteralNumberInteger */ .chroma .mi { color: #a5d6ff } + /* LiteralNumberIntegerLong */ .chroma .il { color: #a5d6ff } + /* LiteralNumberOct */ .chroma .mo { color: #a5d6ff } + /* Operator */ .chroma .o { color: #ff7b72; font-weight: bold } + /* OperatorWord */ .chroma .ow { color: #ff7b72; font-weight: bold } + /* Punctuation */ .chroma .p { } + /* Comment */ .chroma .c { color: #8b949e; font-style: italic } + /* CommentHashbang */ .chroma .ch { color: #8b949e; font-style: italic } + /* CommentMultiline */ .chroma .cm { color: #8b949e; font-style: italic } + /* CommentSingle */ .chroma .c1 { color: #8b949e; font-style: italic } + /* CommentSpecial */ .chroma .cs { color: #8b949e; font-weight: bold; font-style: italic } + /* CommentPreproc */ .chroma .cp { color: #8b949e; font-weight: bold; font-style: italic } + /* CommentPreprocFile */ .chroma .cpf { color: #8b949e; font-weight: bold; font-style: italic } + /* Generic */ .chroma .g { } + /* GenericDeleted */ .chroma .gd { color: #ffa198; background-color: #490202 } + /* GenericEmph */ .chroma .ge { font-style: italic } + /* GenericError */ .chroma .gr { color: #ffa198 } + /* GenericHeading */ .chroma .gh { color: #79c0ff; font-weight: bold } + /* GenericInserted */ .chroma .gi { color: #56d364; background-color: #0f5323 } + /* GenericOutput */ .chroma .go { color: #8b949e } + /* GenericPrompt */ .chroma .gp { color: #8b949e } + /* GenericStrong */ .chroma .gs { font-weight: bold } + /* GenericSubheading */ .chroma .gu { color: #79c0ff } + /* GenericTraceback */ .chroma .gt { color: #ff7b72 } + /* GenericUnderline */ .chroma .gl { text-decoration: underline } + /* TextWhitespace */ .chroma .w { color: #6e7681 } +} + diff --git a/assets/css/chroma/light.css b/assets/css/chroma/light.css new file mode 100644 index 0000000..20990c7 --- /dev/null +++ b/assets/css/chroma/light.css @@ -0,0 +1,90 @@ +/* Light theme for syntax highlight */ +/* Generated using `hugo gen chromastyles --style=github` */ +.highlight { + /* Background .bg { background-color: #ffffff; } */ + /* PreWrapper .chroma { background-color: #ffffff; } */ + /* Other .chroma .x { } */ + /* Error */ .chroma .err { color: #a61717; background-color: #e3d2d2 } + /* CodeLine .chroma .cl { } */ + /* LineLink */ .chroma .lnlinks { outline: none; text-decoration: none; color: inherit } + /* LineTableTD .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } */ + /* LineTable .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; } */ + /* LineHighlight .chroma .hl { background-color: #ffffcc } */ + /* LineNumbersTable .chroma .lnt { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } */ + /* LineNumbers .chroma .ln { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } */ + /* Line */ .chroma .line { display: flex; } + /* Keyword */ .chroma .k { color: #000000; font-weight: bold } + /* KeywordConstant */ .chroma .kc { color: #000000; font-weight: bold } + /* KeywordDeclaration */ .chroma .kd { color: #000000; font-weight: bold } + /* KeywordNamespace */ .chroma .kn { color: #000000; font-weight: bold } + /* KeywordPseudo */ .chroma .kp { color: #000000; font-weight: bold } + /* KeywordReserved */ .chroma .kr { color: #000000; font-weight: bold } + /* KeywordType */ .chroma .kt { color: #445588; font-weight: bold } + /* Name .chroma .n { } */ + /* NameAttribute */ .chroma .na { color: #008080 } + /* NameBuiltin */ .chroma .nb { color: #0086b3 } + /* NameBuiltinPseudo */ .chroma .bp { color: #999999 } + /* NameClass */ .chroma .nc { color: #445588; font-weight: bold } + /* NameConstant */ .chroma .no { color: #008080 } + /* NameDecorator */ .chroma .nd { color: #3c5d5d; font-weight: bold } + /* NameEntity */ .chroma .ni { color: #800080 } + /* NameException */ .chroma .ne { color: #990000; font-weight: bold } + /* NameFunction */ .chroma .nf { color: #990000; font-weight: bold } + /* NameFunctionMagic .chroma .fm { } */ + /* NameLabel */ .chroma .nl { color: #990000; font-weight: bold } + /* NameNamespace */ .chroma .nn { color: #555555 } + /* NameOther .chroma .nx { } */ + /* NameProperty .chroma .py { } */ + /* NameTag */ .chroma .nt { color: #000080 } + /* NameVariable */ .chroma .nv { color: #008080 } + /* NameVariableClass */ .chroma .vc { color: #008080 } + /* NameVariableGlobal */ .chroma .vg { color: #008080 } + /* NameVariableInstance */ .chroma .vi { color: #008080 } + /* NameVariableMagic .chroma .vm { } */ + /* Literal .chroma .l { } */ + /* LiteralDate .chroma .ld { } */ + /* LiteralString */ .chroma .s { color: #dd1144 } + /* LiteralStringAffix */ .chroma .sa { color: #dd1144 } + /* LiteralStringBacktick */ .chroma .sb { color: #dd1144 } + /* LiteralStringChar */ .chroma .sc { color: #dd1144 } + /* LiteralStringDelimiter */ .chroma .dl { color: #dd1144 } + /* LiteralStringDoc */ .chroma .sd { color: #dd1144 } + /* LiteralStringDouble */ .chroma .s2 { color: #dd1144 } + /* LiteralStringEscape */ .chroma .se { color: #dd1144 } + /* LiteralStringHeredoc */ .chroma .sh { color: #dd1144 } + /* LiteralStringInterpol */ .chroma .si { color: #dd1144 } + /* LiteralStringOther */ .chroma .sx { color: #dd1144 } + /* LiteralStringRegex */ .chroma .sr { color: #009926 } + /* LiteralStringSingle */ .chroma .s1 { color: #dd1144 } + /* LiteralStringSymbol */ .chroma .ss { color: #990073 } + /* LiteralNumber */ .chroma .m { color: #009999 } + /* LiteralNumberBin */ .chroma .mb { color: #009999 } + /* LiteralNumberFloat */ .chroma .mf { color: #009999 } + /* LiteralNumberHex */ .chroma .mh { color: #009999 } + /* LiteralNumberInteger */ .chroma .mi { color: #009999 } + /* LiteralNumberIntegerLong */ .chroma .il { color: #009999 } + /* LiteralNumberOct */ .chroma .mo { color: #009999 } + /* Operator */ .chroma .o { color: #000000; font-weight: bold } + /* OperatorWord */ .chroma .ow { color: #000000; font-weight: bold } + /* Punctuation .chroma .p { } */ + /* Comment */ .chroma .c { color: #999988; font-style: italic } + /* CommentHashbang */ .chroma .ch { color: #999988; font-style: italic } + /* CommentMultiline */ .chroma .cm { color: #999988; font-style: italic } + /* CommentSingle */ .chroma .c1 { color: #999988; font-style: italic } + /* CommentSpecial */ .chroma .cs { color: #999999; font-weight: bold; font-style: italic } + /* CommentPreproc */ .chroma .cp { color: #999999; font-weight: bold; font-style: italic } + /* CommentPreprocFile */ .chroma .cpf { color: #999999; font-weight: bold; font-style: italic } + /* Generic .chroma .g { } */ + /* GenericDeleted */ .chroma .gd { color: #000000; background-color: #ffdddd } + /* GenericEmph */ .chroma .ge { color: #000000; font-style: italic } + /* GenericError */ .chroma .gr { color: #aa0000 } + /* GenericHeading */ .chroma .gh { color: #999999 } + /* GenericInserted */ .chroma .gi { color: #000000; background-color: #ddffdd } + /* GenericOutput */ .chroma .go { color: #888888 } + /* GenericPrompt */ .chroma .gp { color: #555555 } + /* GenericStrong */ .chroma .gs { font-weight: bold } + /* GenericSubheading */ .chroma .gu { color: #aaaaaa } + /* GenericTraceback */ .chroma .gt { color: #aa0000 } + /* GenericUnderline */ .chroma .gl { text-decoration: underline } + /* TextWhitespace */ .chroma .w { color: #bbbbbb } +} diff --git a/assets/css/highlight.css b/assets/css/highlight.css new file mode 100644 index 0000000..662cce0 --- /dev/null +++ b/assets/css/highlight.css @@ -0,0 +1,47 @@ +/* Code syntax highlight */ +@import "chroma/light.css"; +@import "chroma/dark.css"; + +.code-block { + @apply text-[.9em] leading-5; + + pre { + @apply text-[.9em] bg-primary-700/5 overflow-x-auto font-medium subpixel-antialiased dark:bg-primary-300/10 contrast-more:border contrast-more:border-primary-900/20 contrast-more:contrast-150 contrast-more:dark:border-primary-100/40; + } + + .filename { + @apply absolute top-0 z-[1] w-full truncate rounded-t-xl bg-primary-700/5 py-2 px-4 text-xs text-gray-700 dark:bg-primary-300/10 dark:text-gray-200; + } +} + +.code-block pre:not(.lntable pre) { + @apply px-4 mb-4 py-4 rounded-xl; +} + +.code-block div:nth-of-type(2) pre { + @apply pt-12 pb-4; +} + +.chroma { + .lntable { + @apply m-0 block w-auto overflow-auto rounded-xl; + + pre { + @apply pt-4 pb-4; + } + } + .lnt, + .ln { + @apply pl-4 pr-4 min-w-[2.6rem] text-neutral-600 dark:text-neutral-300; + } + .lntd { + @apply p-0 align-top; + } + .lntd:last-of-type { + @apply w-full; + } + /* LineHighlight */ + .hl { + @apply block w-full bg-primary-800/10; + } +} diff --git a/assets/css/styles.css b/assets/css/styles.css index b549f54..da3936e 100644 --- a/assets/css/styles.css +++ b/assets/css/styles.css @@ -3,6 +3,7 @@ @tailwind utilities; @import "typography.css"; +@import "highlight.css"; @import "components/cards.css"; @import "components/steps.css"; diff --git a/assets/css/typography.css b/assets/css/typography.css index 58e57a9..00b8b74 100644 --- a/assets/css/typography.css +++ b/assets/css/typography.css @@ -29,13 +29,13 @@ blockquote { @apply mt-6 border-gray-300 italic text-gray-700 dark:border-gray-700 dark:text-gray-400 first:mt-0 ltr:border-l-2 ltr:pl-6 rtl:border-r-2 rtl:pr-6; } - pre { + pre:not(.code-block pre) { @apply bg-primary-700/5 mb-4 overflow-x-auto rounded-xl font-medium subpixel-antialiased dark:bg-primary-300/10 text-[.9em] contrast-more:border contrast-more:border-primary-900/20 contrast-more:contrast-150 contrast-more:dark:border-primary-100/40 py-4; } - code { + code:not(.code-block code) { @apply border-black border-opacity-[0.04] bg-opacity-[0.03] bg-black break-words rounded-md border py-0.5 px-[.25em] text-[.9em] dark:border-white/10 dark:bg-white/10; } - table { + table:not(.code-block table) { @apply block overflow-x-auto mt-6 p-0 first:mt-0; tr { diff --git a/assets/js/code-copy.js b/assets/js/code-copy.js new file mode 100644 index 0000000..5a0d756 --- /dev/null +++ b/assets/js/code-copy.js @@ -0,0 +1,24 @@ +document.querySelectorAll('.code-copy-btn').forEach(function (button) { + button.addEventListener('click', function (e) { + const targetId = e.target.getAttribute('data-clipboard-target'); + const target = document.querySelector(targetId); + const codeElements = target.querySelectorAll('code'); + // Select the last code element in case line numbers are present + const codeElement = codeElements[codeElements.length - 1]; + if (codeElement) { + // Replace double newlines with single newlines in the innerText + // as each line inside has trailing newline '\n' + const code = codeElement.innerText.replace(/\n\n/g, '\n'); + navigator.clipboard.writeText(code).then(function () { + button.classList.add('copied'); + setTimeout(function () { + button.classList.remove('copied'); + }, 500); + }).catch(function (err) { + console.error('Failed to copy text: ', err); + }); + } else { + console.error('Target element not found'); + } + }); +}); diff --git a/content/docs/guide/markdown.md b/content/docs/guide/markdown.md index 5c850b8..c326270 100644 --- a/content/docs/guide/markdown.md +++ b/content/docs/guide/markdown.md @@ -1,5 +1,6 @@ --- title: Markdown +math: true --- This article offers a sample of basic Markdown syntax that can be used in Hugo content files, also it shows whether basic HTML elements are decorated with CSS in a Hugo theme. @@ -55,47 +56,61 @@ Tables aren't part of the core Markdown spec, but Hugo supports them out-of-the- ## Code Blocks -#### Code block with backticks +### Code block with triple backticks -```html - - - - - Example HTML5 Document - - -

Test

- - +``` +def say_hello(): + print("Hello!") ``` -#### Code block indented with four spaces - - - - - Example HTML5 Document - - -

Test

- - +```python +def say_hello(): + print("Hello!") +``` -#### Code block with Hugo's internal highlight shortcode -{{< highlight html >}} - - - - - Example HTML5 Document - - -

Test

- - -{{< /highlight >}} +### Code block with filename + +```python {filename="hello.py"} +def say_hello(): + print("Hello!") +``` + +### Code block highlight with line numbers + +```python {linenos=table,hl_lines=[1, 2],linenostart=1} +def say_hello(): + print("Hello!") + +def main(): + say_hello() +``` + +```python {linenos=table,hl_lines=[1, 2],linenostart=1,filename="hello.py"} +def say_hello(): + print("Hello!") + +def main(): + say_hello() +``` + +## Diagrams + +[Mermaid](https://github.com/mermaid-js/mermaid#readme) is a JavaScript based diagramming and charting tool that takes Markdown-inspired text definitions and creates diagrams dynamically in the browser. + +```mermaid +sequenceDiagram + participant Alice + participant Bob + Alice->>John: Hello John, how are you? + loop Healthcheck + John->>John: Fight against hypochondria + end + Note right of John: Rational thoughts
prevail! + John-->>Alice: Great! + John->>Bob: How about you? + Bob-->>John: Jolly good! +``` ## List Types @@ -121,6 +136,27 @@ Tables aren't part of the core Markdown spec, but Hugo supports them out-of-the- * Milk * Cheese +## Math + +[KaTeX](https://github.com/KaTeX/KaTeX) is a fast math typesetting library for the web. It uses javascript for client-side rendering. + +### Inline + +``` +This $\int\limits_1^\infty \frac{1}{k}\,dk$ is inline. +``` + +This $\int\limits_1^\infty \frac{1}{k}\,dk$ is inline. + +### Separate Paragraph + +``` +$$f(x) = \int_{-\infty}^\infty \hat f(\xi)\,e^{2 \pi i \xi x} \,d\xi$$ +``` + +$$f(x) = \int_{-\infty}^\infty \hat f(\xi)\,e^{2 \pi i \xi x} \,d\xi$$ + + ## Other Elements — abbr, sub, sup, kbd, mark GIF is a bitmap image format. @@ -129,6 +165,6 @@ H2O Xn + Yn = Zn -Press CTRL+ALT+Delete to end the session. +Press CTRL+ALT+Delete to end the session. Most salamanders are nocturnal, and hunt for insects, worms, and other small creatures. diff --git a/data/icons.yaml b/data/icons.yaml index fe54875..d2323d5 100644 --- a/data/icons.yaml +++ b/data/icons.yaml @@ -33,3 +33,6 @@ chevron-right: one: cards: + +copy: +check: diff --git a/hugo.toml b/hugo.toml index bbd4104..d2b53bb 100644 --- a/hugo.toml +++ b/hugo.toml @@ -46,6 +46,9 @@ defaultContentLanguage = 'en' [markup.goldmark] [markup.goldmark.renderer] unsafe = true + [markup.highlight] + noClasses = false + [menu] [[menu.main]] diff --git a/layouts/_default/_markup/render-codeblock-mermaid.html b/layouts/_default/_markup/render-codeblock-mermaid.html new file mode 100644 index 0000000..104c894 --- /dev/null +++ b/layouts/_default/_markup/render-codeblock-mermaid.html @@ -0,0 +1,4 @@ +
+  {{- .Inner | safeHTML }}
+
+{{ .Page.Store.Set "hasMermaid" true }} diff --git a/layouts/_default/_markup/render-codeblock.html b/layouts/_default/_markup/render-codeblock.html new file mode 100644 index 0000000..b0e9b80 --- /dev/null +++ b/layouts/_default/_markup/render-codeblock.html @@ -0,0 +1,23 @@ +{{ $class := .Attributes.class | default "" }} +{{ $filename := .Attributes.filename | default "" }} +{{ $lang := .Attributes.lang | default .Type }} + + +
+ {{- if $filename -}} +
{{ $filename }}
+ {{- end -}} + {{- if transform.CanHighlight $lang -}} +
+ {{- highlight .Inner $lang .Options -}} +
+ {{- else -}} +
{{- .Inner -}}
+ {{- end -}} +
+ +
+
diff --git a/layouts/partials/scripts.html b/layouts/partials/scripts.html index 6df30e6..fa7e004 100644 --- a/layouts/partials/scripts.html +++ b/layouts/partials/scripts.html @@ -1,2 +1,34 @@ {{ $themeJS := resources.Get "js/theme.js" }} + +{{ $codeCopyJS := resources.Get "js/code-copy.js" }} + + +{{ if .Page.Store.Get "hasMermaid" }} + +{{ end }} + +{{ if .Page.Params.math }} + + + + + +{{ end }}