From b614604effda51ca7c76c8901be78ced62b642b2 Mon Sep 17 00:00:00 2001
From: Marcin Aman <maman@virtuslab.com>
Date: Thu, 28 May 2020 16:17:13 +0200
Subject: Update TS migration to current dev, move to a common package, rename
 to frontend

---
 .../frontend/src/main/components/search/search.tsx | 45 ++++++++++++++++++++++
 1 file changed, 45 insertions(+)
 create mode 100644 plugins/base/frontend/src/main/components/search/search.tsx

(limited to 'plugins/base/frontend/src/main/components/search/search.tsx')

diff --git a/plugins/base/frontend/src/main/components/search/search.tsx b/plugins/base/frontend/src/main/components/search/search.tsx
new file mode 100644
index 00000000..f0df0c98
--- /dev/null
+++ b/plugins/base/frontend/src/main/components/search/search.tsx
@@ -0,0 +1,45 @@
+import React, {useCallback, useState} from 'react';
+import {Select} from '@jetbrains/ring-ui';
+import '@jetbrains/ring-ui/components/input-size/input-size.scss';
+import {IWindow, Option, Props, State} from "./types";
+
+const WithFuzzySearchFilterComponent: React.FC<Props> = ({data}: Props) => {
+    const [selected, onSelected] = useState<Option>(data[0]);
+    const onChangeSelected = useCallback(
+        (option: Option) => {
+            window.location.replace(`${(window as IWindow).pathToRoot}${option.location}?query=${option.name}`)
+            onSelected(option);
+        },
+        [data]
+    );
+    return (
+        <div className="search-container">
+            <div className="search">
+                <Select
+                    selectedLabel="Search"
+                    label="Please type page name"
+                    filter={{fuzzy: true}}
+                    clear
+                    selected={selected}
+                    data={data}
+                    onSelect={onChangeSelected}
+                />
+        </div>
+      </div>
+    )
+}
+
+export const WithFuzzySearchFilter = () => {
+    let data: Option[] = [];
+    const pages = (window as IWindow).pages;
+    if (pages) {
+        data = pages.map((page, i) => ({
+            ...page,
+            label: page.name,
+            key: i + 1,
+            type: page.kind
+        }));
+    }
+
+    return <WithFuzzySearchFilterComponent data={data}/>;
+};
-- 
cgit 


From 77c8777b7f66bddd374d68decd507547d356d602 Mon Sep 17 00:00:00 2001
From: Marcin Aman <maman@virtuslab.com>
Date: Sun, 31 May 2020 21:02:46 +0200
Subject: Improve CSS, pages navigation tree and create anchors on page

---
 core/src/main/kotlin/pages/ContentNodes.kt         |  14 +-
 core/src/main/kotlin/pages/PageNodes.kt            |   1 -
 .../frontend/src/main/components/app/index.scss    |   8 +-
 .../frontend/src/main/components/app/index.tsx     |  45 +--
 .../src/main/components/search/search.scss         |  14 +
 .../frontend/src/main/components/search/search.tsx |  16 +-
 .../src/main/kotlin/renderers/html/HtmlRenderer.kt | 258 +++++++++++-----
 .../kotlin/renderers/html/htmlPreprocessors.kt     |  20 +-
 .../kotlin/signatures/KotlinSignatureProvider.kt   |   2 +-
 .../documentables/DefaultPageCreator.kt            |  67 ++---
 .../documentables/PageContentBuilder.kt            |  14 +-
 .../src/main/resources/dokka/scripts/clipboard.js  |  52 ++++
 .../resources/dokka/scripts/navigationLoader.js    |  11 +
 .../base/src/main/resources/dokka/styles/style.css | 330 ++++++++++++++++++---
 .../kotlin/content/params/ContentForParamsTest.kt  | 146 ++++-----
 .../content/seealso/ContentForSeeAlsoTest.kt       | 170 +++++------
 plugins/base/src/test/kotlin/enums/EnumsTest.kt    |   8 +
 .../kotlin/linkableContent/LinkableContentTest.kt  |   4 +-
 plugins/base/src/test/kotlin/markdown/LinkTest.kt  |  13 +-
 .../test/kotlin/renderers/RenderingOnlyTestBase.kt |   1 +
 .../test/kotlin/renderers/html/DivergentTest.kt    |  26 +-
 21 files changed, 842 insertions(+), 378 deletions(-)
 create mode 100644 plugins/base/frontend/src/main/components/search/search.scss
 create mode 100644 plugins/base/src/main/resources/dokka/scripts/clipboard.js

(limited to 'plugins/base/frontend/src/main/components/search/search.tsx')

diff --git a/core/src/main/kotlin/pages/ContentNodes.kt b/core/src/main/kotlin/pages/ContentNodes.kt
index 95b7b371..d6105bec 100644
--- a/core/src/main/kotlin/pages/ContentNodes.kt
+++ b/core/src/main/kotlin/pages/ContentNodes.kt
@@ -13,6 +13,8 @@ interface ContentNode : WithExtraProperties<ContentNode> {
     val dci: DCI
     val sourceSets: Set<SourceSetData>
     val style: Set<Style>
+
+    fun hasAnyContent(): Boolean
 }
 
 /** Simple text */
@@ -24,6 +26,8 @@ data class ContentText(
     override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty()
 ) : ContentNode {
     override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentNode = copy(extra = newExtras)
+
+    override fun hasAnyContent(): Boolean = !text.isBlank()
 }
 
 // TODO: Remove
@@ -34,6 +38,8 @@ data class ContentBreakLine(
     override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty()
 ) : ContentNode {
     override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentNode = copy(extra = newExtras)
+
+    override fun hasAnyContent(): Boolean = true
 }
 
 /** Headers */
@@ -106,6 +112,8 @@ data class ContentEmbeddedResource(
 /** Logical grouping of [ContentNode]s  */
 interface ContentComposite : ContentNode {
     val children: List<ContentNode>
+
+    override fun hasAnyContent(): Boolean = children.any { it.hasAnyContent() }
 }
 
 /** Tables */
@@ -144,7 +152,7 @@ data class ContentGroup(
 }
 
 /**
- * @property groupName is used for finding and copying [ContentDivergentInstance]s when merging [ContentPage]s
+ * @property groupID is used for finding and copying [ContentDivergentInstance]s when merging [ContentPage]s
  */
 data class ContentDivergentGroup(
     override val children: List<ContentDivergentInstance>,
@@ -216,11 +224,11 @@ enum class ContentKind : Kind {
 }
 
 enum class TextStyle : Style {
-    Bold, Italic, Strong, Strikethrough, Paragraph, Block, Span, Monospace, Indented
+    Bold, Italic, Strong, Strikethrough, Paragraph, Block, Monospace, Indented, Cover, UnderCoverText
 }
 
 enum class ContentStyle : Style {
-    RowTitle, TabbedContent, TabbedContentBody, WithExtraAttributes
+    RowTitle, TabbedContent, WithExtraAttributes
 }
 
 object CommentTable: Style
diff --git a/core/src/main/kotlin/pages/PageNodes.kt b/core/src/main/kotlin/pages/PageNodes.kt
index b28e36d6..42ca25cc 100644
--- a/core/src/main/kotlin/pages/PageNodes.kt
+++ b/core/src/main/kotlin/pages/PageNodes.kt
@@ -89,7 +89,6 @@ class PackagePageNode(
     override val name: String,
     override val content: ContentNode,
     override val dri: Set<DRI>,
-
     override val documentable: Documentable?,
     override val children: List<PageNode>,
     override val embeddedResources: List<String> = listOf()
diff --git a/plugins/base/frontend/src/main/components/app/index.scss b/plugins/base/frontend/src/main/components/app/index.scss
index c267bc6e..da5042b1 100644
--- a/plugins/base/frontend/src/main/components/app/index.scss
+++ b/plugins/base/frontend/src/main/components/app/index.scss
@@ -17,5 +17,11 @@ html,
 }
 
 .search-content {
-  padding: 24px 41px;
+  padding-top: 24px;
+  margin: 0 41px;
+  position: absolute;
+  top: 0;
+  right: 0;
+  z-index: 8;
+  background-color: #f4f4f4
 }
diff --git a/plugins/base/frontend/src/main/components/app/index.tsx b/plugins/base/frontend/src/main/components/app/index.tsx
index 1a51e699..4081dec4 100644
--- a/plugins/base/frontend/src/main/components/app/index.tsx
+++ b/plugins/base/frontend/src/main/components/app/index.tsx
@@ -1,49 +1,10 @@
-import React, {useEffect, useRef, useState} from 'react';
+import React from 'react';
 import {WithFuzzySearchFilter} from '../search/search';
 import './index.scss';
 
-function useComponentVisible(initialIsVisible: boolean) {
-    const [isComponentVisible, setIsComponentVisible] = useState(initialIsVisible);
-    const ref = useRef(null);
-
-    const handleHideDropdown = (event: KeyboardEvent) => {
-        if (event.key === "Escape") {
-            setIsComponentVisible(false);
-        }
-    };
-
-    const handleClickOutside = (event : MouseEvent)=> {
-        // @ts-ignore
-        if (ref.current && !ref.current.contains(event.target)) {
-            setIsComponentVisible(false);
-        }
-    };
-
-    useEffect(() => {
-        document.addEventListener("keydown", handleHideDropdown, false);
-        document.addEventListener("click", handleClickOutside, false);
-        return () => {
-            document.removeEventListener("keydown", handleHideDropdown, false);
-            document.removeEventListener("click", handleClickOutside, false);
-        };
-    });
-
-    return { ref, isComponentVisible, setIsComponentVisible };
-}
-
 const App: React.FC = () => {
-    const {
-        ref,
-        isComponentVisible,
-        setIsComponentVisible
-    } = useComponentVisible(false);
-
-    return <div ref={ref} className="search-content">
-        {isComponentVisible && (<WithFuzzySearchFilter/>)}
-        {!isComponentVisible && (
-            <span onClick={() => setIsComponentVisible(true)}>
-                <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><path d="M19.64 18.36l-6.24-6.24a7.52 7.52 0 1 0-1.28 1.28l6.24 6.24zM7.5 13.4a5.9 5.9 0 1 1 5.9-5.9 5.91 5.91 0 0 1-5.9 5.9z"/></svg>
-          </span>)}
+    return <div className="search-content">
+        <WithFuzzySearchFilter/>
     </div>
 }
 
diff --git a/plugins/base/frontend/src/main/components/search/search.scss b/plugins/base/frontend/src/main/components/search/search.scss
new file mode 100644
index 00000000..a891283e
--- /dev/null
+++ b/plugins/base/frontend/src/main/components/search/search.scss
@@ -0,0 +1,14 @@
+.search {
+  button {
+    border: none;
+    fill: #637282;
+
+    &:focus {
+      outline: none;
+    }
+  }
+}
+
+.popup-wrapper {
+  min-width: calc(100% - 360px) !important;
+}
\ No newline at end of file
diff --git a/plugins/base/frontend/src/main/components/search/search.tsx b/plugins/base/frontend/src/main/components/search/search.tsx
index f0df0c98..b0fdd96a 100644
--- a/plugins/base/frontend/src/main/components/search/search.tsx
+++ b/plugins/base/frontend/src/main/components/search/search.tsx
@@ -1,7 +1,8 @@
 import React, {useCallback, useState} from 'react';
 import {Select} from '@jetbrains/ring-ui';
 import '@jetbrains/ring-ui/components/input-size/input-size.scss';
-import {IWindow, Option, Props, State} from "./types";
+import './search.scss';
+import {IWindow, Option, Props} from "./types";
 
 const WithFuzzySearchFilterComponent: React.FC<Props> = ({data}: Props) => {
     const [selected, onSelected] = useState<Option>(data[0]);
@@ -12,6 +13,7 @@ const WithFuzzySearchFilterComponent: React.FC<Props> = ({data}: Props) => {
         },
         [data]
     );
+
     return (
         <div className="search-container">
             <div className="search">
@@ -19,10 +21,22 @@ const WithFuzzySearchFilterComponent: React.FC<Props> = ({data}: Props) => {
                     selectedLabel="Search"
                     label="Please type page name"
                     filter={{fuzzy: true}}
+                    type={Select.Type.CUSTOM}
                     clear
                     selected={selected}
                     data={data}
+                    popupClassName={"popup-wrapper"}
                     onSelect={onChangeSelected}
+                    customAnchor={({wrapperProps, buttonProps, popup}) => (
+                        <span {...wrapperProps}>
+                            <button type="button" {...buttonProps}>
+                                <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+                                    <path d="M19.64 18.36l-6.24-6.24a7.52 7.52 0 1 0-1.28 1.28l6.24 6.24zM7.5 13.4a5.9 5.9 0 1 1 5.9-5.9 5.91 5.91 0 0 1-5.9 5.9z"/>
+                                </svg>
+                            </button>
+                            {popup}
+                        </span>
+                    )}
                 />
         </div>
       </div>
diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
index 7ebcf00e..67fa2d91 100644
--- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
@@ -22,7 +22,7 @@ open class HtmlRenderer(
 
     private val sourceSetDependencyMap = with(context.sourceSetCache) {
         allSourceSets.map { sourceSet ->
-            sourceSet to allSourceSets.filter { sourceSet.dependentSourceSets.contains(it.sourceSetName ) }
+            sourceSet to allSourceSets.filter { sourceSet.dependentSourceSets.contains(it.sourceSetName) }
         }.toMap()
     }
 
@@ -38,22 +38,24 @@ open class HtmlRenderer(
         val additionalClasses = node.style.joinToString(" ") { it.toString().toLowerCase() }
         return when {
             node.hasStyle(ContentStyle.TabbedContent) -> div(additionalClasses) {
-                val secondLevel = node.children.filterIsInstance<ContentComposite>().flatMap { it.children }.filterIsInstance<ContentHeader>().flatMap { it.children }.filterIsInstance<ContentText>()
-                val firstLevel = node.children.filterIsInstance<ContentHeader>().flatMap { it.children }.filterIsInstance<ContentText>()
+                val secondLevel = node.children.filterIsInstance<ContentComposite>().flatMap { it.children }
+                    .filterIsInstance<ContentHeader>().flatMap { it.children }.filterIsInstance<ContentText>()
+                val firstLevel = node.children.filterIsInstance<ContentHeader>().flatMap { it.children }
+                    .filterIsInstance<ContentText>()
 
                 val renderable = firstLevel.union(secondLevel)
 
-                div(classes = "tabs-section"){
+                div(classes = "tabs-section") {
                     attributes["tabs-section"] = "tabs-section"
                     renderable.forEachIndexed { index, node ->
-                        button(classes = "section-tab"){
-                            if(index == 0 ) attributes["data-active"] = ""
+                        button(classes = "section-tab") {
+                            if (index == 0) attributes["data-active"] = ""
                             attributes["data-togglable"] = node.text
                             text(node.text)
                         }
                     }
                 }
-                div(classes = "tabs-section-body"){
+                div(classes = "tabs-section-body") {
                     childrenCallback()
                 }
             }
@@ -61,7 +63,10 @@ open class HtmlRenderer(
                 node.extra.extraHtmlAttributes().forEach { attributes[it.extraKey] = it.extraValue }
                 childrenCallback()
             }
-            node.dci.kind == ContentKind.Symbol -> div("symbol $additionalClasses") { childrenCallback() }
+            node.dci.kind in setOf(ContentKind.Symbol, ContentKind.Sample) -> div("symbol $additionalClasses") {
+                childrenCallback()
+                if (node.hasStyle(TextStyle.Monospace)) copyButton()
+            }
             node.dci.kind == ContentKind.BriefComment -> div("brief $additionalClasses") { childrenCallback() }
             node.dci.kind == ContentKind.Cover -> div("cover $additionalClasses") {
                 filterButtons(node)
@@ -80,7 +85,7 @@ open class HtmlRenderer(
                 button(classes = "platform-tag platform-selector") {
                     attributes["data-active"] = ""
                     attributes["data-filter"] = it.sourceSetName
-                    when(it.platform.key){
+                    when (it.platform.key) {
                         "common" -> classes = classes + "common-like"
                         "native" -> classes = classes + "native-like"
                         "jvm" -> classes = classes + "jvm-like"
@@ -92,40 +97,57 @@ open class HtmlRenderer(
         }
     }
 
+    private fun FlowContent.copyButton() = span(classes = "top-right-position") {
+        span("copy-icon") {
+            unsafe {
+                raw(
+                    """<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+                        <path fill-rule="evenodd" clip-rule="evenodd" d="M5 4H15V16H5V4ZM17 7H19V18V20H17H8V18H17V7Z" fill="black"/>
+                       </svg>""".trimIndent()
+                )
+            }
+        }
+        copiedPopup("Content copied to clipboard", "popup-to-left")
+    }
+
+    private fun FlowContent.copiedPopup(notificationContent: String, additionalClasses: String = "") =
+        div("copy-popup-wrapper $additionalClasses") {
+            unsafe {
+                raw(
+                    """
+                    <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+                        <path d="M18 9C18 14 14 18 9 18C4 18 0 14 0 9C0 4 4 0 9 0C14 0 18 4 18 9ZM14.2 6.2L12.8 4.8L7.5 10.1L5.3 7.8L3.8 9.2L7.5 13L14.2 6.2Z" fill="#4DBB5F"/>
+                    </svg>
+                    """.trimIndent()
+                )
+            }
+            span {
+                text(notificationContent)
+            }
+        }
+
     override fun FlowContent.buildPlatformDependent(content: PlatformHintedContent, pageContext: ContentPage) =
-        buildPlatformDependent(content.sourceSets.map { it to setOf(content.inner) }.toMap(), pageContext, content.extra)
+        buildPlatformDependent(
+            content.sourceSets.map { it to setOf(content.inner) }.toMap(),
+            pageContext,
+            content.extra,
+            content.style
+        )
 
     private fun FlowContent.buildPlatformDependent(
         nodes: Map<SourceSetData, Collection<ContentNode>>,
         pageContext: ContentPage,
-        extra: PropertyContainer<ContentNode> = PropertyContainer.empty()
+        extra: PropertyContainer<ContentNode> = PropertyContainer.empty(),
+        styles: Set<Style> = emptySet()
     ) {
-        var mergedToOneSourceSet : SourceSetData? = null
-        div("platform-hinted") {
+        val contents = contentsForSourceSetDependent(nodes, pageContext)
+        val shouldHaveTabs = contents.size != 1
+
+        val styles = "platform-hinted ${styles.joinToString()}" + if (shouldHaveTabs) " with-platform-tabs" else ""
+        div(styles) {
             attributes["data-platform-hinted"] = "data-platform-hinted"
             extra.extraHtmlAttributes().forEach { attributes[it.extraKey] = it.extraValue }
-            val additionalClasses = if(nodes.toList().size == 1) "single-content" else ""
-            var counter = 0
-            val contents = nodes.toList().map { (sourceSet, elements) ->
-                sourceSet to createHTML(prettyPrint = false).div {
-                    elements.forEach {
-                        buildContentNode(it, pageContext, setOf(sourceSet))
-                    }
-                }.stripDiv()
-            }.groupBy(Pair<SourceSetData, String>::second, Pair<SourceSetData, String>::first).entries.flatMap { (html, sourceSets) ->
-                sourceSets.filterNot {
-                    sourceSetDependencyMap[it].orEmpty().any { dependency -> sourceSets.contains(dependency) }
-                }.map {
-                    it to createHTML(prettyPrint = false).div(classes = "content $additionalClasses") {
-                        if (counter++ == 0) attributes["data-active"] = ""
-                        attributes["data-togglable"] = it.sourceSetName
-                        unsafe {
-                            +html
-                        }
-                    }
-                }
-            }
-            if (contents.size != 1) {
+            if (shouldHaveTabs) {
                 div("platform-bookmarks-row") {
                     attributes["data-toggle-list"] = "data-toggle-list"
                     contents.forEachIndexed { index, pair ->
@@ -134,27 +156,53 @@ open class HtmlRenderer(
                             attributes["data-filterable-set"] = pair.first.sourceSetName
                             if (index == 0) attributes["data-active"] = ""
                             attributes["data-toggle"] = pair.first.sourceSetName
-                            when(
+                            when (
                                 pair.first.platform.key
-                            ){
-                            "common" -> classes = classes + "common-like"
-                            "native" -> classes = classes + "native-like"
-                            "jvm" -> classes = classes + "jvm-like"
-                            "js" -> classes = classes + "js-like"
+                                ) {
+                                "common" -> classes = classes + "common-like"
+                                "native" -> classes = classes + "native-like"
+                                "jvm" -> classes = classes + "jvm-like"
+                                "js" -> classes = classes + "js-like"
                             }
                             attributes["data-toggle"] = pair.first.sourceSetName
                             text(pair.first.sourceSetName)
                         }
                     }
                 }
-            } else if (nodes.size > 1) {
-                mergedToOneSourceSet = contents.first().first
             }
             contents.forEach {
                 consumer.onTagContentUnsafe { +it.second }
             }
         }
-        mergedToOneSourceSet?.let { createPlatformTagBubbles(listOf(it)) }
+    }
+
+    private fun contentsForSourceSetDependent(
+        nodes: Map<SourceSetData, Collection<ContentNode>>,
+        pageContext: ContentPage,
+    ): List<Pair<SourceSetData, String>> {
+        var counter = 0
+        return nodes.toList().map { (sourceSet, elements) ->
+            sourceSet to createHTML(prettyPrint = false).div {
+                elements.forEach {
+                    buildContentNode(it, pageContext, setOf(sourceSet))
+                }
+            }.stripDiv()
+        }.groupBy(
+            Pair<SourceSetData, String>::second,
+            Pair<SourceSetData, String>::first
+        ).entries.flatMap { (html, sourceSets) ->
+            sourceSets.filterNot {
+                sourceSetDependencyMap[it].orEmpty().any { dependency -> sourceSets.contains(dependency) }
+            }.map {
+                it to createHTML(prettyPrint = false).div(classes = "content sourceset-depenent-content") {
+                    if (counter++ == 0) attributes["data-active"] = ""
+                    attributes["data-togglable"] = it.sourceSetName
+                    unsafe {
+                        +html
+                    }
+                }
+            }
+        }
     }
 
     override fun FlowContent.buildDivergent(node: ContentDivergentGroup, pageContext: ContentPage) {
@@ -183,26 +231,38 @@ open class HtmlRenderer(
             val groupedDivergent = it.value.groupBy { it.second }
 
             consumer.onTagContentUnsafe {
-                +createHTML().div("divergent-group"){
+                +createHTML().div("divergent-group") {
                     attributes["data-filterable-current"] = groupedDivergent.keys.joinToString(" ") {
                         it.sourceSetName
                     }
                     attributes["data-filterable-set"] = groupedDivergent.keys.joinToString(" ") {
                         it.sourceSetName
                     }
-                    consumer.onTagContentUnsafe { +it.key.first }
-                    div("main-subrow") {
 
-                        if (node.implicitlySourceSetHinted) {
-                            buildPlatformDependent(
-                                groupedDivergent.map { (sourceSet, elements) ->
-                                    sourceSet to elements.map { e -> e.first.divergent }
-                                }.toMap(),
-                                pageContext
-                            )
-                            if (distinct.size > 1 && groupedDivergent.size == 1) {
-                                createPlatformTags(node, groupedDivergent.keys)
+                    val divergentForPlatformDependent = groupedDivergent.map { (sourceSet, elements) ->
+                        sourceSet to elements.map { e -> e.first.divergent }
+                    }.toMap()
+
+                    val content = contentsForSourceSetDependent(divergentForPlatformDependent, pageContext)
+
+                    consumer.onTagContentUnsafe {
+                        +createHTML().div("brief-with-platform-tags") {
+                            consumer.onTagContentUnsafe { +it.key.first }
+
+                            consumer.onTagContentUnsafe {
+                                +createHTML().span("pull-right") {
+                                    if ((distinct.size > 1 && groupedDivergent.size == 1) || groupedDivergent.size == 1 || content.size == 1) {
+                                        if (node.sourceSets.size != 1) {
+                                            createPlatformTags(node, setOf(content.first().first))
+                                        }
+                                    }
+                                }
                             }
+                        }
+                    }
+                    div("main-subrow") {
+                        if (node.implicitlySourceSetHinted) {
+                            buildPlatformDependent(divergentForPlatformDependent, pageContext)
                         } else {
                             it.value.forEach {
                                 buildContentNode(it.first.divergent, pageContext, setOf(it.second))
@@ -272,7 +332,8 @@ open class HtmlRenderer(
             .filter { sourceSetRestriction == null || it.sourceSets.any { s -> s in sourceSetRestriction } }
             .takeIf { it.isNotEmpty() }
             ?.let {
-                withAnchor(node.dci.dri.first().toString()) {
+                val anchorName = node.dci.dri.first().toString()
+                withAnchor(anchorName) {
                     div(classes = "table-row") {
                         if (!style.contains(MultimoduleTable)) {
                             attributes["data-filterable-current"] = node.sourceSets.joinToString(" ") {
@@ -286,7 +347,10 @@ open class HtmlRenderer(
                             div("main-subrow " + node.style.joinToString(" ")) {
                                 it.filter { sourceSetRestriction == null || it.sourceSets.any { s -> s in sourceSetRestriction } }
                                     .forEach {
-                                        it.build(this, pageContext, sourceSetRestriction)
+                                        span {
+                                            it.build(this, pageContext, sourceSetRestriction)
+                                            buildAnchor(anchorName)
+                                        }
                                         if (ContentKind.shouldBePlatformTagged(node.dci.kind) && (node.sourceSets.size == 1))
                                             createPlatformTags(node)
                                     }
@@ -317,7 +381,7 @@ open class HtmlRenderer(
         div("platform-tags") {
             sourceSets.forEach {
                 div("platform-tag") {
-                    when(it.platform.key){
+                    when (it.platform.key) {
                         "common" -> classes = classes + "common-like"
                         "native" -> classes = classes + "native-like"
                         "jvm" -> classes = classes + "jvm-like"
@@ -331,7 +395,7 @@ open class HtmlRenderer(
 
     private fun FlowContent.createPlatformTags(node: ContentNode, sourceSetRestriction: Set<SourceSetData>? = null) {
         node.takeIf { sourceSetRestriction == null || it.sourceSets.any { s -> s in sourceSetRestriction } }?.let {
-            createPlatformTagBubbles( node.sourceSets.filter {
+            createPlatformTagBubbles(node.sourceSets.filter {
                 sourceSetRestriction == null || it in sourceSetRestriction
             })
         }
@@ -342,7 +406,7 @@ open class HtmlRenderer(
         pageContext: ContentPage,
         sourceSetRestriction: Set<SourceSetData>?
     ) {
-        when(node.dci.kind){
+        when (node.dci.kind) {
             ContentKind.Comment -> buildDefaultTable(node, pageContext, sourceSetRestriction)
             else -> div(classes = "table") {
                 node.extra.extraHtmlAttributes().forEach { attributes[it.extraKey] = it.extraValue }
@@ -388,13 +452,14 @@ open class HtmlRenderer(
 
     override fun FlowContent.buildHeader(level: Int, node: ContentHeader, content: FlowContent.() -> Unit) {
         val anchor = node.extra[SimpleAttr.SimpleAttrKey("anchor")]?.extraValue
+        val classes = node.style.joinToString { it.toString() }.toLowerCase()
         when (level) {
-            1 -> h1() { withAnchor(anchor, content) }
-            2 -> h2() { withAnchor(anchor, content) }
-            3 -> h3() { withAnchor(anchor, content) }
-            4 -> h4() { withAnchor(anchor, content) }
-            5 -> h5() { withAnchor(anchor, content) }
-            else -> h6() { withAnchor(anchor, content) }
+            1 -> h1(classes = classes) { withAnchor(anchor, content) }
+            2 -> h2(classes = classes) { withAnchor(anchor, content) }
+            3 -> h3(classes = classes) { withAnchor(anchor, content) }
+            4 -> h4(classes = classes) { withAnchor(anchor, content) }
+            5 -> h5(classes = classes) { withAnchor(anchor, content) }
+            else -> h6(classes = classes) { withAnchor(anchor, content) }
         }
     }
 
@@ -420,6 +485,23 @@ open class HtmlRenderer(
             text(to.name)
         }
 
+    private fun FlowContent.buildAnchor(pointingTo: String) {
+        span(classes = "anchor-wrapper") {
+            span(classes = "anchor-icon") {
+                attributes["pointing-to"] = pointingTo
+                unsafe {
+                    raw("""
+                <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+                    <path d="M21.2496 5.3C20.3496 4.5 19.2496 4 18.0496 4C16.8496 4 15.6496 4.5 14.8496 5.3L10.3496 9.8L11.7496 11.2L16.2496 6.7C17.2496 5.7 18.8496 5.7 19.8496 6.7C20.8496 7.7 20.8496 9.3 19.8496 10.3L15.3496 14.8L16.7496 16.2L21.2496 11.7C22.1496 10.8 22.5496 9.7 22.5496 8.5C22.5496 7.3 22.1496 6.2 21.2496 5.3Z"/>
+                    <path d="M8.35 16.7998C7.35 17.7998 5.75 17.7998 4.75 16.7998C3.75 15.7998 3.75 14.1998 4.75 13.1998L9.25 8.6998L7.85 7.2998L3.35 11.7998C1.55 13.5998 1.55 16.3998 3.35 18.1998C4.25 19.0998 5.35 19.4998 6.55 19.4998C7.75 19.4998 8.85 19.0998 9.75 18.1998L14.25 13.6998L12.85 12.2998L8.35 16.7998Z"/>
+                </svg>
+            """.trimIndent())
+                }
+            }
+            copiedPopup("Link copied to clipboard")
+        }
+    }
+
     fun FlowContent.buildLink(
         to: DRI,
         platforms: List<SourceSetData>,
@@ -497,7 +579,7 @@ open class HtmlRenderer(
             }
         }
 
-    private fun resolveLink(link: String, page: PageNode) : String = if (URI(link).isAbsolute) link else page.root(link)
+    private fun resolveLink(link: String, page: PageNode): String = if (URI(link).isAbsolute) link else page.root(link)
 
     open fun buildHtml(page: PageNode, resources: List<String>, content: FlowContent.() -> Unit) =
         createHTML().html {
@@ -506,9 +588,13 @@ open class HtmlRenderer(
                 title(page.name)
                 with(resources) {
                     filter { it.substringBefore('?').substringAfterLast('.') == "css" }
-                        .forEach { link(rel = LinkRel.stylesheet, href = resolveLink(it,page)) }
+                        .forEach { link(rel = LinkRel.stylesheet, href = resolveLink(it, page)) }
                     filter { it.substringBefore('?').substringAfterLast('.') == "js" }
-                        .forEach { script(type = ScriptType.textJavaScript, src = resolveLink(it,page)) { async = true } }
+                        .forEach {
+                            script(type = ScriptType.textJavaScript, src = resolveLink(it, page)) {
+                                async = true
+                            }
+                        }
                 }
                 script { unsafe { +"""var pathToRoot = "${locationProvider.resolveRoot(page)}";""" } }
             }
@@ -532,6 +618,38 @@ open class HtmlRenderer(
                         script(type = ScriptType.textJavaScript, src = page.root("scripts/pages.js")) {}
                         script(type = ScriptType.textJavaScript, src = page.root("scripts/main.js")) {}
                         content()
+                        div(classes = "footer") {
+                            span("go-to-top-icon") {
+                                a(href = "#container") {
+                                    unsafe {
+                                        raw(
+                                            """
+                                    <svg width="12" height="10" viewBox="0 0 12 10" fill="none" xmlns="http://www.w3.org/2000/svg">
+                                        <path d="M11.3337 9.66683H0.666992L6.00033 3.66683L11.3337 9.66683Z" fill="black"/>
+                                        <path d="M0.666992 0.333496H11.3337V1.66683H0.666992V0.333496Z" fill="black"/>
+                                    </svg>
+                                """.trimIndent()
+                                        )
+                                    }
+                                }
+                            }
+                            span { text("© 2020 Copyright") }
+                            span { text("Privacy Policy") }
+                            span("pull-right") {
+                                span { text("Sponsored and developed by Dokka") }
+                                span(classes = "padded-icon") {
+                                    unsafe {
+                                        raw(
+                                            """
+                                    <svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
+                                        <path d="M8 0H2.3949L4.84076 2.44586L0 7.28662L0.713376 8L5.55414 3.15924L8 5.6051V0Z" fill="black"/>
+                                    </svg>
+                                """.trimIndent()
+                                        )
+                                    }
+                                }
+                            }
+                        }
                     }
                 }
             }
diff --git a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt
index 45ef1457..f10c85d5 100644
--- a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt
@@ -5,6 +5,8 @@ import kotlinx.html.id
 import kotlinx.html.table
 import kotlinx.html.tbody
 import org.jetbrains.dokka.base.renderers.platforms
+import org.jetbrains.dokka.model.DEnum
+import org.jetbrains.dokka.model.DEnumEntry
 import org.jetbrains.dokka.pages.*
 import org.jetbrains.dokka.plugability.DokkaContext
 import org.jetbrains.dokka.transformers.pages.PageTransformer
@@ -44,12 +46,19 @@ object NavigationPageInstaller : PageTransformer {
             page.name,
             page.dri.first(),
             page.platforms(),
-            if (page !is ClasslikePageNode)
-                page.children.filterIsInstance<ContentPage>()
-                    .map { visit(it) }
-            else
-                emptyList()
+            page.navigableChildren()
         )
+
+    private fun ContentPage.navigableChildren(): List<NavigationNode> {
+        if(this !is ClasslikePageNode){
+            return children.filterIsInstance<ContentPage>()
+                .map { visit(it) }
+        } else if(documentable is DEnum) {
+            return children.filter { it is ContentPage && it.documentable is DEnumEntry }.map { visit(it as ContentPage) }
+        }
+
+        return emptyList()
+    }
 }
 
 object ResourceInstaller : PageTransformer {
@@ -68,6 +77,7 @@ object StyleAndScriptsAppender : PageTransformer {
                 "scripts/navigationLoader.js",
                 "scripts/platformContentHandler.js",
                 "scripts/sourceset_dependencies.js",
+                "scripts/clipboard.js",
                 "styles/jetbrains-mono.css"
             )
         )
diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt
index 4868d91e..5514f9e5 100644
--- a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt
+++ b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt
@@ -253,7 +253,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog
         }
 
     private fun funType(dri: DRI, sourceSets: Set<SourceSetData>, type: TypeConstructor) =
-        contentBuilder.contentFor(dri, sourceSets, ContentKind.Symbol, setOf(TextStyle.Monospace)) {
+        contentBuilder.contentFor(dri, sourceSets) {
             if (type.extension) {
                 signatureForProjection(type.projections.first())
                 text(".")
diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
index 1954cec9..bdc6d680 100644
--- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
+++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
@@ -66,9 +66,11 @@ open class DefaultPageCreator(
 
     protected open fun contentForModule(m: DModule) = contentBuilder.contentFor(m) {
         group(kind = ContentKind.Cover) {
-            header(1, m.name)
-            sourceSetDependentHint(m.dri, m.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint){
-                +contentForDescription(m)
+            cover(m.name)
+            if(contentForDescription(m).isNotEmpty()){
+                sourceSetDependentHint(m.dri, m.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint, styles = setOf(TextStyle.UnderCoverText)){
+                    +contentForDescription(m)
+                }
             }
         }
         +contentForComments(m)
@@ -81,9 +83,11 @@ open class DefaultPageCreator(
 
     protected open fun contentForPackage(p: DPackage) = contentBuilder.contentFor(p) {
         group(kind = ContentKind.Cover) {
-            header(1, "Package ${p.name}")
-            sourceSetDependentHint(p.dri, p.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint){
-                +contentForDescription(p)
+            cover("Package ${p.name}")
+            if(contentForDescription(p).isNotEmpty()){
+                sourceSetDependentHint(p.dri, p.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint, styles = setOf(TextStyle.UnderCoverText)){
+                    +contentForDescription(p)
+                }
             }
         }
         group(styles = setOf(ContentStyle.TabbedContent)){
@@ -92,8 +96,8 @@ open class DefaultPageCreator(
             block("Type aliases", 2, ContentKind.TypeAliases, p.typealiases, p.sourceSets.toSet(), extra = mainExtra + SimpleAttr.header("Type aliases")) {
                 link(it.name, it.dri, kind = ContentKind.Main)
                 sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint, styles = emptySet()) {
-                    +buildSignature(it)
                     contentForBrief(it)
+                    +buildSignature(it)
                 }
             }
         }
@@ -109,9 +113,8 @@ open class DefaultPageCreator(
         block("Properties", 2, ContentKind.Properties, s.properties, sourceSets.toSet(), extra = mainExtra + SimpleAttr.header( "Properties")) {
             link(it.name, it.dri, kind = ContentKind.Main)
             sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint) {
-                +buildSignature(it)
-
                 contentForBrief(it)
+                +buildSignature(it)
             }
         }
         s.safeAs<WithExtraProperties<Documentable>>()?.let { it.extra[InheritorsInfo] }?.let { inheritors ->
@@ -140,10 +143,10 @@ open class DefaultPageCreator(
 
     protected open fun contentForEnumEntry(e: DEnumEntry) = contentBuilder.contentFor(e) {
         group(kind = ContentKind.Cover) {
-            header(1, e.name)
+            cover(e.name)
             sourceSetDependentHint(e.dri, e.sourceSets.toSet()) {
-                +buildSignature(e)
                 +contentForDescription(e)
+                +buildSignature(e)
             }
         }
         group(styles = setOf(ContentStyle.TabbedContent)){
@@ -154,10 +157,10 @@ open class DefaultPageCreator(
 
     protected open fun contentForClasslike(c: DClasslike) = contentBuilder.contentFor(c) {
         group(kind = ContentKind.Cover) {
-            header(1, c.name.orEmpty())
-                sourceSetDependentHint(c.dri, c.sourceSets.toSet()) {
-                +buildSignature(c)
+            cover(c.name.orEmpty())
+            sourceSetDependentHint(c.dri, c.sourceSets.toSet()) {
                 +contentForDescription(c)
+                +buildSignature(c)
             }
         }
 
@@ -174,8 +177,8 @@ open class DefaultPageCreator(
                 ) {
                     link(it.name, it.dri, kind = ContentKind.Main)
                     sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint, styles = emptySet()) {
-                        +buildSignature(it)
                         contentForBrief(it)
+                        +buildSignature(it)
                     }
                 }
             }
@@ -183,8 +186,8 @@ open class DefaultPageCreator(
                 block("Entries", 2, ContentKind.Classlikes, c.entries, c.sourceSets.toSet(), extra = mainExtra + SimpleAttr.header("Entries"), styles = emptySet()) {
                     link(it.name, it.dri)
                     sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependantHint) {
-                        +buildSignature(it)
                         contentForBrief(it)
+                        +buildSignature(it)
                     }
                 }
             }
@@ -215,7 +218,7 @@ open class DefaultPageCreator(
 
         val platforms = d.sourceSets.toSet()
 
-        return contentBuilder.contentFor(d) {
+        return contentBuilder.contentFor(d, styles = setOf(TextStyle.Block)) {
             val description = tags.withTypeUnnamed<Description>()
             if (description.any { it.value.root.children.isNotEmpty() }) {
                 platforms.forEach { platform ->
@@ -317,13 +320,11 @@ open class DefaultPageCreator(
                 header(2, "Samples")
                 group(extra = mainExtra + SimpleAttr.header("Samples"), styles = setOf(ContentStyle.WithExtraAttributes)){
                     sourceSetDependentHint(sourceSets = platforms.toSet(), kind = ContentKind.SourceSetDependantHint) {
-                        table(kind = ContentKind.Sample) {
-                            platforms.map { platformData ->
-                                val content = samples.filter { it.value.isEmpty() || platformData in it.value }
-                                buildGroup(sourceSets = setOf(platformData), styles = setOf(ContentStyle.RowTitle)) {
-                                    content.forEach {
-                                        comment(Text(it.key))
-                                    }
+                        platforms.map { platformData ->
+                            val content = samples.filter { it.value.isEmpty() || platformData in it.value }
+                            group(sourceSets = setOf(platformData), kind = ContentKind.Sample, styles = setOf(TextStyle.Monospace)) {
+                                content.forEach {
+                                    text(it.key)
                                 }
                             }
                         }
@@ -355,17 +356,17 @@ open class DefaultPageCreator(
     protected open fun contentForTypeAlias(t: DTypeAlias) = contentForMember(t)
     protected open fun contentForMember(d: Documentable) = contentBuilder.contentFor(d) {
         group(kind = ContentKind.Cover) {
-            header(1, d.name.orEmpty())
+            cover(d.name.orEmpty())
         }
         divergentGroup(ContentDivergentGroup.GroupID("member")) {
             instance(setOf(d.dri), d.sourceSets.toSet()) {
-                divergent(kind = ContentKind.Symbol) {
-                    +buildSignature(d)
-                }
-                after {
+                before {
                     +contentForDescription(d)
                     +contentForComments(d)
                 }
+                divergent(kind = ContentKind.Symbol) {
+                    +buildSignature(d)
+                }
             }
         }
     }
@@ -389,14 +390,14 @@ open class DefaultPageCreator(
                         ) {
                             elements.map {
                                 instance(setOf(it.dri), it.sourceSets.toSet()) {
+                                    before {
+                                        contentForBrief(it)
+                                    }
                                     divergent {
-                                        group(kind = ContentKind.Symbol) {
+                                        group {
                                             +buildSignature(it)
                                         }
                                     }
-                                    after {
-                                        contentForBrief(it)
-                                    }
                                 }
                             }
                         }
diff --git a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt
index b670626a..c499be6f 100644
--- a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt
+++ b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt
@@ -107,6 +107,16 @@ open class PageContentBuilder(
             )
         }
 
+        fun cover(
+            text: String,
+            platformData: Set<SourceSetData> = mainPlatformData,
+            styles: Set<Style> = mainStyles + TextStyle.Cover,
+            extra: PropertyContainer<ContentNode> = mainExtra,
+            block: DocumentableContentBuilder.() -> Unit = {}
+            ) {
+            header(1, text, platformData = platformData, styles = styles, extra = extra, block = block)
+        }
+
         fun text(
             text: String,
             kind: Kind = ContentKind.Main,
@@ -411,7 +421,7 @@ open class PageContentBuilder(
             block: DocumentableContentBuilder.() -> Unit
         ) {
             contentFor(dri, sourceSets, kind, styles, extra, block)
-                .takeIf { it.children.isNotEmpty() }
+                .takeIf { it.hasAnyContent() }
                 .also { before = it }
         }
 
@@ -435,7 +445,7 @@ open class PageContentBuilder(
             block: DocumentableContentBuilder.() -> Unit
         ) {
             contentFor(dri, sourceSets, kind, styles, extra, block)
-                .takeIf { it.children.isNotEmpty() }
+                .takeIf { it.hasAnyContent() }
                 .also { after = it }
         }
 
diff --git a/plugins/base/src/main/resources/dokka/scripts/clipboard.js b/plugins/base/src/main/resources/dokka/scripts/clipboard.js
new file mode 100644
index 00000000..b00ce246
--- /dev/null
+++ b/plugins/base/src/main/resources/dokka/scripts/clipboard.js
@@ -0,0 +1,52 @@
+window.addEventListener('load', () => {
+    document.querySelectorAll('span.copy-icon').forEach(element => {
+        element.addEventListener('click', (el) => copyElementsContentToClipboard(element));
+    })
+
+    document.querySelectorAll('span.anchor-icon').forEach(element => {
+        element.addEventListener('click', (el) => {
+            if(element.hasAttribute('pointing-to')){
+                const location = hrefWithoutCurrentlyUsedAnchor() + '#' + element.getAttribute('pointing-to')
+                copyTextToClipboard(element, location)
+            }
+        });
+    })
+})
+
+const copyElementsContentToClipboard = (element) => {
+    const selection = window.getSelection();
+    const range = document.createRange();
+    range.selectNodeContents(element.parentNode.parentNode);
+    selection.removeAllRanges();
+    selection.addRange(range);
+
+    copyAndShowPopup(element,  () => selection.removeAllRanges())
+}
+
+const copyTextToClipboard = (element, text) => {
+    var textarea = document.createElement("textarea");
+    textarea.textContent = text;
+    textarea.style.position = "fixed";
+    document.body.appendChild(textarea);
+    textarea.select();
+
+    copyAndShowPopup(element, () => document.body.removeChild(textarea))
+}
+
+const copyAndShowPopup = (element, after) => {
+    try {
+        document.execCommand('copy');
+        element.nextElementSibling.classList.add('active-popup');
+        setTimeout(() => {
+            element.nextElementSibling.classList.remove('active-popup');
+        }, 1200);
+    } catch (e) {
+        console.error('Failed to write to clipboard:', e)
+    }
+    finally {
+        if(after) after()
+    }
+}
+
+const hrefWithoutCurrentlyUsedAnchor = () => window.location.href.split('#')[0]
+
diff --git a/plugins/base/src/main/resources/dokka/scripts/navigationLoader.js b/plugins/base/src/main/resources/dokka/scripts/navigationLoader.js
index cac46d2a..c2f60ec5 100644
--- a/plugins/base/src/main/resources/dokka/scripts/navigationLoader.js
+++ b/plugins/base/src/main/resources/dokka/scripts/navigationLoader.js
@@ -14,6 +14,17 @@ window.addEventListener('load', () => {
     }).then(() => {
         revealNavigationForCurrentPage()
     })
+
+    /* Smooth scrolling support for going to the top of the page */
+    document.querySelectorAll('a[href^="#"]').forEach(anchor => {
+        anchor.addEventListener('click', function (e) {
+            e.preventDefault();
+
+            document.querySelector(this.getAttribute('href')).scrollIntoView({
+                behavior: 'smooth'
+            });
+        });
+    });
 })
 
 revealNavigationForCurrentPage = () => {
diff --git a/plugins/base/src/main/resources/dokka/styles/style.css b/plugins/base/src/main/resources/dokka/styles/style.css
index d573b76c..f180762b 100644
--- a/plugins/base/src/main/resources/dokka/styles/style.css
+++ b/plugins/base/src/main/resources/dokka/styles/style.css
@@ -4,11 +4,14 @@
 
 :root {
     --breadcrumb-font-color: #A6AFBA;
-    --hover-link-color: #5B5DEF
+    --hover-link-color: #5B5DEF;
+    --footer-height: 64px;
+    --horizontal-spacing-for-content: 42px
 }
 
 #content {
-    padding: 0 41px;
+    padding: 0 var(--horizontal-spacing-for-content);
+    height: calc(100% - var(--footer-height));
 }
 
 .breadcrumbs {
@@ -40,7 +43,7 @@
     padding: 11px 3px;
     font-size: 14px;
     color: #637282;
-    outline:none;
+    outline: none;
     margin: 0 8px;
 }
 
@@ -55,29 +58,86 @@
 }
 
 .tabs-section-body {
-    padding: 24px 0
+    margin: 12px 0;
+    background-color: white;
+}
+
+.tabs-section-body > .table {
+    margin: 12px 0;
+}
+
+.tabs-section-body .with-platform-tabs > div {
+    margin: 0 12px;
+}
+
+.tabs-section-body .table .with-platform-tabs > div {
+    margin: 0;
+}
+
+.tabs-section-body .with-platform-tabs {
+    padding-top: 12px;
+    padding-bottom: 12px;
+}
+
+.tabs-section-body .with-platform-tabs .sourceset-depenent-content .table-row {
+    background-color: #f4f4f4;
+    border-bottom: 2px solid white;
 }
 
 .cover > .platform-hinted {
-    padding: 24px 0
+    padding-top: 24px;
+    margin-top: 24px;
+    padding-bottom: 16px;
 }
 
 .cover {
     display: flex;
     flex-direction: column;
+    width: 100%;
+    padding-bottom: 48px;
 }
 
 .tabbedcontent {
-    padding: 24px 0;
+    padding: 14px 0;
 }
 
-.cover .platform-hinted .single-content > .symbol {
+.cover .platform-hinted .sourceset-depenent-content > .symbol,
+.cover > .symbol {
     background-color: white;
 }
 
+.cover .platform-hinted.with-platform-tabs .sourceset-depenent-content > .symbol {
+    background-color: #f4f4f4;
+}
+
+.cover .platform-hinted.with-platform-tabs .sourceset-depenent-content > .block ~ .symbol {
+    padding-top: 16px;
+    padding-left: 0;
+}
+
+.cover .sourceset-depenent-content > .block {
+    padding: 16px 0;
+    font-size: 18px;
+    line-height: 28px;
+}
+
+.cover .platform-hinted.with-platform-tabs .sourceset-depenent-content > .block {
+    padding: 0;
+    font-size: 14px;
+}
+
+.cover ~ .divergent-group {
+    margin-top: 24px;
+    padding: 24px 8px 8px 8px;
+}
+
+.cover ~ .divergent-group .main-subrow .symbol {
+    width: 100%;
+}
+
 .divergent-group {
     background-color: white;
-    padding: 12px 8px;
+    padding: 16px 8px;
     margin-bottom: 2px;
 }
 
@@ -85,6 +145,10 @@
     background-color: #F4F4F4;
 }
 
+.title > .divergent-group:first-of-type {
+    padding-top: 0;
+}
+
 #container {
     display: flex;
     flex-direction: row;
@@ -93,7 +157,7 @@
 
 #main {
     width: 100%;
-    padding-left: 12px;
+    max-width: calc(100% - 280px);
 }
 
 #leftColumn {
@@ -114,10 +178,9 @@
 }
 
 #sideMenu {
-    height: 100%;
+    max-height: calc(100% - 90px);
     padding-top: 16px;
     position: relative;
-    overflow: auto;
 }
 
 #sideMenu img {
@@ -149,7 +212,6 @@
 }
 
 .overview > .navButton {
-    width: 100%;
     height: 100%;
     align-items: center;
     display: flex;
@@ -169,19 +231,65 @@
     background-color: #F4F4F4;
     align-items: center;
     display: flex;
-    padding: 8px 16px;
+    padding: 8px 8px 8px 16px;
     box-sizing: border-box;
     font-weight: bold;
-    flex-wrap: wrap;
+    position: relative;
+    line-height: 24px;
+}
+
+.symbol span.copy-icon path {
+    fill: #637282;
+}
+
+.symbol span.copy-icon:hover path {
+    fill: black;
+}
+
+.copy-popup-wrapper {
+    display: none;
+    align-items: center;
+    position: absolute;
+    z-index: 1000;
+    background: white;
+    font-weight: normal;
+    box-shadow: 0 2px 16px 0 var(--ring-popup-shadow-color);
+    font-family: 'Inter', "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+    width: max-content;
+    font-size: 14px;
+    cursor: default;
+}
+
+.copy-popup-wrapper.popup-to-left {
+    /* since it is in position absolute we can just move it to the left to make it always appear on the left side of the icon */
+    left: -15em;
+}
+
+.copy-popup-wrapper.active-popup {
+    display: flex !important;
+}
+
+.copy-popup-wrapper:hover {
+    font-weight: normal;
+}
+
+.copy-popup-wrapper svg {
+    padding: 8px;
+}
+
+.copy-popup-wrapper > span:last-child {
+    padding-right: 14px;
 }
 
-#nav-submenu > .sideMenuPart {
-    padding-left: 0; /* packages are same level as modules */
+.symbol .top-right-position {
+    /* it is important for a parent to have a position: relative */
+    position: absolute;
+    top: 8px;
+    right: 8px;
 }
 
 .sideMenuPart > .overview {
     height: 40px;
-    width: 100%;
     display: flex;
     align-items: center;
     position: relative;
@@ -194,13 +302,14 @@
     flex: 1;
     height: 100%;
     color: #637282;
+    overflow: hidden;
 }
 
 .sideMenuPart > .overview:before {
     box-sizing: border-box;
     content: '';
     top: 0;
-    width: 200%;
+    width: 280px;
     right: 0;
     bottom: 0;
     position: absolute;
@@ -255,7 +364,7 @@ body, table {
     font-size: 14px;
     line-height: 24px;
     margin: 0;
-    max-width: 1440px;
+    /*max-width: 1440px; TODO: This results in worse experience on ultrawide, but on 16:9/16:10 looks better.*/
 }
 
 table {
@@ -305,30 +414,62 @@ td:first-child {
 
 h1, h2, h3, h4, h5, h6 {
     color: #222;
+    font-weight: bold;
 }
 
 p, ul, ol, table, pre, dl {
     margin: 0 0 20px;
 }
 
-h1, h2, h3 {
-    line-height: 1.1;
+h1 {
+    font-weight: bold;
+    font-size: 40px;
+    line-height: 48px;
+    letter-spacing: -1px;
 }
 
-h1 {
+
+h1.cover {
     font-size: 60px;
     line-height: 64px;
     letter-spacing: -1.5px;
+
+    margin-left: calc(-1 * var(--horizontal-spacing-for-content));
+    margin-right: calc(-1 * var(--horizontal-spacing-for-content));
+    padding-left: var(--horizontal-spacing-for-content);
+    padding-right: var(--horizontal-spacing-for-content);
+    border-bottom: 1px solid #DADFE6;
+
+    margin-bottom: 0;
+    padding-bottom: 32px;
 }
 
 h2 {
     color: #393939;
+    font-size: 31px;
+    line-height: 40px;
+    letter-spacing: -0.5px;
+}
+
+h3 {
+    font-size: 20px;
+    line-height: 28px;
+    letter-spacing: -0.2px;
+}
+
+h4 {
+    margin: 0;
 }
 
 h3, h4, h5, h6 {
     color: #494949;
 }
 
+.UnderCoverText {
+    font-size: 18px;
+    line-height: 28px;
+}
+
 a {
     color: #5B5DEF;
     font-weight: 400;
@@ -367,12 +508,6 @@ code, pre {
 
 pre {
     display: block;
-    /*
-        padding:8px 8px;
-        background: #f8f8f8;
-        border-radius:5px;
-        border:1px solid #e5e5e5;
-    */
     overflow-x: auto;
 }
 
@@ -536,11 +671,14 @@ footer {
 }
 
 .filter-section {
-    display: flex;
     display: flex;
     flex-direction: row;
     align-self: flex-end;
     min-height: 30px;
+    position: absolute;
+    top: 20px;
+    right: 88px;
+    z-index: 0;
 }
 
 .platform-selector:hover {
@@ -552,7 +690,7 @@ footer {
 }
 .platform-selector:not([data-active]) {
     border: 1px solid #DADFE6;
-    background-color: white;
+    background-color: transparent;
     color: #637282;
 }
 
@@ -570,7 +708,12 @@ td.content {
     justify-content: space-between;
 }
 
-.main-subrow > a {
+.main-subrow > span {
+    display: flex;
+    position: relative;
+}
+
+.main-subrow > span > a {
     text-decoration: none;
     font-style: normal;
     font-weight: 600;
@@ -578,10 +721,30 @@ td.content {
     color: #282E34;
 }
 
-.main-subrow > a:hover {
+.main-subrow > span > a:hover {
     color: var(--hover-link-color);
 }
 
+.main-subrow .anchor-icon {
+    padding: 0 8px;
+}
+
+.main-subrow .anchor-icon > svg path {
+    fill: #637282;
+}
+
+.main-subrow .anchor-icon:hover {
+    cursor: pointer;
+}
+
+.main-subrow .anchor-icon:hover > svg path {
+    fill: var(--hover-link-color);
+}
+
+.main-subrow .anchor-wrapper {
+    position: relative;
+}
+
 .platform-hinted {
     flex: auto;
     display: block;
@@ -648,9 +811,52 @@ td.content {
     display: none
 }
 
-.sideMenuPart[data-active] > .overview {
-    background: rgba(91, 93, 239, 0.15);
+.brief-with-platform-tags {
+    display: inline-block;
+    width: 100%;
+}
+
+.tabs-section-body .brief-with-platform-tags {
+    display: inline-flex;
+}
+
+.brief-with-platform-tags ~ .main-subrow {
+    padding-top: 16px;
+}
+
+.cover .with-platform-tabs {
+    background-color: white;
+}
+
+.cover > .with-platform-tabs .platform-bookmarks-row {
+    margin: 0 16px;
+}
+
+.cover > .with-platform-tabs > .content {
+    margin: 0 16px;
+    background-color: #f4f4f4;
+    padding: 8px 16px;
+}
+
+.cover > .block {
+    padding-top: 48px;
+    padding-bottom: 24px;
+    font-size: 18px;
+    line-height: 28px;
+}
+
+.cover > .block:empty {
+    padding-bottom: 0;
+}
+
+.table-row .with-platform-tabs .sourceset-depenent-content .brief {
+    padding: 16px;
+    background-color: #f4f4f4;
+}
+
+.sideMenuPart[data-active] > .overview:before {
     border-left: 4px solid var(--hover-link-color);
+    background: rgba(91, 93, 239, 0.15);
 }
 
 .table {
@@ -662,7 +868,7 @@ td.content {
     display: flex;
     flex-direction: column;
     background: white;
-    margin-bottom: 2px;
+    border-bottom: 2px solid #f4f4f4;
     padding: 16px 24px 16px 24px;
 }
 
@@ -683,7 +889,7 @@ td.content {
 
 @media print, screen and (min-width: 960px) {
     .keyValue {
-        grid-template-columns: 20% auto;
+        grid-template-columns: 20% 80%;
     }
 
     .title-row {
@@ -761,3 +967,55 @@ td.content {
         color: #444;
     }
 }
+
+.footer {
+    clear: both;
+    display: flex;
+    align-items: center;
+    position: relative;
+    height: var(--footer-height);
+    border-top: 1px solid #DADFE6;
+    font-size: 12px;
+    line-height: 16px;
+    letter-spacing: 0.2px;
+    color: var(--breadcrumb-font-color);
+    margin-top: 48px;
+}
+
+.footer span.go-to-top-icon {
+    border-radius: 2em;
+    padding: 11px 10px !important;
+    background-color: white;
+}
+
+.footer span.go-to-top-icon path {
+    fill: #637282;
+}
+
+.footer > span:first-child {
+    margin-left: var(--horizontal-spacing-for-content);
+    padding-left: 0;
+}
+
+.footer > span:last-child {
+    margin-right: var(--horizontal-spacing-for-content);
+    padding-right: 0;
+}
+
+.footer > span {
+    padding: 0 16px;
+}
+
+.footer .padded-icon {
+    padding-left: 0.5em;
+}
+
+/*For svg*/
+.footer path {
+    fill: var(--breadcrumb-font-color);
+}
+
+.pull-right {
+    float: right;
+    margin-left: auto
+}
diff --git a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt
index 31f62918..e25567e0 100644
--- a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt
+++ b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt
@@ -77,6 +77,9 @@ class ContentForParamsTest : AbstractCoreTest() {
                     }
                     divergentGroup {
                         divergentInstance {
+                            before {
+                                pWrapped("comment to function")
+                            }
                             divergent {
                                 bareSignature(
                                     emptyMap(),
@@ -88,9 +91,6 @@ class ContentForParamsTest : AbstractCoreTest() {
                                     "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
                                 )
                             }
-                            after {
-                                pWrapped("comment to function")
-                            }
                         }
                     }
                 }
@@ -122,6 +122,10 @@ class ContentForParamsTest : AbstractCoreTest() {
                     }
                     divergentGroup {
                         divergentInstance {
+                            before {
+                                unnamedTag("Author") { +"Kordyjan" }
+                                unnamedTag("Since") { +"0.11" }
+                            }
                             divergent {
                                 bareSignature(
                                     emptyMap(),
@@ -133,10 +137,6 @@ class ContentForParamsTest : AbstractCoreTest() {
                                     "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
                                 )
                             }
-                            after {
-                                unnamedTag("Author") { +"Kordyjan" }
-                                unnamedTag("Since") { +"0.11" }
-                            }
                         }
                     }
                 }
@@ -169,6 +169,11 @@ class ContentForParamsTest : AbstractCoreTest() {
                     }
                     divergentGroup {
                         divergentInstance {
+                            before {
+                                pWrapped("comment to function")
+                                unnamedTag("Author") { +"Kordyjan" }
+                                unnamedTag("Since") { +"0.11" }
+                            }
                             divergent {
                                 bareSignature(
                                     emptyMap(),
@@ -180,11 +185,6 @@ class ContentForParamsTest : AbstractCoreTest() {
                                     "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
                                 )
                             }
-                            after {
-                                pWrapped("comment to function")
-                                unnamedTag("Author") { +"Kordyjan" }
-                                unnamedTag("Since") { +"0.11" }
-                            }
                         }
                     }
                 }
@@ -216,18 +216,7 @@ class ContentForParamsTest : AbstractCoreTest() {
                     }
                     divergentGroup {
                         divergentInstance {
-                            divergent {
-                                bareSignature(
-                                    emptyMap(),
-                                    "",
-                                    "",
-                                    emptySet(),
-                                    "function",
-                                    null,
-                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
-                                )
-                            }
-                            after {
+                            before {
                                 pWrapped("comment to function")
                                 header(2) { +"Parameters" }
                                 group {
@@ -241,6 +230,17 @@ class ContentForParamsTest : AbstractCoreTest() {
                                     }
                                 }
                             }
+                            divergent {
+                                bareSignature(
+                                    emptyMap(),
+                                    "",
+                                    "",
+                                    emptySet(),
+                                    "function",
+                                    null,
+                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+                                )
+                            }
                         }
                     }
                 }
@@ -274,15 +274,7 @@ class ContentForParamsTest : AbstractCoreTest() {
                     }
                     divergentGroup {
                         divergentInstance {
-                            divergent {
-                                bareSignature(
-                                    emptyMap(), "", "", emptySet(), "function", null,
-                                    "first" to ParamAttributes(emptyMap(), emptySet(), "String"),
-                                    "second" to ParamAttributes(emptyMap(), emptySet(), "Int"),
-                                    "third" to ParamAttributes(emptyMap(), emptySet(), "Double")
-                                )
-                            }
-                            after {
+                            before {
                                 pWrapped("comment to function")
                                 header(2) { +"Parameters" }
                                 group {
@@ -304,6 +296,14 @@ class ContentForParamsTest : AbstractCoreTest() {
                                     }
                                 }
                             }
+                            divergent {
+                                bareSignature(
+                                    emptyMap(), "", "", emptySet(), "function", null,
+                                    "first" to ParamAttributes(emptyMap(), emptySet(), "String"),
+                                    "second" to ParamAttributes(emptyMap(), emptySet(), "Int"),
+                                    "third" to ParamAttributes(emptyMap(), emptySet(), "Double")
+                                )
+                            }
                         }
                     }
                 }
@@ -336,15 +336,7 @@ class ContentForParamsTest : AbstractCoreTest() {
                     }
                     divergentGroup {
                         divergentInstance {
-                            divergent {
-                                bareSignature(
-                                    emptyMap(), "", "", emptySet(), "function", null,
-                                    "first" to ParamAttributes(emptyMap(), emptySet(), "String"),
-                                    "second" to ParamAttributes(emptyMap(), emptySet(), "Int"),
-                                    "third" to ParamAttributes(emptyMap(), emptySet(), "Double")
-                                )
-                            }
-                            after {
+                            before {
                                 header(2) { +"Parameters" }
                                 group {
                                     platformHinted {
@@ -365,6 +357,14 @@ class ContentForParamsTest : AbstractCoreTest() {
                                     }
                                 }
                             }
+                            divergent {
+                                bareSignature(
+                                    emptyMap(), "", "", emptySet(), "function", null,
+                                    "first" to ParamAttributes(emptyMap(), emptySet(), "String"),
+                                    "second" to ParamAttributes(emptyMap(), emptySet(), "Int"),
+                                    "third" to ParamAttributes(emptyMap(), emptySet(), "Double")
+                                )
+                            }
                         }
                     }
                 }
@@ -397,19 +397,7 @@ class ContentForParamsTest : AbstractCoreTest() {
                     }
                     divergentGroup {
                         divergentInstance {
-                            divergent {
-                                bareSignatureWithReceiver(
-                                    emptyMap(),
-                                    "",
-                                    "",
-                                    emptySet(),
-                                    "String",
-                                    "function",
-                                    null,
-                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
-                                )
-                            }
-                            after {
+                            before {
                                 pWrapped("comment to function")
                                 header(2) { +"Parameters" }
                                 group {
@@ -427,6 +415,18 @@ class ContentForParamsTest : AbstractCoreTest() {
                                     }
                                 }
                             }
+                            divergent {
+                                bareSignatureWithReceiver(
+                                    emptyMap(),
+                                    "",
+                                    "",
+                                    emptySet(),
+                                    "String",
+                                    "function",
+                                    null,
+                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+                                )
+                            }
                         }
                     }
                 }
@@ -459,15 +459,7 @@ class ContentForParamsTest : AbstractCoreTest() {
                     }
                     divergentGroup {
                         divergentInstance {
-                            divergent {
-                                bareSignature(
-                                    emptyMap(), "", "", emptySet(), "function", null,
-                                    "first" to ParamAttributes(emptyMap(), emptySet(), "String"),
-                                    "second" to ParamAttributes(emptyMap(), emptySet(), "Int"),
-                                    "third" to ParamAttributes(emptyMap(), emptySet(), "Double")
-                                )
-                            }
-                            after {
+                            before {
                                 pWrapped("comment to function")
                                 header(2) { +"Parameters" }
                                 group {
@@ -485,6 +477,14 @@ class ContentForParamsTest : AbstractCoreTest() {
                                     }
                                 }
                             }
+                            divergent {
+                                bareSignature(
+                                    emptyMap(), "", "", emptySet(), "function", null,
+                                    "first" to ParamAttributes(emptyMap(), emptySet(), "String"),
+                                    "second" to ParamAttributes(emptyMap(), emptySet(), "Int"),
+                                    "third" to ParamAttributes(emptyMap(), emptySet(), "Double")
+                                )
+                            }
                         }
                     }
                 }
@@ -520,15 +520,7 @@ class ContentForParamsTest : AbstractCoreTest() {
                     }
                     divergentGroup {
                         divergentInstance {
-                            divergent {
-                                bareSignature(
-                                    emptyMap(), "", "", emptySet(), "function", null,
-                                    "first" to ParamAttributes(emptyMap(), emptySet(), "String"),
-                                    "second" to ParamAttributes(emptyMap(), emptySet(), "Int"),
-                                    "third" to ParamAttributes(emptyMap(), emptySet(), "Double")
-                                )
-                            }
-                            after {
+                            before {
                                 pWrapped("comment to function")
                                 unnamedTag("Author") { +"Kordyjan" }
                                 unnamedTag("Since") { +"0.11" }
@@ -553,6 +545,14 @@ class ContentForParamsTest : AbstractCoreTest() {
                                     }
                                 }
                             }
+                            divergent {
+                                bareSignature(
+                                    emptyMap(), "", "", emptySet(), "function", null,
+                                    "first" to ParamAttributes(emptyMap(), emptySet(), "String"),
+                                    "second" to ParamAttributes(emptyMap(), emptySet(), "Int"),
+                                    "third" to ParamAttributes(emptyMap(), emptySet(), "Double")
+                                )
+                            }
                         }
                     }
                 }
diff --git a/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt b/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt
index 94288f75..c9adc0bf 100644
--- a/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt
+++ b/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt
@@ -36,7 +36,7 @@ class ContentForSeeAlsoTest : AbstractCoreTest() {
                 val page = module.children.single { it.name == "test" }
                     .children.single { it.name == "function" } as ContentPage
                 page.content.assertNode {
-                    group{
+                    group {
                         header(1) { +"function" }
                     }
                     divergentGroup {
@@ -82,18 +82,7 @@ class ContentForSeeAlsoTest : AbstractCoreTest() {
                     }
                     divergentGroup {
                         divergentInstance {
-                            divergent {
-                                bareSignature(
-                                    emptyMap(),
-                                    "",
-                                    "",
-                                    emptySet(),
-                                    "function",
-                                    null,
-                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
-                                )
-                            }
-                            after {
+                            before {
                                 header(2) { +"See also" }
                                 group {
                                     platformHinted {
@@ -107,6 +96,17 @@ class ContentForSeeAlsoTest : AbstractCoreTest() {
                                     }
                                 }
                             }
+                            divergent {
+                                bareSignature(
+                                    emptyMap(),
+                                    "",
+                                    "",
+                                    emptySet(),
+                                    "function",
+                                    null,
+                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+                                )
+                            }
                         }
                     }
                 }
@@ -137,18 +137,7 @@ class ContentForSeeAlsoTest : AbstractCoreTest() {
                     }
                     divergentGroup {
                         divergentInstance {
-                            divergent {
-                                bareSignature(
-                                    emptyMap(),
-                                    "",
-                                    "",
-                                    emptySet(),
-                                    "function",
-                                    null,
-                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
-                                )
-                            }
-                            after {
+                            before {
                                 header(2) { +"See also" }
                                 group {
                                     platformHinted {
@@ -162,6 +151,17 @@ class ContentForSeeAlsoTest : AbstractCoreTest() {
                                     }
                                 }
                             }
+                            divergent {
+                                bareSignature(
+                                    emptyMap(),
+                                    "",
+                                    "",
+                                    emptySet(),
+                                    "function",
+                                    null,
+                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+                                )
+                            }
                         }
                     }
                 }
@@ -192,18 +192,7 @@ class ContentForSeeAlsoTest : AbstractCoreTest() {
                     }
                     divergentGroup {
                         divergentInstance {
-                            divergent {
-                                bareSignature(
-                                    emptyMap(),
-                                    "",
-                                    "",
-                                    emptySet(),
-                                    "function",
-                                    null,
-                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
-                                )
-                            }
-                            after {
+                            before {
                                 header(2) { +"See also" }
                                 group {
                                     platformHinted {
@@ -217,6 +206,17 @@ class ContentForSeeAlsoTest : AbstractCoreTest() {
                                     }
                                 }
                             }
+                            divergent {
+                                bareSignature(
+                                    emptyMap(),
+                                    "",
+                                    "",
+                                    emptySet(),
+                                    "function",
+                                    null,
+                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+                                )
+                            }
                         }
                     }
                 }
@@ -247,18 +247,7 @@ class ContentForSeeAlsoTest : AbstractCoreTest() {
                     }
                     divergentGroup {
                         divergentInstance {
-                            divergent {
-                                bareSignature(
-                                    emptyMap(),
-                                    "",
-                                    "",
-                                    emptySet(),
-                                    "function",
-                                    null,
-                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
-                                )
-                            }
-                            after {
+                            before {
                                 header(2) { +"See also" }
                                 group {
                                     platformHinted {
@@ -272,6 +261,17 @@ class ContentForSeeAlsoTest : AbstractCoreTest() {
                                     }
                                 }
                             }
+                            divergent {
+                                bareSignature(
+                                    emptyMap(),
+                                    "",
+                                    "",
+                                    emptySet(),
+                                    "function",
+                                    null,
+                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+                                )
+                            }
                         }
                     }
                 }
@@ -305,18 +305,7 @@ class ContentForSeeAlsoTest : AbstractCoreTest() {
                     }
                     divergentGroup {
                         divergentInstance {
-                            divergent {
-                                bareSignature(
-                                    emptyMap(),
-                                    "",
-                                    "",
-                                    emptySet(),
-                                    "function",
-                                    null,
-                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
-                                )
-                            }
-                            after {
+                            before {
                                 pWrapped("random comment")
                                 unnamedTag("Author") { +"pikinier20" }
                                 unnamedTag("Since") { +"0.11" }
@@ -334,6 +323,17 @@ class ContentForSeeAlsoTest : AbstractCoreTest() {
                                     }
                                 }
                             }
+                            divergent {
+                                bareSignature(
+                                    emptyMap(),
+                                    "",
+                                    "",
+                                    emptySet(),
+                                    "function",
+                                    null,
+                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+                                )
+                            }
                         }
                     }
                 }
@@ -365,18 +365,7 @@ class ContentForSeeAlsoTest : AbstractCoreTest() {
                     }
                     divergentGroup {
                         divergentInstance {
-                            divergent {
-                                bareSignature(
-                                    emptyMap(),
-                                    "",
-                                    "",
-                                    emptySet(),
-                                    "function",
-                                    null,
-                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
-                                )
-                            }
-                            after {
+                            before {
                                 header(2) { +"See also" }
                                 group {
                                     platformHinted {
@@ -390,6 +379,17 @@ class ContentForSeeAlsoTest : AbstractCoreTest() {
                                     }
                                 }
                             }
+                            divergent {
+                                bareSignature(
+                                    emptyMap(),
+                                    "",
+                                    "",
+                                    emptySet(),
+                                    "function",
+                                    null,
+                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+                                )
+                            }
                         }
                     }
                 }
@@ -421,18 +421,7 @@ class ContentForSeeAlsoTest : AbstractCoreTest() {
                     }
                     divergentGroup {
                         divergentInstance {
-                            divergent {
-                                bareSignature(
-                                    emptyMap(),
-                                    "",
-                                    "",
-                                    emptySet(),
-                                    "function",
-                                    null,
-                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
-                                )
-                            }
-                            after {
+                            before {
                                 header(2) { +"See also" }
                                 group {
                                     platformHinted {
@@ -451,6 +440,17 @@ class ContentForSeeAlsoTest : AbstractCoreTest() {
                                     }
                                 }
                             }
+                            divergent {
+                                bareSignature(
+                                    emptyMap(),
+                                    "",
+                                    "",
+                                    emptySet(),
+                                    "function",
+                                    null,
+                                    "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+                                )
+                            }
                         }
                     }
                 }
diff --git a/plugins/base/src/test/kotlin/enums/EnumsTest.kt b/plugins/base/src/test/kotlin/enums/EnumsTest.kt
index 74fc18c4..a8b78baa 100644
--- a/plugins/base/src/test/kotlin/enums/EnumsTest.kt
+++ b/plugins/base/src/test/kotlin/enums/EnumsTest.kt
@@ -192,6 +192,9 @@ class EnumsTest : AbstractCoreTest() {
             |package enums
             |
             |enum class Test {
+            |    /**
+            |       Sample docs for E1
+            |    **/
             |    @SinceKotlin("1.3")
             |    E1
             |}
@@ -205,6 +208,11 @@ class EnumsTest : AbstractCoreTest() {
                 signature.assertNode {
                     header(1) { +"E1" }
                     platformHinted {
+                        group {
+                            group {
+                                + "Sample docs for E1"
+                            }
+                        }
                         group {
                             mapOf("SinceKotlin" to setOf("version")).entries.forEach {
                                 group {
diff --git a/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt
index 33d3f64c..7cdb0de3 100644
--- a/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt
+++ b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt
@@ -164,13 +164,11 @@ class LinkableContentTest : AbstractCoreTest() {
                     val function = classChildren.find { it.name == "printWithExclamation" }
                     val text = function.cast<MemberPageNode>().content.cast<ContentGroup>().children.last()
                         .cast<ContentDivergentGroup>().children.single()
-                        .cast<ContentDivergentInstance>().after
+                        .cast<ContentDivergentInstance>().before
                         .cast<ContentGroup>().children.last()
                         .cast<ContentGroup>().children.last()
                         .cast<PlatformHintedContent>().children.single()
                         .cast<ContentGroup>().children.single()
-                        .cast<ContentTable>().children.single()
-                        .cast<ContentGroup>().children.single()
                         .cast<ContentGroup>().children.single()
                         .cast<ContentCode>().children.single().cast<ContentText>().text
                     Assertions.assertEquals(
diff --git a/plugins/base/src/test/kotlin/markdown/LinkTest.kt b/plugins/base/src/test/kotlin/markdown/LinkTest.kt
index fe5b573a..20bd24ee 100644
--- a/plugins/base/src/test/kotlin/markdown/LinkTest.kt
+++ b/plugins/base/src/test/kotlin/markdown/LinkTest.kt
@@ -33,15 +33,12 @@ class LinkTest : AbstractCoreTest() {
             configuration
         ) {
             renderingStage = { rootPageNode, _ ->
-                (rootPageNode.children.single().children.single() as MemberPageNode)
+                assertNotNull((rootPageNode.children.single().children.single() as MemberPageNode)
                     .content
-                    .dfs { node -> node is ContentDRILink }
-                    .let {
-                        assertEquals(
-                            "parser//test/#java.lang.ClassLoader/PointingToDeclaration/",
-                            (it as ContentDRILink).address.toString()
-                        )
-                    }
+                    .dfs { node ->
+                        node is ContentDRILink &&
+                                node.address.toString() == "parser//test/#java.lang.ClassLoader/PointingToDeclaration/"}
+                )
             }
         }
     }
diff --git a/plugins/base/src/test/kotlin/renderers/RenderingOnlyTestBase.kt b/plugins/base/src/test/kotlin/renderers/RenderingOnlyTestBase.kt
index f58b0e24..a3bf9188 100644
--- a/plugins/base/src/test/kotlin/renderers/RenderingOnlyTestBase.kt
+++ b/plugins/base/src/test/kotlin/renderers/RenderingOnlyTestBase.kt
@@ -90,6 +90,7 @@ fun Element.match(vararg matchers: Any): Unit =
 open class Tag(val name: String, vararg val matchers: Any)
 class Div(vararg matchers: Any): Tag("div", *matchers)
 class P(vararg matchers: Any): Tag("p", *matchers)
+class Span(vararg matchers: Any): Tag("span", *matchers)
 
 private fun Any.accepts(n: Node) {
     when (this) {
diff --git a/plugins/base/src/test/kotlin/renderers/html/DivergentTest.kt b/plugins/base/src/test/kotlin/renderers/html/DivergentTest.kt
index 54bac755..fa129760 100644
--- a/plugins/base/src/test/kotlin/renderers/html/DivergentTest.kt
+++ b/plugins/base/src/test/kotlin/renderers/html/DivergentTest.kt
@@ -6,12 +6,8 @@ import org.jetbrains.dokka.base.renderers.html.HtmlRenderer
 import org.jetbrains.dokka.links.DRI
 import org.jetbrains.dokka.model.SourceSetData
 import org.jetbrains.dokka.pages.ContentDivergentGroup
-import org.jetbrains.dokka.pages.TextStyle
 import org.junit.jupiter.api.Test
-import renderers.Div
-import renderers.RenderingOnlyTestBase
-import renderers.TestPage
-import renderers.match
+import renderers.*
 
 class DivergentTest : RenderingOnlyTestBase() {
     private val js = SourceSetData("root", "JS", Platform.js, listOf(SourceRootImpl("pl1")))
@@ -182,10 +178,10 @@ class DivergentTest : RenderingOnlyTestBase() {
 
         HtmlRenderer(context).render(page)
         renderedContent.match(
-            Div(Div(Div(Div("a")), Div(Div())), "a+",),
-            Div(Div(Div(Div("bd")), Div(Div())), "bd+"),
-            Div(Div(Div(Div("c")), Div(Div()))),
-            Div(Div(Div(Div("e")), Div(Div())), "e+")
+            Div(Div(Span(Div(Div("NATIVE")))), Div(Div(Div("a"))),"a+"),
+            Div(Div(Span(Div(Div("JS")))), Div(Div(Div("bd"))),"bd+"),
+            Div(Div(Span(Div(Div("JVM")))), Div(Div(Div("c")))),
+            Div(Div(Span(Div(Div("NATIVE")))), Div(Div(Div("e"))),"e+"),
         )
     }
 
@@ -215,7 +211,10 @@ class DivergentTest : RenderingOnlyTestBase() {
         HtmlRenderer(context).render(page)
         renderedContent.match(
             Div(
-                "ab-",
+                Div(
+                    "ab-",
+                    Span()
+                ),
                 Div(Div(Div("ab")))
             )
         )
@@ -283,10 +282,9 @@ class DivergentTest : RenderingOnlyTestBase() {
         }
 
         HtmlRenderer(context).render(page)
-        val r = renderedContent
         renderedContent.match(
             Div(
-                "ab-",
+                Div("ab-", Span()),
                 Div(Div(Div("ab"))),
                 "ab+"
             )
@@ -324,8 +322,8 @@ class DivergentTest : RenderingOnlyTestBase() {
 
         HtmlRenderer(context).render(page)
         renderedContent.match(
-            Div("a-", Div(Div(Div("a")), Div(Div("NATIVE"))), "ab+"),
-            Div("b-", Div(Div(Div("b")), Div(Div("NATIVE"))), "ab+")
+            Div(Div("a-", Span()), Div(Div(Div("a"))), "ab+"),
+            Div(Div("b-", Span()), Div(Div(Div(("b")))), "ab+")
         )
     }
 }
\ No newline at end of file
-- 
cgit 


From 4a360b13c6d2c175aed57d7af878e06ab6a044a7 Mon Sep 17 00:00:00 2001
From: Filip Zybała <fzybala@virtuslab.com>
Date: Tue, 7 Jul 2020 13:22:09 +0200
Subject: Separated search key and display level in search component

---
 .../src/main/components/search/search.scss         | 22 ++++++++++++++++++++++
 .../frontend/src/main/components/search/search.tsx | 22 +++++++++++++++++++---
 .../frontend/src/main/components/search/types.ts   |  5 +++++
 .../src/main/kotlin/renderers/html/HtmlRenderer.kt |  6 +++---
 4 files changed, 49 insertions(+), 6 deletions(-)

(limited to 'plugins/base/frontend/src/main/components/search/search.tsx')

diff --git a/plugins/base/frontend/src/main/components/search/search.scss b/plugins/base/frontend/src/main/components/search/search.scss
index c3b1ab3b..cc5a61ac 100644
--- a/plugins/base/frontend/src/main/components/search/search.scss
+++ b/plugins/base/frontend/src/main/components/search/search.scss
@@ -12,4 +12,26 @@
 
 .popup-wrapper {
   min-width: calc(100% - 360px) !important;
+}
+
+.indented {
+  text-indent: 10px;
+}
+
+.disabled {
+  color: gray;
+}
+
+.template-wrapper {
+  display: grid;
+  grid-template-columns: auto auto;
+}
+
+.template-name {
+  justify-self: start;
+}
+
+.template-description {
+  color: gray;
+  justify-self: end;
 }
\ No newline at end of file
diff --git a/plugins/base/frontend/src/main/components/search/search.tsx b/plugins/base/frontend/src/main/components/search/search.tsx
index b0fdd96a..c7b36654 100644
--- a/plugins/base/frontend/src/main/components/search/search.tsx
+++ b/plugins/base/frontend/src/main/components/search/search.tsx
@@ -1,8 +1,9 @@
 import React, {useCallback, useState} from 'react';
 import {Select} from '@jetbrains/ring-ui';
+import {List} from '@jetbrains/ring-ui';
 import '@jetbrains/ring-ui/components/input-size/input-size.scss';
 import './search.scss';
-import {IWindow, Option, Props} from "./types";
+import {IWindow, Option, Props, Page} from "./types";
 
 const WithFuzzySearchFilterComponent: React.FC<Props> = ({data}: Props) => {
     const [selected, onSelected] = useState<Option>(data[0]);
@@ -43,15 +44,30 @@ const WithFuzzySearchFilterComponent: React.FC<Props> = ({data}: Props) => {
     )
 }
 
+const templateGenerator = (page:Page) => {
+    let classGenerator = (page:Page) => {
+        let classes = ""
+        if(page.level !== undefined) classes = classes + " indented"
+        if(page.disabled) classes = classes + " disabled"
+        return classes
+    }
+    return <div className="template-wrapper">
+        <span className= {classGenerator(page)}>{page.name}</span>
+        <span className="template-description">{page.description}</span>
+    </div>
+}
+
 export const WithFuzzySearchFilter = () => {
     let data: Option[] = [];
     const pages = (window as IWindow).pages;
     if (pages) {
         data = pages.map((page, i) => ({
             ...page,
-            label: page.name,
+            label: page.searchKey,
             key: i + 1,
-            type: page.kind
+            type: page.kind,
+            template: templateGenerator(page),
+            rgItemType: List.ListProps.Type.CUSTOM
         }));
     }
 
diff --git a/plugins/base/frontend/src/main/components/search/types.ts b/plugins/base/frontend/src/main/components/search/types.ts
index 2900153a..881a16d8 100644
--- a/plugins/base/frontend/src/main/components/search/types.ts
+++ b/plugins/base/frontend/src/main/components/search/types.ts
@@ -2,6 +2,11 @@ export type Page = {
     name: string;
     kind: string;
     location: string;
+    searchKey: string;
+    level: number;
+    index: string;
+    description: string;
+    disabled: boolean;
 }
 
 export type Option = Page & {
diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
index f8fa3b2e..a950667f 100644
--- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
+++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
@@ -605,14 +605,14 @@ open class HtmlRenderer(
             .mapIndexed { topLevelIndex, entry ->
                 if (entry.value.size > 1) {
                     listOf(
-                        "{\'name\': \'${entry.key}\', \'index\': \'$topLevelIndex\', \'disabled\': true}"
+                        "{\'name\': \'${entry.key}\', \'index\': \'$topLevelIndex\', \'disabled\': true, \'searchKey\':\'${entry.key}\' }"
                     ) + entry.value.mapIndexed { index, subentry ->
-                        "{\'name\': \'${subentry.value.first}\', \'level\': 1, \'index\': \'$topLevelIndex.$index\', \'description\':\'${subentry.key}\', \'location\':\'${subentry.value.second}\'}"
+                        "{\'name\': \'${subentry.value.first}\', \'level\': 1, \'index\': \'$topLevelIndex.$index\', \'description\':\'${subentry.key}\', \'location\':\'${subentry.value.second}\', 'searchKey':'${entry.key}'}"
                     }
                 } else {
                     val subentry = entry.value.single()
                     listOf(
-                        "{\'name\': \'${subentry.value.first}\', \'index\': \'$topLevelIndex\', \'description\':\'${subentry.key}\', \'location\':\'${subentry.value.second}\'}"
+                        "{\'name\': \'${subentry.value.first}\', \'index\': \'$topLevelIndex\', \'description\':\'${subentry.key}\', \'location\':\'${subentry.value.second}\', 'searchKey':'${entry.key}'}"
                     )
                 }
             }
-- 
cgit 

