-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
282 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,14 @@ | ||
package dev.triumphteam.backend.website | ||
|
||
import dev.triumphteam.backend.meilisearch.Meili | ||
import dev.triumphteam.backend.website.pages.docs.docsRoutes | ||
import dev.triumphteam.backend.website.pages.home.homeRoutes | ||
import io.ktor.server.application.plugin | ||
import io.ktor.server.routing.Routing | ||
|
||
public fun Routing.websiteRoutes(developmentMode: Boolean) { | ||
val meili = plugin(Meili) | ||
|
||
homeRoutes(developmentMode) | ||
docsRoutes(developmentMode) | ||
docsRoutes(meili, developmentMode) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,23 +2,31 @@ package dev.triumphteam.backend.website.pages.docs | |
|
||
import com.github.benmanes.caffeine.cache.Cache | ||
import com.github.benmanes.caffeine.cache.Caffeine | ||
import dev.triumphteam.backend.api.SearchDocument | ||
import dev.triumphteam.backend.api.database.DocVersionEntity | ||
import dev.triumphteam.backend.api.database.DocVersions | ||
import dev.triumphteam.backend.api.database.PageEntity | ||
import dev.triumphteam.backend.api.database.Pages | ||
import dev.triumphteam.backend.api.database.ProjectEntity | ||
import dev.triumphteam.backend.api.projectIndex | ||
import dev.triumphteam.backend.meilisearch.Meili | ||
import dev.triumphteam.backend.website.pages.createIconPath | ||
import dev.triumphteam.backend.website.pages.docs.components.DropdownOption | ||
import dev.triumphteam.backend.website.pages.docs.components.dropDown | ||
import dev.triumphteam.website.highlightWord | ||
import dev.triumphteam.backend.website.pages.docs.components.noResults | ||
import dev.triumphteam.backend.website.pages.docs.components.search | ||
import dev.triumphteam.backend.website.pages.docs.components.searchArea | ||
import dev.triumphteam.backend.website.pages.docs.components.searchResult | ||
import dev.triumphteam.backend.website.pages.docs.components.toast | ||
import dev.triumphteam.website.trim | ||
import dev.triumphteam.backend.website.pages.setupHead | ||
import dev.triumphteam.backend.website.respondHtmlCached | ||
import dev.triumphteam.website.project.Navigation | ||
import dev.triumphteam.website.project.Page | ||
import io.ktor.http.HttpStatusCode | ||
import io.ktor.server.application.call | ||
import io.ktor.server.html.respondHtml | ||
import io.ktor.server.request.uri | ||
import io.ktor.server.response.respond | ||
import io.ktor.server.response.respondRedirect | ||
|
@@ -27,6 +35,7 @@ import io.ktor.server.routing.get | |
import kotlinx.css.Color | ||
import kotlinx.css.CssBuilder | ||
import kotlinx.css.backgroundColor | ||
import kotlinx.css.borderColor | ||
import kotlinx.css.color | ||
import kotlinx.css.properties.s | ||
import kotlinx.css.transitionDuration | ||
|
@@ -49,6 +58,7 @@ import kotlinx.html.styleLink | |
import kotlinx.html.title | ||
import kotlinx.html.ul | ||
import kotlinx.html.unsafe | ||
import kotlinx.serialization.Serializable | ||
import org.jetbrains.exposed.sql.and | ||
import org.jetbrains.exposed.sql.transactions.transaction | ||
import java.time.LocalDate | ||
|
@@ -59,7 +69,7 @@ private val projectCache: Cache<String, ProjectData> = Caffeine.newBuilder() | |
.expireAfterWrite(5.minutes.toJavaDuration()) | ||
.build() | ||
|
||
public fun Routing.docsRoutes(developmentMode: Boolean) { | ||
public fun Routing.docsRoutes(meili: Meili, developmentMode: Boolean) { | ||
|
||
get("/docs/{param...}") { | ||
|
||
|
@@ -89,6 +99,58 @@ public fun Routing.docsRoutes(developmentMode: Boolean) { | |
renderFullPage(developmentMode, project, currentVersion, page) | ||
} | ||
} | ||
|
||
get("/search") { | ||
|
||
fun HTML.respondEmpty(query: String = "") { | ||
body { | ||
noResults(query) | ||
} | ||
} | ||
|
||
val query = call.request.queryParameters["q"] ?: return@get call.respond(HttpStatusCode.BadRequest) | ||
val projectParam = call.request.queryParameters["p"] ?: return@get call.respond(HttpStatusCode.BadRequest) | ||
val versionParam = call.request.queryParameters["v"] ?: return@get call.respond(HttpStatusCode.BadRequest) | ||
|
||
if (query.isBlank()) { | ||
return@get call.respondHtml { | ||
respondEmpty() | ||
} | ||
} | ||
|
||
val project = getProject(projectParam) ?: return@get call.respond(HttpStatusCode.NotFound) | ||
val pages = project.versions[versionParam]?.pages ?: return@get call.respond(HttpStatusCode.NotFound) | ||
|
||
val result = | ||
meili.client.index(projectIndex(projectParam, versionParam)).search<SearchDocument>(query, limit = 5) | ||
.mapNotNull { document -> | ||
val page = pages[document.pageId] ?: return@mapNotNull null | ||
val summary = page.summary.find { it.href == document.anchor } ?: return@mapNotNull null | ||
|
||
val queryWords = query.split(" ") | ||
val trimmedDescription = summary.terms.joinToString(" ").trim(queryWords.first(), 50) | ||
|
||
SearchResult( | ||
title = "${page.title} | ${summary.literal}", | ||
description = trimmedDescription.highlightWord(queryWords), | ||
link = "/docs/$versionParam/$projectParam/${page.id}#${document.anchor}", | ||
) | ||
} | ||
|
||
if (result.isEmpty()) { | ||
return@get call.respondHtml { | ||
respondEmpty(query) | ||
} | ||
} | ||
|
||
return@get call.respondHtml { | ||
body { | ||
result.forEach { result -> | ||
searchResult(result.title, result.description, result.link) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
private fun HTML.renderFullPage( | ||
|
@@ -103,6 +165,10 @@ private fun HTML.renderFullPage( | |
styleLink("/static/css/docs_content.css") | ||
styleLink("/static/css/themes/one_dark.css") | ||
|
||
script { | ||
src = "https://unpkg.com/[email protected]" | ||
} | ||
|
||
val title = "TrimphTeam | ${project.name} - ${currentPage.title}" | ||
// TODO: Replace with final URL, sucks that it can't be relative | ||
val image = "https://new.triumphteam.dev/assets/${project.id}/${version.reference}/${currentPage.id}/banner.png" | ||
|
@@ -175,10 +241,18 @@ private fun HTML.renderFullPage( | |
transitionDuration = 0.3.s | ||
} | ||
|
||
rule(".project-color-border") { | ||
transitionDuration = 0.3.s | ||
} | ||
|
||
rule(".project-color-hover:hover") { | ||
color = Color(project.color) | ||
} | ||
|
||
rule(".project-color-border:hover") { | ||
borderColor = Color(project.color) | ||
} | ||
|
||
rule(".docs-content a") { | ||
color = Color(project.color) | ||
transitionDuration = 0.3.s | ||
|
@@ -190,17 +264,16 @@ private fun HTML.renderFullPage( | |
rule(".summary-active *") { | ||
color = Color(project.color) | ||
} | ||
|
||
}.toString() | ||
} | ||
} | ||
|
||
body { | ||
|
||
classes = setOf("bg-docs-bg", "text-white", "overflow-y-auto overflow-x-hidden", "opened-search") | ||
classes = setOf("bg-docs-bg", "text-white", "overflow-y-auto overflow-x-hidden") | ||
|
||
// Must be first here | ||
searchArea() | ||
searchArea(project.id, version.reference) | ||
|
||
sideBar(project, version, currentPage) | ||
content(currentPage) | ||
|
@@ -494,3 +567,10 @@ public data class ProjectPage( | |
private fun cacheId(project: ProjectData, version: Version, page: ProjectPage): String { | ||
return "${project.id}:${version.reference}:${page.id}" | ||
} | ||
|
||
@Serializable | ||
private data class SearchResult( | ||
val title: String, | ||
val description: String, | ||
val link: String, | ||
) |
Oops, something went wrong.