From 9be85d17198cb72155c416a4a4dcf31d7c0940b5 Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Tue, 26 Oct 2021 13:00:47 +0200 Subject: [PATCH 01/15] POC --- docs/_layouts/doc-page.html | 54 +++- .../resources/dotty_res/styles/scalastyle.css | 281 +++++++++++++++++- 2 files changed, 330 insertions(+), 5 deletions(-) diff --git a/docs/_layouts/doc-page.html b/docs/_layouts/doc-page.html index 24e69741da00..989ca8622d69 100644 --- a/docs/_layouts/doc-page.html +++ b/docs/_layouts/doc-page.html @@ -2,8 +2,54 @@ layout: main ---
-
-

{{ page.title }}

-
- {{ content }} + +
+

{{ page.title }}

+
+ {{ content }} +
+ diff --git a/scaladoc/resources/dotty_res/styles/scalastyle.css b/scaladoc/resources/dotty_res/styles/scalastyle.css index 747bf7cd5e8b..3a27034be3ab 100644 --- a/scaladoc/resources/dotty_res/styles/scalastyle.css +++ b/scaladoc/resources/dotty_res/styles/scalastyle.css @@ -1035,4 +1035,283 @@ footer .socials { align-self: flex-start; } - +.navigation { + padding: 30px 0; + display: -webkit-box; + display: -moz-box; + display: box; + display: -webkit-flex; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -moz-box-orient: horizontal; + box-orient: horizontal; + -webkit-box-direction: normal; + -moz-box-direction: normal; + box-direction: normal; + -webkit-flex-direction: row; + -moz-flex-direction: row; + flex-direction: row; + -ms-flex-direction: row; + -webkit-box-align: center; + -moz-box-align: center; + box-align: center; + -webkit-align-items: center; + -moz-align-items: center; + -ms-align-items: center; + -o-align-items: center; + align-items: center; + -ms-flex-align: center; + -webkit-box-pack: justify; + -moz-box-pack: justify; + box-pack: justify; + -webkit-justify-content: space-between; + -moz-justify-content: space-between; + -ms-justify-content: space-between; + -o-justify-content: space-between; + justify-content: space-between; + -ms-flex-pack: justify; } + .navigation .navigation-bdand img { + width: 104px; + height: 43px; } + .navigation .navigation-panel-button { + display: none; + font-size: 1.333rem; + color: #fff; + cursor: pointer; } + @media (max-width: 992px) { + .navigation .navigation-panel-button { + order: 3; + display: block; } } + .navigation .navigation-menu .navigation-menu-item { + display: inline-block; } + .navigation .navigation-menu .navigation-menu-item:last-child { + margin-right: 0; } + .navigation .navigation-menu .navigation-menu-item a { + padding: 5px 15px; + text-transform: uppercase; + color: #fff; + border-radius: 300px; + font-weight: 700; } + .navigation .navigation-menu .navigation-menu-item a:active, .navigation .navigation-menu .navigation-menu-item a:focus, .navigation .navigation-menu .navigation-menu-item a:hover, .navigation .navigation-menu .navigation-menu-item a.active { + background: #DC322F; + text-decoration: none; } + +@media (max-width: 992px) { + .navigation .navigation-menu { + padding: 20px; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: auto; + height: 100%; + width: 270px; + -webkit-transform: translateX(270px); + -moz-transform: translateX(270px); + -ms-transform: translateX(270px); + -o-transform: translateX(270px); + transform: translateX(270px); + -webkit-transition: all 0.25s linear; + -moz-transition: all 0.25s linear; + transition: all 0.25s linear; + background: #fff; + -webkit-overflow-scrolling: touch; + overflow-y: auto; + z-index: 100; + background: rgba(0, 43, 54, 0.99); } + .navigation .navigation-menu.is-visible { + -webkit-transform: translateX(0); + -moz-transform: translateX(0); + -ms-transform: translateX(0); + -o-transform: translateX(0); + transform: translateX(0); } + .navigation .navigation-menu .navigation-menu-item { + margin-right: 16px; + padding: 10px 0; + display: block; } } +.navigation-fade-screen { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + -webkit-transition: all 0.15s ease-out 0s; + -moz-transition: all 0.15s ease-out 0s; + transition: all 0.15s ease-out 0s; + background: #000; + opacity: 0; + visibility: hidden; + z-index: 90; } + .navigation-fade-screen.is-visible { + opacity: 0.6; + visibility: visible; } + +.doc-navigation { + padding: 10px 20px; + display: -webkit-box; + display: -moz-box; + display: box; + display: -webkit-flex; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -moz-box-orient: horizontal; + box-orient: horizontal; + -webkit-box-direction: normal; + -moz-box-direction: normal; + box-direction: normal; + -webkit-flex-direction: row; + -moz-flex-direction: row; + flex-direction: row; + -ms-flex-direction: row; + -webkit-box-align: center; + -moz-box-align: center; + box-align: center; + -webkit-align-items: center; + -moz-align-items: center; + -ms-align-items: center; + -o-align-items: center; + align-items: center; + -ms-flex-align: center; + -webkit-box-pack: justify; + -moz-box-pack: justify; + box-pack: justify; + -webkit-justify-content: space-between; + -moz-justify-content: space-between; + -ms-justify-content: space-between; + -o-justify-content: space-between; + justify-content: space-between; + -ms-flex-pack: justify; } + .doc-navigation .navigation-bdand img { + width: 58px; + height: 20px; } + @media (max-width: 992px) { + .doc-navigation .navigation-bdand img { + display: none; } } + .doc-navigation .navigation-bdand .doc-language-version { + font-size: 1.6em; + font-family: "Roboto Slab", serif; + font-weight: bold; + color: #86a1a6; } + @media (max-width: 992px) { + .doc-navigation .navigation-bdand .doc-language-version { + display: none; } } + .doc-navigation .navigation-bdand a, + .doc-navigation .navigation-bdand a:hover, + .doc-navigation .navigation-bdand a:active, + .doc-navigation .navigation-bdand a:focus, + .doc-navigation .navigation-bdand a:hover, + .doc-navigation .navigation-bdand a.active { + text-decoration: none; } + .doc-navigation .navigation-ellipsis { + display: none; + font-size: 1.333rem; + cursor: pointer; } + @media (max-width: 768px) { + .doc-navigation .navigation-ellipsis { + color: rgba(255, 255, 255, 0.5); + order: 3; + display: block; } + .doc-navigation .navigation-ellipsis a:active, .doc-navigation .navigation-ellipsis a:hover { + color: #ffffff; } } + .doc-navigation .navigation-menu .navigation-menu-item { + display: inline-block; } + .doc-navigation .navigation-menu .navigation-menu-item .navigation-dropdown { + background: #15414C; + min-width: 190px; + position: absolute; + margin-top: 10px; + display: none; + z-index: 1; + box-shadow: 0 3px 12px rgba(0, 0, 0, 0.15); } + @media (max-width: 768px) { + .doc-navigation .navigation-menu .navigation-menu-item .navigation-dropdown { + order: 3; } } + .doc-navigation .navigation-menu .navigation-menu-item .navigation-dropdown li { + line-height: 46px; } + .doc-navigation .navigation-menu .navigation-menu-item .navigation-dropdown li:hover { + background: #002B36; + text-decoration: none; } + .doc-navigation .navigation-menu .navigation-menu-item .navigation-dropdown li:hover a { + color: #ffffff; } + .doc-navigation .navigation-menu .navigation-menu-item .navigation-dropdown li:hover a:hover { + text-decoration: none; } + @media (max-width: 768px) { + .doc-navigation .navigation-menu .navigation-menu-item:nth-child(n+4) { + display: none; } } + .doc-navigation .navigation-menu .navigation-menu-item:last-child { + margin-right: 0; } + .doc-navigation .navigation-menu .navigation-menu-item a { + display: block; + padding: 5px 15px; + color: rgba(255, 255, 255, 0.5); + font-weight: 700; } + .doc-navigation .navigation-menu .navigation-menu-item a:focus, .doc-navigation .navigation-menu .navigation-menu-item a.active { + color: #ffffff; + text-decoration: none; } + .doc-navigation .navigation-menu .navigation-menu-item a:hover { + text-decoration: none; } + .doc-navigation .navigation-menu .navigation-menu-item a:not(:only-child):after { + padding-left: 4px; + content: ' ▾'; } + +.navigation-submenu li { + text-align: center; + line-height: 46px; } + .navigation-submenu li:hover { + background: #002B36; + text-decoration: none; } + .navigation-submenu li:hover a { + color: #ffffff; } + .navigation-submenu li a { + padding-left: 20px; + display: block; + color: rgba(255, 255, 255, 0.5); + font-weight: 700; } + .navigation-submenu li a:hover { + text-decoration: none; } + + .two-columns { + display: -webkit-box; + display: -moz-box; + display: box; + display: -webkit-flex; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -moz-box-orient: horizontal; + box-orient: horizontal; + -webkit-box-direction: normal; + -moz-box-direction: normal; + box-direction: normal; + -webkit-flex-direction: row; + -moz-flex-direction: row; + flex-direction: row; + -ms-flex-direction: row; + -webkit-box-lines: multiple; + -moz-box-lines: multiple; + box-lines: multiple; + -webkit-flex-wrap: wrap; + -moz-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -webkit-box-pack: justify; + -moz-box-pack: justify; + box-pack: justify; + -webkit-justify-content: space-between; + -moz-justify-content: space-between; + -ms-justify-content: space-between; + -o-justify-content: space-between; + justify-content: space-between; + -ms-flex-pack: justify; } + .two-columns .first, + .two-columns .second { + flex: 0 1 calc(50% - 1em); } + @media (max-width: 768px) { + .two-columns .first, + .two-columns .second { + flex: 0 1 calc(100% - 0.5em); } } From c233e2f32c3dbc3f45f695d3d405b620db03250c Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Thu, 28 Oct 2021 17:30:34 +0200 Subject: [PATCH 02/15] Walk in order and collect previous/next --- docs/_layouts/blog-page.html | 6 ++- docs/_layouts/doc-page.html | 47 ++----------------- docs/_layouts/main.html | 29 ++++++++++++ project/Build.scala | 3 +- project/CopyDocs.scala | 2 +- .../src/dotty/tools/scaladoc/Scaladoc.scala | 2 + .../tools/scaladoc/ScaladocSettings.scala | 9 ++++ .../tools/scaladoc/renderers/Renderer.scala | 45 +++++++++++++++++- .../scaladoc/site/StaticSiteContext.scala | 6 ++- 9 files changed, 100 insertions(+), 49 deletions(-) diff --git a/docs/_layouts/blog-page.html b/docs/_layouts/blog-page.html index 6baad3d40ed8..1dfd45b670d5 100644 --- a/docs/_layouts/blog-page.html +++ b/docs/_layouts/blog-page.html @@ -1,7 +1,7 @@ --- layout: main --- -
+

{{ page.title }}

{% endif %} +
diff --git a/docs/_layouts/doc-page.html b/docs/_layouts/doc-page.html index 989ca8622d69..628daec6c050 100644 --- a/docs/_layouts/doc-page.html +++ b/docs/_layouts/doc-page.html @@ -1,55 +1,14 @@ --- layout: main --- -
- +

{{ page.title }}

{{ content }}
diff --git a/docs/_layouts/main.html b/docs/_layouts/main.html index e265ac1bed16..8f529fe1c602 100644 --- a/docs/_layouts/main.html +++ b/docs/_layouts/main.html @@ -2,12 +2,41 @@ layout: base ---
+
+ + {% if page.movedTo %} {% endif %} {{ content }} +
+ + +

Redirecting…

+ Click here if you were not redirected. + diff --git a/docs/docs/reference/experimental/named-typeargs.md b/docs/docs/reference/experimental/named-typeargs.md index 0abdd9d40e29..5e80a755293c 100644 --- a/docs/docs/reference/experimental/named-typeargs.md +++ b/docs/docs/reference/experimental/named-typeargs.md @@ -2,6 +2,7 @@ layout: singlepage-overview scala3: true title: "Named Type Arguments" +redirectFrom: reference/other-new-features/named-typeargs.html --- **Note:** This feature is implemented in Scala 3, but is not expected to be part of Scala 3.0. diff --git a/docs/docs/reference/other-new-features/named-typeargs.md b/docs/docs/reference/other-new-features/named-typeargs.md deleted file mode 100644 index 98009d4f3b7d..000000000000 --- a/docs/docs/reference/other-new-features/named-typeargs.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: singlepage-overview -scala3: true -title: "Named Type Arguments" ---- - -[Document was moved](../experimental/named-typeargs.md) diff --git a/project/CopyDocs.scala b/project/CopyDocs.scala index 17f4e1c58f50..4a09be364c95 100644 --- a/project/CopyDocs.scala +++ b/project/CopyDocs.scala @@ -22,7 +22,8 @@ object CopyDocs { implicit def stringToFun(s: String): MyParams => String = _ => s // Patterns, for convenience - val titlePattern = "(?s)^---\n.*?title: ([^\n]*).*?---" + val titlePattern = """(?s)^(---\n.*?title: ([^\n]*).*?---)""" + val redirectFromPattern = """(?s)^---.*?((redirectFrom: ([^\n]*)).*?---|---)""" val jekyllLinkPattern = """\{\% link _overviews/scala3-reference(.*) %\}""" val jekyllLinkSubstitution = "..$1" val jekyllLinkPattern2 = """\{\% link _overviews/scala3-scaladoc(.*) %\}""" @@ -32,7 +33,7 @@ object CopyDocs { case class MyParams(newPath: String) - val commonTransformations: Set[(String, MyParams => String)] = Set( + val commonTransformations: List[(String, MyParams => String)] = List( jekyllLinkPattern -> jekyllLinkSubstitution, jekyllLinkPattern2 -> jekyllLinkSubstitution2, localLinkPattern -> localLinkSubstitution, @@ -40,33 +41,35 @@ object CopyDocs { /** * Structure for holding which transformations should be applied to which directories. - * The outer map is holding morphism `directory prefix` -> `set of transformations`. - * The inner set is a collection of pairs `regex pattern` -> `substitution value`. + * The outer map is holding morphism `directory prefix` -> `List of transformations`. + * The inner list is a collection of pairs `regex pattern` -> `substitution value`. */ - val transformationMap: Map[String, Set[(String, MyParams => String)]] = Map( - "docs/docs/usage/scaladoc/index.md" -> Set( + val transformationMap: Map[String, List[(String, MyParams => String)]] = Map( + "docs/docs/usage/scaladoc/index.md" -> List( ("""\{\{ site\.baseurl \}\}/resources/images/scala3/scaladoc/logo\.svg""" -> "images/scaladoc_logo.svg"), ), - "docs/docs/usage/scaladoc/site-versioning.md" -> Set( + "docs/docs/usage/scaladoc/site-versioning.md" -> List( ("""/resources/images/scala3/scaladoc/nightly\.gif""" -> "images/scaladoc/nightly.gif"), ), - "docs/docs/usage/scaladoc/search-engine.md" -> Set( + "docs/docs/usage/scaladoc/search-engine.md" -> List( ("""/resources/images/scala3/scaladoc/inkuire-1\.0\.0-M2_js_flatMap\.gif""" -> "images/scaladoc/inkuire-1.0.0-M2_js_flatMap.gif"), ), - "docs/docs/reference/other-new-features/explicit-nulls.md" -> Set( + "docs/docs/reference/other-new-features/explicit-nulls.md" -> List( ("""/resources/images/scala3/explicit-nulls/explicit-nulls-type-hierarchy\.png""" -> "images/explicit-nulls/explicit-nulls-type-hierarchy.png"), ), - "docs/docs/reference/" -> (commonTransformations + - (titlePattern -> ((p) => s"---\nlayout: doc-page\ntitle: $$1\nmovedTo: https://docs.scala-lang.org/scala3/reference/${p.newPath}.html\n---")), - ), + "docs/docs/reference/" -> (commonTransformations ++ List[(String, MyParams => String)]( + (titlePattern -> ((p) => s"$$1\nlayout: doc-page\ntitle: $$2\nmovedTo: https://docs.scala-lang.org/scala3/reference/${p.newPath}.html\n---")), + (redirectFromPattern -> "---\n$2") + )), - "docs/docs/usage/scaladoc/" -> (commonTransformations + - (titlePattern -> s"---\nlayout: doc-page\ntitle: $$1\n---"), - ), + "docs/docs/usage/scaladoc/" -> (commonTransformations ++ List[(String, MyParams => String)]( + (titlePattern -> s"$$1\nlayout: doc-page\ntitle: $$2\n---"), + (redirectFromPattern -> "---\n$2") + )), ) def copyDocs() = { diff --git a/scaladoc/resources/dotty_res/styles/scalastyle.css b/scaladoc/resources/dotty_res/styles/scalastyle.css index a35d9f725870..0c639a324f24 100644 --- a/scaladoc/resources/dotty_res/styles/scalastyle.css +++ b/scaladoc/resources/dotty_res/styles/scalastyle.css @@ -1066,10 +1066,9 @@ footer .socials { /* Nav Icons */ -.nav-chapters { - font-size: 2.5em; +.arrows { + font-size: 3em; text-align: center; - text-decoration: none; position: fixed; top: 0; @@ -1080,53 +1079,32 @@ footer .socials { display: flex; justify-content: center; - align-content: center; flex-direction: column; transition: color 0.5s, background-color 0.5s; } -.nav-chapters.previous, .nav-chapters.next { +.arrows.previous, .arrows.next { color: var(--grey400); } -.nav-chapters:hover { +.arrows:hover { text-decoration: none; color: var(--grey300); background-color: var(--grey900); transition: background-color 0.15s, color 0.15s; } -.nav-wrapper { - margin-top: 50px; - display: none; -} - -.mobile-nav-chapters { - font-size: 2.5em; - text-align: center; - text-decoration: none; - width: 90px; - border-radius: 5px; - background-color: var(--sidebar-bg); -} - .previous { - float: left; left: var(--side-width); + float: left; } .next { - float: right; right: 0; + float: right; } -@media only screen and (max-width: 1080px) { - .nav-wide-wrapper { display: none; } - .nav-wrapper { display: block; } -} - -@media only screen and (max-width: 1380px) { - .sidebar-visible .nav-wide-wrapper { display: none; } - .sidebar-visible .nav-wrapper { display: block; } +@media screen and (max-width: 1000px) { + .arrows-wrapper { display: none; } } diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala index 41db1bd2a356..37ca1dd6d233 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala @@ -116,6 +116,8 @@ abstract class Renderer(rootPackage: Member, val members: Map[DRI, Member], prot (siteContext.orphanedTemplates ++ actualIndexTemplate).map(templateToPage(_, siteContext)) + val redirectPages: Seq[Page] = staticSite.map(siteContext => siteContext.redirectTemplates.map(templateToPage(_, siteContext))).get + /** * Here we have to retrive index pages from hidden pages and replace fake index pages in navigable page tree. */ @@ -132,7 +134,7 @@ abstract class Renderer(rootPackage: Member, val members: Map[DRI, Member], prot val (newNavigablePage, pagesToRemove) = traversePages(navigablePage) - val all = newNavigablePage +: hiddenPages.filterNot(pagesToRemove.contains) + val all = newNavigablePage +: (hiddenPages.filterNot(pagesToRemove.contains) ++ redirectPages) // We need to check for conflicts only if we have top-level member called blog or docs val hasPotentialConflict = rootPackage.members.exists(m => m.name.startsWith("docs") || m.name.startsWith("blog")) diff --git a/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteContext.scala b/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteContext.scala index dd8cb1de8709..672efe6e97d5 100644 --- a/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteContext.scala +++ b/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteContext.scala @@ -71,6 +71,19 @@ class StaticSiteContext( orphanedFiles.flatMap(p => loadTemplate(p.toFile, isBlog = false)) } + lazy val redirectTemplates: Seq[LoadedTemplate] = { + def doFlatten(t: LoadedTemplate): Seq[LoadedTemplate] = + t +: t.children.flatMap(doFlatten) + val mainFiles = templates.flatMap(doFlatten) + mainFiles.flatMap { loadedTemplate => + loadedTemplate.templateFile.settings.apply("page").asInstanceOf[Map[String, Object]].get("redirectFrom").map { case redirectFrom: String => + val fakeFile = new File(docsPath.toFile, redirectFrom) + val redirectTo = fakeFile.toPath.getParent.relativize(loadedTemplate.file.toPath).toString.stripSuffix(".md") + ".html" + LoadedTemplate(layouts("redirectFrom").copy(settings = layouts("redirectFrom").settings ++ Map("redirectTo" -> redirectTo)), List.empty, fakeFile) + } + } + } + val docsPath = root.toPath.resolve("docs") private def isValidTemplate(file: File): Boolean = From 104efd946ae29b7a2c1dbf1ebb28fd5b30132421 Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Mon, 15 Nov 2021 14:04:42 +0100 Subject: [PATCH 08/15] Fix leftover --- scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala index 37ca1dd6d233..0ff4a29ecff1 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala @@ -116,7 +116,7 @@ abstract class Renderer(rootPackage: Member, val members: Map[DRI, Member], prot (siteContext.orphanedTemplates ++ actualIndexTemplate).map(templateToPage(_, siteContext)) - val redirectPages: Seq[Page] = staticSite.map(siteContext => siteContext.redirectTemplates.map(templateToPage(_, siteContext))).get + val redirectPages: Seq[Page] = staticSite.fold(Seq.empty)(siteContext => siteContext.redirectTemplates.map(templateToPage(_, siteContext))) /** * Here we have to retrive index pages from hidden pages and replace fake index pages in navigable page tree. From 773ce3130e04f0086dfd12e842f19747e3279b38 Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Mon, 15 Nov 2021 18:08:53 +0100 Subject: [PATCH 09/15] Fix tests --- docs/_layouts/blog-page.html | 2 +- docs/_layouts/doc-page.html | 10 +++-- docs/_layouts/main.html | 42 +----------------- docs/_layouts/static-site-main.html | 44 +++++++++++++++++++ docs/blog/index.html | 2 +- .../ContentContributors.scala | 33 +++++++------- .../tools/scaladoc/renderers/Renderer.scala | 4 +- .../scaladoc/site/StaticSiteContext.scala | 6 +-- .../dotty/tools/scaladoc/ReportingTest.scala | 2 +- 9 files changed, 76 insertions(+), 69 deletions(-) create mode 100644 docs/_layouts/static-site-main.html diff --git a/docs/_layouts/blog-page.html b/docs/_layouts/blog-page.html index 2c0c8906ce5d..c5d0fe8875e7 100644 --- a/docs/_layouts/blog-page.html +++ b/docs/_layouts/blog-page.html @@ -1,5 +1,5 @@ --- -layout: main +layout: static-site-main ---
diff --git a/docs/_layouts/doc-page.html b/docs/_layouts/doc-page.html index 45e9a7b006d6..57980aac0d3d 100644 --- a/docs/_layouts/doc-page.html +++ b/docs/_layouts/doc-page.html @@ -1,15 +1,17 @@ --- -layout: main +layout: static-site-main ---

{{ page.title }}

+ {% if urls.editSource %} + {% endif %}
{{ content }}