diff --git a/.gitignore b/.gitignore index c58e766..fd4efa3 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,11 @@ out/ .DS_Store # Maven -target/ \ No newline at end of file +target/ + +/config +/h2 +/files + +# Deployment env files +/deploy/*.env \ No newline at end of file diff --git a/con4md.jar b/con4md.jar deleted file mode 100644 index 5d76406..0000000 Binary files a/con4md.jar and /dev/null differ diff --git a/deploy/Dockerfile b/deploy/Dockerfile new file mode 100644 index 0000000..3d34be8 --- /dev/null +++ b/deploy/Dockerfile @@ -0,0 +1,10 @@ +FROM maven:3.9.7-eclipse-temurin-21 AS build +WORKDIR /build +COPY pom.xml . +COPY src ./src +RUN mvn package -DskipTests + +FROM eclipse-temurin:21-jre +WORKDIR /app +COPY --from=build /build/target/*SNAPSHOT.jar app.jar +ENTRYPOINT ["java","-jar","app.jar"] diff --git a/deploy/deploy.ps1 b/deploy/deploy.ps1 new file mode 100644 index 0000000..6efb0b8 --- /dev/null +++ b/deploy/deploy.ps1 @@ -0,0 +1,38 @@ +param( + [Parameter(Mandatory=$true)] + [string]$EnvFile +) + +if (!(Test-Path $EnvFile)) { + Write-Error "Environment file '$EnvFile' not found" + exit 1 +} + +Get-Content $EnvFile | Where-Object { $_ -match '=' } | ForEach-Object { + $pair = $_ -split '=',2 + $name = $pair[0] + $value = $pair[1] + Set-Item -Path "Env:$name" -Value $value +} + +$azCmd = Get-Command az -ErrorAction SilentlyContinue +if (-not $azCmd) { + Write-Error "Azure CLI (az) not found. Please install it: https://learn.microsoft.com/en-us/cli/azure/install-azure-cli" + exit 1 +} + +$null = az account show 2>$null +if ($LASTEXITCODE -ne 0) { + az login --tenant $Env:TENANT_ID +} +$null = az account show --subscription $Env:SUBSCRIPTION_ID 2>$null +if ($LASTEXITCODE -ne 0) { + az login --tenant $Env:TENANT_ID +} +az account set --subscription $Env:SUBSCRIPTION_ID +az acr login --name $Env:ACR_NAME +$RepoRoot = Resolve-Path (Join-Path $PSScriptRoot '..') +docker build -f "$PSScriptRoot/Dockerfile" -t $Env:IMAGE_NAME $RepoRoot +docker tag $Env:IMAGE_NAME "$($Env:ACR_NAME).azurecr.io/$($Env:IMAGE_NAME)" +docker push "$($Env:ACR_NAME).azurecr.io/$($Env:IMAGE_NAME)" + diff --git a/deploy/deploy.sh b/deploy/deploy.sh new file mode 100644 index 0000000..50e609e --- /dev/null +++ b/deploy/deploy.sh @@ -0,0 +1,41 @@ +#!/bin/bash +set -e + +if [ -z "$1" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +ENV_FILE="$1" +if [ ! -f "$ENV_FILE" ]; then + echo "Environment file '$ENV_FILE' not found" >&2 + exit 1 +fi + +# Load variables from the env file +set -o allexport + +source "$ENV_FILE" +set +o allexport + +# Ensure Azure CLI is installed +if ! command -v az >/dev/null 2>&1; then + echo "Azure CLI (az) not found. Please install it: https://learn.microsoft.com/en-us/cli/azure/install-azure-cli" >&2 + exit 1 +fi + +# Login and push image to ACR +if ! az account show >/dev/null 2>&1; then + az login --tenant "$TENANT_ID" +fi +if ! az account show --subscription "$SUBSCRIPTION_ID" >/dev/null 2>&1; then + az login --tenant "$TENANT_ID" +fi +az account set --subscription "$SUBSCRIPTION_ID" +az acr login --name "$ACR_NAME" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +docker build -f "$SCRIPT_DIR/Dockerfile" -t "$IMAGE_NAME" "$REPO_ROOT" +docker tag "$IMAGE_NAME" "$ACR_NAME.azurecr.io/$IMAGE_NAME" +docker push "$ACR_NAME.azurecr.io/$IMAGE_NAME" + diff --git a/deploy/deploy_prod.ps1 b/deploy/deploy_prod.ps1 new file mode 100644 index 0000000..3b28c38 --- /dev/null +++ b/deploy/deploy_prod.ps1 @@ -0,0 +1,3 @@ +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +& "$scriptDir/deploy.ps1" "$scriptDir/prod.env" + diff --git a/deploy/deploy_prod.sh b/deploy/deploy_prod.sh new file mode 100644 index 0000000..b341dd3 --- /dev/null +++ b/deploy/deploy_prod.sh @@ -0,0 +1,4 @@ +#!/bin/bash +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +"$SCRIPT_DIR/deploy.sh" "$SCRIPT_DIR/prod.env" + diff --git a/deploy/deploy_test.ps1 b/deploy/deploy_test.ps1 new file mode 100644 index 0000000..c3142cd --- /dev/null +++ b/deploy/deploy_test.ps1 @@ -0,0 +1,2 @@ +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +powershell.exe -ExecutionPolicy Bypass -File "$scriptDir/deploy.ps1" "$scriptDir/test.env" \ No newline at end of file diff --git a/deploy/deploy_test.sh b/deploy/deploy_test.sh new file mode 100644 index 0000000..ac9c6bc --- /dev/null +++ b/deploy/deploy_test.sh @@ -0,0 +1,4 @@ +#!/bin/bash +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +"$SCRIPT_DIR/deploy.sh" "$SCRIPT_DIR/test.env" + diff --git a/deploy/test.env.sample b/deploy/test.env.sample new file mode 100644 index 0000000..39db523 --- /dev/null +++ b/deploy/test.env.sample @@ -0,0 +1,6 @@ +# Sample environment configuration for deployment +SUBSCRIPTION_ID=your-subscription-id +RESOURCE_GROUP=your-resource-group +ACR_NAME=your-acr-name +IMAGE_NAME=myapp:latest +TENANT_ID=your-tenant-id diff --git a/done.png b/done.png deleted file mode 100644 index 97b7492..0000000 Binary files a/done.png and /dev/null differ diff --git a/mcon.bat b/mcon.bat deleted file mode 100644 index 270c7d9..0000000 --- a/mcon.bat +++ /dev/null @@ -1 +0,0 @@ -@java -jar con4md.jar --marker="##" %* \ No newline at end of file diff --git a/mcon.sh b/mcon.sh deleted file mode 100644 index 20841ad..0000000 --- a/mcon.sh +++ /dev/null @@ -1 +0,0 @@ -java -jar con4md.jar --marker="##" $@ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..6e1968f --- /dev/null +++ b/pom.xml @@ -0,0 +1,71 @@ + + 4.0.0 + + com.li.javainterview + interview-web + 0.0.1-SNAPSHOT + jar + interview-web + + + org.springframework.boot + spring-boot-starter-parent + 3.5.0 + + + + + 21 + 21 + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + org.commonmark + commonmark + 0.25.0 + + + org.projectlombok + lombok + 1.18.32 + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + maven-compiler-plugin + + ${maven.compiler.release} + + + org.projectlombok + lombok + 1.18.32 + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/src/main/java/com/li/javainterview/InterviewWebApplication.java b/src/main/java/com/li/javainterview/InterviewWebApplication.java new file mode 100644 index 0000000..f32cf4e --- /dev/null +++ b/src/main/java/com/li/javainterview/InterviewWebApplication.java @@ -0,0 +1,11 @@ +package com.li.javainterview; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class InterviewWebApplication { + public static void main(String[] args) { + SpringApplication.run(InterviewWebApplication.class, args); + } +} diff --git a/src/main/java/com/li/javainterview/controller/DataController.java b/src/main/java/com/li/javainterview/controller/DataController.java new file mode 100644 index 0000000..a8ec453 --- /dev/null +++ b/src/main/java/com/li/javainterview/controller/DataController.java @@ -0,0 +1,37 @@ +package com.li.javainterview.controller; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.http.MediaType; +import org.springframework.http.MediaTypeFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.util.AntPathMatcher; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.HandlerMapping; + +import java.io.IOException; + +@RestController +public class DataController { + private final ResourceLoader resourceLoader; + private final AntPathMatcher matcher = new AntPathMatcher(); + + public DataController(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + + @GetMapping("/data/**") + public ResponseEntity getData(HttpServletRequest request) throws IOException { + String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE); + String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); + String relativePath = matcher.extractPathWithinPattern(bestMatchPattern, path); + Resource resource = resourceLoader.getResource("classpath:data/" + relativePath); + if (!resource.exists()) { + return ResponseEntity.notFound().build(); + } + MediaType type = MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM); + return ResponseEntity.ok().contentType(type).body(resource); + } +} diff --git a/src/main/java/com/li/javainterview/controller/QuestionController.java b/src/main/java/com/li/javainterview/controller/QuestionController.java new file mode 100644 index 0000000..3311e64 --- /dev/null +++ b/src/main/java/com/li/javainterview/controller/QuestionController.java @@ -0,0 +1,120 @@ +package com.li.javainterview.controller; + +import com.li.javainterview.service.QuestionService; +import com.li.javainterview.model.QuestionAnswer; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +@Controller +public class QuestionController { + + private final QuestionService service; + + public QuestionController(QuestionService service) { + this.service = service; + } + + @GetMapping("/") + public String categories(Model model, + @CookieValue(value = "categories", required = false) String categoriesCookie, + HttpSession session) { + model.addAttribute("categories", service.categories()); + if (categoriesCookie != null) { + List selected = parseCategories(categoriesCookie); + model.addAttribute("selectedCategories", selected); + session.setAttribute("categories", selected); + } + session.removeAttribute("history"); + session.removeAttribute("index"); + return "index"; + } + + @GetMapping("/question") + public String randomQuestion(@RequestParam(name = "category", required = false) List categories, + @RequestParam(name = "nav", required = false) String nav, + @CookieValue(value = "categories", required = false) String categoriesCookie, + HttpServletResponse response, + HttpSession session, + Model model) { + if (categories != null) { + String raw = String.join(",", categories); + String encoded = URLEncoder.encode(raw, StandardCharsets.UTF_8); + Cookie c = new Cookie("categories", encoded); + c.setPath("/"); + c.setMaxAge(60 * 60 * 24 * 30); + response.addCookie(c); + session.setAttribute("categories", categories); + session.removeAttribute("history"); + session.removeAttribute("index"); + } else { + Object fromSession = session.getAttribute("categories"); + if (fromSession == null && categoriesCookie != null) { + categories = parseCategories(categoriesCookie); + session.setAttribute("categories", categories); + } else { + categories = (List) fromSession; + } + } + + List history = (List) session.getAttribute("history"); + Integer index = (Integer) session.getAttribute("index"); + if (history == null) { + history = new ArrayList<>(); + index = -1; + } + + QuestionAnswer qa = null; + if ("back".equals(nav)) { + if (index != null && index > 0) { + index--; + qa = history.get(index); + } + } else { + if (index != null && history.size() > index + 1) { + history = history.subList(0, index + 1); + } + Optional opt = service.getRandomQuestion(categories); + if (opt.isPresent()) { + qa = opt.get(); + history.add(qa); + index = index == null ? 0 : index + 1; + } + } + + session.setAttribute("history", history); + session.setAttribute("index", index); + + if (qa != null) { + model.addAttribute("qa", qa); + } + model.addAttribute("categories", categories); + return "question"; + } + + private List parseCategories(String value) { + String decoded; + try { + decoded = URLDecoder.decode(value, StandardCharsets.UTF_8); + } catch (IllegalArgumentException e) { + decoded = ""; + } + return Arrays.stream(decoded.split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .toList(); + } +} diff --git a/src/main/java/com/li/javainterview/model/QuestionAnswer.java b/src/main/java/com/li/javainterview/model/QuestionAnswer.java new file mode 100644 index 0000000..8357f26 --- /dev/null +++ b/src/main/java/com/li/javainterview/model/QuestionAnswer.java @@ -0,0 +1,12 @@ +package com.li.javainterview.model; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class QuestionAnswer { + private String category; + private String question; + private String answer; +} diff --git a/src/main/java/com/li/javainterview/service/MarkdownService.java b/src/main/java/com/li/javainterview/service/MarkdownService.java new file mode 100644 index 0000000..731a5c5 --- /dev/null +++ b/src/main/java/com/li/javainterview/service/MarkdownService.java @@ -0,0 +1,18 @@ +package com.li.javainterview.service; + +import org.commonmark.parser.Parser; +import org.commonmark.renderer.html.HtmlRenderer; +import org.springframework.stereotype.Service; + +@Service +public class MarkdownService { + private final Parser parser = Parser.builder().build(); + private final HtmlRenderer renderer = HtmlRenderer.builder().build(); + + public String toHtml(String markdown) { + if (markdown == null || markdown.isEmpty()) { + return ""; + } + return renderer.render(parser.parse(markdown)); + } +} diff --git a/src/main/java/com/li/javainterview/service/QuestionService.java b/src/main/java/com/li/javainterview/service/QuestionService.java new file mode 100644 index 0000000..ebe18a3 --- /dev/null +++ b/src/main/java/com/li/javainterview/service/QuestionService.java @@ -0,0 +1,152 @@ +package com.li.javainterview.service; + +import com.li.javainterview.model.QuestionAnswer; +import jakarta.annotation.PostConstruct; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.stereotype.Service; + + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Service +public class QuestionService { + private final Map> questionsByCategory = new LinkedHashMap<>(); + private final Random random = new Random(); + private final ResourceLoader resourceLoader; + private final MarkdownService markdownService; + + public QuestionService(ResourceLoader resourceLoader, MarkdownService markdownService) { + this.resourceLoader = resourceLoader; + this.markdownService = markdownService; + } + + private static final Pattern LINK_PATTERN = Pattern.compile("\\+ \\[(.+?)\\]\\((.+?)\\)"); + + @PostConstruct + public void init() throws IOException { + parseReadme(); + } + + private void parseReadme() throws IOException { + Resource res = resourceLoader.getResource("classpath:data/README.md"); + if (!res.exists()) { + return; + } + List lines; + try (BufferedReader br = new BufferedReader(new InputStreamReader(res.getInputStream(), StandardCharsets.UTF_8))) { + lines = br.lines().toList(); + } + List categories = new ArrayList<>(); + boolean inCategoryList = false; + for (String l : lines) { + String line = l.trim(); + if (line.startsWith("# Вопросы для собеседования")) { + inCategoryList = true; + continue; + } + if (inCategoryList) { + if (line.startsWith("+ [")) { + Matcher m = LINK_PATTERN.matcher(line); + if (m.find()) { + categories.add(m.group(1)); + } + } else if (line.startsWith("[done]")) { + inCategoryList = false; + } + continue; + } + } + + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i).trim(); + if (line.startsWith("## ")) { + String header = line.substring(3).trim(); + if (categories.contains(header)) { + String category = header; + List list = new ArrayList<>(); + for (int j = i + 1; j < lines.size(); j++) { + String qLine = lines.get(j).trim(); + if (qLine.startsWith("+ [")) { + Matcher m = LINK_PATTERN.matcher(qLine); + if (m.find()) { + String questionRaw = m.group(1); + String link = m.group(2); + String fileName = link.split("#", 2)[0]; + String answerMd = parseQuestionFile(fileName, questionRaw); + String question = markdownService.toHtml(questionRaw); + String answer = markdownService.toHtml(answerMd); + list.add(new QuestionAnswer(category, question, answer)); + } + } else if (qLine.startsWith("## ") || qLine.startsWith("[к оглавлению") || qLine.isEmpty()) { + break; + } + } + if (!list.isEmpty()) { + questionsByCategory.put(category, list); + } + } + } + } + } + + private String parseQuestionFile(String fileName, String question) { + Resource res = resourceLoader.getResource("classpath:data/" + fileName); + if (!res.exists()) { + return ""; + } + try (BufferedReader br = new BufferedReader(new InputStreamReader(res.getInputStream(), StandardCharsets.UTF_8))) { + List lines = br.lines().toList(); + String header = "## " + question; + boolean capture = false; + StringBuilder sb = new StringBuilder(); + for (String line : lines) { + if (!capture && line.trim().equals(header)) { + capture = true; + continue; + } + if (capture) { + if (line.startsWith("## ")) { + break; + } + if (line.startsWith("[к оглавлению")) { + continue; + } + sb.append(line).append('\n'); + } + } + return sb.toString().trim(); + } catch (IOException e) { + return ""; + } + } + + public Set categories() { + return questionsByCategory.keySet(); + } + + public Optional getRandomQuestion(String category) { + List list = questionsByCategory.get(category); + if (list == null || list.isEmpty()) { + return Optional.empty(); + } + return Optional.of(list.get(random.nextInt(list.size()))); + } + + public Optional getRandomQuestion(List categories) { + List allowed = categories.stream() + .filter(questionsByCategory::containsKey) + .toList(); + if (allowed.isEmpty()) { + return Optional.empty(); + } + String category = allowed.get(random.nextInt(allowed.size())); + return getRandomQuestion(category); + } +} diff --git a/README.md b/src/main/resources/data/README.md similarity index 100% rename from README.md rename to src/main/resources/data/README.md diff --git a/book/Java_questions.pdf b/src/main/resources/data/book/Java_questions.pdf similarity index 100% rename from book/Java_questions.pdf rename to src/main/resources/data/book/Java_questions.pdf diff --git a/concurrency.md b/src/main/resources/data/concurrency.md similarity index 100% rename from concurrency.md rename to src/main/resources/data/concurrency.md diff --git a/core.md b/src/main/resources/data/core.md similarity index 100% rename from core.md rename to src/main/resources/data/core.md diff --git a/css.md b/src/main/resources/data/css.md similarity index 100% rename from css.md rename to src/main/resources/data/css.md diff --git a/db.md b/src/main/resources/data/db.md similarity index 100% rename from db.md rename to src/main/resources/data/db.md diff --git a/examples/LICENSE b/src/main/resources/data/examples/LICENSE similarity index 100% rename from examples/LICENSE rename to src/main/resources/data/examples/LICENSE diff --git a/examples/README.md b/src/main/resources/data/examples/README.md similarity index 100% rename from examples/README.md rename to src/main/resources/data/examples/README.md diff --git a/examples/pom.xml b/src/main/resources/data/examples/pom.xml similarity index 100% rename from examples/pom.xml rename to src/main/resources/data/examples/pom.xml diff --git a/examples/src/main/java/xyz/enhorse/example/Application.java b/src/main/resources/data/examples/src/main/java/xyz/enhorse/example/Application.java similarity index 100% rename from examples/src/main/java/xyz/enhorse/example/Application.java rename to src/main/resources/data/examples/src/main/java/xyz/enhorse/example/Application.java diff --git a/examples/src/main/java/xyz/enhorse/example/concurrency/VolatileBehaviour.java b/src/main/resources/data/examples/src/main/java/xyz/enhorse/example/concurrency/VolatileBehaviour.java similarity index 100% rename from examples/src/main/java/xyz/enhorse/example/concurrency/VolatileBehaviour.java rename to src/main/resources/data/examples/src/main/java/xyz/enhorse/example/concurrency/VolatileBehaviour.java diff --git a/examples/src/main/java/xyz/enhorse/example/concurrency/WaitNotify.java b/src/main/resources/data/examples/src/main/java/xyz/enhorse/example/concurrency/WaitNotify.java similarity index 100% rename from examples/src/main/java/xyz/enhorse/example/concurrency/WaitNotify.java rename to src/main/resources/data/examples/src/main/java/xyz/enhorse/example/concurrency/WaitNotify.java diff --git a/examples/src/main/java/xyz/enhorse/example/oop/PolymorphicTicTock.java b/src/main/resources/data/examples/src/main/java/xyz/enhorse/example/oop/PolymorphicTicTock.java similarity index 100% rename from examples/src/main/java/xyz/enhorse/example/oop/PolymorphicTicTock.java rename to src/main/resources/data/examples/src/main/java/xyz/enhorse/example/oop/PolymorphicTicTock.java diff --git a/examples/src/main/resources/google_checkstyle.xml b/src/main/resources/data/examples/src/main/resources/google_checkstyle.xml similarity index 100% rename from examples/src/main/resources/google_checkstyle.xml rename to src/main/resources/data/examples/src/main/resources/google_checkstyle.xml diff --git a/examples/src/main/resources/log4j.properties b/src/main/resources/data/examples/src/main/resources/log4j.properties similarity index 100% rename from examples/src/main/resources/log4j.properties rename to src/main/resources/data/examples/src/main/resources/log4j.properties diff --git a/src/main/resources/data/examples_README.md b/src/main/resources/data/examples_README.md new file mode 100644 index 0000000..c188080 --- /dev/null +++ b/src/main/resources/data/examples_README.md @@ -0,0 +1,6 @@ +# Learning Java +1. [Homeworks](https://github.com/enhorse/learningJava/tree/master/src/main/java/xyz/enhorse/javarush) for the course from [Java Rush](http://javarush.ru/) +2. [Homeworks](https://github.com/enhorse/learningJava/tree/master/src/main/java/xyz/enhorse/m101j) for the course [M101J: MongoDB for Java Developers](https://university.mongodb.com/courses/M101J/about) +3. [Homeworks](https://github.com/enhorse/learningJava/tree/master/src/main/java/xyz/enhorse/stepic/algo) for the course [Алгоритмы: теория и практика. Методы](https://stepic.org/course/217) +4. [Homeworks](https://github.com/enhorse/learningJava/tree/master/src/main/java/xyz/enhorse/stepic/djwebservice) for the course [Разработка веб сервиса на Java (часть 1)](https://stepic.org/course/146) +5. [Homeworks](https://github.com/enhorse/learningJava/tree/master/src/main/java/xyz/enhorse/stepic/hadoop) for the course [Hadoop. Система для обработки больших объемов данных](https://stepic.org/course/150) diff --git a/html.md b/src/main/resources/data/html.md similarity index 100% rename from html.md rename to src/main/resources/data/html.md diff --git a/images/Placeholder.txt b/src/main/resources/data/images/Placeholder.txt similarity index 100% rename from images/Placeholder.txt rename to src/main/resources/data/images/Placeholder.txt diff --git a/images/SQL/Placeholder.txt b/src/main/resources/data/images/SQL/Placeholder.txt similarity index 100% rename from images/SQL/Placeholder.txt rename to src/main/resources/data/images/SQL/Placeholder.txt diff --git a/images/SQL/dense_ranksql-rank-function.png b/src/main/resources/data/images/SQL/dense_ranksql-rank-function.png similarity index 100% rename from images/SQL/dense_ranksql-rank-function.png rename to src/main/resources/data/images/SQL/dense_ranksql-rank-function.png diff --git a/images/SQL/difference-between-rank-and-dense_rank-functio.png b/src/main/resources/data/images/SQL/difference-between-rank-and-dense_rank-functio.png similarity index 100% rename from images/SQL/difference-between-rank-and-dense_rank-functio.png rename to src/main/resources/data/images/SQL/difference-between-rank-and-dense_rank-functio.png diff --git a/images/SQL/difference-between-rank-and-dense_rank.png b/src/main/resources/data/images/SQL/difference-between-rank-and-dense_rank.png similarity index 100% rename from images/SQL/difference-between-rank-and-dense_rank.png rename to src/main/resources/data/images/SQL/difference-between-rank-and-dense_rank.png diff --git a/images/SQL/image.png b/src/main/resources/data/images/SQL/image.png similarity index 100% rename from images/SQL/image.png rename to src/main/resources/data/images/SQL/image.png diff --git a/images/SQL/ntilen-function-with-partition.png b/src/main/resources/data/images/SQL/ntilen-function-with-partition.png similarity index 100% rename from images/SQL/ntilen-function-with-partition.png rename to src/main/resources/data/images/SQL/ntilen-function-with-partition.png diff --git a/images/SQL/ntilen-sql-rank-function.png b/src/main/resources/data/images/SQL/ntilen-sql-rank-function.png similarity index 100% rename from images/SQL/ntilen-sql-rank-function.png rename to src/main/resources/data/images/SQL/ntilen-sql-rank-function.png diff --git a/images/SQL/output-of-dense_rank-function.png b/src/main/resources/data/images/SQL/output-of-dense_rank-function.png similarity index 100% rename from images/SQL/output-of-dense_rank-function.png rename to src/main/resources/data/images/SQL/output-of-dense_rank-function.png diff --git a/images/SQL/output-of-ntilen-function-with-partition.png b/src/main/resources/data/images/SQL/output-of-ntilen-function-with-partition.png similarity index 100% rename from images/SQL/output-of-ntilen-function-with-partition.png rename to src/main/resources/data/images/SQL/output-of-ntilen-function-with-partition.png diff --git a/images/SQL/output-of-rank-function-for-similar-values.png b/src/main/resources/data/images/SQL/output-of-rank-function-for-similar-values.png similarity index 100% rename from images/SQL/output-of-rank-function-for-similar-values.png rename to src/main/resources/data/images/SQL/output-of-rank-function-for-similar-values.png diff --git a/images/SQL/ranksql-rank-function.png b/src/main/resources/data/images/SQL/ranksql-rank-function.png similarity index 100% rename from images/SQL/ranksql-rank-function.png rename to src/main/resources/data/images/SQL/ranksql-rank-function.png diff --git a/images/SQL/row_number-example.png b/src/main/resources/data/images/SQL/row_number-example.png similarity index 100% rename from images/SQL/row_number-example.png rename to src/main/resources/data/images/SQL/row_number-example.png diff --git a/images/SQL/row_number-sql-rank-function.png b/src/main/resources/data/images/SQL/row_number-sql-rank-function.png similarity index 100% rename from images/SQL/row_number-sql-rank-function.png rename to src/main/resources/data/images/SQL/row_number-sql-rank-function.png diff --git a/io.md b/src/main/resources/data/io.md similarity index 100% rename from io.md rename to src/main/resources/data/io.md diff --git a/java8.md b/src/main/resources/data/java8.md similarity index 100% rename from java8.md rename to src/main/resources/data/java8.md diff --git a/jcf.md b/src/main/resources/data/jcf.md similarity index 100% rename from jcf.md rename to src/main/resources/data/jcf.md diff --git a/jdbc.md b/src/main/resources/data/jdbc.md similarity index 100% rename from jdbc.md rename to src/main/resources/data/jdbc.md diff --git a/jvm.md b/src/main/resources/data/jvm.md similarity index 100% rename from jvm.md rename to src/main/resources/data/jvm.md diff --git a/kafka.md b/src/main/resources/data/kafka.md similarity index 100% rename from kafka.md rename to src/main/resources/data/kafka.md diff --git a/log.md b/src/main/resources/data/log.md similarity index 100% rename from log.md rename to src/main/resources/data/log.md diff --git a/oop.md b/src/main/resources/data/oop.md similarity index 100% rename from oop.md rename to src/main/resources/data/oop.md diff --git a/patterns.md b/src/main/resources/data/patterns.md similarity index 100% rename from patterns.md rename to src/main/resources/data/patterns.md diff --git a/reactive.md b/src/main/resources/data/reactive.md similarity index 100% rename from reactive.md rename to src/main/resources/data/reactive.md diff --git a/serialization.md b/src/main/resources/data/serialization.md similarity index 100% rename from serialization.md rename to src/main/resources/data/serialization.md diff --git a/servlets.md b/src/main/resources/data/servlets.md similarity index 100% rename from servlets.md rename to src/main/resources/data/servlets.md diff --git a/sql.md b/src/main/resources/data/sql.md similarity index 100% rename from sql.md rename to src/main/resources/data/sql.md diff --git a/test.md b/src/main/resources/data/test.md similarity index 100% rename from test.md rename to src/main/resources/data/test.md diff --git a/uml.md b/src/main/resources/data/uml.md similarity index 100% rename from uml.md rename to src/main/resources/data/uml.md diff --git a/web.md b/src/main/resources/data/web.md similarity index 100% rename from web.md rename to src/main/resources/data/web.md diff --git a/xml.md b/src/main/resources/data/xml.md similarity index 100% rename from xml.md rename to src/main/resources/data/xml.md diff --git a/src/main/resources/static/github-markdown-dark.css b/src/main/resources/static/github-markdown-dark.css new file mode 100644 index 0000000..a5a7343 --- /dev/null +++ b/src/main/resources/static/github-markdown-dark.css @@ -0,0 +1,1105 @@ +/* dark */ +.markdown-body { + color-scheme: dark; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + margin: 0; + color: #f0f6fc; + background-color: #0d1117; + font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"; + font-size: 16px; + line-height: 1.5; + word-wrap: break-word; +} + +.markdown-body .octicon { + display: inline-block; + fill: currentColor; + vertical-align: text-bottom; +} + +.markdown-body h1:hover .anchor .octicon-link:before, +.markdown-body h2:hover .anchor .octicon-link:before, +.markdown-body h3:hover .anchor .octicon-link:before, +.markdown-body h4:hover .anchor .octicon-link:before, +.markdown-body h5:hover .anchor .octicon-link:before, +.markdown-body h6:hover .anchor .octicon-link:before { + width: 16px; + height: 16px; + content: ' '; + display: inline-block; + background-color: currentColor; + -webkit-mask-image: url("data:image/svg+xml,"); + mask-image: url("data:image/svg+xml,"); +} + +.markdown-body details, +.markdown-body figcaption, +.markdown-body figure { + display: block; +} + +.markdown-body summary { + display: list-item; +} + +.markdown-body [hidden] { + display: none !important; +} + +.markdown-body a { + background-color: transparent; + color: #4493f8; + text-decoration: none; +} + +.markdown-body abbr[title] { + border-bottom: none; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +.markdown-body b, +.markdown-body strong { + font-weight: 600; +} + +.markdown-body dfn { + font-style: italic; +} + +.markdown-body h1 { + margin: .67em 0; + font-weight: 600; + padding-bottom: .3em; + font-size: 2em; + border-bottom: 1px solid #3d444db3; +} + +.markdown-body mark { + background-color: #bb800926; + color: #f0f6fc; +} + +.markdown-body small { + font-size: 90%; +} + +.markdown-body sub, +.markdown-body sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +.markdown-body sub { + bottom: -0.25em; +} + +.markdown-body sup { + top: -0.5em; +} + +.markdown-body img { + border-style: none; + max-width: 100%; + box-sizing: content-box; +} + +.markdown-body code, +.markdown-body kbd, +.markdown-body pre, +.markdown-body samp { + font-family: monospace; + font-size: 1em; +} + +.markdown-body figure { + margin: 1em 2.5rem; +} + +.markdown-body hr { + box-sizing: content-box; + overflow: hidden; + background: transparent; + border-bottom: 1px solid #3d444db3; + height: .25em; + padding: 0; + margin: 1.5rem 0; + background-color: #3d444d; + border: 0; +} + +.markdown-body input { + font: inherit; + margin: 0; + overflow: visible; + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +.markdown-body [type=button], +.markdown-body [type=reset], +.markdown-body [type=submit] { + -webkit-appearance: button; + appearance: button; +} + +.markdown-body [type=checkbox], +.markdown-body [type=radio] { + box-sizing: border-box; + padding: 0; +} + +.markdown-body [type=number]::-webkit-inner-spin-button, +.markdown-body [type=number]::-webkit-outer-spin-button { + height: auto; +} + +.markdown-body [type=search]::-webkit-search-cancel-button, +.markdown-body [type=search]::-webkit-search-decoration { + -webkit-appearance: none; + appearance: none; +} + +.markdown-body ::-webkit-input-placeholder { + color: inherit; + opacity: .54; +} + +.markdown-body ::-webkit-file-upload-button { + -webkit-appearance: button; + appearance: button; + font: inherit; +} + +.markdown-body a:hover { + text-decoration: underline; +} + +.markdown-body ::placeholder { + color: #9198a1; + opacity: 1; +} + +.markdown-body hr::before { + display: table; + content: ""; +} + +.markdown-body hr::after { + display: table; + clear: both; + content: ""; +} + +.markdown-body table { + border-spacing: 0; + border-collapse: collapse; + display: block; + width: max-content; + max-width: 100%; + overflow: auto; + font-variant: tabular-nums; +} + +.markdown-body td, +.markdown-body th { + padding: 0; +} + +.markdown-body details summary { + cursor: pointer; +} + +.markdown-body a:focus, +.markdown-body [role=button]:focus, +.markdown-body input[type=radio]:focus, +.markdown-body input[type=checkbox]:focus { + outline: 2px solid #1f6feb; + outline-offset: -2px; + box-shadow: none; +} + +.markdown-body a:focus:not(:focus-visible), +.markdown-body [role=button]:focus:not(:focus-visible), +.markdown-body input[type=radio]:focus:not(:focus-visible), +.markdown-body input[type=checkbox]:focus:not(:focus-visible) { + outline: solid 1px transparent; +} + +.markdown-body a:focus-visible, +.markdown-body [role=button]:focus-visible, +.markdown-body input[type=radio]:focus-visible, +.markdown-body input[type=checkbox]:focus-visible { + outline: 2px solid #1f6feb; + outline-offset: -2px; + box-shadow: none; +} + +.markdown-body a:not([class]):focus, +.markdown-body a:not([class]):focus-visible, +.markdown-body input[type=radio]:focus, +.markdown-body input[type=radio]:focus-visible, +.markdown-body input[type=checkbox]:focus, +.markdown-body input[type=checkbox]:focus-visible { + outline-offset: 0; +} + +.markdown-body kbd { + display: inline-block; + padding: 0.25rem; + font: 11px ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; + line-height: 10px; + color: #f0f6fc; + vertical-align: middle; + background-color: #151b23; + border: solid 1px #3d444db3; + border-bottom-color: #3d444db3; + border-radius: 6px; + box-shadow: inset 0 -1px 0 #3d444db3; +} + +.markdown-body h1, +.markdown-body h2, +.markdown-body h3, +.markdown-body h4, +.markdown-body h5, +.markdown-body h6 { + margin-top: 1.5rem; + margin-bottom: 1rem; + font-weight: 600; + line-height: 1.25; +} + +.markdown-body h2 { + font-weight: 600; + padding-bottom: .3em; + font-size: 1.5em; + border-bottom: 1px solid #3d444db3; +} + +.markdown-body h3 { + font-weight: 600; + font-size: 1.25em; +} + +.markdown-body h4 { + font-weight: 600; + font-size: 1em; +} + +.markdown-body h5 { + font-weight: 600; + font-size: .875em; +} + +.markdown-body h6 { + font-weight: 600; + font-size: .85em; + color: #9198a1; +} + +.markdown-body p { + margin-top: 0; + margin-bottom: 10px; +} + +.markdown-body blockquote { + margin: 0; + padding: 0 1em; + color: #9198a1; + border-left: .25em solid #3d444d; +} + +.markdown-body ul, +.markdown-body ol { + margin-top: 0; + margin-bottom: 0; + padding-left: 2em; +} + +.markdown-body ol ol, +.markdown-body ul ol { + list-style-type: lower-roman; +} + +.markdown-body ul ul ol, +.markdown-body ul ol ol, +.markdown-body ol ul ol, +.markdown-body ol ol ol { + list-style-type: lower-alpha; +} + +.markdown-body dd { + margin-left: 0; +} + +.markdown-body tt, +.markdown-body code, +.markdown-body samp { + font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; + font-size: 12px; +} + +.markdown-body pre { + margin-top: 0; + margin-bottom: 0; + font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; + font-size: 12px; + word-wrap: normal; +} + +.markdown-body .octicon { + display: inline-block; + overflow: visible !important; + vertical-align: text-bottom; + fill: currentColor; +} + +.markdown-body input::-webkit-outer-spin-button, +.markdown-body input::-webkit-inner-spin-button { + margin: 0; + appearance: none; +} + +.markdown-body .mr-2 { + margin-right: 0.5rem !important; +} + +.markdown-body::before { + display: table; + content: ""; +} + +.markdown-body::after { + display: table; + clear: both; + content: ""; +} + +.markdown-body>*:first-child { + margin-top: 0 !important; +} + +.markdown-body>*:last-child { + margin-bottom: 0 !important; +} + +.markdown-body a:not([href]) { + color: inherit; + text-decoration: none; +} + +.markdown-body .absent { + color: #f85149; +} + +.markdown-body .anchor { + float: left; + padding-right: 0.25rem; + margin-left: -20px; + line-height: 1; +} + +.markdown-body .anchor:focus { + outline: none; +} + +.markdown-body p, +.markdown-body blockquote, +.markdown-body ul, +.markdown-body ol, +.markdown-body dl, +.markdown-body table, +.markdown-body pre, +.markdown-body details { + margin-top: 0; + margin-bottom: 1rem; +} + +.markdown-body blockquote>:first-child { + margin-top: 0; +} + +.markdown-body blockquote>:last-child { + margin-bottom: 0; +} + +.markdown-body h1 .octicon-link, +.markdown-body h2 .octicon-link, +.markdown-body h3 .octicon-link, +.markdown-body h4 .octicon-link, +.markdown-body h5 .octicon-link, +.markdown-body h6 .octicon-link { + color: #f0f6fc; + vertical-align: middle; + visibility: hidden; +} + +.markdown-body h1:hover .anchor, +.markdown-body h2:hover .anchor, +.markdown-body h3:hover .anchor, +.markdown-body h4:hover .anchor, +.markdown-body h5:hover .anchor, +.markdown-body h6:hover .anchor { + text-decoration: none; +} + +.markdown-body h1:hover .anchor .octicon-link, +.markdown-body h2:hover .anchor .octicon-link, +.markdown-body h3:hover .anchor .octicon-link, +.markdown-body h4:hover .anchor .octicon-link, +.markdown-body h5:hover .anchor .octicon-link, +.markdown-body h6:hover .anchor .octicon-link { + visibility: visible; +} + +.markdown-body h1 tt, +.markdown-body h1 code, +.markdown-body h2 tt, +.markdown-body h2 code, +.markdown-body h3 tt, +.markdown-body h3 code, +.markdown-body h4 tt, +.markdown-body h4 code, +.markdown-body h5 tt, +.markdown-body h5 code, +.markdown-body h6 tt, +.markdown-body h6 code { + padding: 0 .2em; + font-size: inherit; +} + +.markdown-body summary h1, +.markdown-body summary h2, +.markdown-body summary h3, +.markdown-body summary h4, +.markdown-body summary h5, +.markdown-body summary h6 { + display: inline-block; +} + +.markdown-body summary h1 .anchor, +.markdown-body summary h2 .anchor, +.markdown-body summary h3 .anchor, +.markdown-body summary h4 .anchor, +.markdown-body summary h5 .anchor, +.markdown-body summary h6 .anchor { + margin-left: -40px; +} + +.markdown-body summary h1, +.markdown-body summary h2 { + padding-bottom: 0; + border-bottom: 0; +} + +.markdown-body ul.no-list, +.markdown-body ol.no-list { + padding: 0; + list-style-type: none; +} + +.markdown-body ol[type="a s"] { + list-style-type: lower-alpha; +} + +.markdown-body ol[type="A s"] { + list-style-type: upper-alpha; +} + +.markdown-body ol[type="i s"] { + list-style-type: lower-roman; +} + +.markdown-body ol[type="I s"] { + list-style-type: upper-roman; +} + +.markdown-body ol[type="1"] { + list-style-type: decimal; +} + +.markdown-body div>ol:not([type]) { + list-style-type: decimal; +} + +.markdown-body ul ul, +.markdown-body ul ol, +.markdown-body ol ol, +.markdown-body ol ul { + margin-top: 0; + margin-bottom: 0; +} + +.markdown-body li>p { + margin-top: 1rem; +} + +.markdown-body li+li { + margin-top: .25em; +} + +.markdown-body dl { + padding: 0; +} + +.markdown-body dl dt { + padding: 0; + margin-top: 1rem; + font-size: 1em; + font-style: italic; + font-weight: 600; +} + +.markdown-body dl dd { + padding: 0 1rem; + margin-bottom: 1rem; +} + +.markdown-body table th { + font-weight: 600; +} + +.markdown-body table th, +.markdown-body table td { + padding: 6px 13px; + border: 1px solid #3d444d; +} + +.markdown-body table td>:last-child { + margin-bottom: 0; +} + +.markdown-body table tr { + background-color: #0d1117; + border-top: 1px solid #3d444db3; +} + +.markdown-body table tr:nth-child(2n) { + background-color: #151b23; +} + +.markdown-body table img { + background-color: transparent; +} + +.markdown-body img[align=right] { + padding-left: 20px; +} + +.markdown-body img[align=left] { + padding-right: 20px; +} + +.markdown-body .emoji { + max-width: none; + vertical-align: text-top; + background-color: transparent; +} + +.markdown-body span.frame { + display: block; + overflow: hidden; +} + +.markdown-body span.frame>span { + display: block; + float: left; + width: auto; + padding: 7px; + margin: 13px 0 0; + overflow: hidden; + border: 1px solid #3d444d; +} + +.markdown-body span.frame span img { + display: block; + float: left; +} + +.markdown-body span.frame span span { + display: block; + padding: 5px 0 0; + clear: both; + color: #f0f6fc; +} + +.markdown-body span.align-center { + display: block; + overflow: hidden; + clear: both; +} + +.markdown-body span.align-center>span { + display: block; + margin: 13px auto 0; + overflow: hidden; + text-align: center; +} + +.markdown-body span.align-center span img { + margin: 0 auto; + text-align: center; +} + +.markdown-body span.align-right { + display: block; + overflow: hidden; + clear: both; +} + +.markdown-body span.align-right>span { + display: block; + margin: 13px 0 0; + overflow: hidden; + text-align: right; +} + +.markdown-body span.align-right span img { + margin: 0; + text-align: right; +} + +.markdown-body span.float-left { + display: block; + float: left; + margin-right: 13px; + overflow: hidden; +} + +.markdown-body span.float-left span { + margin: 13px 0 0; +} + +.markdown-body span.float-right { + display: block; + float: right; + margin-left: 13px; + overflow: hidden; +} + +.markdown-body span.float-right>span { + display: block; + margin: 13px auto 0; + overflow: hidden; + text-align: right; +} + +.markdown-body code, +.markdown-body tt { + padding: .2em .4em; + margin: 0; + font-size: 85%; + white-space: break-spaces; + background-color: #656c7633; + border-radius: 6px; +} + +.markdown-body code br, +.markdown-body tt br { + display: none; +} + +.markdown-body del code { + text-decoration: inherit; +} + +.markdown-body samp { + font-size: 85%; +} + +.markdown-body pre code { + font-size: 100%; +} + +.markdown-body pre>code { + padding: 0; + margin: 0; + word-break: normal; + white-space: pre; + background: transparent; + border: 0; +} + +.markdown-body .highlight { + margin-bottom: 1rem; +} + +.markdown-body .highlight pre { + margin-bottom: 0; + word-break: normal; +} + +.markdown-body .highlight pre, +.markdown-body pre { + padding: 1rem; + overflow: auto; + font-size: 85%; + line-height: 1.45; + color: #f0f6fc; + background-color: #151b23; + border-radius: 6px; +} + +.markdown-body pre code, +.markdown-body pre tt { + display: inline; + max-width: auto; + padding: 0; + margin: 0; + overflow: visible; + line-height: inherit; + word-wrap: normal; + background-color: transparent; + border: 0; +} + +.markdown-body .csv-data td, +.markdown-body .csv-data th { + padding: 5px; + overflow: hidden; + font-size: 12px; + line-height: 1; + text-align: left; + white-space: nowrap; +} + +.markdown-body .csv-data .blob-num { + padding: 10px 0.5rem 9px; + text-align: right; + background: #0d1117; + border: 0; +} + +.markdown-body .csv-data tr { + border-top: 0; +} + +.markdown-body .csv-data th { + font-weight: 600; + background: #151b23; + border-top: 0; +} + +.markdown-body [data-footnote-ref]::before { + content: "["; +} + +.markdown-body [data-footnote-ref]::after { + content: "]"; +} + +.markdown-body .footnotes { + font-size: 12px; + color: #9198a1; + border-top: 1px solid #3d444d; +} + +.markdown-body .footnotes ol { + padding-left: 1rem; +} + +.markdown-body .footnotes ol ul { + display: inline-block; + padding-left: 1rem; + margin-top: 1rem; +} + +.markdown-body .footnotes li { + position: relative; +} + +.markdown-body .footnotes li:target::before { + position: absolute; + top: calc(0.5rem*-1); + right: calc(0.5rem*-1); + bottom: calc(0.5rem*-1); + left: calc(1.5rem*-1); + pointer-events: none; + content: ""; + border: 2px solid #1f6feb; + border-radius: 6px; +} + +.markdown-body .footnotes li:target { + color: #f0f6fc; +} + +.markdown-body .footnotes .data-footnote-backref g-emoji { + font-family: monospace; +} + +.markdown-body body:has(:modal) { + padding-right: var(--dialog-scrollgutter) !important; +} + +.markdown-body .pl-c { + color: #9198a1; +} + +.markdown-body .pl-c1, +.markdown-body .pl-s .pl-v { + color: #79c0ff; +} + +.markdown-body .pl-e, +.markdown-body .pl-en { + color: #d2a8ff; +} + +.markdown-body .pl-smi, +.markdown-body .pl-s .pl-s1 { + color: #f0f6fc; +} + +.markdown-body .pl-ent { + color: #7ee787; +} + +.markdown-body .pl-k { + color: #ff7b72; +} + +.markdown-body .pl-s, +.markdown-body .pl-pds, +.markdown-body .pl-s .pl-pse .pl-s1, +.markdown-body .pl-sr, +.markdown-body .pl-sr .pl-cce, +.markdown-body .pl-sr .pl-sre, +.markdown-body .pl-sr .pl-sra { + color: #a5d6ff; +} + +.markdown-body .pl-v, +.markdown-body .pl-smw { + color: #ffa657; +} + +.markdown-body .pl-bu { + color: #f85149; +} + +.markdown-body .pl-ii { + color: #f0f6fc; + background-color: #8e1519; +} + +.markdown-body .pl-c2 { + color: #f0f6fc; + background-color: #b62324; +} + +.markdown-body .pl-sr .pl-cce { + font-weight: bold; + color: #7ee787; +} + +.markdown-body .pl-ml { + color: #f2cc60; +} + +.markdown-body .pl-mh, +.markdown-body .pl-mh .pl-en, +.markdown-body .pl-ms { + font-weight: bold; + color: #1f6feb; +} + +.markdown-body .pl-mi { + font-style: italic; + color: #f0f6fc; +} + +.markdown-body .pl-mb { + font-weight: bold; + color: #f0f6fc; +} + +.markdown-body .pl-md { + color: #ffdcd7; + background-color: #67060c; +} + +.markdown-body .pl-mi1 { + color: #aff5b4; + background-color: #033a16; +} + +.markdown-body .pl-mc { + color: #ffdfb6; + background-color: #5a1e02; +} + +.markdown-body .pl-mi2 { + color: #f0f6fc; + background-color: #1158c7; +} + +.markdown-body .pl-mdr { + font-weight: bold; + color: #d2a8ff; +} + +.markdown-body .pl-ba { + color: #9198a1; +} + +.markdown-body .pl-sg { + color: #3d444d; +} + +.markdown-body .pl-corl { + text-decoration: underline; + color: #a5d6ff; +} + +.markdown-body [role=button]:focus:not(:focus-visible), +.markdown-body [role=tabpanel][tabindex="0"]:focus:not(:focus-visible), +.markdown-body button:focus:not(:focus-visible), +.markdown-body summary:focus:not(:focus-visible), +.markdown-body a:focus:not(:focus-visible) { + outline: none; + box-shadow: none; +} + +.markdown-body [tabindex="0"]:focus:not(:focus-visible), +.markdown-body details-dialog:focus:not(:focus-visible) { + outline: none; +} + +.markdown-body g-emoji { + display: inline-block; + min-width: 1ch; + font-family: "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; + font-size: 1em; + font-style: normal !important; + font-weight: 400; + line-height: 1; + vertical-align: -0.075em; +} + +.markdown-body g-emoji img { + width: 1em; + height: 1em; +} + +.markdown-body .task-list-item { + list-style-type: none; +} + +.markdown-body .task-list-item label { + font-weight: 400; +} + +.markdown-body .task-list-item.enabled label { + cursor: pointer; +} + +.markdown-body .task-list-item+.task-list-item { + margin-top: 0.25rem; +} + +.markdown-body .task-list-item .handle { + display: none; +} + +.markdown-body .task-list-item-checkbox { + margin: 0 .2em .25em -1.4em; + vertical-align: middle; +} + +.markdown-body ul:dir(rtl) .task-list-item-checkbox { + margin: 0 -1.6em .25em .2em; +} + +.markdown-body ol:dir(rtl) .task-list-item-checkbox { + margin: 0 -1.6em .25em .2em; +} + +.markdown-body .contains-task-list:hover .task-list-item-convert-container, +.markdown-body .contains-task-list:focus-within .task-list-item-convert-container { + display: block; + width: auto; + height: 24px; + overflow: visible; + clip: auto; +} + +.markdown-body ::-webkit-calendar-picker-indicator { + filter: invert(50%); +} + +.markdown-body .markdown-alert { + padding: 0.5rem 1rem; + margin-bottom: 1rem; + color: inherit; + border-left: .25em solid #3d444d; +} + +.markdown-body .markdown-alert>:first-child { + margin-top: 0; +} + +.markdown-body .markdown-alert>:last-child { + margin-bottom: 0; +} + +.markdown-body .markdown-alert .markdown-alert-title { + display: flex; + font-weight: 500; + align-items: center; + line-height: 1; +} + +.markdown-body .markdown-alert.markdown-alert-note { + border-left-color: #1f6feb; +} + +.markdown-body .markdown-alert.markdown-alert-note .markdown-alert-title { + color: #4493f8; +} + +.markdown-body .markdown-alert.markdown-alert-important { + border-left-color: #8957e5; +} + +.markdown-body .markdown-alert.markdown-alert-important .markdown-alert-title { + color: #ab7df8; +} + +.markdown-body .markdown-alert.markdown-alert-warning { + border-left-color: #9e6a03; +} + +.markdown-body .markdown-alert.markdown-alert-warning .markdown-alert-title { + color: #d29922; +} + +.markdown-body .markdown-alert.markdown-alert-tip { + border-left-color: #238636; +} + +.markdown-body .markdown-alert.markdown-alert-tip .markdown-alert-title { + color: #3fb950; +} + +.markdown-body .markdown-alert.markdown-alert-caution { + border-left-color: #da3633; +} + +.markdown-body .markdown-alert.markdown-alert-caution .markdown-alert-title { + color: #f85149; +} + +.markdown-body>*:first-child>.heading-element:first-child { + margin-top: 0 !important; +} + +.markdown-body .highlight pre:has(+.zeroclipboard-container) { + min-height: 52px; +} + diff --git a/src/main/resources/static/github-markdown-license b/src/main/resources/static/github-markdown-license new file mode 100644 index 0000000..fa7ceba --- /dev/null +++ b/src/main/resources/static/github-markdown-license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/resources/static/github-markdown-light.css b/src/main/resources/static/github-markdown-light.css new file mode 100644 index 0000000..ce0861d --- /dev/null +++ b/src/main/resources/static/github-markdown-light.css @@ -0,0 +1,1105 @@ +/* light */ +.markdown-body { + color-scheme: light; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + margin: 0; + color: #1f2328; + background-color: #ffffff; + font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"; + font-size: 16px; + line-height: 1.5; + word-wrap: break-word; +} + +.markdown-body .octicon { + display: inline-block; + fill: currentColor; + vertical-align: text-bottom; +} + +.markdown-body h1:hover .anchor .octicon-link:before, +.markdown-body h2:hover .anchor .octicon-link:before, +.markdown-body h3:hover .anchor .octicon-link:before, +.markdown-body h4:hover .anchor .octicon-link:before, +.markdown-body h5:hover .anchor .octicon-link:before, +.markdown-body h6:hover .anchor .octicon-link:before { + width: 16px; + height: 16px; + content: ' '; + display: inline-block; + background-color: currentColor; + -webkit-mask-image: url("data:image/svg+xml,"); + mask-image: url("data:image/svg+xml,"); +} + +.markdown-body details, +.markdown-body figcaption, +.markdown-body figure { + display: block; +} + +.markdown-body summary { + display: list-item; +} + +.markdown-body [hidden] { + display: none !important; +} + +.markdown-body a { + background-color: transparent; + color: #0969da; + text-decoration: none; +} + +.markdown-body abbr[title] { + border-bottom: none; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +.markdown-body b, +.markdown-body strong { + font-weight: 600; +} + +.markdown-body dfn { + font-style: italic; +} + +.markdown-body h1 { + margin: .67em 0; + font-weight: 600; + padding-bottom: .3em; + font-size: 2em; + border-bottom: 1px solid #d1d9e0b3; +} + +.markdown-body mark { + background-color: #fff8c5; + color: #1f2328; +} + +.markdown-body small { + font-size: 90%; +} + +.markdown-body sub, +.markdown-body sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +.markdown-body sub { + bottom: -0.25em; +} + +.markdown-body sup { + top: -0.5em; +} + +.markdown-body img { + border-style: none; + max-width: 100%; + box-sizing: content-box; +} + +.markdown-body code, +.markdown-body kbd, +.markdown-body pre, +.markdown-body samp { + font-family: monospace; + font-size: 1em; +} + +.markdown-body figure { + margin: 1em 2.5rem; +} + +.markdown-body hr { + box-sizing: content-box; + overflow: hidden; + background: transparent; + border-bottom: 1px solid #d1d9e0b3; + height: .25em; + padding: 0; + margin: 1.5rem 0; + background-color: #d1d9e0; + border: 0; +} + +.markdown-body input { + font: inherit; + margin: 0; + overflow: visible; + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +.markdown-body [type=button], +.markdown-body [type=reset], +.markdown-body [type=submit] { + -webkit-appearance: button; + appearance: button; +} + +.markdown-body [type=checkbox], +.markdown-body [type=radio] { + box-sizing: border-box; + padding: 0; +} + +.markdown-body [type=number]::-webkit-inner-spin-button, +.markdown-body [type=number]::-webkit-outer-spin-button { + height: auto; +} + +.markdown-body [type=search]::-webkit-search-cancel-button, +.markdown-body [type=search]::-webkit-search-decoration { + -webkit-appearance: none; + appearance: none; +} + +.markdown-body ::-webkit-input-placeholder { + color: inherit; + opacity: .54; +} + +.markdown-body ::-webkit-file-upload-button { + -webkit-appearance: button; + appearance: button; + font: inherit; +} + +.markdown-body a:hover { + text-decoration: underline; +} + +.markdown-body ::placeholder { + color: #59636e; + opacity: 1; +} + +.markdown-body hr::before { + display: table; + content: ""; +} + +.markdown-body hr::after { + display: table; + clear: both; + content: ""; +} + +.markdown-body table { + border-spacing: 0; + border-collapse: collapse; + display: block; + width: max-content; + max-width: 100%; + overflow: auto; + font-variant: tabular-nums; +} + +.markdown-body td, +.markdown-body th { + padding: 0; +} + +.markdown-body details summary { + cursor: pointer; +} + +.markdown-body a:focus, +.markdown-body [role=button]:focus, +.markdown-body input[type=radio]:focus, +.markdown-body input[type=checkbox]:focus { + outline: 2px solid #0969da; + outline-offset: -2px; + box-shadow: none; +} + +.markdown-body a:focus:not(:focus-visible), +.markdown-body [role=button]:focus:not(:focus-visible), +.markdown-body input[type=radio]:focus:not(:focus-visible), +.markdown-body input[type=checkbox]:focus:not(:focus-visible) { + outline: solid 1px transparent; +} + +.markdown-body a:focus-visible, +.markdown-body [role=button]:focus-visible, +.markdown-body input[type=radio]:focus-visible, +.markdown-body input[type=checkbox]:focus-visible { + outline: 2px solid #0969da; + outline-offset: -2px; + box-shadow: none; +} + +.markdown-body a:not([class]):focus, +.markdown-body a:not([class]):focus-visible, +.markdown-body input[type=radio]:focus, +.markdown-body input[type=radio]:focus-visible, +.markdown-body input[type=checkbox]:focus, +.markdown-body input[type=checkbox]:focus-visible { + outline-offset: 0; +} + +.markdown-body kbd { + display: inline-block; + padding: 0.25rem; + font: 11px ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; + line-height: 10px; + color: #1f2328; + vertical-align: middle; + background-color: #f6f8fa; + border: solid 1px #d1d9e0b3; + border-bottom-color: #d1d9e0b3; + border-radius: 6px; + box-shadow: inset 0 -1px 0 #d1d9e0b3; +} + +.markdown-body h1, +.markdown-body h2, +.markdown-body h3, +.markdown-body h4, +.markdown-body h5, +.markdown-body h6 { + margin-top: 1.5rem; + margin-bottom: 1rem; + font-weight: 600; + line-height: 1.25; +} + +.markdown-body h2 { + font-weight: 600; + padding-bottom: .3em; + font-size: 1.5em; + border-bottom: 1px solid #d1d9e0b3; +} + +.markdown-body h3 { + font-weight: 600; + font-size: 1.25em; +} + +.markdown-body h4 { + font-weight: 600; + font-size: 1em; +} + +.markdown-body h5 { + font-weight: 600; + font-size: .875em; +} + +.markdown-body h6 { + font-weight: 600; + font-size: .85em; + color: #59636e; +} + +.markdown-body p { + margin-top: 0; + margin-bottom: 10px; +} + +.markdown-body blockquote { + margin: 0; + padding: 0 1em; + color: #59636e; + border-left: .25em solid #d1d9e0; +} + +.markdown-body ul, +.markdown-body ol { + margin-top: 0; + margin-bottom: 0; + padding-left: 2em; +} + +.markdown-body ol ol, +.markdown-body ul ol { + list-style-type: lower-roman; +} + +.markdown-body ul ul ol, +.markdown-body ul ol ol, +.markdown-body ol ul ol, +.markdown-body ol ol ol { + list-style-type: lower-alpha; +} + +.markdown-body dd { + margin-left: 0; +} + +.markdown-body tt, +.markdown-body code, +.markdown-body samp { + font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; + font-size: 12px; +} + +.markdown-body pre { + margin-top: 0; + margin-bottom: 0; + font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; + font-size: 12px; + word-wrap: normal; +} + +.markdown-body .octicon { + display: inline-block; + overflow: visible !important; + vertical-align: text-bottom; + fill: currentColor; +} + +.markdown-body input::-webkit-outer-spin-button, +.markdown-body input::-webkit-inner-spin-button { + margin: 0; + appearance: none; +} + +.markdown-body .mr-2 { + margin-right: 0.5rem !important; +} + +.markdown-body::before { + display: table; + content: ""; +} + +.markdown-body::after { + display: table; + clear: both; + content: ""; +} + +.markdown-body>*:first-child { + margin-top: 0 !important; +} + +.markdown-body>*:last-child { + margin-bottom: 0 !important; +} + +.markdown-body a:not([href]) { + color: inherit; + text-decoration: none; +} + +.markdown-body .absent { + color: #d1242f; +} + +.markdown-body .anchor { + float: left; + padding-right: 0.25rem; + margin-left: -20px; + line-height: 1; +} + +.markdown-body .anchor:focus { + outline: none; +} + +.markdown-body p, +.markdown-body blockquote, +.markdown-body ul, +.markdown-body ol, +.markdown-body dl, +.markdown-body table, +.markdown-body pre, +.markdown-body details { + margin-top: 0; + margin-bottom: 1rem; +} + +.markdown-body blockquote>:first-child { + margin-top: 0; +} + +.markdown-body blockquote>:last-child { + margin-bottom: 0; +} + +.markdown-body h1 .octicon-link, +.markdown-body h2 .octicon-link, +.markdown-body h3 .octicon-link, +.markdown-body h4 .octicon-link, +.markdown-body h5 .octicon-link, +.markdown-body h6 .octicon-link { + color: #1f2328; + vertical-align: middle; + visibility: hidden; +} + +.markdown-body h1:hover .anchor, +.markdown-body h2:hover .anchor, +.markdown-body h3:hover .anchor, +.markdown-body h4:hover .anchor, +.markdown-body h5:hover .anchor, +.markdown-body h6:hover .anchor { + text-decoration: none; +} + +.markdown-body h1:hover .anchor .octicon-link, +.markdown-body h2:hover .anchor .octicon-link, +.markdown-body h3:hover .anchor .octicon-link, +.markdown-body h4:hover .anchor .octicon-link, +.markdown-body h5:hover .anchor .octicon-link, +.markdown-body h6:hover .anchor .octicon-link { + visibility: visible; +} + +.markdown-body h1 tt, +.markdown-body h1 code, +.markdown-body h2 tt, +.markdown-body h2 code, +.markdown-body h3 tt, +.markdown-body h3 code, +.markdown-body h4 tt, +.markdown-body h4 code, +.markdown-body h5 tt, +.markdown-body h5 code, +.markdown-body h6 tt, +.markdown-body h6 code { + padding: 0 .2em; + font-size: inherit; +} + +.markdown-body summary h1, +.markdown-body summary h2, +.markdown-body summary h3, +.markdown-body summary h4, +.markdown-body summary h5, +.markdown-body summary h6 { + display: inline-block; +} + +.markdown-body summary h1 .anchor, +.markdown-body summary h2 .anchor, +.markdown-body summary h3 .anchor, +.markdown-body summary h4 .anchor, +.markdown-body summary h5 .anchor, +.markdown-body summary h6 .anchor { + margin-left: -40px; +} + +.markdown-body summary h1, +.markdown-body summary h2 { + padding-bottom: 0; + border-bottom: 0; +} + +.markdown-body ul.no-list, +.markdown-body ol.no-list { + padding: 0; + list-style-type: none; +} + +.markdown-body ol[type="a s"] { + list-style-type: lower-alpha; +} + +.markdown-body ol[type="A s"] { + list-style-type: upper-alpha; +} + +.markdown-body ol[type="i s"] { + list-style-type: lower-roman; +} + +.markdown-body ol[type="I s"] { + list-style-type: upper-roman; +} + +.markdown-body ol[type="1"] { + list-style-type: decimal; +} + +.markdown-body div>ol:not([type]) { + list-style-type: decimal; +} + +.markdown-body ul ul, +.markdown-body ul ol, +.markdown-body ol ol, +.markdown-body ol ul { + margin-top: 0; + margin-bottom: 0; +} + +.markdown-body li>p { + margin-top: 1rem; +} + +.markdown-body li+li { + margin-top: .25em; +} + +.markdown-body dl { + padding: 0; +} + +.markdown-body dl dt { + padding: 0; + margin-top: 1rem; + font-size: 1em; + font-style: italic; + font-weight: 600; +} + +.markdown-body dl dd { + padding: 0 1rem; + margin-bottom: 1rem; +} + +.markdown-body table th { + font-weight: 600; +} + +.markdown-body table th, +.markdown-body table td { + padding: 6px 13px; + border: 1px solid #d1d9e0; +} + +.markdown-body table td>:last-child { + margin-bottom: 0; +} + +.markdown-body table tr { + background-color: #ffffff; + border-top: 1px solid #d1d9e0b3; +} + +.markdown-body table tr:nth-child(2n) { + background-color: #f6f8fa; +} + +.markdown-body table img { + background-color: transparent; +} + +.markdown-body img[align=right] { + padding-left: 20px; +} + +.markdown-body img[align=left] { + padding-right: 20px; +} + +.markdown-body .emoji { + max-width: none; + vertical-align: text-top; + background-color: transparent; +} + +.markdown-body span.frame { + display: block; + overflow: hidden; +} + +.markdown-body span.frame>span { + display: block; + float: left; + width: auto; + padding: 7px; + margin: 13px 0 0; + overflow: hidden; + border: 1px solid #d1d9e0; +} + +.markdown-body span.frame span img { + display: block; + float: left; +} + +.markdown-body span.frame span span { + display: block; + padding: 5px 0 0; + clear: both; + color: #1f2328; +} + +.markdown-body span.align-center { + display: block; + overflow: hidden; + clear: both; +} + +.markdown-body span.align-center>span { + display: block; + margin: 13px auto 0; + overflow: hidden; + text-align: center; +} + +.markdown-body span.align-center span img { + margin: 0 auto; + text-align: center; +} + +.markdown-body span.align-right { + display: block; + overflow: hidden; + clear: both; +} + +.markdown-body span.align-right>span { + display: block; + margin: 13px 0 0; + overflow: hidden; + text-align: right; +} + +.markdown-body span.align-right span img { + margin: 0; + text-align: right; +} + +.markdown-body span.float-left { + display: block; + float: left; + margin-right: 13px; + overflow: hidden; +} + +.markdown-body span.float-left span { + margin: 13px 0 0; +} + +.markdown-body span.float-right { + display: block; + float: right; + margin-left: 13px; + overflow: hidden; +} + +.markdown-body span.float-right>span { + display: block; + margin: 13px auto 0; + overflow: hidden; + text-align: right; +} + +.markdown-body code, +.markdown-body tt { + padding: .2em .4em; + margin: 0; + font-size: 85%; + white-space: break-spaces; + background-color: #818b981f; + border-radius: 6px; +} + +.markdown-body code br, +.markdown-body tt br { + display: none; +} + +.markdown-body del code { + text-decoration: inherit; +} + +.markdown-body samp { + font-size: 85%; +} + +.markdown-body pre code { + font-size: 100%; +} + +.markdown-body pre>code { + padding: 0; + margin: 0; + word-break: normal; + white-space: pre; + background: transparent; + border: 0; +} + +.markdown-body .highlight { + margin-bottom: 1rem; +} + +.markdown-body .highlight pre { + margin-bottom: 0; + word-break: normal; +} + +.markdown-body .highlight pre, +.markdown-body pre { + padding: 1rem; + overflow: auto; + font-size: 85%; + line-height: 1.45; + color: #1f2328; + background-color: #f6f8fa; + border-radius: 6px; +} + +.markdown-body pre code, +.markdown-body pre tt { + display: inline; + max-width: auto; + padding: 0; + margin: 0; + overflow: visible; + line-height: inherit; + word-wrap: normal; + background-color: transparent; + border: 0; +} + +.markdown-body .csv-data td, +.markdown-body .csv-data th { + padding: 5px; + overflow: hidden; + font-size: 12px; + line-height: 1; + text-align: left; + white-space: nowrap; +} + +.markdown-body .csv-data .blob-num { + padding: 10px 0.5rem 9px; + text-align: right; + background: #ffffff; + border: 0; +} + +.markdown-body .csv-data tr { + border-top: 0; +} + +.markdown-body .csv-data th { + font-weight: 600; + background: #f6f8fa; + border-top: 0; +} + +.markdown-body [data-footnote-ref]::before { + content: "["; +} + +.markdown-body [data-footnote-ref]::after { + content: "]"; +} + +.markdown-body .footnotes { + font-size: 12px; + color: #59636e; + border-top: 1px solid #d1d9e0; +} + +.markdown-body .footnotes ol { + padding-left: 1rem; +} + +.markdown-body .footnotes ol ul { + display: inline-block; + padding-left: 1rem; + margin-top: 1rem; +} + +.markdown-body .footnotes li { + position: relative; +} + +.markdown-body .footnotes li:target::before { + position: absolute; + top: calc(0.5rem*-1); + right: calc(0.5rem*-1); + bottom: calc(0.5rem*-1); + left: calc(1.5rem*-1); + pointer-events: none; + content: ""; + border: 2px solid #0969da; + border-radius: 6px; +} + +.markdown-body .footnotes li:target { + color: #1f2328; +} + +.markdown-body .footnotes .data-footnote-backref g-emoji { + font-family: monospace; +} + +.markdown-body body:has(:modal) { + padding-right: var(--dialog-scrollgutter) !important; +} + +.markdown-body .pl-c { + color: #59636e; +} + +.markdown-body .pl-c1, +.markdown-body .pl-s .pl-v { + color: #0550ae; +} + +.markdown-body .pl-e, +.markdown-body .pl-en { + color: #6639ba; +} + +.markdown-body .pl-smi, +.markdown-body .pl-s .pl-s1 { + color: #1f2328; +} + +.markdown-body .pl-ent { + color: #0550ae; +} + +.markdown-body .pl-k { + color: #cf222e; +} + +.markdown-body .pl-s, +.markdown-body .pl-pds, +.markdown-body .pl-s .pl-pse .pl-s1, +.markdown-body .pl-sr, +.markdown-body .pl-sr .pl-cce, +.markdown-body .pl-sr .pl-sre, +.markdown-body .pl-sr .pl-sra { + color: #0a3069; +} + +.markdown-body .pl-v, +.markdown-body .pl-smw { + color: #953800; +} + +.markdown-body .pl-bu { + color: #82071e; +} + +.markdown-body .pl-ii { + color: #f6f8fa; + background-color: #82071e; +} + +.markdown-body .pl-c2 { + color: #f6f8fa; + background-color: #cf222e; +} + +.markdown-body .pl-sr .pl-cce { + font-weight: bold; + color: #116329; +} + +.markdown-body .pl-ml { + color: #3b2300; +} + +.markdown-body .pl-mh, +.markdown-body .pl-mh .pl-en, +.markdown-body .pl-ms { + font-weight: bold; + color: #0550ae; +} + +.markdown-body .pl-mi { + font-style: italic; + color: #1f2328; +} + +.markdown-body .pl-mb { + font-weight: bold; + color: #1f2328; +} + +.markdown-body .pl-md { + color: #82071e; + background-color: #ffebe9; +} + +.markdown-body .pl-mi1 { + color: #116329; + background-color: #dafbe1; +} + +.markdown-body .pl-mc { + color: #953800; + background-color: #ffd8b5; +} + +.markdown-body .pl-mi2 { + color: #d1d9e0; + background-color: #0550ae; +} + +.markdown-body .pl-mdr { + font-weight: bold; + color: #8250df; +} + +.markdown-body .pl-ba { + color: #59636e; +} + +.markdown-body .pl-sg { + color: #818b98; +} + +.markdown-body .pl-corl { + text-decoration: underline; + color: #0a3069; +} + +.markdown-body [role=button]:focus:not(:focus-visible), +.markdown-body [role=tabpanel][tabindex="0"]:focus:not(:focus-visible), +.markdown-body button:focus:not(:focus-visible), +.markdown-body summary:focus:not(:focus-visible), +.markdown-body a:focus:not(:focus-visible) { + outline: none; + box-shadow: none; +} + +.markdown-body [tabindex="0"]:focus:not(:focus-visible), +.markdown-body details-dialog:focus:not(:focus-visible) { + outline: none; +} + +.markdown-body g-emoji { + display: inline-block; + min-width: 1ch; + font-family: "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; + font-size: 1em; + font-style: normal !important; + font-weight: 400; + line-height: 1; + vertical-align: -0.075em; +} + +.markdown-body g-emoji img { + width: 1em; + height: 1em; +} + +.markdown-body .task-list-item { + list-style-type: none; +} + +.markdown-body .task-list-item label { + font-weight: 400; +} + +.markdown-body .task-list-item.enabled label { + cursor: pointer; +} + +.markdown-body .task-list-item+.task-list-item { + margin-top: 0.25rem; +} + +.markdown-body .task-list-item .handle { + display: none; +} + +.markdown-body .task-list-item-checkbox { + margin: 0 .2em .25em -1.4em; + vertical-align: middle; +} + +.markdown-body ul:dir(rtl) .task-list-item-checkbox { + margin: 0 -1.6em .25em .2em; +} + +.markdown-body ol:dir(rtl) .task-list-item-checkbox { + margin: 0 -1.6em .25em .2em; +} + +.markdown-body .contains-task-list:hover .task-list-item-convert-container, +.markdown-body .contains-task-list:focus-within .task-list-item-convert-container { + display: block; + width: auto; + height: 24px; + overflow: visible; + clip: auto; +} + +.markdown-body ::-webkit-calendar-picker-indicator { + filter: invert(50%); +} + +.markdown-body .markdown-alert { + padding: 0.5rem 1rem; + margin-bottom: 1rem; + color: inherit; + border-left: .25em solid #d1d9e0; +} + +.markdown-body .markdown-alert>:first-child { + margin-top: 0; +} + +.markdown-body .markdown-alert>:last-child { + margin-bottom: 0; +} + +.markdown-body .markdown-alert .markdown-alert-title { + display: flex; + font-weight: 500; + align-items: center; + line-height: 1; +} + +.markdown-body .markdown-alert.markdown-alert-note { + border-left-color: #0969da; +} + +.markdown-body .markdown-alert.markdown-alert-note .markdown-alert-title { + color: #0969da; +} + +.markdown-body .markdown-alert.markdown-alert-important { + border-left-color: #8250df; +} + +.markdown-body .markdown-alert.markdown-alert-important .markdown-alert-title { + color: #8250df; +} + +.markdown-body .markdown-alert.markdown-alert-warning { + border-left-color: #9a6700; +} + +.markdown-body .markdown-alert.markdown-alert-warning .markdown-alert-title { + color: #9a6700; +} + +.markdown-body .markdown-alert.markdown-alert-tip { + border-left-color: #1a7f37; +} + +.markdown-body .markdown-alert.markdown-alert-tip .markdown-alert-title { + color: #1a7f37; +} + +.markdown-body .markdown-alert.markdown-alert-caution { + border-left-color: #cf222e; +} + +.markdown-body .markdown-alert.markdown-alert-caution .markdown-alert-title { + color: #d1242f; +} + +.markdown-body>*:first-child>.heading-element:first-child { + margin-top: 0 !important; +} + +.markdown-body .highlight pre:has(+.zeroclipboard-container) { + min-height: 52px; +} + diff --git a/src/main/resources/static/script.js b/src/main/resources/static/script.js new file mode 100644 index 0000000..624aca4 --- /dev/null +++ b/src/main/resources/static/script.js @@ -0,0 +1,12 @@ +// Smooth animation for
+document.addEventListener('DOMContentLoaded', function () { + document.querySelectorAll('details').forEach(function (detail) { + detail.addEventListener('toggle', function () { + if (detail.open) { + detail.classList.add('open'); + } else { + detail.classList.remove('open'); + } + }); + }); +}); diff --git a/src/main/resources/static/style.css b/src/main/resources/static/style.css new file mode 100644 index 0000000..c144634 --- /dev/null +++ b/src/main/resources/static/style.css @@ -0,0 +1,129 @@ +/* Base layout inspired by syntax.tailwindui.com */ +body { + background: #ffffff; + color: #0f172a; + font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, + Arial, sans-serif; + display: flex; + flex-direction: column; + min-height: 100vh; + margin: 0; + font-size: 1.875rem; /* enlarge text by 50% */ +} + +@media (prefers-color-scheme: dark) { + body { + background: #0f172a; + color: #f1f5f9; + } +} +h1, h2, p { + margin: 0.75em 0; /* increased spacing */ +} + +h1 { + font-family: "Georgia", "Times New Roman", serif; /* distinct font */ + font-size: 4rem; /* significantly larger */ + font-weight: 700; + margin-bottom: 1.125em; /* increased spacing */ +} + +h2 { + font-family: "Georgia", "Times New Roman", serif; /* distinct font */ + font-size: 3rem; /* significantly larger */ + font-weight: 600; + margin-bottom: 0.75em; /* increased spacing */ +} + +ul { list-style: none; padding: 0; } +ul li { margin-bottom: 0.25em; } +.categories-list { + font-size: 75%; /* 25% smaller text */ + display: grid; + grid-template-columns: repeat(2, 1fr); /* two columns */ + gap: 0.25rem 1rem; +} +.categories-list li { + padding-top: 0.25rem; /* 50% less vertical padding */ + padding-bottom: 0.25rem; +} +summary { + cursor: pointer; + font-weight: bold; +} +details.open > * { animation: fadeIn 0.5s ease forwards; } +details[open] summary::after { + animation: fadeIn 0.5s ease forwards; +} +main { + width: 100%; + max-width: 52.5rem; /* wider center */ + margin: 0 auto; + padding: 1rem; + text-align: left; +} + +.markdown-body { + font-size: 2.25em; /* larger markdown text */ +} +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +/* style all buttons and button-like links */ +.btn { + padding-top: 0.5rem; /* increased padding */ + padding-bottom: 0.5rem; + font-weight: 700; + font-size: 1.575rem; /* 50% larger */ +} + +.question { + margin: 2rem 0; /* more space around question */ +} + +details.answer-container { + margin-top: 1.5rem; /* increased spacing */ +} + +details.answer-container summary { + background-color: #1f2937; /* dark style */ + padding: 0.375rem 0.625rem; /* reduced padding */ + border-radius: 0.375rem; + border: 1px solid #0f172a; + color: #f8fafc; + font-size: 1.1em; /* smaller button */ + font-weight: 600; + transition: background-color 0.3s, color 0.3s; + position: relative; +} + +details.answer-container summary:hover { + background-color: #374151; +} + +details.answer-container summary::after { + content: "\25BC"; + position: absolute; + right: 0.75rem; + color: #f8fafc; /* ensure arrow matches text */ + transition: transform 0.3s ease; +} + +details.answer-container.open summary::after { + transform: rotate(180deg); +} + +details.answer-container .answer { + max-height: 0; + overflow: hidden; + opacity: 0; + transition: max-height 0.5s ease, opacity 0.5s ease; + margin: 2rem 0; /* more space around answer */ +} + +details.answer-container.open .answer { + max-height: 100vh; + opacity: 1; +} diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html new file mode 100644 index 0000000..e585b47 --- /dev/null +++ b/src/main/resources/templates/index.html @@ -0,0 +1,27 @@ + + + + Interview Questions + + + + + + + +
+

Выберите категории

+
+
    +
  • + +
  • +
+ +
+
+ + diff --git a/src/main/resources/templates/question.html b/src/main/resources/templates/question.html new file mode 100644 index 0000000..c5e6ed2 --- /dev/null +++ b/src/main/resources/templates/question.html @@ -0,0 +1,32 @@ + + + + Вопрос + + + + + + + + + +
+

+
+

+
+
+ Ответ +
+
+
+
+

No questions found.

+
+К категориям +Назад +Следующий +
+ + diff --git a/src/test/java/com/li/javainterview/InterviewWebApplicationTests.java b/src/test/java/com/li/javainterview/InterviewWebApplicationTests.java new file mode 100644 index 0000000..2dd3de2 --- /dev/null +++ b/src/test/java/com/li/javainterview/InterviewWebApplicationTests.java @@ -0,0 +1,12 @@ +package com.li.javainterview; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class InterviewWebApplicationTests { + + @Test + void contextLoads() { + } +}