diff --git a/.gitignore b/.gitignore index f9cab2f80..debc86d46 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,13 @@ +.DS_Store +.hugo_build.lock /.idea /.vscode -/public /dist -node_modules +/public +hugo_stats.json +node_modules/ nohup.out -.DS_Store -trace.out -.hugo_build.lock -resources/_gen/images/ \ No newline at end of file +package-lock.json +public/ +resources/ +trace.out \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md index b09cd7856..979711275 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,201 +1,3 @@ -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +See [content/LICENSE.md](content/LICENSE.md) for the license of the content of this repository. - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +The theme (layouts, CSS, JavaScript etc.) of this repository has no open source license. It is custom made for the Hugo sites and is not meant for reuse. \ No newline at end of file diff --git a/README.md b/README.md index 7550a93cd..64ba31a48 100644 --- a/README.md +++ b/README.md @@ -16,3 +16,12 @@ Please see the [contributing] section for guidelines, examples, and process. [friends]: https://github.com/gohugoio/hugo/graphs/contributors [go]: https://go.dev/ [contributing]: https://gohugo.io/contribute/documentation + +# Install + +```bash +npm i +hugo server +``` + +**Note:** We're working on removing the need to run `npm i` for local development. Stay tuned. \ No newline at end of file diff --git a/assets/css/components/all.css b/assets/css/components/all.css new file mode 100644 index 000000000..117f19998 --- /dev/null +++ b/assets/css/components/all.css @@ -0,0 +1,7 @@ +/* The ordeer of these does not matter. */ +@import "./content.css"; +@import "./fonts.css"; +@import "./helpers.css"; +@import "./shortcodes.css"; +@import "./tableofcontents.css"; +@import "./view-transitions.css"; diff --git a/assets/css/components/chroma.css b/assets/css/components/chroma.css new file mode 100644 index 000000000..9d4c91f7b --- /dev/null +++ b/assets/css/components/chroma.css @@ -0,0 +1,85 @@ +/* Background */ .bg { background-color: var(--color-light); } +/* PreWrapper */ .chroma { background-color: var(--color-light); } +/* Other */ .chroma .x { } +/* Error */ .chroma .err { color: #a61717; background-color: #e3d2d2 } +/* CodeLine */ .chroma .cl { } +/* 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; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } +/* LineNumbers */ .chroma .ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } +/* Line */ .chroma .line { display: flex; } +/* Keyword */ .chroma .k { font-weight: bold } +/* KeywordConstant */ .chroma .kc { font-weight: bold } +/* KeywordDeclaration */ .chroma .kd { font-weight: bold } +/* KeywordNamespace */ .chroma .kn { font-weight: bold } +/* KeywordPseudo */ .chroma .kp { font-weight: bold } +/* KeywordReserved */ .chroma .kr { font-weight: bold } +/* KeywordType */ .chroma .kt { color: #445588; font-weight: bold } +/* Name */ .chroma .n { } +/* NameAttribute */ .chroma .na { color: #008080 } +/* NameBuiltin */ .chroma .nb { color: #999999 } +/* NameBuiltinPseudo */ .chroma .bp { } +/* NameClass */ .chroma .nc { color: #445588; font-weight: bold } +/* NameConstant */ .chroma .no { color: #008080 } +/* NameDecorator */ .chroma .nd { } +/* 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 { } +/* NameNamespace */ .chroma .nn { color: #555555 } +/* NameOther */ .chroma .nx { } +/* NameProperty */ .chroma .py { } +/* NameTag */ .chroma .nt { color: #000080 } +/* NameVariable */ .chroma .nv { color: #008080 } +/* NameVariableClass */ .chroma .vc { } +/* NameVariableGlobal */ .chroma .vg { } +/* NameVariableInstance */ .chroma .vi { } +/* NameVariableMagic */ .chroma .vm { } +/* Literal */ .chroma .l { } +/* LiteralDate */ .chroma .ld { } +/* LiteralString */ .chroma .s { color: #bb8844 } +/* LiteralStringAffix */ .chroma .sa { color: #bb8844 } +/* LiteralStringBacktick */ .chroma .sb { color: #bb8844 } +/* LiteralStringChar */ .chroma .sc { color: #bb8844 } +/* LiteralStringDelimiter */ .chroma .dl { color: #bb8844 } +/* LiteralStringDoc */ .chroma .sd { color: #bb8844 } +/* LiteralStringDouble */ .chroma .s2 { color: #bb8844 } +/* LiteralStringEscape */ .chroma .se { color: #bb8844 } +/* LiteralStringHeredoc */ .chroma .sh { color: #bb8844 } +/* LiteralStringInterpol */ .chroma .si { color: #bb8844 } +/* LiteralStringOther */ .chroma .sx { color: #bb8844 } +/* LiteralStringRegex */ .chroma .sr { color: #808000 } +/* LiteralStringSingle */ .chroma .s1 { color: #bb8844 } +/* LiteralStringSymbol */ .chroma .ss { color: #bb8844 } +/* 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 { font-weight: bold } +/* OperatorWord */ .chroma .ow { 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 } +/* CommentPreprocFile */ .chroma .cpf { color: #999999; font-weight: bold } +/* Generic */ .chroma .g { } +/* GenericDeleted */ .chroma .gd { color: #000000; background-color: #ffdddd } +/* GenericEmph */ .chroma .ge { 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/components/chroma_dark.css b/assets/css/components/chroma_dark.css new file mode 100644 index 000000000..0dd9d7bfa --- /dev/null +++ b/assets/css/components/chroma_dark.css @@ -0,0 +1,85 @@ +/* Background */.dark .bg { background-color: var(--color-dark); } +/* PreWrapper */ .dark .chroma { background-color: var(--color-dark); } +/* Other */ .dark .chroma .x { } +/* Error */ .dark .chroma .err { color: #ef6155 } +/* CodeLine */ .dark .chroma .cl { } +/* LineTableTD */ .dark .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } +/* LineTable */ .dark .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; } +/* LineHighlight */ .dark .chroma .hl { background-color: #ffffcc } +/* LineNumbersTable */ .dark .chroma .lnt { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } +/* LineNumbers */ .dark .chroma .ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } +/* Line */ .dark .chroma .line { display: flex; } +/* Keyword */ .dark .chroma .k { color: #815ba4 } +/* KeywordConstant */ .dark .chroma .kc { color: #815ba4 } +/* KeywordDeclaration */ .dark .chroma .kd { color: #815ba4 } +/* KeywordNamespace */ .dark .chroma .kn { color: #5bc4bf } +/* KeywordPseudo */ .dark .chroma .kp { color: #815ba4 } +/* KeywordReserved */ .dark .chroma .kr { color: #815ba4 } +/* KeywordType */ .dark .chroma .kt { color: #fec418 } +/* Name */ .dark .chroma .n { } +/* NameAttribute */ .dark .chroma .na { color: #06b6ef } +/* NameBuiltin */ .dark .chroma .nb { } +/* NameBuiltinPseudo */ .dark .chroma .bp { } +/* NameClass */ .dark .chroma .nc { color: #fec418 } +/* NameConstant */ .dark .chroma .no { color: #ef6155 } +/* NameDecorator */ .dark .chroma .nd { color: #5bc4bf } +/* NameEntity */ .dark .chroma .ni { } +/* NameException */ .dark .chroma .ne { color: #ef6155 } +/* NameFunction */ .dark .chroma .nf { color: #06b6ef } +/* NameFunctionMagic */ .dark .chroma .fm { } +/* NameLabel */ .dark .chroma .nl { } +/* NameNamespace */ .dark .chroma .nn { color: #fec418 } +/* NameOther */ .dark .chroma .nx { color: #06b6ef } +/* NameProperty */ .dark .chroma .py { } +/* NameTag */ .dark .chroma .nt { color: #5bc4bf } +/* NameVariable */ .dark .chroma .nv { color: #ef6155 } +/* NameVariableClass */ .dark .chroma .vc { } +/* NameVariableGlobal */ .dark .chroma .vg { } +/* NameVariableInstance */ .dark .chroma .vi { } +/* NameVariableMagic */ .dark .chroma .vm { } +/* Literal */ .dark .chroma .l { color: #f99b15 } +/* LiteralDate */ .dark .chroma .ld { color: #48b685 } +/* LiteralString */ .dark .chroma .s { color: #48b685 } +/* LiteralStringAffix */ .dark .chroma .sa { color: #48b685 } +/* LiteralStringBacktick */ .dark .chroma .sb { color: #48b685 } +/* LiteralStringChar */ .dark .chroma .sc { } +/* LiteralStringDelimiter */ .dark .chroma .dl { color: #48b685 } +/* LiteralStringDoc */ .dark .chroma .sd { color: #776e71 } +/* LiteralStringDouble */ .dark .chroma .s2 { color: #48b685 } +/* LiteralStringEscape */ .dark .chroma .se { color: #f99b15 } +/* LiteralStringHeredoc */ .dark .chroma .sh { color: #48b685 } +/* LiteralStringInterpol */ .dark .chroma .si { color: #f99b15 } +/* LiteralStringOther */ .dark .chroma .sx { color: #48b685 } +/* LiteralStringRegex */ .dark .chroma .sr { color: #48b685 } +/* LiteralStringSingle */ .dark .chroma .s1 { color: #48b685 } +/* LiteralStringSymbol */ .dark .chroma .ss { color: #48b685 } +/* LiteralNumber */ .dark .chroma .m { color: #f99b15 } +/* LiteralNumberBin */ .dark .chroma .mb { color: #f99b15 } +/* LiteralNumberFloat */ .dark .chroma .mf { color: #f99b15 } +/* LiteralNumberHex */ .dark .chroma .mh { color: #f99b15 } +/* LiteralNumberInteger */ .dark .chroma .mi { color: #f99b15 } +/* LiteralNumberIntegerLong */ .dark .chroma .il { color: #f99b15 } +/* LiteralNumberOct */ .dark .chroma .mo { color: #f99b15 } +/* Operator */ .dark .chroma .o { color: #5bc4bf } +/* OperatorWord */ .dark .chroma .ow { color: #5bc4bf } +/* Punctuation */ .dark .chroma .p { } +/* Comment */ .dark .chroma .c { color: #776e71 } +/* CommentHashbang */ .dark .chroma .ch { color: #776e71 } +/* CommentMultiline */ .dark .chroma .cm { color: #776e71 } +/* CommentSingle */ .dark .chroma .c1 { color: #776e71 } +/* CommentSpecial */ .dark .chroma .cs { color: #776e71 } +/* CommentPreproc */ .dark .chroma .cp { color: #776e71 } +/* CommentPreprocFile */ .dark .chroma .cpf { color: #776e71 } +/* Generic */ .dark .chroma .g { } +/* GenericDeleted */ .dark .chroma .gd { color: #ef6155 } +/* GenericEmph */ .dark .chroma .ge { font-style: italic } +/* GenericError */ .dark .chroma .gr { } +/* GenericHeading */ .dark .chroma .gh { font-weight: bold } +/* GenericInserted */ .dark .chroma .gi { color: #48b685 } +/* GenericOutput */ .dark .chroma .go { } +/* GenericPrompt */ .dark .chroma .gp { color: #776e71; font-weight: bold } +/* GenericStrong */ .dark .chroma .gs { font-weight: bold } +/* GenericSubheading */ .dark .chroma .gu { color: #5bc4bf; font-weight: bold } +/* GenericTraceback */ .dark .chroma .gt { } +/* GenericUnderline */ .dark .chroma .gl { } +/* TextWhitespace */ .dark .chroma .w { } diff --git a/assets/css/components/content.css b/assets/css/components/content.css new file mode 100644 index 000000000..6ad1c6bc6 --- /dev/null +++ b/assets/css/components/content.css @@ -0,0 +1,38 @@ +@import "./chroma_dark.css"; +@import "./chroma.css"; +@import "./highlight.css"; + +/* Some contrast ratio fixes as reported by Google Page Speed. */ +.chroma .c1 { + @apply text-gray-500; +} + +.dark .chroma .c1 { + @apply text-gray-400; +} + +.content { + @apply prose prose-stone max-w-none dark:prose-invert dark:text-slate-400; + /* headings */ + @apply prose-h6:font-bold; + /* lead */ + @apply prose-lead:text-slate-500 prose-lead:text-xl prose-lead:mt-2 sm:prose-lead:mt-4 prose-lead:leading-relaxed dark:prose-lead:text-slate-400; + /* links */ + @apply prose-a:text-primary prose-a:hover:text-primary/70 prose-a:underline; + @apply prose-a:prose-code:underline prose-a:prose-code:hover:text-primary/70 prose-a:prose-code:hover:underline; + /* pre */ + @apply prose-pre:text-gray-800 prose-pre:border-1 prose-pre:border-gray-100 prose-pre:bg-light dark:prose-pre:bg-dark dark:prose-pre:ring-1 dark:prose-pre:ring-slate-300/10; + /* code */ + @apply prose-code:px-0.5 prose-code:text-gray-900 prose-code:dark:text-gray-300 border-none; + @apply prose-code:before:hidden prose-code:after:hidden prose-code:font-mono; + /* tables */ + @apply prose-table:border-2 prose-table:border-gray-100 prose-table:dark:border-gray-800 prose-table:relative prose-table:overflow-scroll prose-table:prose-th:font-bold prose-table:prose-th:bg-blue-500 dark:prose-table:prose-th:bg-blue-500/50 prose-table:prose-th:p-2 prose-table:prose-td:p-2 prose-table:prose-th:text-white; + /* hr */ + @apply dark:prose-hr:border-slate-800; +} + +/* This will not match highlighting inside e.g. the code-toggle shortcode. */ +/* For more fine grained control of this, see components/shortcodes.css. */ +.content > .highlight { + @apply border-1 border-gray-200 dark:border-slate-600 mt-6 mb-8; +} diff --git a/assets/css/components/fonts.css b/assets/css/components/fonts.css new file mode 100644 index 000000000..06f40b4bf --- /dev/null +++ b/assets/css/components/fonts.css @@ -0,0 +1,15 @@ +@font-face { + font-family: "Mulish"; + font-style: normal; + src: url("../fonts/Mulish-VariableFont_wght.ttf") format("truetype"); + font-weight: 1 999; + font-display: swap; +} + +@font-face { + font-family: "Mulish"; + font-style: italic; + src: url("../fonts/Mulish-Italic-VariableFont_wght.ttf") format("truetype"); + font-weight: 1 999; + font-display: swap; +} diff --git a/assets/css/components/helpers.css b/assets/css/components/helpers.css new file mode 100644 index 000000000..8eb6930b8 --- /dev/null +++ b/assets/css/components/helpers.css @@ -0,0 +1,19 @@ +/* Helper class to limit a text block to two lines. */ +.two-lines-ellipsis { + display: block; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; +} + +/* Helper class to limit a text block to three lines. */ +.three-lines-ellipsis { + display: block; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/assets/css/components/highlight.css b/assets/css/components/highlight.css new file mode 100644 index 000000000..5f25fe368 --- /dev/null +++ b/assets/css/components/highlight.css @@ -0,0 +1,11 @@ +.highlight { + @apply bg-light dark:bg-dark rounded-none; +} + +.highlight pre { + @apply m-0 p-3 w-full h-full overflow-x-auto dark:border-black rounded-none; +} + +.highlight pre code { + @apply m-0 p-0 w-full h-full; +} diff --git a/assets/css/components/shortcodes.css b/assets/css/components/shortcodes.css new file mode 100644 index 000000000..7314d5b20 --- /dev/null +++ b/assets/css/components/shortcodes.css @@ -0,0 +1,4 @@ +.shortcode-code { + .highlight { + } +} diff --git a/assets/css/components/tableofcontents.css b/assets/css/components/tableofcontents.css new file mode 100644 index 000000000..3640adf6d --- /dev/null +++ b/assets/css/components/tableofcontents.css @@ -0,0 +1,14 @@ +.tableofcontents { + ul { + @apply list-none; + li { + @apply mb-2; + a { + @apply text-primary; + &:hover { + @apply text-primary/60; + } + } + } + } +} diff --git a/assets/css/components/view-transitions.css b/assets/css/components/view-transitions.css new file mode 100644 index 000000000..3e7217c4e --- /dev/null +++ b/assets/css/components/view-transitions.css @@ -0,0 +1,22 @@ +/* Global slight fade */ +::view-transition-old(root), +::view-transition-new(root) { + animation-duration: 200ms; +} + +::view-transition-old(qr), +::view-transition-new(qr) { + animation-duration: 800ms; + animation-delay: 250ms; +} + +.view-transition-qr { + view-transition-name: qr; +} + +/* Turbo styles */ +.turbo-progress-bar { + @apply bg-blue-500; + opacity: 0.35; + height: 3px; +} diff --git a/assets/css/styles.css b/assets/css/styles.css new file mode 100644 index 000000000..5e872b2a0 --- /dev/null +++ b/assets/css/styles.css @@ -0,0 +1,120 @@ +@import "tailwindcss"; +@plugin "@tailwindcss/typography"; +@variant dark (&:where(.dark, .dark *)); + +@import "components/all.css"; + +/* TailwindCSS ignores files in .gitignore, so make it explicit. */ +@source "hugo_stats.json"; + +@theme { + /* Breakpoints. */ + --breakpoint-sm: 40rem; + --breakpoint-md: 48rem; + --breakpoint-lg: 68rem; /* Default 64rem; */ + --breakpoint-xl: 80rem; + --breakpoint-2xl: 96rem; + + /* Colors. */ + --color-primary: var(--color-blue-600); + --color-dark: #000; + --color-light: var(--color-gray-50); + --color-accent: var(--color-orange-500); + --color-accent-light: var(--color-pink-500); + --color-accent-dark: var(--color-green-500); + + /* https://www.tints.dev/blue/0594CB */ + --color-blue-50: #e1f6fe; + --color-blue-100: #c3edfe; + --color-blue-200: #88dbfc; + --color-blue-300: #4cc9fb; + --color-blue-400: #15b9f9; + --color-blue-500: #0594cb; + --color-blue-600: #0477a4; + --color-blue-700: #035677; + --color-blue-800: #023a50; + --color-blue-900: #011d28; + --color-blue-950: #000e14; + + /* https://www.tints.dev/orange/EBB951 */ + --color-orange-50: #fdf8ed; + --color-orange-100: #fbf1da; + --color-orange-200: #f7e4ba; + --color-orange-300: #f3d596; + --color-orange-400: #efc976; + --color-orange-500: #ebb951; + --color-orange-600: #e5a51a; + --color-orange-700: #a97a13; + --color-orange-800: #72520d; + --color-orange-900: #372806; + --color-orange-950: #1b1403; + + /* https://www.tints.dev/pink/FF4088 */ + --color-pink-50: #ffebf2; + --color-pink-100: #ffdbe9; + --color-pink-200: #ffb3d0; + --color-pink-300: #ff8fba; + --color-pink-400: #ff66a1; + --color-pink-500: #ff4088; + --color-pink-600: #ff0062; + --color-pink-700: #c2004a; + --color-pink-800: #800031; + --color-pink-900: #420019; + --color-pink-950: #1f000c; + + /* https://www.tints.dev/green/33BA91 */ + --color-green-50: #ebfaf5; + --color-green-100: #d3f3e9; + --color-green-200: #abe8d6; + --color-green-300: #7fdcc0; + --color-green-400: #53d0aa; + --color-green-500: #33ba91; + --color-green-600: #299474; + --color-green-700: #1f7058; + --color-green-800: #154c3b; + --color-green-900: #0a241c; + --color-green-950: #051410; + + /* Fonts. */ + --font-sans: "Mulish", ui-sans-serif, system-ui, sans-serif, + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; +} + +html { + scroll-padding-top: 100px; +} + +body { + @apply antialiased font-sans text-black dark:text-gray-100; +} + +.p-safe-area-x { + padding-left: env(safe-area-inset-left); + padding-right: env(safe-area-inset-right); +} + +.p-safe-area-y { + padding-top: env(safe-area-inset-top); + padding-bottom: env(safe-area-inset-bottom); +} + +.px-main { + padding-left: max(env(safe-area-inset-left), 1rem); + padding-right: max(env(safe-area-inset-right), 1rem); +} + +@media screen(md) { + .px-main { + padding-left: max(env(safe-area-inset-left), 2rem); + padding-right: max(env(safe-area-inset-right), 2rem); + } +} + +@media screen(lg) { + .px-main { + padding-left: max(env(safe-area-inset-left), 3rem); + padding-right: max(env(safe-area-inset-right), 3rem); + } +} + +/* flex-1 mx-auto lg:mx-0 px-4 md:px-8 lg:px-12 w-full max-w-3x lg:max-w-3x py-8 sm:py-14 */ diff --git a/assets/images/examples/landscape-exif-orientation-5.jpg b/assets/images/examples/landscape-exif-orientation-5.jpg new file mode 100644 index 000000000..ad64835eb Binary files /dev/null and b/assets/images/examples/landscape-exif-orientation-5.jpg differ diff --git a/assets/images/examples/mask.png b/assets/images/examples/mask.png new file mode 100644 index 000000000..c3005a669 Binary files /dev/null and b/assets/images/examples/mask.png differ diff --git a/assets/images/examples/zion-national-park.jpg b/assets/images/examples/zion-national-park.jpg new file mode 100644 index 000000000..7980abccb Binary files /dev/null and b/assets/images/examples/zion-national-park.jpg differ diff --git a/assets/images/hugo-github-screenshot.png b/assets/images/hugo-github-screenshot.png new file mode 100644 index 000000000..275b6969d Binary files /dev/null and b/assets/images/hugo-github-screenshot.png differ diff --git a/assets/images/logos/logo-128x128.png b/assets/images/logos/logo-128x128.png new file mode 100644 index 000000000..ec1a2d6e1 Binary files /dev/null and b/assets/images/logos/logo-128x128.png differ diff --git a/assets/images/logos/logo-256x256.png b/assets/images/logos/logo-256x256.png new file mode 100644 index 000000000..d9fdb888a Binary files /dev/null and b/assets/images/logos/logo-256x256.png differ diff --git a/assets/images/logos/logo-512x512.png b/assets/images/logos/logo-512x512.png new file mode 100644 index 000000000..76d463600 Binary files /dev/null and b/assets/images/logos/logo-512x512.png differ diff --git a/assets/images/logos/logo-64x64.png b/assets/images/logos/logo-64x64.png new file mode 100644 index 000000000..9857bcea1 Binary files /dev/null and b/assets/images/logos/logo-64x64.png differ diff --git a/assets/images/logos/logo-96x96.png b/assets/images/logos/logo-96x96.png new file mode 100644 index 000000000..48d0cb98e Binary files /dev/null and b/assets/images/logos/logo-96x96.png differ diff --git a/assets/images/sponsors/Route4MeLogoBlueOnWhite.svg b/assets/images/sponsors/Route4MeLogoBlueOnWhite.svg new file mode 100644 index 000000000..d4334e8d8 --- /dev/null +++ b/assets/images/sponsors/Route4MeLogoBlueOnWhite.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/assets/images/sponsors/bep-consulting.svg b/assets/images/sponsors/bep-consulting.svg new file mode 100644 index 000000000..598a1eb71 --- /dev/null +++ b/assets/images/sponsors/bep-consulting.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/sponsors/butter-dark.svg b/assets/images/sponsors/butter-dark.svg new file mode 100644 index 000000000..657b75c50 --- /dev/null +++ b/assets/images/sponsors/butter-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/sponsors/butter-light.svg b/assets/images/sponsors/butter-light.svg new file mode 100644 index 000000000..a0697df08 --- /dev/null +++ b/assets/images/sponsors/butter-light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/sponsors/cloudcannon-blue.svg b/assets/images/sponsors/cloudcannon-blue.svg new file mode 100644 index 000000000..79b13f431 --- /dev/null +++ b/assets/images/sponsors/cloudcannon-blue.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/sponsors/cloudcannon-white.svg b/assets/images/sponsors/cloudcannon-white.svg new file mode 100644 index 000000000..83e319a6d --- /dev/null +++ b/assets/images/sponsors/cloudcannon-white.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/sponsors/esolia-logo.svg b/assets/images/sponsors/esolia-logo.svg new file mode 100644 index 000000000..3f5344c61 --- /dev/null +++ b/assets/images/sponsors/esolia-logo.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/sponsors/goland.svg b/assets/images/sponsors/goland.svg new file mode 100644 index 000000000..c32f25d7f --- /dev/null +++ b/assets/images/sponsors/goland.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/sponsors/graitykit-dark.svg b/assets/images/sponsors/graitykit-dark.svg new file mode 100644 index 000000000..fd7d12f5c --- /dev/null +++ b/assets/images/sponsors/graitykit-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/sponsors/linode-logo.svg b/assets/images/sponsors/linode-logo.svg new file mode 100644 index 000000000..873678398 --- /dev/null +++ b/assets/images/sponsors/linode-logo.svg @@ -0,0 +1 @@ + diff --git a/assets/images/sponsors/linode-logo_standard_light_medium.png b/assets/images/sponsors/linode-logo_standard_light_medium.png new file mode 100644 index 000000000..269e6af84 Binary files /dev/null and b/assets/images/sponsors/linode-logo_standard_light_medium.png differ diff --git a/assets/images/sponsors/your-company-dark.svg b/assets/images/sponsors/your-company-dark.svg new file mode 100644 index 000000000..58fd601f5 --- /dev/null +++ b/assets/images/sponsors/your-company-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/sponsors/your-company.svg b/assets/images/sponsors/your-company.svg new file mode 100644 index 000000000..3b85ece5c --- /dev/null +++ b/assets/images/sponsors/your-company.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/js/alpinejs/data/explorer.js b/assets/js/alpinejs/data/explorer.js new file mode 100644 index 000000000..b1496d35d --- /dev/null +++ b/assets/js/alpinejs/data/explorer.js @@ -0,0 +1,123 @@ +var debug = 0 ? console.log.bind(console, '[explorer]') : function () {}; + +// This is cureently not used, but kept in case I change my mind. +export const explorer = (Alpine) => ({ + uiState: { + containerScrollTop: -1, + lastActiveRef: '', + }, + treeState: { + // The href of the current page. + currentNode: '', + // The state of each node in the tree. + nodes: {}, + + // We currenty only list the sections, not regular pages, in the side bar. + // This strikes me as the right balance. The pages gets listed on the section pages. + // This array is sorted by length, so we can find the longest prefix of the current page + // without having to iterate over all the keys. + nodeRefsByLength: [], + }, + async init() { + let keys = Reflect.ownKeys(this.$refs); + for (let key of keys) { + let n = { + open: false, + active: false, + }; + this.treeState.nodes[key] = n; + this.treeState.nodeRefsByLength.push(key); + } + + this.treeState.nodeRefsByLength.sort((a, b) => b.length - a.length); + + this.setCurrentActive(); + }, + + longestPrefix(ref) { + let longestPrefix = ''; + for (let key of this.treeState.nodeRefsByLength) { + if (ref.startsWith(key)) { + longestPrefix = key; + break; + } + } + return longestPrefix; + }, + + setCurrentActive() { + let ref = this.longestPrefix(window.location.pathname); + let activeChanged = this.uiState.lastActiveRef !== ref; + debug('setCurrentActive', this.uiState.lastActiveRef, window.location.pathname, '=>', ref, activeChanged); + this.uiState.lastActiveRef = ref; + if (this.uiState.containerScrollTop === -1 && activeChanged) { + // Navigation outside of the explorer menu. + let el = document.querySelector(`[x-ref="${ref}"]`); + if (el) { + this.$nextTick(() => { + debug('scrolling to', ref); + el.scrollIntoView({ behavior: 'smooth', block: 'center' }); + }); + } + } + this.treeState.currentNode = ref; + for (let key in this.treeState.nodes) { + let n = this.treeState.nodes[key]; + n.active = false; + n.open = ref == key || ref.startsWith(key); + if (n.open) { + debug('open', key); + } + } + + let n = this.treeState.nodes[this.longestPrefix(ref)]; + if (n) { + n.active = true; + } + }, + + getScrollingContainer() { + return document.getElementById('leftsidebar'); + }, + + onLoad() { + debug('onLoad', this.uiState.containerScrollTop); + if (this.uiState.containerScrollTop >= 0) { + debug('onLoad: scrolling to', this.uiState.containerScrollTop); + this.getScrollingContainer().scrollTo(0, this.uiState.containerScrollTop); + } + this.uiState.containerScrollTop = -1; + }, + + onBeforeRender() { + debug('onBeforeRender', this.uiState.containerScrollTop); + this.setCurrentActive(); + }, + + toggleNode(ref) { + this.uiState.containerScrollTop = this.getScrollingContainer().scrollTop; + this.uiState.lastActiveRef = ''; + debug('toggleNode', ref, this.uiState.containerScrollTop); + + let node = this.treeState.nodes[ref]; + if (!node) { + debug('node not found', ref); + return; + } + let wasOpen = node.open; + }, + + isCurrent(ref) { + let n = this.treeState.nodes[ref]; + return n && n.active; + }, + + isOpen(ref) { + let node = this.treeState.nodes[ref]; + if (!node) return false; + if (node.open) { + debug('isOpen', ref); + } + return node.open; + }, +}); diff --git a/assets/js/alpinejs/data/index.js b/assets/js/alpinejs/data/index.js new file mode 100644 index 000000000..7bf0532e3 --- /dev/null +++ b/assets/js/alpinejs/data/index.js @@ -0,0 +1,3 @@ +export * from './navbar'; +export * from './search'; +export * from './toc'; diff --git a/assets/js/alpinejs/data/navbar.js b/assets/js/alpinejs/data/navbar.js new file mode 100644 index 000000000..ec1ce73fa --- /dev/null +++ b/assets/js/alpinejs/data/navbar.js @@ -0,0 +1,12 @@ +export const navbar = (Alpine) => ({ + atTop: true, + + init: function () { + Alpine.bind(this.$root, this.root); + }, + root: { + ['@scroll.window.debounce.10ms'](event) { + this.atTop = window.scrollY < 40 ? true : false; + }, + }, +}); diff --git a/assets/js/alpinejs/data/search.js b/assets/js/alpinejs/data/search.js new file mode 100644 index 000000000..c1572d020 --- /dev/null +++ b/assets/js/alpinejs/data/search.js @@ -0,0 +1,85 @@ +const designMode = false; + +const groupByLvl0 = (array) => { + if (!array) return []; + return array.reduce((result, currentValue) => { + (result[currentValue.hierarchy.lvl0] = result[currentValue.hierarchy.lvl0] || []).push(currentValue); + return result; + }, {}); +}; + +export const search = (Alpine, cfg) => ({ + query: designMode ? 'shortcodes' : '', + open: designMode, + result: {}, + + init() { + Alpine.bind(this.$root, this.root); + + this.checkOpen(); + return this.$nextTick(() => { + this.$watch('query', () => { + this.search(); + }); + }); + }, + toggleOpen: function () { + this.open = !this.open; + this.checkOpen(); + }, + checkOpen: function () { + if (!this.open) { + return; + } + this.search(); + this.$nextTick(() => { + this.$refs.input.focus(); + }); + }, + + search: function () { + if (!this.query) { + this.result = {}; + return; + } + var queries = { + requests: [ + { + indexName: cfg.index, + params: `query=${encodeURIComponent(this.query)}`, + attributesToHighlight: ['hierarchy', 'content'], + attributesToRetrieve: ['hierarchy', 'url', 'content'], + }, + ], + }; + + const host = `https://${cfg.app_id}-dsn.algolia.net`; + const url = `${host}/1/indexes/*/queries`; + + fetch(url, { + method: 'POST', + headers: { + 'X-Algolia-Application-Id': cfg.app_id, + 'X-Algolia-API-Key': cfg.api_key, + }, + body: JSON.stringify(queries), + }) + .then((response) => response.json()) + .then((data) => { + this.result = groupByLvl0(data.results[0].hits); + }); + }, + root: { + ['@click']() { + if (!this.open) { + this.toggleOpen(); + } + }, + ['@search-toggle.window']() { + this.toggleOpen(); + }, + ['@keydown.meta.k.window']() { + this.toggleOpen(); + }, + }, +}); diff --git a/assets/js/alpinejs/data/toc.js b/assets/js/alpinejs/data/toc.js new file mode 100644 index 000000000..e0818fa98 --- /dev/null +++ b/assets/js/alpinejs/data/toc.js @@ -0,0 +1,65 @@ +var debug = 0 ? console.log.bind(console, '[toc]') : function () {}; + +export const toc = (Alpine) => ({ + contentScrollSpy: null, + activeHeading: '', + justClicked: false, + + setActive(id) { + debug('setActive', id); + this.activeHeading = id; + // Prevent the intersection observer from changing the active heading right away. + this.justClicked = true; + setTimeout(() => { + this.justClicked = false; + }, 200); + }, + + init() { + return this.$nextTick(() => { + let contentEl = document.getElementById('content'); + if (contentEl) { + const handleIntersect = (entries) => { + if (this.justClicked) { + return; + } + for (let entry of entries) { + if (entry.isIntersecting) { + let id = entry.target.id; + this.activeHeading = id; + let liEl = this.$refs[id]; + if (liEl) { + // If liEl is not in the viewport, scroll it into view. + let bounding = liEl.getBoundingClientRect(); + if (bounding.top < 0 || bounding.bottom > window.innerHeight) { + this.$root.scrollTop = liEl.offsetTop - 100; + } + } + debug('intersecting', id); + break; + } + } + }; + + let opts = { + rootMargin: '0px 0px -75%', + threshold: 0.75, + }; + + this.contentScrollSpy = new IntersectionObserver(handleIntersect, opts); + // Observe all headings. + let headings = contentEl.querySelectorAll('h2, h3, h4, h5, h6'); + for (let heading of headings) { + this.contentScrollSpy.observe(heading); + } + } + }); + }, + + destroy() { + if (this.contentScrollSpy) { + debug('disconnecting'); + this.contentScrollSpy.disconnect(); + } + }, +}); diff --git a/assets/js/alpinejs/magics/helpers.js b/assets/js/alpinejs/magics/helpers.js new file mode 100644 index 000000000..f7046259d --- /dev/null +++ b/assets/js/alpinejs/magics/helpers.js @@ -0,0 +1,29 @@ +'use strict'; + +export function registerMagics(Alpine) { + Alpine.magic('copy', (currentEl) => { + return function (el) { + if (!el) { + el = currentEl; + } + let lntds = el.querySelectorAll('.lntable .lntd'); + if (lntds && lntds.length === 2) { + el = lntds[1]; + } + + // Trim whitespace. + let text = el.textContent.trim(); + + navigator.clipboard.writeText(text); + }; + }); + + Alpine.magic('isScrollX', (currentEl) => { + return function (el) { + if (!el) { + el = currentEl; + } + return el.clientWidth < el.scrollWidth; + }; + }); +} diff --git a/assets/js/alpinejs/magics/index.js b/assets/js/alpinejs/magics/index.js new file mode 100644 index 000000000..c5f595cf9 --- /dev/null +++ b/assets/js/alpinejs/magics/index.js @@ -0,0 +1 @@ +export * from './helpers'; diff --git a/assets/js/alpinejs/stores/index.js b/assets/js/alpinejs/stores/index.js new file mode 100644 index 000000000..17e2a347b --- /dev/null +++ b/assets/js/alpinejs/stores/index.js @@ -0,0 +1 @@ +export * from './nav.js'; diff --git a/assets/js/alpinejs/stores/nav.js b/assets/js/alpinejs/stores/nav.js new file mode 100644 index 000000000..7e67d85ce --- /dev/null +++ b/assets/js/alpinejs/stores/nav.js @@ -0,0 +1,90 @@ +var debug = 1 ? console.log.bind(console, '[navStore]') : function () {}; + +var ColorScheme = { + System: 1, + Light: 2, + Dark: 3, +}; + +const localStorageUserSettingsKey = 'hugoDocsUserSettings'; + +export const navStore = (Alpine) => ({ + init() { + // There is no $watch available in Alpine stores, + // but this has the same effect. + this.userSettings.onColorSchemeChanged = Alpine.effect(() => { + if (this.userSettings.settings.colorScheme) { + this.userSettings.isDark = isDark(this.userSettings.settings.colorScheme); + toggleDarkMode(this.userSettings.isDark); + } + }); + + // Also react to changes in system settings. + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { + this.userSettings.setColorScheme(ColorScheme.System); + }); + }, + + destroy() {}, + + userSettings: { + // settings gets persisted between page navigations. + settings: Alpine.$persist({ + // light, dark or system mode. + // If not set, we use the OS setting. + colorScheme: ColorScheme.System, + // Used to show the most relevant tab in config listings etc. + configFileType: 'toml', + }).as(localStorageUserSettingsKey), + + isDark: false, + + setColorScheme(colorScheme) { + this.settings.colorScheme = colorScheme; + this.isDark = isDark(colorScheme); + }, + + toggleColorScheme() { + let next = this.settings.colorScheme + 1; + if (next > ColorScheme.Dark) { + next = ColorScheme.System; + } + this.setColorScheme(next); + }, + colorScheme() { + return this.settings.colorScheme ? this.settings.colorScheme : ColorScheme.System; + }, + }, +}); + +function isMediaDark() { + return window.matchMedia('(prefers-color-scheme: dark)').matches; +} + +function isDark(colorScheme) { + if (!colorScheme || colorScheme == ColorScheme.System) { + return isMediaDark(); + } + + return colorScheme == ColorScheme.Dark; +} + +export function initColorScheme() { + // The AlpineJS store has not have been initialized yet, so access the + // localStorage directly. + let settingsJSON = localStorage[localStorageUserSettingsKey]; + if (settingsJSON) { + let settings = JSON.parse(settingsJSON); + toggleDarkMode(isDark(settings.colorScheme)); + return; + } + toggleDarkMode(isDark(null)); +} + +const toggleDarkMode = function (dark) { + if (dark) { + document.body.classList.add('dark'); + } else { + document.body.classList.remove('dark'); + } +}; diff --git a/assets/js/body-start.js b/assets/js/body-start.js new file mode 100644 index 000000000..f9b596671 --- /dev/null +++ b/assets/js/body-start.js @@ -0,0 +1,6 @@ +import { initColorScheme } from './alpinejs/stores/index'; + +(function () { + // This allows us to initialize the color scheme before AlpineJS etc. is loaded. + initColorScheme(); +})(); diff --git a/assets/js/head-early.js b/assets/js/head-early.js new file mode 100644 index 000000000..ca86b5694 --- /dev/null +++ b/assets/js/head-early.js @@ -0,0 +1,11 @@ +import { scrollToActive } from 'js/helpers/index'; + +(function () { + // Now we know that the browser has JS enabled. + document.documentElement.classList.remove('no-js'); + + // Wait for the DOM to be ready. + document.addEventListener('DOMContentLoaded', function () { + scrollToActive('DOMContentLoaded'); + }); +})(); diff --git a/assets/js/helpers/bridgeTurboAndAlpine.js b/assets/js/helpers/bridgeTurboAndAlpine.js new file mode 100644 index 000000000..0494d02f2 --- /dev/null +++ b/assets/js/helpers/bridgeTurboAndAlpine.js @@ -0,0 +1,67 @@ +export function bridgeTurboAndAlpine(Alpine) { + document.addEventListener('turbo:before-render', (event) => { + event.detail.newBody.querySelectorAll('[data-alpine-generated]').forEach((el) => { + if (el.hasAttribute('data-alpine-generated')) { + el.removeAttribute('data-alpine-generated'); + el.remove(); + } + }); + }); + + document.addEventListener('turbo:render', () => { + if (document.documentElement.hasAttribute('data-turbo-preview')) { + return; + } + + document.querySelectorAll('[data-alpine-ignored]').forEach((el) => { + el.removeAttribute('x-ignore'); + el.removeAttribute('data-alpine-ignored'); + }); + + document.body.querySelectorAll('[x-data]').forEach((el) => { + if (el.hasAttribute('data-turbo-permanent')) { + return; + } + Alpine.initTree(el); + }); + + Alpine.startObservingMutations(); + }); + + // Cleanup Alpine state on navigation. + document.addEventListener('turbo:before-cache', () => { + // This will be restarted in turbo:render. + Alpine.stopObservingMutations(); + + document.body.querySelectorAll('[data-turbo-permanent]').forEach((el) => { + if (!el.hasAttribute('x-ignore')) { + el.setAttribute('x-ignore', true); + el.setAttribute('data-alpine-ignored', true); + } + }); + + document.body.querySelectorAll('[x-for],[x-if],[x-teleport]').forEach((el) => { + if (el.hasAttribute('x-for') && el._x_lookup) { + Object.values(el._x_lookup).forEach((el) => el.setAttribute('data-alpine-generated', true)); + } + + if (el.hasAttribute('x-if') && el._x_currentIfEl) { + el._x_currentIfEl.setAttribute('data-alpine-generated', true); + } + + if (el.hasAttribute('x-teleport') && el._x_teleport) { + el._x_teleport.setAttribute('data-alpine-generated', true); + } + }); + + document.body.querySelectorAll('[x-data]').forEach((el) => { + if (!el.hasAttribute('data-turbo-permanent')) { + Alpine.destroyTree(el); + // Turbo leaks DOM elements via their data-turbo-permanent handling. + // That needs to be fixed upstream, but until then. + let clone = el.cloneNode(true); + el.replaceWith(clone); + } + }); + }); +} diff --git a/assets/js/helpers/helpers.js b/assets/js/helpers/helpers.js new file mode 100644 index 000000000..818eac40c --- /dev/null +++ b/assets/js/helpers/helpers.js @@ -0,0 +1,17 @@ +export const scrollToActive = (when) => { + let els = document.querySelectorAll('.scroll-active'); + if (!els.length) { + return; + } + els.forEach((el) => { + // Find scrolling container. + let container = el.closest('[data-turbo-preserve-scroll-container]'); + if (container) { + // Avoid scrolling if el is already in view. + if (el.offsetTop >= container.scrollTop && el.offsetTop <= container.scrollTop + container.clientHeight) { + return; + } + container.scrollTop = el.offsetTop - container.offsetTop; + } + }); +}; diff --git a/assets/js/helpers/index.js b/assets/js/helpers/index.js new file mode 100644 index 000000000..b02d81cf0 --- /dev/null +++ b/assets/js/helpers/index.js @@ -0,0 +1,2 @@ +export * from './bridgeTurboAndAlpine'; +export * from './helpers'; diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 000000000..14440044b --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,89 @@ +import Alpine from 'alpinejs'; +import { registerMagics } from './alpinejs/magics/index'; +import { navbar, search, toc } from './alpinejs/data/index'; +import { navStore, initColorScheme } from './alpinejs/stores/index'; +import { bridgeTurboAndAlpine } from './helpers/index'; +import persist from '@alpinejs/persist'; +import focus from '@alpinejs/focus'; + +var debug = 0 ? console.log.bind(console, '[index]') : function () {}; + +// Turbolinks init. +(function () { + document.addEventListener('turbo:render', function (e) { + // This is also called right after the body start. This is added to prevent flicker on navigation. + initColorScheme(); + }); +})(); + +// Set up and start Alpine. +(function () { + // Register AlpineJS plugins. + { + Alpine.plugin(focus); + Alpine.plugin(persist); + } + // Register AlpineJS magics and directives. + { + // Handles copy to clipboard etc. + registerMagics(Alpine); + } + + // Register AlpineJS controllers. + { + // Register AlpineJS data controllers. + let searchConfig = { + index: 'hugodocs', + app_id: 'D1BPLZHGYQ', + api_key: '6df94e1e5d55d258c56f60d974d10314', + }; + + Alpine.data('navbar', () => navbar(Alpine)); + Alpine.data('search', () => search(Alpine, searchConfig)); + Alpine.data('toc', () => toc(Alpine)); + } + + // Register AlpineJS stores. + { + Alpine.store('nav', navStore(Alpine)); + } + + // Start AlpineJS. + Alpine.start(); + + // Start the Turbo-Alpine bridge. + bridgeTurboAndAlpine(Alpine); + + { + let containerScrollTops = {}; + + // To preserve scroll position in scrolling elements on navigation add data-turbo-preserve-scroll-container="somename" to the scrolling container. + addEventListener('turbo:click', () => { + document.querySelectorAll('[data-turbo-preserve-scroll-container]').forEach((el2) => { + containerScrollTops[el2.dataset.turboPreserveScrollContainer] = el2.scrollTop; + }); + }); + + addEventListener('turbo:render', () => { + document.querySelectorAll('[data-turbo-preserve-scroll-container]').forEach((ele) => { + const containerScrollTop = containerScrollTops[ele.dataset.turboPreserveScrollContainer]; + if (containerScrollTop) { + ele.scrollTop = containerScrollTop; + } else { + let els = ele.querySelectorAll('.scroll-active'); + if (els.length) { + els.forEach((el) => { + // Avoid scrolling if el is already in view. + if (el.offsetTop >= ele.scrollTop && el.offsetTop <= ele.scrollTop + ele.clientHeight) { + return; + } + ele.scrollTop = el.offsetTop - ele.offsetTop; + }); + } + } + }); + + containerScrollTops = {}; + }); + } +})(); diff --git a/assets/js/turbo.js b/assets/js/turbo.js new file mode 100644 index 000000000..c007896f6 --- /dev/null +++ b/assets/js/turbo.js @@ -0,0 +1 @@ +import * as Turbo from '@hotwired/turbo'; diff --git a/assets/jsconfig.json b/assets/jsconfig.json new file mode 100644 index 000000000..377218ccb --- /dev/null +++ b/assets/jsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "*": [ + "*" + ] + } + } +} \ No newline at end of file diff --git a/assets/opengraph/gohugoio-card-base-1.png b/assets/opengraph/gohugoio-card-base-1.png new file mode 100644 index 000000000..65555845b Binary files /dev/null and b/assets/opengraph/gohugoio-card-base-1.png differ diff --git a/assets/opengraph/mulish-black.ttf b/assets/opengraph/mulish-black.ttf new file mode 100644 index 000000000..db680a088 Binary files /dev/null and b/assets/opengraph/mulish-black.ttf differ diff --git a/content/LICENSE.md b/content/LICENSE.md new file mode 100644 index 000000000..b09cd7856 --- /dev/null +++ b/content/LICENSE.md @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/go.mod b/go.mod index 56a48deac..4b9e0a369 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,3 @@ module github.com/gohugoio/hugoDocs go 1.22.0 - -require github.com/gohugoio/gohugoioTheme v0.0.0-20250116152525-2d382cae7743 // indirect diff --git a/hugo.toml b/hugo.toml index 2a271b6e3..1e7aaa4b7 100644 --- a/hugo.toml +++ b/hugo.toml @@ -1,9 +1,7 @@ -# This his the main configuration file. There are also environment specific configuration stored in the /config directory. - baseURL = "https://gohugo.io/" defaultContentLanguage = "en" enableEmoji = true -ignoreErrors = ["error-remote-getjson", "error-missing-instagram-accesstoken"] +ignoreLogs = ["error-missing-instagram-accesstoken"] languageCode = "en-us" pluralizeListTitles = false timeZone = "Europe/Oslo" @@ -13,90 +11,161 @@ title = "Hugo" disableAliases = true [pagination] -pagerSize = 100 + pagerSize = 100 [services.googleAnalytics] -ID = 'G-MBZGKNMDWC' - -[minify] - [minify.tdewolff] - [minify.tdewolff.html] - keepWhitespace = true - -[module] - [module.hugoVersion] - min = "0.56.0" - [[module.imports]] - path = "github.com/gohugoio/gohugoioTheme" + ID = 'G-MBZGKNMDWC' [outputs] - home = ["HTML", "RSS", "REDIR", "HEADERS"] - section = ["HTML"] + home = ["html", "rss", "redir", "headers"] + section = ["html"] + page = ["html"] + taxonomy = ["html"] + term = ["html"] -[mediaTypes] - [mediaTypes."text/netlify"] - delimiter = "" +[params] + description = "The world’s fastest framework for building websites" + ghrepo = "https://github.com/gohugoio/hugoDocs/" + +[languages] + [languages.en] + languageName = "English" + weight = 1 + +[security] + enableInlineShortcodes = false + [security.funcs] + getenv = ['^HUGO_', '^REPOSITORY_URL$', '^BRANCH$'] + [security.http] + methods = ['(?i)GET|POST'] + urls = ['.*'] [outputFormats] - [outputFormats.REDIR] - mediatype = "text/netlify" - baseName = "_redirects" - isPlainText = true - notAlternative = true - [outputFormats.HEADERS] - mediatype = "text/netlify" - baseName = "_headers" - isPlainText = true - notAlternative = true + [outputFormats.redir] + mediatype = "text/netlify" + baseName = "_redirects" + isPlainText = true + [outputFormats.headers] + mediatype = "text/netlify" + baseName = "_headers" + isPlainText = true + notAlternative = true -[caches] - [caches.getjson] - dir = ":cacheDir/:project" - maxAge = -1 - [caches.getcsv] - dir = ":cacheDir/:project" - maxAge = -1 - [caches.images] - dir = ":cacheDir/images" - maxAge = "1440h" - [caches.assets] - dir = ":resourceDir/_gen" - maxAge = -1 - [caches.getresource] - dir = ":cacheDir/:project" - maxage = '1h' +[markup] + [markup.highlight] + style = 'solarized-dark' + lineNumbersInTable = true + noClasses = false + wrapperClass = 'highlight not-prose' -[related] - threshold = 80 - includeNewer = true - toLower = false - [[related.indices]] - name = "keywords" - weight = 60 - [[related.indices]] - # Can be used as a front matter slice to link to other page fragments (headings) using their ID. - # This isn't particular useful in the current docs, but we're planning on getting a auto generated - # reference section with a better ID setup. - # For now, we just use it to give pages with same headings some similarity score. - name = "fragmentrefs" - type = "fragments" - applyFilter = false - weight = 60 - cardinalityThreshold = 50 + [markup.goldmark.renderer] + hardWraps = false + unsafe = false + xhtml = false -[imaging] - # See https://github.com/disintegration/imaging - # CatmullRom is a sharp bicubic filter which should fit the docs site well with its many screenshots. - # Note that you can also set this per image processing. - resampleFilter = "CatmullRom" - # Default JPEG quality setting. Default is 75. - quality = 75 - anchor = "smart" + [markup.goldmark.extensions] + definitionList = true + footnote = true + linkify = true + strikethrough = true + table = true + taskList = true + typographer = true -[taxonomies] - category = "categories" + [markup.goldmark.extensions.passthrough] + enable = true -[[cascade]] -categories = ['commands'] -[cascade._target] -path = '/commands/**' + [markup.goldmark.extensions.passthrough.delimiters] + block = [['\[', '\]'], ['$$', '$$']] + inline = [['\(', '\)']] + + [markup.goldmark.parser] + autoHeadingID = true + autoHeadingIDType = "github" + + [markup.goldmark.parser.attribute] + block = true + title = true + +[mediaTypes] + [mediaTypes."text/netlify"] + delimiter = "" + +[module] + [module.hugoVersion] + min = "0.141.0" + [[module.mounts]] + source = "assets" + target = "assets" + [[module.mounts]] + lang = 'en' + source = 'content/en' + target = 'content' + [[module.mounts]] + source = "hugo_stats.json" + target = "assets/notwatching/hugo_stats.json" + disableWatch = true + +[build] + [build.buildStats] + disableIDs = true + enable = true + [[build.cachebusters]] + source = "assets/notwatching/hugo_stats\\.json" + target = "css" + [[build.cachebusters]] + source = "(postcss|tailwind)\\.config\\.js" + target = "css" + +[server] + [[server.headers]] + for = "/*" + + [server.headers.values] + X-Frame-Options = "DENY" + X-XSS-Protection = "1; mode=block" + X-Content-Type-Options = "nosniff" + Referrer-Policy = "no-referrer" + + [[server.headers]] + for = "/**.{css,js}" + +[minify] + [minify.tdewolff] + [minify.tdewolff.html] + keepSpecialComments = true + keepWhitespace = false + +######## GLOBAL ITEMS TO BE SHARED WITH THE HUGO SITES ######## +[menus] + [[menus.global]] + name = 'News' + weight = 1 + identifier = 'news' + pageRef = '/news/' + + [[menus.global]] + name = 'Docs' + weight = 5 + identifier = 'docs' + url = '/documentation/' + + [[menus.global]] + name = 'Themes' + weight = 10 + identifier = 'themes' + url = 'https://themes.gohugo.io/' + + [[menus.global]] + name = 'Community' + weight = 150 + identifier = 'community' + post = 'external' + url = 'https://discourse.gohugo.io/' + + [[menus.global]] + name = 'GitHub' + weight = 200 + identifier = 'github' + post = 'external' + url = 'https://github.com/gohugoio/hugo' diff --git a/hugo.work b/hugo.work index b2ae38c07..02c0ba91f 100644 --- a/hugo.work +++ b/hugo.work @@ -1,4 +1,4 @@ go 1.22.0 use . -use ../gohugoioTheme + diff --git a/layouts/404.html b/layouts/404.html new file mode 100644 index 000000000..bb92ea160 --- /dev/null +++ b/layouts/404.html @@ -0,0 +1,22 @@ +{{ define "main" }} +
+
+

+ Page not found + gopher +

+ + +
+
+{{ end }} diff --git a/layouts/_default/_markup/render-heading.html b/layouts/_default/_markup/render-heading.html new file mode 100644 index 000000000..c0e6c63ec --- /dev/null +++ b/layouts/_default/_markup/render-heading.html @@ -0,0 +1,10 @@ +{{ .Text | safeHTML }} + {{- if in (slice 2 3 4 6) .Level }}{{" " -}} + + + + + + +{{- end -}} + diff --git a/layouts/_default/_markup/render-link.html b/layouts/_default/_markup/render-link.html new file mode 100644 index 000000000..726610258 --- /dev/null +++ b/layouts/_default/_markup/render-link.html @@ -0,0 +1,317 @@ +{{- /* Last modified: 2025-01-19T14:44:56-08:00 */}} + +{{- /* +Copyright 2025 Veriphor LLC + +Licensed under the Apache License, Version 2.0 (the "License"); you may not +use this file except in compliance with the License. You may obtain a copy of +the License at + +https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations under +the License. +*/}} + +{{- /* +This render hook resolves internal destinations by looking for a matching: + + 1. Content page + 2. Page resource (a file in the current page bundle) + 3. Section resource (a file in the current section) + 4. Global resource (a file in the assets directory) + +It skips the section resource lookup if the current page is a leaf bundle. + +External destinations are not modified. + +You must place global resources in the assets directory. If you have placed +your resources in the static directory, and you are unable or unwilling to move +them, you must mount the static directory to the assets directory by including +both of these entries in your site configuration: + + [[module.mounts]] + source = 'assets' + target = 'assets' + + [[module.mounts]] + source = 'static' + target = 'assets' + +By default, if this render hook is unable to resolve a destination, including a +fragment if present, it passes the destination through without modification. To +emit a warning or error, set the error level in your site configuration: + + [params.render_hooks.link] + errorLevel = 'warning' # ignore (default), warning, or error (fails the build) + +When you set the error level to warning, and you are in a development +environment, you can visually highlight broken internal links: + + [params.render_hooks.link] + errorLevel = 'warning' # ignore (default), warning, or error (fails the build) + highlightBroken = true # true or false (default) + +This will add a "broken" class to anchor elements with invalid src attributes. +Add a rule to your CSS targeting the broken links: + + a.broken { + background: #ff0; + border: 2px solid #f00; + padding: 0.1em 0.2em; + } + +This render hook may be unable to resolve destinations created with the ref and +relref shortcodes. Unless you set the error level to ignore you should not use +either of these shortcodes in conjunction with this render hook. + +@context {string} Destination The link destination. +@context {page} Page A reference to the page containing the link. +@context {string} PlainText The link description as plain text. +@context {string} Text The link description. +@context {string} Title The link title. + +@returns {template.html} +*/}} + +{{- /* Initialize. */}} +{{- $renderHookName := "link" }} + +{{- /* Verify minimum required version. */}} +{{- $minHugoVersion := "0.141.0" }} +{{- if lt hugo.Version $minHugoVersion }} + {{- errorf "The %q render hook requires Hugo v%s or later." $renderHookName $minHugoVersion }} +{{- end }} + +{{- /* Error level when unable to resolve destination: ignore, warning, or error. */}} +{{- $errorLevel := or site.Params.render_hooks.link.errorLevel "ignore" | lower }} + +{{- /* If true, adds "broken" class to broken links. Applicable in development environment when errorLevel is warning. */}} +{{- $highlightBrokenLinks := or site.Params.render_hooks.link.highlightBroken false }} + +{{- /* Validate error level. */}} +{{- if not (in (slice "ignore" "warning" "error") $errorLevel) }} + {{- errorf "The %q render hook is misconfigured. The errorLevel %q is invalid. Please check your site configuration." $renderHookName $errorLevel }} +{{- end }} + +{{- /* Determine content path for warning and error messages. */}} +{{- $contentPath := .Page.String }} + +{{- /* Parse destination. */}} +{{- $u := urls.Parse .Destination }} + +{{- /* Set common message. */}} +{{- $msg := printf "The %q render hook was unable to resolve the destination %q in %s" $renderHookName $u.String $contentPath }} + +{{- /* Set attributes for anchor element. */}} +{{- $attrs := dict "href" $u.String }} +{{- if eq $u.String "g" }} + {{- /* Destination is a glossary term. */}} + {{- $ctx := dict + "contentPath" $contentPath + "errorLevel" $errorLevel + "renderHookName" $renderHookName + "text" .Text + }} + {{- $attrs = partial "inline/h-rh-l/get-glossary-link-attributes.html" $ctx }} +{{- else if $u.IsAbs }} + {{- /* Destination is a remote resource. */}} + {{- $attrs = merge $attrs (dict "rel" "external") }} +{{- else }} + {{- with $u.Path }} + {{- with $p := or ($.PageInner.GetPage .) ($.PageInner.GetPage (strings.TrimRight "/" .)) }} + {{- /* Destination is a page. */}} + {{- $href := .RelPermalink }} + {{- with $u.RawQuery }} + {{- $href = printf "%s?%s" $href . }} + {{- end }} + {{- with $u.Fragment }} + {{- $ctx := dict + "contentPath" $contentPath + "errorLevel" $errorLevel + "page" $p + "parsedURL" $u + "renderHookName" $renderHookName + }} + {{- partial "inline/h-rh-l/validate-fragment.html" $ctx }} + {{- $href = printf "%s#%s" $href . }} + {{- end }} + {{- $attrs = dict "href" $href }} + {{- else with $.PageInner.Resources.Get $u.Path }} + {{- /* Destination is a page resource; drop query and fragment. */}} + {{- $attrs = dict "href" .RelPermalink }} + {{- else with (and (ne $.Page.BundleType "leaf") ($.Page.CurrentSection.Resources.Get $u.Path)) }} + {{- /* Destination is a section resource, and current page is not a leaf bundle. */}} + {{- $attrs = dict "href" .RelPermalink }} + {{- else with resources.Get $u.Path }} + {{- /* Destination is a global resource; drop query and fragment. */}} + {{- $attrs = dict "href" .RelPermalink }} + {{- else }} + {{- if eq $errorLevel "warning" }} + {{- warnf $msg }} + {{- if and $highlightBrokenLinks hugo.IsDevelopment }} + {{- $attrs = merge $attrs (dict "class" "broken") }} + {{- end }} + {{- else if eq $errorLevel "error" }} + {{- errorf $msg }} + {{- end }} + {{- end }} + {{- else }} + {{- with $u.Fragment }} + {{- /* Destination is on the same page; prepend relative permalink. */}} + {{- $ctx := dict + "contentPath" $contentPath + "errorLevel" $errorLevel + "page" $.Page + "parsedURL" $u + "renderHookName" $renderHookName + }} + {{- partial "inline/h-rh-l/validate-fragment.html" $ctx }} + {{- $attrs = dict "href" (printf "%s#%s" $.Page.RelPermalink .) }} + {{- else }} + {{- if eq $errorLevel "warning" }} + {{- warnf $msg }} + {{- if and $highlightBrokenLinks hugo.IsDevelopment }} + {{- $attrs = merge $attrs (dict "class" "broken") }} + {{- end }} + {{- else if eq $errorLevel "error" }} + {{- errorf $msg }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} + +{{- /* Render anchor element. */ -}} +{{ .Text }} + +{{- define "partials/inline/h-rh-l/validate-fragment.html" }} + {{- /* + Validates the fragment portion of a link destination. + + @context {string} contentPath The page containing the link. + @context {string} errorLevel The error level when unable to resolve destination; ignore (default), warning, or error. + @context {page} page The page corresponding to the link destination + @context {struct} parsedURL The link destination parsed by urls.Parse. + @context {string} renderHookName The name of the render hook. + */}} + + {{- /* Initialize. */}} + {{- $contentPath := .contentPath }} + {{- $errorLevel := .errorLevel }} + {{- $p := .page }} + {{- $u := .parsedURL }} + {{- $renderHookName := .renderHookName }} + + {{- /* Validate. */}} + {{- with $u.Fragment }} + {{- if $p.Fragments.Identifiers.Contains . }} + {{- if gt ($p.Fragments.Identifiers.Count .) 1 }} + {{- $msg := printf "The %q render hook detected duplicate heading IDs %q in %s" $renderHookName . $contentPath }} + {{- if eq $errorLevel "warning" }} + {{- warnf $msg }} + {{- else if eq $errorLevel "error" }} + {{- errorf $msg }} + {{- end }} + {{- end }} + {{- else }} + {{- /* Determine target path for warning and error message. */}} + {{- $targetPath := "" }} + {{- with $p.File }} + {{- $targetPath = .Path }} + {{- else }} + {{- $targetPath = .Path }} + {{- end }} + {{- /* Set common message. */}} + {{- $msg := printf "The %q render hook was unable to find heading ID %q in %s. See %s" $renderHookName . $targetPath $contentPath }} + {{- if eq $targetPath $contentPath }} + {{- $msg = printf "The %q render hook was unable to find heading ID %q in %s" $renderHookName . $targetPath }} + {{- end }} + {{- /* Throw warning or error. */}} + {{- if eq $errorLevel "warning" }} + {{- warnf $msg }} + {{- else if eq $errorLevel "error" }} + {{- errorf $msg }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} + +{{- define "partials/inline/h-rh-l/get-glossary-link-attributes.html" }} + {{- /* + Returns the anchor element attributes for a link to the given glossary term. + + It first checks for the existence of a glossary page for the given term. If + no page is found, it then checks for a glossary page for the singular form of + the term. If neither page exists it throws a warning or error dependent on + the errorLevel setting + + The returned href attribute does not point to the glossary term page. + Instead, via its fragment, it points to an entry on the glossary page. + + @context {string} contentPath The page containing the link. + @context {string} errorLevel The error level when unable to resolve destination; ignore (default), warning, or error. + @context {string} renderHookName The name of the render hook. + @context {string} text The link text. + */}} + + {{- /* Get context.. */}} + {{- $contentPath := .contentPath }} + {{- $errorLevel := .errorLevel }} + {{- $renderHookName := .renderHookName }} + {{- $text := .text | transform.Plainify | strings.ToLower }} + + {{- /* Initialize. */}} + {{- $glossaryPath := "/quick-reference/glossary" }} + {{- $termGiven := $text }} + {{- $termActual := "" }} + {{- $termSingular := inflect.Singularize $termGiven }} + + {{- /* Verify that the glossary page exists. */}} + {{- $glossaryPage := site.GetPage $glossaryPath }} + {{- if not $glossaryPage }} + {{- errorf "The %q render hook was unable to find %s: see %s" $renderHookName $glossaryPath $contentPath }} + {{- end }} + + {{- /* There's a better way to handle this, but it works for now. */}} + {{- $cheating := dict + "chaining" "chain" + "localize" "localization" + "localized" "localization" + "paginating" "paginate" + "walking" "walk" + }} + + {{- /* Verify that a glossary term page exists for the given term. */}} + {{- if site.GetPage (urls.JoinPath $glossaryPath ($termGiven | urlize)) }} + {{- $termActual = $termGiven }} + {{- else if site.GetPage (urls.JoinPath $glossaryPath ($termSingular | urlize)) }} + {{- $termActual = $termSingular }} + {{- else }} + {{- $termToTest := index $cheating $termGiven }} + {{- if site.GetPage (urls.JoinPath $glossaryPath ($termToTest | urlize)) }} + {{- $termActual = $termToTest }} + {{- end }} + {{- end }} + + {{- if not $termActual }} + {{- errorf "The %q render hook was unable to find a glossary page for either the singular or plural form of the term %q: see %s" $renderHookName $termGiven $contentPath }} + {{- end }} + + {{- /* Create the href attribute. */}} + {{- $href := ""}} + {{- if $termActual }} + {{- $href = fmt.Printf "%s#%s" $glossaryPage.RelPermalink (anchorize $termActual) }} + {{- end }} + + {{- return (dict "href" $href) }} +{{- end -}} diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html new file mode 100644 index 000000000..f8258b33b --- /dev/null +++ b/layouts/_default/baseof.html @@ -0,0 +1,67 @@ + + + + + + {{ .Title }} + + + + {{ partial "layouts/head/head-js.html" . }} + {{ with (templates.Defer (dict "key" "global")) }} + {{ $t := debug.Timer "tailwindcss" }} + {{ with resources.Get "css/styles.css" }} + {{ $opts := dict + "inlineImports" true + "minify" (not hugo.IsDevelopment) + }} + {{ with . | css.TailwindCSS $opts }} + {{ partial "helpers/linkcss.html" (dict "r" .) }} + {{ end }} + {{ end }} + {{ $t.Stop }} + {{ end }} + {{ partial "layouts/head/head.html" . }} + + + {{ partial "layouts/hooks/body-start.html" . }} + {{/* Layout. */}} + {{ block "header" . }} + {{ partial "layouts/header/header.html" . }} + {{ end }} + {{ block "hero" . }} + {{ end }} +
+
+ {{ block "main" . }}{{ end }} +
+ {{ block "rightsidebar" . }} + + {{ end }} +
+ {{/* Common icons. */}} + {{ partial "layouts/icons.html" . }} + {{/* Footer. */}} + {{ block "footer" . }} + {{ partial "layouts/footer.html" . }} + {{ end }} + {{ partial "layouts/hooks/body-end.html" . }} + + diff --git a/layouts/_default/list.html b/layouts/_default/list.html new file mode 100644 index 000000000..bccedb778 --- /dev/null +++ b/layouts/_default/list.html @@ -0,0 +1,62 @@ +{{ define "main" }} + {{ $pages := "" }} + {{ $showDate := false }} + {{ if .IsPage }} + {{/* We currently have a slightly odd content structure with no top level /docs sectin. */}} + {{ $pages = .CurrentSection.Pages }} + {{ else }} + {{ if eq .Section "news" }} + {{ $pages = partial "news/get-news-items.html" . }} + {{ $showDate = true }} + {{ else }} + {{ $pages = .Pages }} + {{ end }} + {{ end }} + + +
+ {{ partial "layouts/docsheader.html" . }} +
+ {{ range $pages }} + {{ if eq . $ }} + {{ continue }} + {{ end }} + + {{ if $showDate }} +

+ {{ .Date.Format "January 2, 2006" }} +

+ {{ end }} +

+ {{ .LinkTitle }} +

+ + {{ with .Params.action.signatures }} + {{/* Set in functions and methods pages. */}} + {{ with $signature := index . 0 }} + {{ if $.Params.action.returnType }} + {{ $signature = printf "%s ⟼ %s" $signature $.context.Params.action.returnType }} + {{ end }} +
+ {{- $signature -}} +
+ {{ end }} + {{ end }} +

+ {{ (or .Params.description .Summary) | plainify | safeHTML }} +

+
+ {{ end }} +
+
+{{ end }} + +{{ define "rightsidebar" }} + {{ printf "%c" '\u00A0' }} +{{ end }} diff --git a/layouts/_default/single.html b/layouts/_default/single.html new file mode 100644 index 000000000..ec21aa85c --- /dev/null +++ b/layouts/_default/single.html @@ -0,0 +1,64 @@ +{{ define "main" }} + {{ $ttop := debug.Timer "single" }} +
+ {{ partial "layouts/docsheader.html" . }} +
+ {{ with .Params.description }} +
+ {{ . | markdownify }} +
+ {{ end }} + + {{ $t := debug.Timer "single.categories" }} + {{ $tags := .GetTerms "categories" }} + {{ range $tags }} + {{ $text := .LinkTitle }} + {{ $class := "" }} + {{ range (slice true false ) }} + {{ $color := partial "helpers/funcs/color-from-string.html" (dict "text" $text "dark" . ) }} + + {{ $prefix := "" }} + {{ if . }} + {{ $prefix = "dark:" }} + {{ end }} + {{ $class = printf "%sbg-%s-%d %stext-%s-%d border %sborder-%s-%d" + $prefix $color.color $color.shade1 + $prefix $color.color $color.shade2 + $prefix $color.color $color.shade2 + }} + {{ end }} + + + + {{ .LinkTitle | upper }} + + {{ end }} + {{ $t.Stop }} + + {{ if .Params.action.signatures }} +
+ {{- partial "docs/functions-signatures.html" . -}} + {{- partial "docs/functions-return-type.html" . -}} + {{- partial "docs/functions-aliases.html" . -}} +
+ {{ end }} + {{ $t := debug.Timer "single.content" }} + {{ .Content }} + {{ $t.Stop }} + {{ $t := debug.Timer "single.page-edit" }} + {{ partial "layouts/page-edit.html" . }} + {{ $t.Stop }} +
+
+ {{ $ttop.Stop }} +{{ end }} + +{{ define "rightsidebar_content" }} + {{ if not .Params.hide_in_this_section }} + {{ partial "layouts/in-this-section.html" . }} + {{ end }} + {{ partial "layouts/related.html" . }} + {{ partial "layouts/toc.html" . }} +{{ end }} diff --git a/layouts/index.headers b/layouts/index.headers new file mode 100644 index 000000000..1216e42d4 --- /dev/null +++ b/layouts/index.headers @@ -0,0 +1,5 @@ +/* + X-Frame-Options: DENY + X-XSS-Protection: 1; mode=block + X-Content-Type-Options: nosniff + Referrer-Policy: origin-when-cross-origin diff --git a/layouts/index.html b/layouts/index.html new file mode 100644 index 000000000..d969c86db --- /dev/null +++ b/layouts/index.html @@ -0,0 +1,52 @@ +{{ define "main" }} +
+ {{ partial "layouts/home/opensource.html" . }} +
+ {{ partial "layouts/home/sponsors.html" (dict "ctx" . "gtag" "home" ) }} +
+ {{ partial "layouts/home/features.html" . }} +
+{{ end }} + +{{ define "hero" }} +
+
+
+ Hugo Logo +

+ The world’s fastest framework for building websites +

+
+ Hugo is one of the most popular open-source static site generators. + With its amazing speed and flexibility, Hugo makes building websites + fun again. +
+
+ {{ with site.GetPage "/getting-started" }} + {{ .LinkTitle }} + {{ end }} +
+ {{ partial "layouts/search/input-standalone.html" . }} +
+
+
+
+
+{{ end }} + +{{ define "rightsidebar" }} + {{ printf "%c" '\u00A0' }} +{{ end }} + +{{ define "leftsidebar" }} + {{ printf "%c" '\u00A0' }} +{{ end }} diff --git a/layouts/index.redir b/layouts/index.redir new file mode 100644 index 000000000..2dfd2bc0f --- /dev/null +++ b/layouts/index.redir @@ -0,0 +1,6 @@ +# Netlify redirects. See https://www.netlify.com/docs/redirects/ +{{ range $p := .Site.Pages -}} +{{ range .Aliases }} +{{ . | printf "%-35s" }} {{ $p.RelPermalink -}} +{{ end -}} +{{- end -}} diff --git a/layouts/index.rss.xml b/layouts/index.rss.xml new file mode 100644 index 000000000..460c4d30e --- /dev/null +++ b/layouts/index.rss.xml @@ -0,0 +1,68 @@ +{{- printf "" | safeHTML }} + + + Hugo News + Recent news about Hugo, a static site generator written in Go, optimized for speed and designed for flexibility. + {{ .Permalink }} + Hugo {{ hugo.Version }} + {{ or site.Language.LanguageCode site.Language.Lang }} + {{- with site.Copyright }} + {{ . }} + {{- end }} + {{- with .OutputFormats.Get "rss" }} + {{ printf "" .Permalink .MediaType | safeHTML }} + {{- end }} + + {{- $news_items := slice }} + + {{- /* Get releases from GitHub. */}} + {{- $u := "https://api.github.com/repos/gohugoio/hugo/releases" }} + {{- $releases := partial "helpers/funcs/get-remote-data.html" $u }} + {{- $releases = where $releases "draft" false }} + {{- $releases = where $releases "prerelease" false }} + {{- range $releases | first 20 }} + {{- $summary := printf + "Hugo %s was released on %s. See [release notes](%s) for details." + .tag_name + (.published_at | time.AsTime | time.Format "2 Jan 2006") + .html_url + }} + {{- $ctx := dict + "PublishDate" (.published_at | time.AsTime) + "Title" (printf "Release %s" .name) + "Permalink" .html_url + "Section" "news" + "Summary" ($summary | $.Page.RenderString) + }} + {{- $news_items = $news_items | append $ctx }} + {{- end }} + + {{- /* Get content pages from news section. */}} + {{- range where site.RegularPages "Section" "news" }} + {{- $ctx := dict + "PublishDate" .PublishDate + "Title" .Title + "RelPermalink" .RelPermalink + "Section" "news" + "Summary" .Summary + "Params" (dict "description" .Description) + }} + {{- $news_items = $news_items | append $ctx }} + {{- end }} + {{- /* Sort, limit, and render lastBuildDate. */}} + {{- $limit := cond (gt site.Config.Services.RSS.Limit 1) site.Config.Services.RSS.Limit 999 }} + {{- $news_items = sort $news_items "PublishDate" "desc" | first $limit }} + {{ (index $news_items 0).PublishDate.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }} + + {{- /* Render items. */}} + {{- range $news_items }} + + {{ .Title }} + {{ .Permalink }} + {{ .PublishDate.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }} + {{ .Permalink }} + {{ .Summary | transform.XMLEscape | safeHTML }} + + {{- end }} + + diff --git a/layouts/partials/docs/functions-aliases.html b/layouts/partials/docs/functions-aliases.html new file mode 100644 index 000000000..c1c4090e9 --- /dev/null +++ b/layouts/partials/docs/functions-aliases.html @@ -0,0 +1,12 @@ +{{- with .Params.action.aliases }} + {{- $label := "Alias" }} + {{- if gt (len .) 1 }} + {{- $label = "Aliases" }} + {{- end }} +

{{ $label }}

+ {{- range . }} +
+ {{- . -}} +
+ {{- end }} +{{- end -}} diff --git a/layouts/partials/docs/functions-return-type.html b/layouts/partials/docs/functions-return-type.html new file mode 100644 index 000000000..6b56a1806 --- /dev/null +++ b/layouts/partials/docs/functions-return-type.html @@ -0,0 +1,6 @@ +{{- with .Params.action.returnType }} +

Returns

+
+ {{- . -}} +
+{{- end -}} diff --git a/layouts/partials/docs/functions-signatures.html b/layouts/partials/docs/functions-signatures.html new file mode 100644 index 000000000..5cabdfba0 --- /dev/null +++ b/layouts/partials/docs/functions-signatures.html @@ -0,0 +1,12 @@ +{{- with .Params.action.signatures }} +

Syntax

+ {{- range . }} + {{- $signature := . }} + {{- if $.Params.function.returnType }} + {{- $signature = printf "%s ⟼ %s" . $.Params.function.returnType }} + {{- end }} +
+ {{- $signature -}} +
+ {{- end }} +{{- end -}} diff --git a/layouts/partials/helpers/funcs/color-from-string.html b/layouts/partials/helpers/funcs/color-from-string.html new file mode 100644 index 000000000..cfb54b0be --- /dev/null +++ b/layouts/partials/helpers/funcs/color-from-string.html @@ -0,0 +1,18 @@ +{{ $colors := slice "blue" "orange" "green" "pink" }} +{{ $shades := slice 200 300 500 }} +{{ if .dark }} + {{ $shades = slice 700 800 900 }} +{{ end }} +{{ $hash := (hash.FNV32a .text) }} +{{ $i := mod $hash (len $colors) }} +{{ $j := mod $hash (len $shades) }} +{{ $color := index $colors $i }} +{{ $shade1 := index $shades $j }} +{{ $shade2 := 0 }} +{{ if gt $shade1 400 }} + {{ $shade2 = math.Min (sub $shade1 400) 100 | int }} +{{ else }} + {{ $shade2 = math.Max (add $shade1 400) 800 | int }} +{{ end }} +{{ $res := dict "color" $color "shade1" $shade1 "shade2" $shade2 }} +{{ return $res }} diff --git a/layouts/partials/helpers/funcs/get-github-info.html b/layouts/partials/helpers/funcs/get-github-info.html new file mode 100644 index 000000000..7e2dc89fa --- /dev/null +++ b/layouts/partials/helpers/funcs/get-github-info.html @@ -0,0 +1,28 @@ +{{ $url := "https://api.github.com/repos/gohugoio/hugo" }} +{{ $cacheKey := print $url (now.Format "2006-01-02") }} +{{ $headers := dict }} +{{ with os.Getenv "HUGO_GH_TOKEN" }} + {{ $headers = dict "Authorization" (printf "Bearer %s" .) }} +{{ end }} +{{ $opts := dict "headers" $headers "key" $cacheKey }} +{{ $githubRepoInfo := dict }} +{{ with try (resources.GetRemote $url $opts) }} + {{ with .Err }} + {{ warnf "Failed to get GitHub repo info: %s" . }} + {{ else with (.Value | transform.Unmarshal) }} + {{ $githubRepoInfo = dict + "html_url" .html_url + "stargazers_url" .stargazers_url + "watchers_count" .watchers_count + "stargazers_count" .stargazers_count + "forks_count" .forks_count + "contributors_url" .contributors_url + "releases_url" .releases_url + "forks_count" .forks_count + }} + {{ else }} + {{ errorf "Unable to get remote resource %q" $url }} + {{ end }} +{{ end }} + +{{ return $githubRepoInfo }} diff --git a/layouts/partials/helpers/funcs/get-remote-data.html b/layouts/partials/helpers/funcs/get-remote-data.html new file mode 100644 index 000000000..78b2ba06e --- /dev/null +++ b/layouts/partials/helpers/funcs/get-remote-data.html @@ -0,0 +1,23 @@ +{{/* +Parses the serialized data from the given URL and returns a map or an array. + +Supports CSV, JSON, TOML, YAML, and XML. + +@param {string} . The URL from which to retrieve the serialized data. +@returns {any} + +@example {{ partial "get-remote-data.html" "https://example.org/foo.json" }} +*/}} + +{{ $url := . }} +{{ $data := dict }} +{{ with try (resources.GetRemote $url) }} + {{ with .Err }} + {{ errorf "%s" . }} + {{ else with .Value }} + {{ $data = .Content | transform.Unmarshal }} + {{ else }} + {{ errorf "Unable to get remote resource %q" $url }} + {{ end }} +{{ end }} +{{ return $data }} diff --git a/layouts/partials/helpers/gtag.html b/layouts/partials/helpers/gtag.html new file mode 100644 index 000000000..59bf36ba2 --- /dev/null +++ b/layouts/partials/helpers/gtag.html @@ -0,0 +1,27 @@ +{{ with site.Config.Services.GoogleAnalytics.ID }} + + +{{ end }} diff --git a/layouts/partials/helpers/linkcss.html b/layouts/partials/helpers/linkcss.html new file mode 100644 index 000000000..c12406757 --- /dev/null +++ b/layouts/partials/helpers/linkcss.html @@ -0,0 +1,28 @@ +{{ $r := .r }} +{{ $attr := .attributes | default dict }} + +{{ if hugo.IsDevelopment }} + +{{ else }} + {{ with $r | minify | fingerprint }} + + {{ end }} +{{ end }} + +{{ define "render-attributes" }} + {{- range $k, $v := . -}} + {{- if $v -}} + {{- printf ` %s=%q` $k $v | safeHTMLAttr -}} + {{- else -}} + {{- printf ` %s` $k | safeHTMLAttr -}} + {{- end -}} + {{- end -}} +{{ end }} diff --git a/layouts/partials/helpers/linkjs.html b/layouts/partials/helpers/linkjs.html new file mode 100644 index 000000000..00129ad38 --- /dev/null +++ b/layouts/partials/helpers/linkjs.html @@ -0,0 +1,15 @@ +{{ $r := .r }} +{{ $attr := .attributes | default dict }} +{{ if hugo.IsDevelopment }} + +{{ else }} + {{ with $r | fingerprint }} + + {{ end }} +{{ end }} diff --git a/layouts/partials/helpers/picture.html b/layouts/partials/helpers/picture.html new file mode 100644 index 000000000..7f30f1437 --- /dev/null +++ b/layouts/partials/helpers/picture.html @@ -0,0 +1,25 @@ +{{ $image := .image }} +{{ $width := .width | default 1000 }} +{{ $width1x := div $width 2 }} +{{ $imageWebp := $image.Resize (printf "%dx webp" $width) }} +{{ $image1x := $image.Resize (printf "%dx" $width1x) }} +{{ $image1xWebp := $image.Resize (printf "%dx webp" $width1x) }} +{{ $class := .class | default "h-64 tablet:h-96 lg:h-full w-full object-cover lg:absolute" }} + + + + + + + diff --git a/layouts/partials/layouts/blocks/alert.html b/layouts/partials/layouts/blocks/alert.html new file mode 100644 index 000000000..4db3cbd24 --- /dev/null +++ b/layouts/partials/layouts/blocks/alert.html @@ -0,0 +1,27 @@ +{{ $title := .title | default "" }} +{{ $color := .color | default "yellow" }} +{{ $icon := .icon | default "exclamation" }} +{{ $text := .text | default "" }} +{{ $class := .class | default "mt-6 mb-8" }} +
+
+
+ + + +
+
+ {{ with $title }} +

+ {{ . }} +

+ {{ end }} +
+

+ {{ $text }} +

+
+
+
+
diff --git a/layouts/partials/layouts/blocks/modal.html b/layouts/partials/layouts/blocks/modal.html new file mode 100644 index 000000000..2ae145cc7 --- /dev/null +++ b/layouts/partials/layouts/blocks/modal.html @@ -0,0 +1,29 @@ +
+
+ {{ .modal_button }} +
+ +
diff --git a/layouts/partials/layouts/breadcrumbs.html b/layouts/partials/layouts/breadcrumbs.html new file mode 100644 index 000000000..d7fd8fa08 --- /dev/null +++ b/layouts/partials/layouts/breadcrumbs.html @@ -0,0 +1,30 @@ +{{ $documentation := site.GetPage "/documentation" }} + + + + +{{ define "breadcrumbs-arrow" }} + + + +{{ end }} diff --git a/layouts/partials/layouts/docsheader.html b/layouts/partials/layouts/docsheader.html new file mode 100644 index 000000000..7e8e950f3 --- /dev/null +++ b/layouts/partials/layouts/docsheader.html @@ -0,0 +1,9 @@ +
+ {{ partial "layouts/breadcrumbs.html" . }} + {{ if and .IsPage (not (eq .Layout "list")) }} +

+ {{ .Title }} +

+ {{ end }} +
diff --git a/layouts/partials/layouts/explorer.html b/layouts/partials/layouts/explorer.html new file mode 100644 index 000000000..bb6f8e96a --- /dev/null +++ b/layouts/partials/layouts/explorer.html @@ -0,0 +1,47 @@ +{{/* This is currently not in use, but kept in case I change my mind. */}} + + +{{ define "docs-explorer-section" }} + {{ $p := .p }} + {{ $level := .level }} + {{ $pleft := $level }} + {{ if gt $level 0 }} + {{ $pleft = add $level 1 }} + {{ end }} + {{ $pl := printf "pl-%d" $pleft }} + {{ $pages := $p.Sections }} + + {{ range $pages }} + {{ $hasChildren := gt (len .Pages) 0 }} + {{ $class := cond (eq $level 0) "text-primary hover:text-primary/70" "text-gray-900 dark:text-gray-400 hover:dark:text-gray-300" }} +
  • + + {{ .LinkTitle }} + + {{ if $hasChildren }} + + {{ end }} +
  • + {{ end }} + +{{ end }} diff --git a/layouts/partials/layouts/footer.html b/layouts/partials/layouts/footer.html new file mode 100644 index 000000000..89190b45f --- /dev/null +++ b/layouts/partials/layouts/footer.html @@ -0,0 +1,72 @@ + diff --git a/layouts/partials/layouts/head/head-js.html b/layouts/partials/layouts/head/head-js.html new file mode 100644 index 000000000..d83efcd0f --- /dev/null +++ b/layouts/partials/layouts/head/head-js.html @@ -0,0 +1,11 @@ +{{ $githubInfo := partialCached "helpers/funcs/get-github-info.html" . "-" }} +{{ $opts := dict "minify" true }} +{{ with resources.Get "js/head-early.js" | js.Build $opts }} + {{ partial "helpers/linkjs.html" (dict "r" . "attributes" (dict "async" "")) }} +{{ end }} +{{ with resources.Get "js/main.js" | js.Build $opts }} + {{ partial "helpers/linkjs.html" (dict "r" . "attributes" (dict "defer" "")) }} +{{ end }} +{{ with resources.Get "js/turbo.js" | js.Build $opts }} + {{ partial "helpers/linkjs.html" (dict "r" . "attributes" (dict "defer" "")) }} +{{ end }} diff --git a/layouts/partials/layouts/head/head.html b/layouts/partials/layouts/head/head.html new file mode 100644 index 000000000..391e80dca --- /dev/null +++ b/layouts/partials/layouts/head/head.html @@ -0,0 +1,52 @@ + + + + +{{ hugo.Generator }} + +{{ if hugo.IsProduction }} + +{{ else }} + +{{ end }} + + + {{ with .Title }}{{ . }} |{{ end }} + {{ .Site.Title }} + + + + + + + + + + + +{{ range .AlternativeOutputFormats -}} + +{{ end -}} + + + + + + +{{ partial "opengraph/opengraph.html" . }} +{{- template "_internal/schema.html" . -}} +{{- template "_internal/twitter_cards.html" . -}} + +{{ if hugo.IsProduction }} + {{ partial "helpers/gtag.html" . }} +{{ end }} diff --git a/layouts/partials/layouts/header/githubstars.html b/layouts/partials/layouts/header/githubstars.html new file mode 100644 index 000000000..7e38cabc8 --- /dev/null +++ b/layouts/partials/layouts/header/githubstars.html @@ -0,0 +1,16 @@ +{{ with partialCached "helpers/funcs/get-github-info.html" . "-" }} + + + + + + + + {{ printf "%0.1fk" (div .stargazers_count 1000) }} + + +{{ end }} diff --git a/layouts/partials/layouts/header/header.html b/layouts/partials/layouts/header/header.html new file mode 100644 index 000000000..e4dd3a5a1 --- /dev/null +++ b/layouts/partials/layouts/header/header.html @@ -0,0 +1,43 @@ +
    +
    + {{ with site.Home }} + HUGO + {{ end }} +
    +
    + {{ range .Site.Menus.global }} + {{ .Name }} + {{ end }} + +
    + +
    + {{/* Search. */}} + {{ partial "layouts/search/input.html" . }} +
    +
    + {{/* QR code. */}} + {{ partial "layouts/header/qr.html" . }} + {{/* Theme selector. */}} + {{ partial "layouts/header/theme.html" . }} + + {{/* Social. */}} + +
    +
    diff --git a/layouts/partials/layouts/header/qr.html b/layouts/partials/layouts/header/qr.html new file mode 100644 index 000000000..c022e7242 --- /dev/null +++ b/layouts/partials/layouts/header/qr.html @@ -0,0 +1,24 @@ +{{ $t := debug.Timer "qr" }} +{{ $qr := partial "partials/_inline/qr" (dict + "page" $ + "img_class" "w-10 bg-white view-transition-qr" ) +}} +{{ $qrBig := partial "partials/_inline/qr" (dict "page" $ "img_class" "w-64 p-4") }} +{{ $t.Stop }} + + +{{ define "partials/_inline/qr" }} + {{ $img_class := .img_class | default "w-10" }} + {{ with images.QR $.page.Permalink (dict "targetDir" "images/qr") }} + + QR code linking to {{ $.page.Permalink }} + {{ end }} +{{ end }} diff --git a/layouts/partials/layouts/header/theme.html b/layouts/partials/layouts/header/theme.html new file mode 100644 index 000000000..006e48f9d --- /dev/null +++ b/layouts/partials/layouts/header/theme.html @@ -0,0 +1,35 @@ +
    + +
    diff --git a/layouts/partials/layouts/home/features.html b/layouts/partials/layouts/home/features.html new file mode 100644 index 000000000..527c98cb1 --- /dev/null +++ b/layouts/partials/layouts/home/features.html @@ -0,0 +1,56 @@ +{{/* icons source: https://heroicons.com/ */}} +{{ $dataTOML := ` + [[features]] + heading = "Optimized for speed" + copy = "Written in Go, optimized for speed and designed for flexibility. With its advanced templating system and fast asset pipelines, Hugo renders a large site in seconds, often less." + icon = """ + + + """ + [[features]] + heading = "Flexible framework" + copy = "With its multilingual support, and powerful taxonomy system, Hugo is widely used to create documentation sites, landing pages, corporate, government, nonprofit, education, news, event, and project sites." + icon = """ + + + """ + [[features]] + heading = "Fast assets pipeline" + copy = "Image processing (convert, resize, crop, rotate, adjust colors, apply filters, overlay text and images, and extract EXIF data), JavaScript bundling (tree shake, code splitting), Sass processing, great TailwindCSS support." + icon = """ + + + """ + [[features]] + heading = "Embedded web server" + copy = "Use Hugo's embedded web server during development to instantly see changes to content, structure, behavior, and presentation. " + icon = """ + + + """ + ` +}} +{{ $data := $dataTOML | transform.Unmarshal }} +
    +
    +
    + {{ range $data.features }} +
    +
    +
    + {{ .icon | safeHTML }} +
    + {{ .heading }} +
    +
    + {{ .copy }} +
    +
    + {{ end }} + +
    +
    +
    diff --git a/layouts/partials/layouts/home/opensource.html b/layouts/partials/layouts/home/opensource.html new file mode 100644 index 000000000..b64bdd22b --- /dev/null +++ b/layouts/partials/layouts/home/opensource.html @@ -0,0 +1,110 @@ +{{ $githubInfo := partialCached "helpers/funcs/get-github-info.html" . "-" }} +
    +
    +
    +
    +

    + Open source +

    +

    + Hugo is open source and free to use. It is distributed under the + Apache 2.0 License. +

    +
    +
    +
    + + + + Popular. +
    +
    + As of writing this, Hugo has + {{ $githubInfo.stargazers_count }} + stars on GitHub. Join the crowd and hit the + Star button. +
    +
    +
    +
    + + + + Active. +
    +
    + Hugo has a large and active community. If you have questions or + need help, you can ask in the + Hugo forums. +
    +
    +
    +
    + + + + Frequent releases. +
    +
    + Hugo has a fast + release + cycle. The project is actively maintained and new features are + added regularly. +
    +
    +
    +
    +
    + {{ partial "helpers/picture.html" (dict + "image" (resources.Get "images/hugo-github-screenshot.png") + "alt" "Hugo GitHub Repository" + "width" 640 + "class" "w-full max-w-[38rem] ring-1 shadow-xl dark:shadow-gray-500 ring-gray-400/10") + }} +
    +
    diff --git a/layouts/partials/layouts/home/sponsors.html b/layouts/partials/layouts/home/sponsors.html new file mode 100644 index 000000000..1f92e1174 --- /dev/null +++ b/layouts/partials/layouts/home/sponsors.html @@ -0,0 +1,43 @@ +{{ $gtag := .gtag | default "unknown" }} +{{ $gtag := .gtag | default "unknown" }} +{{ $isFooter := (eq $gtag "footer") }} +{{ $utmSource := cond $isFooter "hugofooter" "hugohome" }} +{{ $containerClass := .containerClass | default "mx-auto max-w-7xl px-6 lg:px-8" }} +{{/* TODO1 prod: onclick="trackOutboundLink({{ printf "'%s', '%s'" $gtagID $url | safeJS }});" +*/}} +{{ with .ctx.Site.Data.sponsors }} +
    +

    Hugo Sponsors

    +
    + {{ range .banners }} +
    + {{ $query_params := .query_params | default "" }} + {{ $url := .link }} + {{ if not .no_query_params }} + {{ $url = printf "%s?%s%s" .link $query_params (querify "utm_source" (.utm_source | default $utmSource ) "utm_medium" (.utm_medium | default "banner") "utm_campaign" (.utm_campaign | default "hugosponsor") "utm_content" (.utm_content | default "gohugoio")) | safeURL }} + {{ end }} + {{ $logo := resources.Get .logo }} + {{ $gtagID := printf "Sponsor %s %s" .name $gtag | title }} + + + +
    + {{ end }} +
    +
    +{{ end }} diff --git a/layouts/partials/layouts/hooks/body-end.html b/layouts/partials/layouts/hooks/body-end.html new file mode 100644 index 000000000..ba4a81300 --- /dev/null +++ b/layouts/partials/layouts/hooks/body-end.html @@ -0,0 +1 @@ +{{/* Empty for now */}} diff --git a/layouts/partials/layouts/hooks/body-start.html b/layouts/partials/layouts/hooks/body-start.html new file mode 100644 index 000000000..3430bd846 --- /dev/null +++ b/layouts/partials/layouts/hooks/body-start.html @@ -0,0 +1,3 @@ +{{ with resources.Get "js/body-start.js" | js.Build (dict "minify" true) }} + {{ partial "helpers/linkjs.html" (dict "r" . "attributes" (dict "" "")) }} +{{ end }} diff --git a/layouts/partials/layouts/icons.html b/layouts/partials/layouts/icons.html new file mode 100644 index 000000000..c7194bc03 --- /dev/null +++ b/layouts/partials/layouts/icons.html @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/layouts/partials/layouts/in-this-section.html b/layouts/partials/layouts/in-this-section.html new file mode 100644 index 000000000..71b4bf905 --- /dev/null +++ b/layouts/partials/layouts/in-this-section.html @@ -0,0 +1,29 @@ +{{- with .CurrentSection.RegularPages }} +

    + {{ "In this section" | title }} +

    +
    + +
    +{{- end }} diff --git a/layouts/partials/layouts/page-edit.html b/layouts/partials/layouts/page-edit.html new file mode 100644 index 000000000..b24c4c588 --- /dev/null +++ b/layouts/partials/layouts/page-edit.html @@ -0,0 +1,22 @@ +
    + +
    + Last updated: + {{ .Lastmod.Format "January 2, 2006" }}{{ with .GitInfo }} + : + {{ .Subject }} ({{ .AbbreviatedHash }}) + {{ end }} +
    + +{{ with .File }} + {{ $href := printf "%sedit/master/content/%s/%s" site.Params.ghrepo $.Lang .Path }} + + Improve this page + +{{ end }} diff --git a/layouts/partials/layouts/related.html b/layouts/partials/layouts/related.html new file mode 100644 index 000000000..a10f5adb4 --- /dev/null +++ b/layouts/partials/layouts/related.html @@ -0,0 +1,32 @@ +{{- $heading := "See also" | title }} +{{- $related := site.RegularPages.Related . | first 5 }} + +{{- if .Params.action.related }} + {{- $related = slice }} + {{- range .Params.action.related }} + {{- $path := . | lower }} + {{- with or (site.GetPage $path) ($.GetPage $path) }} + {{- $related = $related | append . }} + {{- else }} + {{/* TODO1 make error */}} + {{- warnf "The 'related' partial was unable to get page %s" . }} + {{- end }} + {{- end }} +{{- end }} +{{- with $related }} +

    + {{ $heading }} +

    + +{{- end }} diff --git a/layouts/partials/layouts/search/input-standalone.html b/layouts/partials/layouts/search/input-standalone.html new file mode 100644 index 000000000..15251eff2 --- /dev/null +++ b/layouts/partials/layouts/search/input-standalone.html @@ -0,0 +1,12 @@ +
    + + + + +
    diff --git a/layouts/partials/layouts/search/input.html b/layouts/partials/layouts/search/input.html new file mode 100644 index 000000000..e9862e5ca --- /dev/null +++ b/layouts/partials/layouts/search/input.html @@ -0,0 +1,15 @@ +
    +
    + + + + +
    + {{ partial "layouts/search/results.html" . }} +
    diff --git a/layouts/partials/layouts/search/results.html b/layouts/partials/layouts/search/results.html new file mode 100644 index 000000000..6f4ba698e --- /dev/null +++ b/layouts/partials/layouts/search/results.html @@ -0,0 +1,82 @@ + diff --git a/layouts/partials/layouts/toc.html b/layouts/partials/layouts/toc.html new file mode 100644 index 000000000..50b949e29 --- /dev/null +++ b/layouts/partials/layouts/toc.html @@ -0,0 +1,41 @@ +{{ with .Fragments.Headings }} +
    +

    + {{ "On this page" | title }} +

    + +
    +{{ end }} + +{{ define "render-toc-level" }} + {{ range . }} + {{ if and .ID (or (ge .Level 2) (lt .Level 4)) }} +
  • + + {{ .Title | safeHTML }} + +
  • + {{ end }} + {{ with .Headings }} + + {{ end }} + {{ end }} +{{ end }} diff --git a/layouts/partials/news/get-news-items.html b/layouts/partials/news/get-news-items.html new file mode 100644 index 000000000..cb1a930d3 --- /dev/null +++ b/layouts/partials/news/get-news-items.html @@ -0,0 +1,45 @@ +{{ $news_items := slice }} + +{{/* Get releases from GitHub. */}} +{{ $u := "https://api.github.com/repos/gohugoio/hugo/releases" }} +{{ $releases := partial "helpers/funcs/get-remote-data.html" $u }} +{{ $releases = where $releases "draft" false }} +{{ $releases = where $releases "prerelease" false }} +{{ range $releases | first 20 }} + {{ $publishDate := .published_at | time.AsTime }} + + {{/* Correct the v0.138.0 release date. See https://github.com/gohugoio/hugo/issues/13066. */}} + {{ if eq .name "v0.138.0" }} + {{ $publishDate = "2024-11-06T11:22:34Z" | time.AsTime }} + {{ end }} + + {{ $ctx := dict + "Date" $publishDate + "Title" (printf "Release %s" .name) + "LinkTitle" (printf "Release %s" .name) + "Permalink" .html_url + "RelPermalink" .html_url + "Section" "news" + "Summary" "" + }} + {{ $news_items = $news_items | append $ctx }} +{{ end }} + +{{/* Get content pages from news section. */}} +{{ range .Pages }} + {{ $ctx := dict + "Date" .Date + "Title" .Title + "LinkTitle" .Title + "RelPermalink" .RelPermalink + "Section" "news" + "Summary" .Summary + "Params" (dict "description" .Description) + }} + {{ $news_items = $news_items | append $ctx }} +{{ end }} + +{{/* Sort by date (descending) and render. */}} +{{ $news_items = sort $news_items "Date" "desc" }} + +{{ return $news_items }} diff --git a/layouts/partials/opengraph/get-featured-image.html b/layouts/partials/opengraph/get-featured-image.html new file mode 100644 index 000000000..50ee2a44d --- /dev/null +++ b/layouts/partials/opengraph/get-featured-image.html @@ -0,0 +1,26 @@ +{{ $images := $.Resources.ByType "image" }} +{{ $featured := $images.GetMatch "*feature*" }} +{{ if not $featured }} + {{ $featured = $images.GetMatch "{*cover*,*thumbnail*}" }} +{{ end }} +{{ if not $featured }} + {{ $featured = resources.Get "/opengraph/gohugoio-card-base-1.png" }} + {{ $size := 80 }} + {{ $title := $.LinkTitle }} + {{ if gt (len $title) 20 }} + {{ $size = 70 }} + {{ end }} + + {{ $text := $title }} + {{ $textOptions := dict + "color" "#FFF" + "size" $size + "lineSpacing" 10 + "x" 65 "y" 80 + "font" (resources.Get "/opengraph/mulish-black.ttf") + }} + + {{ $featured = $featured | images.Filter (images.Text $text $textOptions) }} +{{ end }} + +{{ return $featured }} diff --git a/layouts/partials/opengraph/opengraph.html b/layouts/partials/opengraph/opengraph.html new file mode 100644 index 000000000..1c89b0814 --- /dev/null +++ b/layouts/partials/opengraph/opengraph.html @@ -0,0 +1,84 @@ + + + + + +{{- with $.Params.images -}} + {{- range first 6 . }} + + {{ end -}} +{{- else -}} + {{- $featured := partial "opengraph/get-featured-image.html" . }} + {{- with $featured -}} + + {{- else -}} + {{- with $.Site.Params.images }} + + {{ end -}} + {{- end -}} +{{- end -}} + +{{- if .IsPage }} + {{- $iso8601 := "2006-01-02T15:04:05-07:00" -}} + + {{ with .PublishDate }} + + {{ end }} + {{ with .Lastmod }} + + {{ end }} +{{- end -}} + +{{- with .Params.audio }}{{ end }} +{{- with .Params.locale }} + +{{ end }} +{{- with .Site.Params.title }} + +{{ end }} +{{- with .Params.videos }} + {{- range . }} + + {{ end }} + +{{ end }} + +{{- /* If it is part of a series, link to related articles */}} +{{- $permalink := .Permalink }} +{{- $siteSeries := .Site.Taxonomies.series }} +{{ with .Params.series }} + {{- range $name := . }} + {{- $series := index $siteSeries ($name | urlize) }} + {{- range $page := first 6 $series.Pages }} + {{- if ne $page.Permalink $permalink }} + + {{ end }} + {{- end }} + {{ end }} + +{{ end }} + +{{- /* Facebook Page Admin ID for Domain Insights */}} +{{- with site.Params.social.facebook_admin }} + +{{ end }} diff --git a/layouts/shortcodes/chroma-lexers.html b/layouts/shortcodes/chroma-lexers.html new file mode 100644 index 000000000..38241be33 --- /dev/null +++ b/layouts/shortcodes/chroma-lexers.html @@ -0,0 +1,7 @@ +
    + {{ range .Site.Data.docs.chroma.lexers }} +
    {{ .Name }}
    +
    {{ with .Aliases }}{{ delimit . ", " }}{{ end }}
    + {{ end }} +
    + \ No newline at end of file diff --git a/layouts/shortcodes/code-toggle.html b/layouts/shortcodes/code-toggle.html new file mode 100644 index 000000000..f22bd0158 --- /dev/null +++ b/layouts/shortcodes/code-toggle.html @@ -0,0 +1,100 @@ +{{- /* + Renders syntax-highlighted configuration data in JSON, TOML, and YAML formats. + + @param {string} [config] The section of site.Data.docs.config to render. + @param {bool} [copy=false] If true, display a copy to clipboard button. + @param {string} [file] The file name to display above the rendered code. + @param {bool} [fm=false] If true, render the code as front matter. + @param {bool} [skipHeader=false] If false, omit top level key(s) when rendering a section of site.Data.docs.config. + + @returns {template.HTML} +*/}} + +{{- /* Initialize. */}} +{{- $config := "" }} +{{- $dataKey := "" }} +{{- $copy := false }} +{{- $file := "" }} +{{- $fm := false }} +{{- $skipHeader := false }} + +{{- /* Get parameters. */}} +{{- $config = .Get "config" }} +{{- $dataKey = .Get "dataKey" }} +{{- $file = .Get "file" }} +{{- if in (slice "false" false 0) (.Get "copy") }} + {{- $copy = false }} +{{- else if in (slice "true" true 1) (.Get "copy") }} + {{- $copy = true }} +{{- end }} +{{- if in (slice "false" false 0) (.Get "fm") }} + {{- $fm = false }} +{{- else if in (slice "true" true 1) (.Get "fm") }} + {{- $fm = true }} +{{- end }} +{{- if in (slice "false" false 0) (.Get "skipHeader") }} + {{- $skipHeader = false }} +{{- else if in (slice "true" true 1) (.Get "skipHeader") }} + {{- $skipHeader = true }} +{{- end }} + +{{- /* Define constants. */}} +{{- $delimiters := dict "toml" "+++" "yaml" "---" }} +{{- $langs := slice "yaml" "toml" "json" }} +{{- $placeHolder := "#-hugo-placeholder-#" }} + +{{- /* Render. */}} +{{- $code := "" }} +{{- if $config }} + {{- $file = $file | default "hugo" }} + {{- $sections := (split $config ".") }} + {{- $configSection := index $.Site.Data.docs.config $sections }} + {{- $code = dict $sections $configSection }} + {{- if $skipHeader }} + {{- $code = $configSection }} + {{- end }} +{{- else if $dataKey }} + {{- $file = $file | default $dataKey }} + {{- $sections := (split $dataKey ".") }} + {{- $code = index $.Site.Data.docs $sections }} +{{- else }} + {{- $code = $.Inner }} +{{- end }} +{{ $langs := (slice "yaml" "toml" "json") }} +
    +
    + + + + + {{ if $code }} + {{ range $i, $lang := $langs }} +
    + {{ highlight ($code | transform.Remarshal . | safeHTML) . "" }} +
    + {{ end }} + {{ end }} +
    +
    diff --git a/layouts/shortcodes/code.html b/layouts/shortcodes/code.html new file mode 100644 index 000000000..8de38ee27 --- /dev/null +++ b/layouts/shortcodes/code.html @@ -0,0 +1,30 @@ +{{ $codeLang := "go-html-template" }} +
    + {{- with .Get "file" -}} +
    + {{ . }} +
    + {{- end -}} + + {{ if ne (.Get "copy") "false" }} + + + + {{ end }} +
    + {{ $inner := trim .Inner "\n" | safeHTML }} + {{ if .Get "nocode" }} + {{ $inner }} + {{ else }} + {{ with $codeLang }} + {{ highlight $inner . "" }} + {{ else }} +
    {{ $inner }}
    + {{ end }} + {{ end }} +
    +
    diff --git a/layouts/shortcodes/datatable-filtered.html b/layouts/shortcodes/datatable-filtered.html new file mode 100644 index 000000000..ff3f299bd --- /dev/null +++ b/layouts/shortcodes/datatable-filtered.html @@ -0,0 +1,39 @@ +{{ $package := (index .Params 0) }} +{{ $listname := (index .Params 1) }} +{{ $filter := split (index .Params 2) " " }} +{{ $filter1 := index $filter 0 }} +{{ $filter2 := index $filter 1 }} +{{ $filter3 := index $filter 2 }} + +{{ $list := (index (index .Site.Data.docs $package) $listname) }} +{{ $fields := after 3 .Params }} +{{ $list := where $list $filter1 $filter2 $filter3 }} + + + + {{ range $fields }} + + {{ end }} + + {{ range $list }} + + {{ range $k, $v := . }} + {{ $.Scratch.Set $k $v }} + {{ end }} + {{ range $k, $v := $fields }} + + {{ end }} + + {{ end }} +
    {{ . }}
    + {{ $tdContent := $.Scratch.Get . }} + {{ if eq $k 3 }} + {{ printf "%v" $tdContent | + strings.ReplaceRE `\[` "
    1. " | + strings.ReplaceRE `\s` "
    2. " | + strings.ReplaceRE `\]` "
    " | + safeHTML }} + {{ else }} + {{ $tdContent }} + {{ end}} +
    diff --git a/layouts/shortcodes/datatable.html b/layouts/shortcodes/datatable.html new file mode 100644 index 000000000..12054f89d --- /dev/null +++ b/layouts/shortcodes/datatable.html @@ -0,0 +1,33 @@ +{{ $package := (index .Params 0) }} +{{ $listname := (index .Params 1) }} +{{ $list := (index (index .Site.Data.docs $package) $listname) }} +{{ $fields := after 2 .Params }} + + + + + {{ range $fields }} + {{ $s := . }} + {{ if eq $s "_key" }} + {{ $s = "Type" }} + {{ end }} + + {{ end }} + + {{ range $k1, $v1 := $list }} + + {{ range $k2, $v2 := . }} + {{ $.Scratch.Set $k2 $v2 }} + {{ end }} + {{ range $fields }} + {{ $s := "" }} + {{ if eq . "_key" }} + {{ $s = $k1 }} + {{ else }} + {{ $s = $.Scratch.Get . }} + {{ end }} + + {{ end }} + + {{ end }} +
    {{ $s }}
    {{ $s }}
    diff --git a/layouts/shortcodes/deprecated-in.html b/layouts/shortcodes/deprecated-in.html new file mode 100644 index 000000000..0272ea4a6 --- /dev/null +++ b/layouts/shortcodes/deprecated-in.html @@ -0,0 +1,17 @@ +{{ $_hugo_config := `{ "version": 1 }` }} + +{{ with .Get 0 }} + {{ $version := printf "v%v" (strings.TrimLeft "vV" .) }} + {{ $href := printf "https://github.com/gohugoio/hugo/releases/tag/%s" $version }} + {{ $text := (printf `Deprecated in %s. +%s` $href $version $.Inner) | safeHTML }} + + {{ partial "layouts/blocks/alert.html" (dict + "text" $text + "color" "orange" + "icon" "exclamation" + ) +}} +{{ else }} + {{ errorf "The %q shortcode requires a single positional parameter indicating version. See %s" .Name .Position }} +{{ end }} diff --git a/layouts/shortcodes/eturl.html b/layouts/shortcodes/eturl.html new file mode 100644 index 000000000..c0cf30aec --- /dev/null +++ b/layouts/shortcodes/eturl.html @@ -0,0 +1,36 @@ +{{- /* +Renders an absolute URL to the source code for an embedded template. + +Accepts either positional or named parameters, and depends on the +embedded_templates.toml file in the data directory. + +@param {string} filename The embedded template's file name, excluding extension. + +@returns template.HTML + +@example {{% et robots.txt %}} +@example {{% et filename=robots.txt %}} +*/}} + +{{- /* Get parameters. */}} +{{- $filename := "" -}} +{{- if .IsNamedParams -}} + {{- $filename = .Get "filename" -}} +{{- else -}} + {{- $filename = .Get 0 -}} +{{- end -}} + +{{- /* Render. */}} +{{- with $filename -}} + {{- with site.Data.embedded_template_urls -}} + {{- with index . $filename -}} + {{- urls.JoinPath site.Data.embedded_template_urls.base_url . -}} + {{- else -}} + {{- errorf "The %q shortcode was unable to find a URL for the embedded template named %q. Check the name. See %s" $.Name $filename $.Position -}} + {{- end -}} + {{- else -}} + {{- errorf "The %q shortcode was unable to find the embedded_template_urls data file in the site's data directory. See %s" $.Name $.Position -}} + {{- end -}} +{{- else -}} + {{- errorf "The %q shortcodes requires a named or positional parameter, the file name of the embedded template, excluding its extension. See %s" .Name .Position -}} +{{- end -}} diff --git a/layouts/shortcodes/glossary-term.html b/layouts/shortcodes/glossary-term.html new file mode 100644 index 000000000..7aace730e --- /dev/null +++ b/layouts/shortcodes/glossary-term.html @@ -0,0 +1,20 @@ +{{- /* +Renders the definition of the given glossary term. + +@param {string} (.Get 0) The glossary term. +@returns {template.HTML} + +@example {{% glossary-term float %}} +@example {{% glossary-term "floating point" %}} +*/}} + +{{- with .Get 0 }} + {{- $path := printf "/quick-reference/glossary/%s" (urlize .) }} + {{- with site.GetPage $path }} +{{ .RenderShortcodes }} {{/* Do not indent. Do not remove non-breaking space. */}} + {{- else }} + {{- errorf "The glossary term (%s) shortcode was unable to find %s: see %s" $.Name $path $.Position }} + {{- end }} +{{- else }} + {{- errorf "The glossary term (%s) shortcode requires one positional parameter: see %s" $.Name $.Position }} +{{- end -}} diff --git a/layouts/shortcodes/glossary.html b/layouts/shortcodes/glossary.html new file mode 100644 index 000000000..e91be32ce --- /dev/null +++ b/layouts/shortcodes/glossary.html @@ -0,0 +1,56 @@ +{{- /* +Renders the glossary of terms. + +When you call this shortcode using the {{% %}} notation, the glossary terms are +Markdown headings (level 6) which means they are members of .Page.Fragments. +This allows the link render hook to verify links to glossary terms. + +Yes, the terms themselves are pages, but we don't want to link to the pages, at +least not right now. Instead, we want to link to the fragments rendered by this +shortcode. + +@returns {template.HTML} + +@example {{% glossary %}} +*/}} +{{- $path := "/quick-reference/glossary" }} +{{- with site.GetPage $path }} + + {{- /* Build and render alphabetical index. */}} + {{- $m := dict }} + {{- range $p := .Pages.ByTitle }} + {{- $k := substr .Title 0 1 | strings.ToUpper }} + {{- if index $m $k }} + {{- continue }} + {{- end }} + {{- $anchor := path.BaseName .Path | anchorize }} + {{- $m = merge $m (dict $k $anchor) }} + {{- end }} + {{- range $k, $v := $m }} +[{{ $k }}](#{{ $v }}) {{/* Do not indent. */}} + {{- end }} + + {{- /* Render glossary terms. */}} + {{- range $p := .Pages.ByTitle }} +###### {{ .Title }}{{/* Do not indent. */}} +{{ .RenderShortcodes }}{{/* Do not indent. */}} + {{- with .Params.reference }} + {{- $destination := "" }} + {{- with $u := urls.Parse . }} + {{- if $u.IsAbs }} + {{- $destination = $u.String }} + {{- else }} + {{- with site.GetPage $u.Path -}} + {{- $destination = .RelPermalink }} + {{- else }} + {{- errorf "The %q shortcode was unable to find the reference link %s: see %s" $.Name . $p.String }} + {{- end }} + {{- end }} + {{- end -}} + See [details]({{ $destination }}).{{/* Do not indent. */}} + {{- end }} + {{- end }} + +{{- else }} + {{- errorf "The %q shortcode was unable to get %s: see %s" .Name $path .Position}} +{{- end }} diff --git a/layouts/shortcodes/gomodules-info.html b/layouts/shortcodes/gomodules-info.html new file mode 100644 index 000000000..126d846c0 --- /dev/null +++ b/layouts/shortcodes/gomodules-info.html @@ -0,0 +1,12 @@ +{{ $text := ` + Most of the commands for **Hugo Modules** require a newer version (>= 1.18) of Go installed (see https://golang.org/dl/) and the relevant VCS client (e.g. Git, see https://git-scm.com/downloads/ ). + If you have an "older" site running on Netlify, you may have to set GO_VERSION to 1.19 or newer in your Environment settings. + + For more information about Go Modules, see: + + * https://go.dev/wiki/Modules + * https://blog.golang.org/using-go-modules + ` +}} + +{{ partial "layouts/blocks/alert.html" (dict "title" "Go Modules" "text" ($text | markdownify) "color" "orange" "icon" "exclamation") }} diff --git a/layouts/shortcodes/hl.html b/layouts/shortcodes/hl.html new file mode 100644 index 000000000..1a5b4e1ec --- /dev/null +++ b/layouts/shortcodes/hl.html @@ -0,0 +1,11 @@ +{{- /* +Returns syntax-highlighted code from the given text. + +This is useful as a terse way to highlight inline code snippets. Calling the +highlight shortcode for inline snippets is verbose. +*/}} + +{{- $code := .Inner | strings.TrimSpace }} +{{- $lang := or (.Get 0) "go" }} +{{- $opts := dict "hl_inline" true "noClasses" true }} +{{- transform.Highlight $code $lang $opts }} diff --git a/layouts/shortcodes/img.html b/layouts/shortcodes/img.html new file mode 100644 index 000000000..7c2d805d2 --- /dev/null +++ b/layouts/shortcodes/img.html @@ -0,0 +1,391 @@ +{{- /* +Renders the given image using the given filter, if any. + +@param {string} src The path to the image which must be a remote, page, or global resource. +@param {string} [filter] The filter to apply to the image (case-insensitive). +@param {string} [filterArgs] A comma-delimited list of arguments to pass to the filter. +@param {bool} [example=false] If true, renders a before/after example. +@param {int} [exampleWidth=384] Image width, in pixels, when rendering a before/after example. + +@returns {template.HTML} + +@examples + + {{< img src="zion-national-park.jpg" >}} + + {{< img src="zion-national-park.jpg" alt="Zion National Park" >}} + + {{< img + src="zion-national-park.jpg" + alt="Zion National Park" + filter="grayscale" + >}} + + {{< img + src="zion-national-park.jpg" + alt="Zion National Park" + filter="process" + filterArgs="resize 400x webp" + >}} + + {{< img + src="zion-national-park.jpg" + alt="Zion National Park" + filter="colorize" + filterArgs="180,50,20" + >}} + + {{< img + src="zion-national-park.jpg" + alt="Zion National Park" + filter="grayscale" + example=true + >}} + + {{< img + src="zion-national-park.jpg" + alt="Zion National Park" + filter="grayscale" + example=true + exampleWidth=400 + >}} + + When using the text filter, provide the arguments in this order: + + 0. The text + 1. The horizontal offset, in pixels, relative to the left of the image (default 20) + 2. The vertical offset, in pixels, relative to the top of the image (default 20) + 3. The font size in pixels (default 64) + 4. The line height (default 1.2) + 5. The font color (default #ffffff) + + {{< img + src="images/examples/zion-national-park.jpg" + alt="Zion National Park" + filter="Text" + filterArgs="Zion National Park,25,250,56" + example=true + >}} + + When using the padding filter, provide all arguments in this order: + + 0. Padding top + 1. Padding right + 2. Padding bottom + 3. Padding right + 4. Canvas color + + {{< img + src="images/examples/zion-national-park.jpg" + alt="Zion National Park" + filter="Padding" + filterArgs="20,50,20,50,#0705" + example=true + >}} + +*/}} + +{{- /* Initialize. */}} +{{- $alt := "" }} +{{- $src := "" }} +{{- $filter := "" }} +{{- $filterArgs := slice }} +{{- $example := false }} +{{- $exampleWidth := 384 }} + +{{- /* Default values to use with the text filter. */}} +{{ $textFilterOpts := dict + "xOffset" 20 + "yOffset" 20 + "fontSize" 64 + "lineHeight" 1.2 + "fontColor" "#ffffff" + "fontPath" "https://github.com/google/fonts/raw/main/ofl/lato/Lato-Regular.ttf" +}} + +{{- /* Get and validate parameters. */}} +{{- with .Get "alt" }} + {{- $alt = .}} +{{- end }} + +{{- with .Get "src" }} + {{- $src = . }} +{{- else }} + {{- errorf "The %q shortcode requires a file parameter. See %s" .Name .Position }} +{{- end }} + +{{- with .Get "filter" }} + {{- $filter = . | lower }} +{{- end }} + +{{- $validFilters := slice + "autoorient" "brightness" "colorbalance" "colorize" "contrast" "dither" + "gamma" "gaussianblur" "grayscale" "hue" "invert" "mask" "none" "opacity" + "overlay" "padding" "pixelate" "process" "saturation" "sepia" "sigmoid" "text" + "unsharpmask" +}} + +{{- with $filter }} + {{- if not (in $validFilters .) }} + {{- errorf "The filter passed to the %q shortcode is invalid. The filter must be one of %s. See %s" $.Name (delimit $validFilters ", " ", or ") $.Position }} + {{- end }} +{{- end }} + +{{- with .Get "filterArgs" }} + {{- $filterArgs = split . "," }} + {{- $filterArgs = apply $filterArgs "trim" "." " " }} +{{- end }} + +{{- if in (slice "false" false 0) (.Get "example") }} + {{- $example = false }} +{{- else if in (slice "true" true 1) (.Get "example")}} + {{- $example = true }} +{{- end }} + +{{- with .Get "exampleWidth" }} + {{- $exampleWidth = . | int }} +{{- end }} + +{{- /* Get image. */}} +{{- $ctx := dict "page" .Page "src" $src "name" .Name "position" .Position }} +{{- $i := partial "inline/get-resource.html" $ctx }} + +{{- /* Resize if rendering before/after examples. */}} +{{- if $example }} + {{- $i = $i.Resize (printf "%dx" $exampleWidth) }} +{{- end }} + +{{- /* Create filter. */}} +{{- $f := "" }} +{{- $ctx := dict "filter" $filter "args" $filterArgs "name" .Name "position" .Position }} +{{- if eq $filter "autoorient" }} + {{- $ctx = merge $ctx (dict "argsRequired" 0) }} + {{- template "validate-arg-count" $ctx }} + {{- $f = images.AutoOrient }} +{{- else if eq $filter "brightness" }} + {{- $ctx = merge $ctx (dict "argsRequired" 1) }} + {{- template "validate-arg-count" $ctx }} + {{- $filterArgs = apply $filterArgs "float" "." }} + {{- $ctx = merge $ctx (dict "argName" "percentage" "argValue" (index $filterArgs 0) "min" -100 "max" 100) }} + {{- template "validate-arg-value" $ctx }} + {{- $f = images.Brightness (index $filterArgs 0) }} +{{- else if eq $filter "colorbalance" }} + {{- $ctx = merge $ctx (dict "argsRequired" 3) }} + {{- template "validate-arg-count" $ctx }} + {{- $filterArgs = apply $filterArgs "float" "." }} + {{- $ctx = merge $ctx (dict "argName" "percentage red" "argValue" (index $filterArgs 0) "min" -100 "max" 500) }} + {{- template "validate-arg-value" $ctx }} + {{- $ctx = merge $ctx (dict "argName" "percentage green" "argValue" (index $filterArgs 1) "min" -100 "max" 500) }} + {{- template "validate-arg-value" $ctx }} + {{- $ctx = merge $ctx (dict "argName" "percentage blue" "argValue" (index $filterArgs 2) "min" -100 "max" 500) }} + {{- template "validate-arg-value" $ctx }} + {{- $f = images.ColorBalance (index $filterArgs 0) (index $filterArgs 1) (index $filterArgs 2) }} +{{- else if eq $filter "colorize" }} + {{- $ctx = merge $ctx (dict "argsRequired" 3) }} + {{- template "validate-arg-count" $ctx }} + {{- $filterArgs = apply $filterArgs "float" "." }} + {{- $ctx = merge $ctx (dict "argName" "hue" "argValue" (index $filterArgs 0) "min" 0 "max" 360) }} + {{- template "validate-arg-value" $ctx }} + {{- $ctx = merge $ctx (dict "argName" "saturation" "argValue" (index $filterArgs 1) "min" 0 "max" 100) }} + {{- template "validate-arg-value" $ctx }} + {{- $ctx = merge $ctx (dict "argName" "percentage" "argValue" (index $filterArgs 2) "min" 0 "max" 100) }} + {{- template "validate-arg-value" $ctx }} + {{- $f = images.Colorize (index $filterArgs 0) (index $filterArgs 1) (index $filterArgs 2) }} +{{- else if eq $filter "contrast" }} + {{- $ctx = merge $ctx (dict "argsRequired" 1) }} + {{- template "validate-arg-count" $ctx }} + {{- $filterArgs = apply $filterArgs "float" "." }} + {{- $ctx = merge $ctx (dict "argName" "percentage" "argValue" (index $filterArgs 0) "min" -100 "max" 100) }} + {{- template "validate-arg-value" $ctx }} + {{- $f = images.Contrast (index $filterArgs 0) }} +{{- else if eq $filter "dither" }} + {{- $f = images.Dither }} +{{- else if eq $filter "gamma" }} + {{- $ctx = merge $ctx (dict "argsRequired" 1) }} + {{- template "validate-arg-count" $ctx }} + {{- $filterArgs = apply $filterArgs "float" "." }} + {{- $ctx = merge $ctx (dict "argName" "gamma" "argValue" (index $filterArgs 0) "min" 0 "max" 100) }} + {{- template "validate-arg-value" $ctx }} + {{- $f = images.Gamma (index $filterArgs 0) }} +{{- else if eq $filter "gaussianblur" }} + {{- $ctx = merge $ctx (dict "argsRequired" 1) }} + {{- template "validate-arg-count" $ctx }} + {{- $filterArgs = apply $filterArgs "float" "." }} + {{- $ctx = merge $ctx (dict "argName" "sigma" "argValue" (index $filterArgs 0) "min" 0 "max" 1000) }} + {{- template "validate-arg-value" $ctx }} + {{- $f = images.GaussianBlur (index $filterArgs 0) }} +{{- else if eq $filter "grayscale" }} + {{- $ctx = merge $ctx (dict "argsRequired" 0) }} + {{- template "validate-arg-count" $ctx }} + {{- $f = images.Grayscale }} +{{- else if eq $filter "hue" }} + {{- $ctx = merge $ctx (dict "argsRequired" 1) }} + {{- template "validate-arg-count" $ctx }} + {{- $filterArgs = apply $filterArgs "float" "." }} + {{- $ctx = merge $ctx (dict "argName" "shift" "argValue" (index $filterArgs 0) "min" -180 "max" 180) }} + {{- template "validate-arg-value" $ctx }} + {{- $f = images.Hue (index $filterArgs 0) }} +{{- else if eq $filter "invert" }} + {{- $ctx = merge $ctx (dict "argsRequired" 0) }} + {{- template "validate-arg-count" $ctx }} + {{- $f = images.Invert }} +{{- else if eq $filter "mask" }} + {{- $ctx = merge $ctx (dict "argsRequired" 1) }} + {{- template "validate-arg-count" $ctx }} + {{- $ctx := dict "src" (index $filterArgs 0) "name" .Name "position" .Position }} + {{- $maskImage := partial "inline/get-resource.html" $ctx }} + {{- $f = images.Mask $maskImage }} +{{- else if eq $filter "opacity" }} + {{- $ctx = merge $ctx (dict "argsRequired" 1) }} + {{- template "validate-arg-count" $ctx }} + {{- $filterArgs = apply $filterArgs "float" "." }} + {{- $ctx = merge $ctx (dict "argName" "opacity" "argValue" (index $filterArgs 0) "min" 0 "max" 1) }} + {{- template "validate-arg-value" $ctx }} + {{- $f = images.Opacity (index $filterArgs 0) }} +{{- else if eq $filter "overlay" }} + {{- $ctx = merge $ctx (dict "argsRequired" 3) }} + {{- template "validate-arg-count" $ctx }} + {{- $ctx := dict "src" (index $filterArgs 0) "name" .Name "position" .Position }} + {{- $overlayImg := partial "inline/get-resource.html" $ctx }} + {{- $f = images.Overlay $overlayImg (index $filterArgs 1 | float ) (index $filterArgs 2 | float) }} +{{- else if eq $filter "padding" }} + {{- $ctx = merge $ctx (dict "argsRequired" 5) }} + {{- template "validate-arg-count" $ctx }} + {{- $f = images.Padding + (index $filterArgs 0 | int) + (index $filterArgs 1 | int) + (index $filterArgs 2 | int) + (index $filterArgs 3 | int) + (index $filterArgs 4) + }} +{{- else if eq $filter "pixelate" }} + {{- $ctx = merge $ctx (dict "argsRequired" 1) }} + {{- template "validate-arg-count" $ctx }} + {{- $filterArgs = apply $filterArgs "float" "." }} + {{- $ctx = merge $ctx (dict "argName" "size" "argValue" (index $filterArgs 0) "min" 0 "max" 1000) }} + {{- template "validate-arg-value" $ctx }} + {{- $f = images.Pixelate (index $filterArgs 0) }} +{{- else if eq $filter "process" }} + {{- $ctx = merge $ctx (dict "argsRequired" 1) }} + {{- template "validate-arg-count" $ctx }} + {{- $f = images.Process (index $filterArgs 0) }} +{{- else if eq $filter "saturation" }} + {{- $ctx = merge $ctx (dict "argsRequired" 1) }} + {{- template "validate-arg-count" $ctx }} + {{- $filterArgs = apply $filterArgs "float" "." }} + {{- $ctx = merge $ctx (dict "argName" "percentage" "argValue" (index $filterArgs 0) "min" -100 "max" 500) }} + {{- template "validate-arg-value" $ctx }} + {{- $f = images.Saturation (index $filterArgs 0) }} +{{- else if eq $filter "sepia" }} + {{- $ctx = merge $ctx (dict "argsRequired" 1) }} + {{- template "validate-arg-count" $ctx }} + {{- $filterArgs = apply $filterArgs "float" "." }} + {{- $ctx = merge $ctx (dict "argName" "percentage" "argValue" (index $filterArgs 0) "min" 0 "max" 100) }} + {{- template "validate-arg-value" $ctx }} + {{- $f = images.Sepia (index $filterArgs 0) }} +{{- else if eq $filter "sigmoid" }} + {{- $ctx = merge $ctx (dict "argsRequired" 2) }} + {{- template "validate-arg-count" $ctx }} + {{- $filterArgs = apply $filterArgs "float" "." }} + {{- $ctx = merge $ctx (dict "argName" "midpoint" "argValue" (index $filterArgs 0) "min" 0 "max" 1) }} + {{- template "validate-arg-value" $ctx }} + {{- $ctx = merge $ctx (dict "argName" "factor" "argValue" (index $filterArgs 1) "min" -10 "max" 10) }} + {{- template "validate-arg-value" $ctx }} + {{- $f = images.Sigmoid (index $filterArgs 0) (index $filterArgs 1) }} +{{- else if eq $filter "text" }} + {{- $ctx = merge $ctx (dict "argsRequired" 1) }} + {{- template "validate-arg-count" $ctx }} + {{- $ctx := dict "src" $textFilterOpts.fontPath "name" .Name "position" .Position }} + {{- $font := or (partial "inline/get-resource.html" $ctx) }} + {{- $fontSize := or (index $filterArgs 3 | int) $textFilterOpts.fontSize }} + {{- $lineHeight := math.Max (or (index $filterArgs 4 | float) $textFilterOpts.lineHeight) 1 }} + {{- $opts := dict + "x" (or (index $filterArgs 1 | int) $textFilterOpts.xOffset) + "y" (or (index $filterArgs 2 | int) $textFilterOpts.yOffset) + "size" $fontSize + "linespacing" (mul (sub $lineHeight 1) $fontSize) + "color" (or (index $filterArgs 5) $textFilterOpts.fontColor) + "font" $font + }} + {{- $f = images.Text (index $filterArgs 0) $opts }} +{{- else if eq $filter "unsharpmask" }} + {{- $ctx = merge $ctx (dict "argsRequired" 3) }} + {{- template "validate-arg-count" $ctx }} + {{- $filterArgs = apply $filterArgs "float" "." }} + {{- $ctx = merge $ctx (dict "argName" "sigma" "argValue" (index $filterArgs 0) "min" 0 "max" 500) }} + {{- template "validate-arg-value" $ctx }} + {{- $ctx = merge $ctx (dict "argName" "amount" "argValue" (index $filterArgs 1) "min" 0 "max" 100) }} + {{- template "validate-arg-value" $ctx }} + {{- $ctx = merge $ctx (dict "argName" "threshold" "argValue" (index $filterArgs 2) "min" 0 "max" 1) }} + {{- template "validate-arg-value" $ctx }} + {{- $f = images.UnsharpMask (index $filterArgs 0) (index $filterArgs 1) (index $filterArgs 2) }} +{{- end }} + +{{- /* Apply filter. */}} +{{- $fi := $i }} +{{- with $f }} + {{- $fi = $i.Filter . }} +{{- end }} + +{{- /* Render. */}} +{{- $class := "di va b--black-20" }} +{{- if eq $filter "mask" }} + {{- $class = "di va" }} +{{- end }} +{{- if $example }} +

    Original

    + {{ $alt }} +

    Processed

    + {{ $alt }} +{{- else -}} + {{ $alt }} +{{- end }} + +{{- define "validate-arg-count" }} + {{- $msg := "When using the %q filter, the %q shortcode requires an args parameter with %d %s. See %s" }} + {{- if lt (len .args) .argsRequired }} + {{- $text := "values" }} + {{- if eq 1 .argsRequired }} + {{- $text = "value" }} + {{- end }} + {{- errorf $msg .filter .name .argsRequired $text .position }} + {{- end }} +{{- end }} + +{{- define "validate-arg-value" }} + {{- $msg := "The %q argument passed to the %q shortcode is invalid. Expected a value in the range [%v,%v], but received %v. See %s" }} + {{- if or (lt .argValue .min) (gt .argValue .max) }} + {{- errorf $msg .argName .name .min .max .argValue .position }} + {{- end }} +{{- end }} + +{{- define "partials/inline/get-resource.html" }} + {{- $r := "" }} + {{- $u := urls.Parse .src }} + {{- $msg := "The %q shortcode was unable to resolve %s. See %s" }} + {{- if $u.IsAbs }} + {{- with try (resources.GetRemote $u.String) }} + {{- with .Err }} + {{- errorf "%s" . }} + {{- else with .Value }} + {{- /* This is a remote resource. */}} + {{- $r = . }} + {{- else }} + {{- errorf $msg $.name $u.String $.position }} + {{- end }} + {{- end }} + {{- else }} + {{- with .page.Resources.Get (strings.TrimPrefix "./" $u.Path) }} + {{- /* This is a page resource. */}} + {{- $r = . }} + {{- else }} + {{- with resources.Get $u.Path }} + {{- /* This is a global resource. */}} + {{- $r = . }} + {{- else }} + {{- errorf $msg $.name $u.Path $.position }} + {{- end }} + {{- end }} + {{- end }} + {{- return $r}} +{{- end -}} diff --git a/layouts/shortcodes/imgproc.html b/layouts/shortcodes/imgproc.html new file mode 100644 index 000000000..c09133ba8 --- /dev/null +++ b/layouts/shortcodes/imgproc.html @@ -0,0 +1,37 @@ +{{- /* +Renders the given image using the given process specification. + +@param {string} (positional parameter 0) The path to the image, relative to the current page. The image must be a page resource. +@param {string}} (positional parameter 1) The image processing specification. + +@returns template.HTML + +@example {{< imgproc "sunset.jpg" "resize 300x" />}} +*/}} + +{{- with $.Get 0 }} + {{- with $i := $.Page.Resources.Get . }} + {{- with $spec := $.Get 1 }} + {{- with $i.Process . }} +
    + +
    + + {{- with $.Inner }} + {{ . }} + {{- else }} + {{ $spec }} + {{- end }} + +
    +
    + {{- end }} + {{- else }} + {{- errorf "The %q shortcode requires a positional parameter (1) containing the image processing specification. See %s" $.Name $.Position }} + {{- end }} + {{- else }} + {{- errorf "The %q shortcode was unable to find %q. See %s" $.Name . $.Position }} + {{- end }} +{{- else }} + {{- errorf "The %q shortcode requires a positional parameter (0) indicating the image path, relative to the current page. See %s" $.Name $.Position }} +{{- end }} diff --git a/layouts/shortcodes/include.html b/layouts/shortcodes/include.html new file mode 100644 index 000000000..b4a20cd72 --- /dev/null +++ b/layouts/shortcodes/include.html @@ -0,0 +1,21 @@ +{{- /* +Renders the page using the RenderShortcode method on the Page object. + +You must call this shortcode using the {{% %}} notation. + +@param {string} (positional parameter 0) The path to the page, relative to the content directory. +@returns template.HTML + +@example {{% include "functions/_common/glob-patterns" %}} +*/}} + +{{- with .Get 0 }} + {{- with or ($.Page.GetPage .) (site.GetPage .) }} + {{- .RenderShortcodes }} + {{- else }} + {{/* TODO1 make error */}} + {{- warnf "The %q shortcode was unable to find %q. See %s" $.Name . $.Position }} + {{- end }} +{{- else }} + {{- errorf "The %q shortcode requires a positional parameter indicating the path of the file to include. See %s" .Name .Position }} +{{- end }} diff --git a/layouts/shortcodes/list-pages-in-section.html b/layouts/shortcodes/list-pages-in-section.html new file mode 100644 index 000000000..73e7f85a9 --- /dev/null +++ b/layouts/shortcodes/list-pages-in-section.html @@ -0,0 +1,96 @@ +{{- /* +Renders a description list of the pages in the given section. + +Render a subset of the pages in the section by specifying a predefined filter, +and whether to include those pages. + +Filters are defined in the data directory, in the file named page_filters. Each +filter is an array of paths to a file, relative to the root of the content +directory. Hugo will throw an error if the specified filter does not exist, or +if any of the pages in the filter do not exist. + +The definition term elements (dt) have an id attribute derived from the title +of the page. This is probably unique, because pages of the same title in the +same section is unlikely. + +If you render a complete list on a page, then call the shortcode again to +render a subset, you will generate duplicate element ids. In this case, set +omitElementIDs to true for the subset. + +@param {string} path The path to the section. +@param {string} [filter=""] The name of filter list. +@param {string} [filterType=""] The type of filter, either include or exclude. +@param {string} [omitElementIDs=false] Whether to omit dt element ids. +@param {string} [titlePrefix=""] The string to prepend to the link title. + +@returns template.HTML + +@example {{< list-pages-in-section path=/methods/resources >}} +@example {{< list-pages-in-section path=/functions/images filter=some_filter filterType=exclude >}} +@example {{< list-pages-in-section path=/functions/images filter=some_filter filterType=exclude titlePrefix=foo >}} +@example {{< list-pages-in-section path=/functions/images filter=some_filter filterType=exclude titlePrefix=foo omitElementIDs=true >}} +*/}} + +{{- /* Initialize. */}} +{{- $filter := or "" (.Get "filter" | lower)}} +{{- $filterType := or (.Get "filterType") "none" | lower }} +{{- $filteredPages := slice }} +{{- $titlePrefix := or (.Get "titlePrefix") "" }} +{{- $omitElementIDs := false }} + +{{- /* Get boolean parameters. */}} +{{- if in (slice "false" false 0) (.Get "omitElementIDs") }} + {{- $omitElementIDs = false }} +{{- else if in (slice "true" true 1) (.Get "omitElementIDs")}} + {{- $omitElementIDs = true }} +{{- end }} + +{{- /* Build slice of filtered pages. */}} +{{- with $filter }} + {{- with index site.Data.page_filters . }} + {{- range . }} + {{- with site.GetPage . }} + {{- $filteredPages = $filteredPages | append . }} + {{- else }} + {{- errorf "The %q shortcode was unable to find %q as specified in the page_filters data file. See %s" $.Name . $.Position }} + {{- end }} + {{- end }} + {{- else }} + {{- errorf "The %q shortcode was unable to find the %q filter in the page_filters data file. See %s" $.Name . $.Position }} + {{- end }} +{{- end }} + +{{- /* Render */}} +{{- with $sectionPath := .Get "path" }} + {{- with site.GetPage . }} + {{- with .RegularPages }} +
    + {{- range $page := .ByTitle }} + {{- if or + (and (eq $filterType "include") (in $filteredPages $page)) + (and (eq $filterType "exclude") (not (in $filteredPages $page))) + (eq $filterType "none") + }} + {{- $linkTitle := .LinkTitle }} + {{- with $titlePrefix }} + {{- $linkTitle = printf "%s%s" . $linkTitle }} + {{- end }} + {{- $idAttribute := "" }} + {{- if not $omitElementIDs }} + {{- $id := path.Join .File.Dir .File.ContentBaseName | replaceRE `[\|/]` ":" | lower }} + {{- $idAttribute = printf " id=%q" $id }} + {{- end }} +
    {{ $linkTitle }}
    +
    {{- $page.Description | $page.RenderString }}
    + {{- end }} + {{- end }} +
    + {{- else }} + {{- warnf "The %q shortcode found no pages in the %q section. See %s" $.Name $sectionPath $.Position }} + {{- end }} + {{- else }} + {{- errorf "The %q shortcode was unable to find %q. See %s" $.Name $sectionPath $.Position }} + {{- end }} +{{- else }} + {{- errorf "The %q shortcode requires a 'path' parameter indicating the path to the section. See %s" $.Name $.Position }} +{{- end }} diff --git a/layouts/shortcodes/module-mounts-note.html b/layouts/shortcodes/module-mounts-note.html new file mode 100644 index 000000000..c1c67991e --- /dev/null +++ b/layouts/shortcodes/module-mounts-note.html @@ -0,0 +1 @@ +Also see [Module Mounts Config](/hugo-modules/configuration/#module-configuration-mounts) for an alternative way to configure this directory. diff --git a/layouts/shortcodes/new-in.html b/layouts/shortcodes/new-in.html new file mode 100644 index 000000000..c725ae14b --- /dev/null +++ b/layouts/shortcodes/new-in.html @@ -0,0 +1,51 @@ +{{- /* +Renders a "new in" button indicating the version in which a feature was added. + +When comparing the current version to the specified version, the "new in" +button will be hidden if any of the following conditions is true: + +- The major version difference exceeds the majorVersionDiffThreshold +- The minor version difference exceeds the minorVersionDiffThreshold + +@param {string} version The semantic version string, with or without a leading v. +@returns {template.HTML} + +@examples {{< new-in 0.100.0 />}} + + {{< new-in 0.100.0 >}} + Some descriptive text here. + {{< /new-in >}} +*/}} + + +{{ $_hugo_config := `{ "version": 1 }` }} + +{{- /* Set defaults. */}} +{{- $majorVersionDiffThreshold := 0 }} +{{- $minorVersionDiffThreshold := 30 }} +{{- $displayExpirationWarning := true }} + +{{- /* Render. */}} +{{- with $version := .Get 0 | strings.TrimPrefix "v" }} + {{- $majorVersionDiff := sub (index (split hugo.Version ".") 0 | int) (index (split $version ".") 0 | int) }} + {{- $minorVersionDiff := sub (index (split hugo.Version ".") 1 | int) (index (split $version ".") 1 | int) }} + {{- if or (gt $majorVersionDiff $majorVersionDiffThreshold) (gt $minorVersionDiff $minorVersionDiffThreshold) }} + {{- if $displayExpirationWarning }} + {{- warnf "This call to the %q shortcode should be removed: %s. The button is now hidden because the specified version (%s) is older than the display threshold." $.Name $.Position $version }} + {{- end }} + {{- else }} + {{- $href := printf "https://github.com/gohugoio/hugo/releases/tag/v%s" $version }} + {{- with $.Inner }} + + {{- else }} + New in v{{ $version }} + {{- end }} + {{- end }} +{{- else }} + {{- errorf "The %q shortcode requires a positional parameter (version). See %s" .Name .Position }} +{{- end -}} diff --git a/layouts/shortcodes/note.html b/layouts/shortcodes/note.html new file mode 100644 index 000000000..b18d53bc7 --- /dev/null +++ b/layouts/shortcodes/note.html @@ -0,0 +1,7 @@ +{{ $_hugo_config := `{ "version": 1 }` }} +{{ partial "layouts/blocks/alert.html" (dict + "text" .Inner + "color" "blue" + "icon" "exclamation" + ) +}} diff --git a/layouts/shortcodes/quick-reference.html b/layouts/shortcodes/quick-reference.html new file mode 100644 index 000000000..fc53c93bd --- /dev/null +++ b/layouts/shortcodes/quick-reference.html @@ -0,0 +1,39 @@ +{{/* +Renders the child sections of the given top-level section, listing each child's immediate descendants. + +@param {string} section The top-level section to render. +@returns template.HTML + +@example {{% quick-reference section="functions" %}} +*/}} + +{{ $section := "" }} +{{ with .Get "section" }} + {{ $section = . }} +{{ else }} + {{ errorf "The %q shortcodes requires a 'section' parameter. See %s" .Name .Position }} +{{ end }} + +{{/* Do not change the markdown indentation, and do not remove blank lines. */}} +{{ with site.GetPage $section }} + {{ range .Sections }} + +## {{ .LinkTitle }} +{{ .RawContent }} + + {{ range .Pages }} + {{ $aliases := "" }} + {{ if eq .Section "functions" }} + {{ with .Params.action.aliases }} + {{ $aliases = delimit . " or " }} + {{ end }} + {{ end }} + +[{{ .LinkTitle }}]({{ .RelPermalink }}) {{ with $aliases }}({{ . }}){{ end }} +: {{ .Description }} + + {{ end }} + {{ end }} +{{ else }} + {{ errorf "The %q shortcodes was unable to find the %q section. See %s" .Name $section .Position }} +{{ end }} diff --git a/layouts/shortcodes/readfile.html b/layouts/shortcodes/readfile.html new file mode 100644 index 000000000..1a7d6a320 --- /dev/null +++ b/layouts/shortcodes/readfile.html @@ -0,0 +1 @@ +TODO readfile.html diff --git a/netlify.toml b/netlify.toml index d9225229b..add253cb3 100644 --- a/netlify.toml +++ b/netlify.toml @@ -24,7 +24,32 @@ [context.next.environment] HUGO_ENABLEGITINFO = "true" -[[redirects]] - from = "/npmjs/*" - to = "/npmjs/" - status = 200 +[[headers]] + for = "/*.jpg" + + [headers.values] + Cache-Control = "public, max-age=31536000" + +[[headers]] + for = "/*.png" + + [headers.values] + Cache-Control = "public, max-age=31536000" + +[[headers]] + for = "/*.css" + + [headers.values] + Cache-Control = "public, max-age=31536000" + +[[headers]] + for = "/*.js" + + [headers.values] + Cache-Control = "public, max-age=31536000" + +[[headers]] + for = "/*.ttf" + + [headers.values] + Cache-Control = "public, max-age=31536000" diff --git a/package.json b/package.json new file mode 100644 index 000000000..92339ba25 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "hugoDocs", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "", + "devDependencies": { + "@tailwindcss/cli": "^4.0.0", + "@tailwindcss/typography": "^0.5.15", + "tailwindcss": "^4.0.0" + }, + "dependencies": { + "@alpinejs/focus": "^3.14.8", + "@alpinejs/persist": "^3.14.8", + "@hotwired/turbo": "^8.0.12", + "alpinejs": "^3.14.8" + } +} diff --git a/static/android-chrome-144x144.png b/static/android-chrome-144x144.png new file mode 100644 index 000000000..975cb33ba Binary files /dev/null and b/static/android-chrome-144x144.png differ diff --git a/static/android-chrome-192x192.png b/static/android-chrome-192x192.png new file mode 100644 index 000000000..7ab6c3849 Binary files /dev/null and b/static/android-chrome-192x192.png differ diff --git a/static/android-chrome-256x256.png b/static/android-chrome-256x256.png new file mode 100644 index 000000000..ed88a2224 Binary files /dev/null and b/static/android-chrome-256x256.png differ diff --git a/static/android-chrome-36x36.png b/static/android-chrome-36x36.png new file mode 100644 index 000000000..3695eb088 Binary files /dev/null and b/static/android-chrome-36x36.png differ diff --git a/static/android-chrome-48x48.png b/static/android-chrome-48x48.png new file mode 100644 index 000000000..ca275dad6 Binary files /dev/null and b/static/android-chrome-48x48.png differ diff --git a/static/android-chrome-72x72.png b/static/android-chrome-72x72.png new file mode 100644 index 000000000..966891f25 Binary files /dev/null and b/static/android-chrome-72x72.png differ diff --git a/static/android-chrome-96x96.png b/static/android-chrome-96x96.png new file mode 100644 index 000000000..feb1d3ebf Binary files /dev/null and b/static/android-chrome-96x96.png differ diff --git a/static/apple-touch-icon.png b/static/apple-touch-icon.png new file mode 100644 index 000000000..ecf1fc020 Binary files /dev/null and b/static/apple-touch-icon.png differ diff --git a/static/browserconfig.xml b/static/browserconfig.xml new file mode 100644 index 000000000..62400c5f2 --- /dev/null +++ b/static/browserconfig.xml @@ -0,0 +1,10 @@ + + + + + + + #2d89ef + + + diff --git a/static/favicon-16x16.png b/static/favicon-16x16.png new file mode 100644 index 000000000..c62ce6fb2 Binary files /dev/null and b/static/favicon-16x16.png differ diff --git a/static/favicon-32x32.png b/static/favicon-32x32.png new file mode 100644 index 000000000..57a018e35 Binary files /dev/null and b/static/favicon-32x32.png differ diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 000000000..dc007a99e Binary files /dev/null and b/static/favicon.ico differ diff --git a/static/fonts/Mulish-Italic-VariableFont_wght.ttf b/static/fonts/Mulish-Italic-VariableFont_wght.ttf new file mode 100644 index 000000000..e5425c75e Binary files /dev/null and b/static/fonts/Mulish-Italic-VariableFont_wght.ttf differ diff --git a/static/fonts/Mulish-VariableFont_wght.ttf b/static/fonts/Mulish-VariableFont_wght.ttf new file mode 100644 index 000000000..410f7aa63 Binary files /dev/null and b/static/fonts/Mulish-VariableFont_wght.ttf differ diff --git a/static/images/gopher-hero.svg b/static/images/gopher-hero.svg new file mode 100644 index 000000000..36d9f1c41 --- /dev/null +++ b/static/images/gopher-hero.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/gopher-side_color.svg b/static/images/gopher-side_color.svg new file mode 100644 index 000000000..85f949783 --- /dev/null +++ b/static/images/gopher-side_color.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/hugo-logo-wide.svg b/static/images/hugo-logo-wide.svg new file mode 100644 index 000000000..1f6a79ea6 --- /dev/null +++ b/static/images/hugo-logo-wide.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/manifest.json b/static/manifest.json new file mode 100644 index 000000000..e671ac45a --- /dev/null +++ b/static/manifest.json @@ -0,0 +1,45 @@ +{ + "name": "Hugo", + "short_name": "Hugo", + "icons": [ + { + "src": "/android-chrome-36x36.png", + "sizes": "36x36", + "type": "image/png" + }, + { + "src": "/android-chrome-48x48.png", + "sizes": "48x48", + "type": "image/png" + }, + { + "src": "/android-chrome-72x72.png", + "sizes": "72x72", + "type": "image/png" + }, + { + "src": "/android-chrome-96x96.png", + "sizes": "96x96", + "type": "image/png" + }, + { + "src": "/android-chrome-144x144.png", + "sizes": "144x144", + "type": "image/png" + }, + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-256x256.png", + "sizes": "256x256", + "type": "image/png" + } + ], + "start_url": "./?utm_source=web_app_manifest", + "theme_color": "#0A1922", + "background_color": "#FFF", + "display": "standalone" +} diff --git a/static/mstile-144x144.png b/static/mstile-144x144.png new file mode 100644 index 000000000..e54b4bd75 Binary files /dev/null and b/static/mstile-144x144.png differ diff --git a/static/mstile-150x150.png b/static/mstile-150x150.png new file mode 100644 index 000000000..c7b84c690 Binary files /dev/null and b/static/mstile-150x150.png differ diff --git a/static/mstile-310x310.png b/static/mstile-310x310.png new file mode 100644 index 000000000..2cde5c08c Binary files /dev/null and b/static/mstile-310x310.png differ diff --git a/static/safari-pinned-tab.svg b/static/safari-pinned-tab.svg new file mode 100644 index 000000000..80ff2dae3 --- /dev/null +++ b/static/safari-pinned-tab.svg @@ -0,0 +1,22 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 000000000..9d3c29050 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1 @@ +/* Empty for now. */