diff --git a/ui/NOTICE b/ui/NOTICE index 96bdacb01..cbeebd036 100644 --- a/ui/NOTICE +++ b/ui/NOTICE @@ -3,6 +3,12 @@ This UI is a fork of the Antora Default UI Mozilla Public License 2.0 (see LICENSE). Modifications are (c) Stackable GmbH and remain under MPL-2.0. +Upstream commits are periodically ported into this fork with their +original authorship preserved in the git history. The upstream authors +are not signatories of the Stackable CLA; their code is included solely +under the terms of the MPL-2.0. See README.adoc, section "Provenance +and the Stackable CLA". + This directory ships with (and its build output bundles) the following third-party components. Build-time minification strips embedded license header comments; this file preserves the attributions and is included diff --git a/ui/README.adoc b/ui/README.adoc index 73dd0a09f..72bdd3dfb 100644 --- a/ui/README.adoc +++ b/ui/README.adoc @@ -92,6 +92,23 @@ Copyright (C) 2017-present OpenDevise Inc. and the Antora Project. Use of this software is granted under the terms of the https://www.mozilla.org/en-US/MPL/2.0/[Mozilla Public License Version 2.0] (MPL-2.0). See link:LICENSE[] to find the full license text. +=== Provenance and the Stackable CLA + +This directory is a fork of the MPL-2.0-licensed https://gitlab.com/antora/antora-ui-default[Antora Default UI], and upstream commits are periodically ported into it (see link:UPSTREAM[]). +Ported commits keep their original upstream authorship in the git history. + +The upstream authors have not signed the Stackable CLA and are not expected to. +Their code is included solely under the terms of the MPL-2.0 (see link:LICENSE[]), not under the CLA. +This is intentional and applies only to the contents of this directory. +As a practical consequence, the CLA check fails on pull requests that port upstream commits; such PRs are merged with an administrative override after review. + +To list the authors of ported code: + +[source,bash] +---- +git log --format='%an <%ae>' -- ui/ | sort -u +---- + == Authors Development of Antora is led and sponsored by {url-opendevise}[OpenDevise Inc]. diff --git a/ui/src/css/base.css b/ui/src/css/base.css index f3906f6f5..70b28f1ad 100644 --- a/ui/src/css/base.css +++ b/ui/src/css/base.css @@ -8,6 +8,7 @@ html { box-sizing: border-box; font-size: var(--body-font-size); height: 100%; + scroll-behavior: smooth; } @media screen and (min-width: 1024px) { @@ -22,6 +23,7 @@ body { font-family: var(--body-font-family); line-height: var(--body-line-height); margin: 0; + tab-size: 4; word-wrap: anywhere; /* aka overflow-wrap; used when hyphens are disabled or aren't sufficient */ } @@ -98,6 +100,15 @@ object[type="image/svg+xml"]:not([width]) { width: fit-content; } +.hide, +.is-hidden { + display: none !important; +} + +::placeholder { + opacity: 0.5; +} + @supports (scrollbar-width: thin) { body * { scrollbar-width: thin; diff --git a/ui/src/css/breadcrumbs.css b/ui/src/css/breadcrumbs.css index be1114028..4bd05a39c 100644 --- a/ui/src/css/breadcrumbs.css +++ b/ui/src/css/breadcrumbs.css @@ -2,7 +2,7 @@ display: none; flex: 1 1; padding: 0 0.5rem 0 0.75rem; - line-height: var(--nav-line-height); + line-height: 1.25; } @media screen and (min-width: 1024px) { diff --git a/ui/src/css/doc.css b/ui/src/css/doc.css index 77bd07b38..81babc982 100644 --- a/ui/src/css/doc.css +++ b/ui/src/css/doc.css @@ -194,7 +194,7 @@ } .doc .paragraph.lead > p { - font-size: calc(18 / var(--rem-base) * 1rem); + font-size: calc(19.5 / var(--rem-base) * 1rem); } .doc .right { @@ -205,10 +205,48 @@ float: left; } +.doc .float-gap.right { + margin: 0 1rem 1rem 0; +} + +.doc .float-gap.left { + margin: 0 0 1rem 1rem; +} + +.doc .float-group::after { + content: ""; + display: table; + clear: both; +} + +.doc .text-left { + text-align: left; +} + +.doc .text-center { + text-align: center; +} + +.doc .text-right { + text-align: right; +} + +.doc .text-justify { + text-align: justify; +} + .doc .stretch { width: 100%; } +.doc .big { + font-size: larger; +} + +.doc .small { + font-size: smaller; +} + .doc .underline { text-decoration: underline; } @@ -238,17 +276,25 @@ .doc table.tableblock { font-size: calc(15 / var(--rem-base) * 1rem); - margin: 1.5rem 0 0; } +.doc table.tableblock, .doc table.tableblock + * { margin-top: 1.5rem; } +.doc table.tableblock pre { + font-size: inherit; +} + .doc p.tableblock + p.tableblock { margin-top: 0.5rem; } +.doc td.tableblock > .content { + word-wrap: anywhere; /* aka overflow-wrap; used when hyphens are disabled or aren't sufficient */ +} + .doc td.tableblock > .content > :first-child { margin-top: 0; } @@ -378,55 +424,59 @@ word-wrap: anywhere; } -.doc .admonitionblock .icon { +.doc .admonitionblock td.icon { + font-size: calc(15 / var(--rem-base) * 1rem); + left: 0; + line-height: 1; + padding: 0; position: absolute; top: 0; - left: 0; - font-size: calc(15 / var(--rem-base) * 1rem); - padding: 0 0.5rem; + transform: translate(-0.5rem, -50%); +} + +.doc .admonitionblock td.icon i { + align-items: center; + border-radius: 0.45rem; + display: inline-flex; + filter: initial; height: 1.25rem; - line-height: 1; + padding: 0 0.5rem; + vertical-align: initial; + width: fit-content; +} + +.doc .admonitionblock td.icon i::after { + content: attr(title); font-weight: var(--admonition-label-font-weight); + font-style: normal; text-transform: uppercase; - border-radius: 0.45rem; - transform: translate(-0.5rem, -50%); } -.doc .admonitionblock.caution .icon { +.doc .admonitionblock td.icon i.icon-caution { background-color: var(--caution-color); color: var(--caution-on-color); } -.doc .admonitionblock.important .icon { +.doc .admonitionblock td.icon i.icon-important { background-color: var(--important-color); color: var(--important-on-color); } -.doc .admonitionblock.note .icon { +.doc .admonitionblock td.icon i.icon-note { background-color: var(--note-color); color: var(--note-on-color); } -.doc .admonitionblock.tip .icon { +.doc .admonitionblock td.icon i.icon-tip { background-color: var(--tip-color); color: var(--tip-on-color); } -.doc .admonitionblock.warning .icon { +.doc .admonitionblock td.icon i.icon-warning { background-color: var(--warning-color); color: var(--warning-on-color); } -.doc .admonitionblock .icon i { - display: inline-flex; - align-items: center; - height: 100%; -} - -.doc .admonitionblock .icon i::after { - content: attr(title); -} - .doc .imageblock, .doc .videoblock { display: flex; @@ -434,6 +484,29 @@ align-items: center; } +.doc .imageblock .content { + align-self: stretch; + text-align: center; +} + +.doc .imageblock.text-left, +.doc .videoblock.text-left { + align-items: flex-start; +} + +.doc .imageblock.text-left .content { + text-align: left; +} + +.doc .imageblock.text-right, +.doc .videoblock.text-right { + align-items: flex-end; +} + +.doc .imageblock.text-right .content { + text-align: right; +} + .doc .imageblock img, .doc .imageblock object, .doc .imageblock svg, @@ -529,17 +602,23 @@ } .doc ul.circle { - list-style-type: square; + list-style-type: circle; } .doc ul.disc { - list-style-type: square; + list-style-type: disc; } .doc ul.square { list-style-type: square; } +.doc ul.circle ul:not([class]), +.doc ul.disc ul:not([class]), +.doc ul.square ul:not([class]) { + list-style: inherit; +} + .doc ol.arabic { list-style-type: decimal; } @@ -569,14 +648,14 @@ } .doc ul.checklist { - padding-left: 0.5rem; + padding-left: 1rem; } .doc ul.checklist p > i.fa-check-square-o:first-child, .doc ul.checklist p > i.fa-square-o:first-child { display: inline-flex; justify-content: center; - width: 1.25rem; + width: 1em; } .doc ul.checklist i.fa-check-square-o::before { @@ -718,10 +797,11 @@ font-size: calc(22.5 / var(--rem-base) * 1rem); font-weight: var(--alt-heading-font-weight); line-height: 1.3; - margin-bottom: -0.3em; + margin-bottom: 0.5rem; text-align: center; } +.doc .sidebarblock > .content > .title + *, .doc .sidebarblock > .content > :not(.title):first-child { margin-top: 0; } @@ -732,17 +812,17 @@ white-space: pre-wrap; } -.doc pre.highlight code, +.doc pre.highlight > code, .doc .listingblock pre:not(.highlight), .doc .literalblock pre { background: var(--pre-background); box-shadow: inset 0 0 1.75px var(--pre-border-color); display: block; overflow-x: auto; - padding: 0.75rem; + padding: 0.875em; } -.doc pre.highlight { +.doc .listingblock > .content { position: relative; } @@ -754,8 +834,11 @@ right: 0.5rem; color: var(--pre-annotation-font-color); font-family: var(--body-font-family); - font-size: calc(13.5 / var(--rem-base) * 1rem); + font-size: calc(13 / var(--rem-base) * 1rem); line-height: 1; + user-select: none; + white-space: nowrap; + z-index: 1; } .doc .listingblock:hover .source-toolbox { @@ -765,8 +848,6 @@ .doc .source-toolbox .source-lang { text-transform: uppercase; letter-spacing: 0.075em; - font-size: 0.96em; - line-height: 1.0425; } .doc .source-toolbox > :not(:last-child)::after { @@ -779,7 +860,7 @@ display: flex; flex-direction: column; align-items: center; - background: transparent; + background: none; border: none; color: inherit; outline: none; @@ -816,7 +897,7 @@ color: var(--color-white); cursor: auto; opacity: 0; - transition: opacity 0.5s ease 0.75s; + transition: opacity 0.5s ease 0.5s; } .doc .source-toolbox .copy-toast::after { @@ -845,11 +926,12 @@ } .doc .dlist dd { - margin: 0 0 0.25rem 1.5rem; + margin: 0 0 0 1.5rem; } -.doc .dlist dd:last-of-type { - margin-bottom: 0; +.doc .dlist dd + dt, +.doc .dlist dd > p:first-child { + margin-top: 0.5rem; } .doc td.hdlist1, @@ -985,6 +1067,10 @@ word-wrap: normal; } +.doc :not(pre).pre-wrap { + white-space: pre-wrap; +} + #footnotes { font-size: 0.85em; line-height: 1.5; diff --git a/ui/src/css/nav.css b/ui/src/css/nav.css index 12e1f0b4c..d13239484 100644 --- a/ui/src/css/nav.css +++ b/ui/src/css/nav.css @@ -61,6 +61,10 @@ } } +.nav a { + color: inherit; +} + .nav .panels { display: flex; flex-direction: column; @@ -112,11 +116,8 @@ html.is-clipped--nav { padding: 0.25em 0 0.125em; } -.nav-menu a { - color: inherit; -} - .nav-list { + list-style: none; margin: 0 0 0 0.75rem; padding: 0; } @@ -126,7 +127,6 @@ html.is-clipped--nav { } .nav-item { - list-style: none; margin-top: 0.5em; } @@ -150,6 +150,7 @@ html.is-clipped--nav { border: none; outline: none; line-height: inherit; + padding: 0; position: absolute; height: calc(var(--nav-line-height) * 1em); width: calc(var(--nav-line-height) * 1em); @@ -193,11 +194,14 @@ html.is-clipped--nav { display: flex; align-items: center; justify-content: space-between; - cursor: pointer; line-height: 1; height: var(--drawer-height); } +.nav-panel-explore:not(:first-child) .context { + cursor: pointer; +} + .nav-panel-explore .context .version { display: flex; align-items: inherit; @@ -211,13 +215,14 @@ html.is-clipped--nav { } .nav-panel-explore .components { - line-height: var(--doc-line-height); + line-height: var(--nav-line-height); flex-grow: 1; box-shadow: inset 0 1px 5px var(--nav-panel-divider-color); background: var(--nav-secondary-background); - padding: 0.5rem 0.75rem 0 0.75rem; + padding: 0.75rem 0.75rem 0 0.75rem; margin: 0; overflow-y: scroll; + overscroll-behavior: none; max-height: 100%; display: block; } @@ -231,7 +236,7 @@ html.is-clipped--nav { } .nav-panel-explore .component + .component { - margin-top: 0.5rem; + margin-top: 0.75rem; } .nav-panel-explore .component:last-child { @@ -240,37 +245,36 @@ html.is-clipped--nav { .nav-panel-explore .component .title { font-weight: var(--body-font-weight-bold); - color: inherit; + text-indent: 0.375rem hanging; } .nav-panel-explore .versions { display: flex; flex-wrap: wrap; - list-style: none; padding-left: 0; - margin-top: -0.25rem; + margin: -0.125rem -0.375rem 0 0.375rem; line-height: 1; + list-style: none; } .nav-panel-explore .component .version { - display: block; margin: 0.375rem 0.375rem 0 0; } .nav-panel-explore .component .version a { - border: 1px solid var(--nav-border-color); + background: var(--nav-border-color); border-radius: 0.25rem; - color: inherit; - opacity: 0.75; white-space: nowrap; - padding: 0.125em 0.25em; + padding: 0.25em 0.5em; display: inherit; + opacity: 0.75; } .nav-panel-explore .component .is-current a { - border-color: currentColor; - opacity: 0.9; + background: var(--nav-heading-font-color); + color: var(--nav-secondary-background); font-weight: var(--body-font-weight-bold); + opacity: 1; } .nav-menu .title-wrapper { diff --git a/ui/src/css/print.css b/ui/src/css/print.css index 0df133e01..62fc6b48c 100644 --- a/ui/src/css/print.css +++ b/ui/src/css/print.css @@ -67,6 +67,10 @@ padding-bottom: 2rem; } + .doc .admonitionblock td.icon { + color-adjust: exact; + } + .doc .listingblock code[data-lang]::before { display: block; } diff --git a/ui/src/css/toc.css b/ui/src/css/toc.css index 62f36d767..01a77c2aa 100644 --- a/ui/src/css/toc.css +++ b/ui/src/css/toc.css @@ -91,7 +91,3 @@ .sidebar.toc .toc-menu a:focus { background: var(--panel-background); } - -.toc .toc-menu .is-hidden-toc { - display: none !important; -} diff --git a/ui/src/helpers/detag.js b/ui/src/helpers/detag.js index e32f14766..e6e70e249 100644 --- a/ui/src/helpers/detag.js +++ b/ui/src/helpers/detag.js @@ -1,5 +1,7 @@ 'use strict' const TAG_ALL_RX = /<[^>]+>/g +const QUOT_RX = /"/g -module.exports = (html) => html && html.replace(TAG_ALL_RX, '') +module.exports = (html, { hash: { attribute } }) => + html && (attribute ? html.replace(TAG_ALL_RX, '').replace(QUOT_RX, '"') : html.replace(TAG_ALL_RX, '')) diff --git a/ui/src/helpers/relativize.js b/ui/src/helpers/relativize.js index 6fdfb45e6..798931915 100644 --- a/ui/src/helpers/relativize.js +++ b/ui/src/helpers/relativize.js @@ -4,19 +4,19 @@ const { posix: path } = require('path') module.exports = (to, from, ctx) => { if (!to) return '#' + if (to.charAt() !== '/') return to // NOTE only legacy invocation provides both to and from if (!ctx) from = (ctx = from).data.root.page.url - if (to.charAt() !== '/') return to if (!from) return (ctx.data.root.site.path || '') + to let hash = '' const hashIdx = to.indexOf('#') if (~hashIdx) { - hash = to.substr(hashIdx) - to = to.substr(0, hashIdx) + hash = to.slice(hashIdx) + to = to.slice(0, hashIdx) } - return to === from - ? hash || (isDir(to) ? './' : path.basename(to)) - : (path.relative(path.dirname(from + '.'), to) || '.') + (isDir(to) ? '/' + hash : hash) + if (to === from) return hash || (isDir(to) ? './' : path.basename(to)) + const rel = path.relative(path.dirname(from + '.'), to) + return rel ? (isDir(to) ? rel + '/' : rel) + hash : (isDir(to) ? './' : '../' + path.basename(to)) + hash } function isDir (str) { diff --git a/ui/src/js/02-on-this-page.js b/ui/src/js/02-on-this-page.js index 035bea456..3271eacae 100644 --- a/ui/src/js/02-on-this-page.js +++ b/ui/src/js/02-on-this-page.js @@ -14,7 +14,7 @@ var headingSelector = [articleSelector] if (level) { for (var l = 1; l <= level; l++) headingSelector.push((l === 2 ? '.sectionbody>' : '') + '.sect' + l) - headingSelector.push('h' + (level + 1) + '[id]') + headingSelector.push('h' + (level + 1) + '[id]' + (level > 1 ? ':not(.discrete)' : '')) } else { headingSelector.push('h1[id].sect0') } diff --git a/ui/src/js/03-fragment-jumper.js b/ui/src/js/03-fragment-jumper.js index 13f687a17..940857b0a 100644 --- a/ui/src/js/03-fragment-jumper.js +++ b/ui/src/js/03-fragment-jumper.js @@ -3,6 +3,7 @@ var article = document.querySelector('article.doc') var toolbar = document.querySelector('.toolbar') + var supportsScrollToOptions = 'scrollTo' in document.documentElement function decodeFragment (hash) { return hash && (~hash.indexOf('%') ? decodeURIComponent(hash) : hash).slice(1) @@ -18,14 +19,16 @@ window.location.hash = '#' + this.id e.preventDefault() } - window.scrollTo(0, computePosition(this, 0) - toolbar.getBoundingClientRect().bottom) + var y = computePosition(this, 0) - toolbar.getBoundingClientRect().bottom + var instant = e === false && supportsScrollToOptions + instant ? window.scrollTo({ left: 0, top: y, behavior: 'instant' }) : window.scrollTo(0, y) } window.addEventListener('load', function jumpOnLoad () { var fragment, target if ((fragment = decodeFragment(window.location.hash)) && (target = document.getElementById(fragment))) { - jumpToAnchor.bind(target)() - setTimeout(jumpToAnchor.bind(target), 0) + jumpToAnchor.call(target, false) + setTimeout(jumpToAnchor.bind(target, false), 250) } window.removeEventListener('load', jumpOnLoad) }) diff --git a/ui/src/js/06-copy-to-clipboard.js b/ui/src/js/06-copy-to-clipboard.js index 9fc24394d..03096f159 100644 --- a/ui/src/js/06-copy-to-clipboard.js +++ b/ui/src/js/06-copy-to-clipboard.js @@ -21,7 +21,7 @@ pre.classList.add('highlightjs', 'highlight') ;(code = document.createElement('code')).className = 'language-console hljs' code.dataset.lang = 'console' - code.appendChild(pre.firstChild) + while (pre.hasChildNodes()) code.appendChild(pre.firstChild) pre.appendChild(code) } else { return @@ -50,7 +50,7 @@ copy.appendChild(toast) toolbox.appendChild(copy) } - pre.appendChild(toolbox) + pre.parentNode.appendChild(toolbox) if (copy) copy.addEventListener('click', writeToClipboard.bind(copy, code)) }) diff --git a/ui/src/js/07-breadcrumbs.js b/ui/src/js/07-breadcrumbs.js new file mode 100644 index 000000000..1e5b80d31 --- /dev/null +++ b/ui/src/js/07-breadcrumbs.js @@ -0,0 +1,38 @@ +;(function () { + 'use strict' + + function truncate (toolbar, breadcrumbs, truncationLength) { + if (toolbar.scrollHeight === toolbar.getBoundingClientRect().height) return + breadcrumbs = breadcrumbs.slice(1, -1).reverse() + while (breadcrumbs.length && toolbar.scrollHeight > toolbar.getBoundingClientRect().height) { + var breadcrumb = breadcrumbs.pop() + var text = (breadcrumb = breadcrumb.querySelector('a') || breadcrumb).innerText.trim() + if (text.length > truncationLength) { + breadcrumb.setAttribute('title', text) + var offset = truncationLength - 3 + var truncatedText = text.slice(0, offset) + (text.charAt(offset) === ' ' ? ' ' : '') + '...' + breadcrumb.innerText = truncatedText + } + } + } + + function onResize (toolbar, breadcrumbs, truncationLength) { + breadcrumbs.forEach(function (breadcrumb) { + if (!(breadcrumb = breadcrumb.querySelector('a') || breadcrumb).hasAttribute('title')) return + breadcrumb.innerText = breadcrumb.getAttribute('title') + breadcrumb.removeAttribute('title') + }) + truncate(toolbar, breadcrumbs, truncationLength) + } + + ;(function () { + var toolbar = document.querySelector('.toolbar') + if (!toolbar) return + var breadcrumbs = Array.prototype.slice.call(toolbar.querySelectorAll('.breadcrumbs li') || []) + if (breadcrumbs.length < 3) return + var truncationLength = parseInt(toolbar.querySelector('.breadcrumbs').dataset.truncationLength || 15, 10) + if (truncationLength < 0) return + truncate(toolbar, breadcrumbs, truncationLength) + window.addEventListener('resize', onResize.bind(null, toolbar, breadcrumbs, truncationLength)) + })() +})() diff --git a/ui/src/js/07-search.js b/ui/src/js/08-search.js similarity index 100% rename from ui/src/js/07-search.js rename to ui/src/js/08-search.js diff --git a/ui/src/partials/article-404.hbs b/ui/src/partials/article-404.hbs index 436b28011..e88599594 100644 --- a/ui/src/partials/article-404.hbs +++ b/ui/src/partials/article-404.hbs @@ -1,7 +1,7 @@

{{{or page.title 'Page Not Found'}}}

-

The page you’re looking for does not exist. It may have been moved. You can{{#with site.homeUrl}} return to the start page, or{{/with}} follow one of the links in the navigation to the left.

+

The page you’re looking for does not exist. It may have been moved. You can{{#with site.homeUrl}} return to the start page, or{{/with}} follow one of the links in the navigation to the left.

If you arrived on this page by clicking on a link, please notify the owner of the site that the link is broken. diff --git a/ui/src/partials/footer-scripts.hbs b/ui/src/partials/footer-scripts.hbs index b22e1a3c9..171536393 100644 --- a/ui/src/partials/footer-scripts.hbs +++ b/ui/src/partials/footer-scripts.hbs @@ -2,7 +2,7 @@