diff --git a/.Rbuildignore b/.Rbuildignore index e15309a2e..502786a7f 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -26,7 +26,6 @@ ^Makefile$ ^NEWS\.0\.md$ ^NEWS\.1\.md$ -^_pkgdown\.yml$ ^src/Makevars$ ^CODEOWNERS$ ^GOVERNANCE\.md$ @@ -45,11 +44,11 @@ ^.*\.dll$ ^bus$ -^pkgdown$ ^docs$ ^lib$ ^library$ ^devwd$ +^site$ # only the inst/po compressed files are needed, not raw .pot/.po ^po$ diff --git a/.gitignore b/.gitignore index dba3d1306..eb5f090fc 100644 --- a/.gitignore +++ b/.gitignore @@ -57,5 +57,10 @@ dev.R *.diff *.patch -# pkgdown -docs +# litedown +site/*.html +site/*.md +site/doc/ +site/man/ +site/reference/ +site/reference_index.rds diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 05cc1e445..b13117b15 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -351,10 +351,10 @@ test-mac-old: # merging package tarballs and binaries into single R repository # rendering documentation # setting up CRAN-like structure -# generating pkgdown website +# generating litedown website integration: stage: integration - image: registry.gitlab.com/rdatatable/dockerfiles/r-pkgdown + image: registry.gitlab.com/rdatatable/dockerfiles/r-litedown tags: - saas-linux-medium-amd64 only: @@ -362,9 +362,18 @@ integration: needs: ["mirror-packages","build","test-lin-rel","test-lin-rel-cran","test-lin-dev-gcc-strict-cran","test-lin-dev-clang-cran","test-lin-rel-vanilla","test-lin-ancient-cran","test-lin-dev-clang-san","test-lin-dev-gcc-san","test-win-rel","test-win-dev" ,"test-win-old","test-mac-rel","test-mac-old"] script: - R --version - - *install-deps ## markdown pkg not present in r-pkgdown image - - mkdir -p ./pkgdown/favicon/ && cp .graphics/favicon/* ./pkgdown/favicon/ ## copy favicons - - Rscript -e 'pkgdown::build_site(override=list(destination="./website"))' + - *install-deps ## markdown pkg not present in r-litedown image + ## Copy assets to site directory + - mkdir -p ./site/assets/favicon/ + - cp .graphics/favicon/* ./site/assets/favicon/ + ## Generate reference pages first (creates reference_index.rds needed by manual.Rmd) + - Rscript site/generate_reference.R + ## Build litedown site (now manual.Rmd can use reference_index.rds) + - cd site && Rscript -e 'litedown::fuse_site()' && cd .. + - mkdir -p website + - cp -r site/*.html website/ + - cp -r site/reference website/ 2>/dev/null || true + - cp -r site/assets website/ ## html manual, vignettes, repos, cran_web, cran_checks - echo 'source(".ci/ci.R"); source(".ci/publish.R")' >> .Rprofile ## list of available test-* jobs dynamically based on bus/test-* directories @@ -436,8 +445,14 @@ integration: - Rscript -e 'check.index("data.table", names(test.jobs))' ## web/checks/check_flavors.html - Rscript -e 'check.flavors(names(test.jobs))' - ## pkgdown merge - - Rscript -e 'common_files<-function(path1, path2) intersect(list.files(path1, all.files=TRUE, no..=TRUE), list.files(path2, all.files=TRUE, no..=TRUE)); msg = if (length(f<-common_files("website","bus/integration/cran"))) paste(c("Following artifacts will be overwritten by pkgdown artifacts:", paste0(" ", f)), collapse="\n") else "No overlapping files from pkgdown artifacts"; message(msg); q("no")' + ## Add backwards compatibility redirects + - mkdir -p website/news website/reference website/articles + - echo '' > website/news/index.html + - echo '' > website/reference/index.html + ## Symlink vignettes from library to articles (pkgdown convention) + - ln -s library/data.table/doc website/articles || cp -r bus/integration/cran/library/data.table/doc/* website/articles/ 2>/dev/null || true + ## litedown merge + - Rscript -e 'common_files<-function(path1, path2) intersect(list.files(path1, all.files=TRUE, no..=TRUE), list.files(path2, all.files=TRUE, no..=TRUE)); msg = if (length(f<-common_files("website","bus/integration/cran"))) paste(c("Following artifacts will be overwritten by litedown artifacts:", paste0(" ", f)), collapse="\n") else "No overlapping files from litedown artifacts"; message(msg); q("no")' - mv website/* bus/integration/cran/ ## add plausible.io stats - find bus/integration/cran -type f -iname "*.html" | xargs sed -i 's!!!g' @@ -447,7 +462,6 @@ integration: # R repository # test jobs summaries # html documentation of all packages in repo -# pkgdown website pages: stage: deploy environment: production diff --git a/_pkgdown.yml b/_pkgdown.yml deleted file mode 100644 index 63e50c248..000000000 --- a/_pkgdown.yml +++ /dev/null @@ -1,82 +0,0 @@ -url: https://rdatatable.gitlab.io/data.table - -template: - bootstrap: 5 - light-switch: true - -development: - version_tooltip: "Development version" - -news: - one_page: true - -home: - links: - - text: CRAN-like website - href: web/packages/data.table/index.html - - text: CRAN-like checks - href: web/checks/check_results_data.table.html - - text: Community blog - href: https://rdatatable-community.github.io/The-Raft/ - -navbar: - structure: - left: [home, introduction, articles, news, benchmarks, presentations, communityarticles, reference, cheatsheet] - right: [search, github, lightswitch] - components: - home: - icon: fas fa-home fa-lg - href: index.html - introduction: - text: Introduction - href: articles/datatable-intro.html - articles: - text: Vignettes - menu: - - text: "Introduction to data.table" - href: articles/datatable-intro.html - - text: "Reference semantics" - href: articles/datatable-reference-semantics.html - - text: "Using .SD for Data Analysis" - href: articles/datatable-sd-usage.html - - text: "Keys and fast binary search based subset" - href: articles/datatable-keys-fast-subset.html - - text: "Joins in data.table" - href: articles/datatable-joins.html - - text: "Secondary indices and auto indexing" - href: articles/datatable-secondary-indices-and-auto-indexing.html - - text: "Efficient reshaping using data.table" - href: articles/datatable-reshape.html - - text: "Programming on data.table" - href: articles/datatable-programming.html - - text: "Frequently asked questions" - href: articles/datatable-faq.html - - text: "Importing data.table" - href: articles/datatable-importing.html - - text: "Benchmarking data.table" - href: articles/datatable-benchmarking.html - news: - text: News - href: news/index.html - benchmarks: - text: Benchmarks - href: https://duckdblabs.github.io/db-benchmark - presentations: - text: Presentations - href: https://github.com/Rdatatable/data.table/wiki/Presentations - communityarticles: - text: Articles - href: https://github.com/Rdatatable/data.table/wiki/Articles - reference: - text: Manual - href: reference/index.html - cheatsheet: - text: Cheatsheet - menu: - - text: "English" - href: datatable_cheatsheet.pdf - - text: "Français" - href: datatable_cheatsheet_fr.pdf - github: - icon: fab fa-github fa-lg - href: https://github.com/Rdatatable/data.table diff --git a/site/_litedown.yml b/site/_litedown.yml new file mode 100644 index 000000000..268424f54 --- /dev/null +++ b/site/_litedown.yml @@ -0,0 +1,22 @@ +--- +site: + rebuild: "always" + pattern: "[.]Rmd$" + +output: + html: + meta: + css2: ["https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css", "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css", "assets/style.css"] + js2: ["https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js", "assets/toc.js"] + title: "data.table" + description: "Extension of data.frame" + lang: "en" + favicon: "assets/favicon/favicon-32x32.png" + footer: "" + include_before: "_navbar.html" + include_after: '' + options: + toc: false + number_sections: false + embed_resources: false +--- diff --git a/site/_navbar.html b/site/_navbar.html new file mode 100644 index 000000000..e1ddaa9b7 --- /dev/null +++ b/site/_navbar.html @@ -0,0 +1,44 @@ + diff --git a/pkgdown/assets/datatable_cheatsheet.pdf b/site/assets/datatable_cheatsheet.pdf similarity index 100% rename from pkgdown/assets/datatable_cheatsheet.pdf rename to site/assets/datatable_cheatsheet.pdf diff --git a/pkgdown/assets/datatable_cheatsheet_fr.pdf b/site/assets/datatable_cheatsheet_fr.pdf similarity index 100% rename from pkgdown/assets/datatable_cheatsheet_fr.pdf rename to site/assets/datatable_cheatsheet_fr.pdf diff --git a/site/assets/style.css b/site/assets/style.css new file mode 100644 index 000000000..a3e83cda8 --- /dev/null +++ b/site/assets/style.css @@ -0,0 +1,112 @@ +/* data.table website styles */ + +:root { + --dt-toc-width: 320px; +} + +body { + font-family: "Helvetica Neue", Arial, sans-serif; + line-height: 1.6; + color: #212529; + margin: 0 !important; + padding: 0 !important; + max-width: none !important; +} + +/* Layout */ +.frontmatter, +.body { + max-width: 1320px !important; + width: 100% !important; + margin: 0 auto !important; + padding: 0 clamp(1.5rem, 3vw, 4rem) !important; +} + +.frontmatter { + padding-top: 1.5rem; +} + +.external-link::after { + content: "ext"; + font-size: 0.75rem; + margin-left: 0.25rem; +} + +/* Code blocks */ +pre { + background-color: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 4px; + padding: 1rem; + overflow-x: auto; +} + +code { + background-color: #f8f9fa; + padding: 0.2rem 0.35rem; + border-radius: 3px; + font-family: "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + font-size: 0.875em; +} + +pre code { + background-color: transparent; + padding: 0; +} + +/* TOC sidebar */ +#TOC { + background-color: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 4px; + padding: 1rem; + margin: 1rem 0 2rem 0; +} + +#TOC ul { + list-style: none; + padding-left: 0; + margin: 0; +} + +#TOC a { + color: #495057; + font-size: 0.9rem; +} + +@media (min-width: 1200px) { + .body:has(#TOC) { + display: grid; + grid-template-columns: 1fr var(--dt-toc-width); + gap: 2rem; + align-items: start; + } + + #TOC { + grid-column: 2; + grid-row: 1 / 999; + position: sticky; + top: 5rem; + margin: 0; + max-height: calc(100vh - 6rem); + overflow-y: auto; + } + + .body:has(#TOC) > *:not(#TOC) { + grid-column: 1; + } +} + +@media (min-width: 1400px) { + .frontmatter, + .body { + padding: 0 3rem; + } +} + +@media (min-width: 1800px) { + .frontmatter, + .body { + padding: 0 5rem; + } +} diff --git a/site/assets/toc.js b/site/assets/toc.js new file mode 100644 index 000000000..7b93e29a4 --- /dev/null +++ b/site/assets/toc.js @@ -0,0 +1,51 @@ +(() => { + const toc = document.getElementById("TOC"); + if (!toc) return; + + const content = document.querySelector(".body"); + if (!content) return; + + const headings = Array.from( + content.querySelectorAll("h2[id], h3[id], h4[id]") + ); + if (!headings.length) return; + + const rootList = document.createElement("ul"); + rootList.className = "nav flex-column"; + toc.appendChild(rootList); + + const listStack = [{ level: 2, list: rootList }]; + + headings.forEach((heading) => { + const level = Number(heading.tagName.slice(1)); + const text = heading.textContent.trim(); + if (!text) return; + + while (listStack.length && level < listStack[listStack.length - 1].level) { + listStack.pop(); + } + + if (level > listStack[listStack.length - 1].level) { + const nestedList = document.createElement("ul"); + nestedList.className = "nav flex-column ms-3"; + const parentList = listStack[listStack.length - 1].list; + const lastItem = parentList.lastElementChild; + if (lastItem) { + lastItem.appendChild(nestedList); + listStack.push({ level, list: nestedList }); + } + } + + const currentList = listStack[listStack.length - 1].list; + const item = document.createElement("li"); + item.className = "nav-item"; + + const link = document.createElement("a"); + link.className = "nav-link px-0"; + link.href = `#${heading.id}`; + link.textContent = text; + + item.appendChild(link); + currentList.appendChild(item); + }); +})(); diff --git a/site/generate_reference.R b/site/generate_reference.R new file mode 100644 index 000000000..1293af37e --- /dev/null +++ b/site/generate_reference.R @@ -0,0 +1,96 @@ +#!/usr/bin/env Rscript +# Generate individual reference pages from .Rd files + +library(tools) + +# Create reference directory +ref_dir = "site/reference" +if (!dir.exists(ref_dir)) dir.create(ref_dir, recursive = TRUE) + +# Get all .Rd files +rd_files = list.files("man", pattern = "\\.Rd$", full.names = TRUE) + +# Function to extract metadata from .Rd file +get_rd_info = function(rd_file) { + rd = parse_Rd(rd_file) + + title_idx = which(sapply(rd, attr, "Rd_tag") == "\\title") + title = if (length(title_idx) > 0) { + gsub("^\\s+|\\s+$", "", paste(unlist(rd[[title_idx[1]]]), collapse = "")) + } else NA + + alias_idx = which(sapply(rd, attr, "Rd_tag") == "\\alias") + aliases = if (length(alias_idx) > 0) { + sapply(alias_idx, function(i) paste(unlist(rd[[i]]), collapse = "")) + } else character(0) + + list( + file = basename(rd_file), + name = sub("\\.Rd$", "", basename(rd_file)), + title = title, + aliases = aliases + ) +} + +# Generate HTML for each .Rd file +cat("Generating individual reference pages...\n") +all_info = list() + +for (rd_file in rd_files) { + info = get_rd_info(rd_file) + all_info[[length(all_info) + 1]] <- info + + out_file = file.path(ref_dir, paste0(info$name, ".html")) + + temp_html = tempfile(fileext = ".html") + Rd2HTML(rd_file, out = temp_html, package = "data.table") + + html_content = readLines(temp_html, warn = FALSE) + navbar_html = readLines("site/_navbar.html", warn = FALSE) + navbar_html = gsub('href="index.html"', 'href="../index.html"', navbar_html) + navbar_html = gsub('href="news.html"', 'href="../news.html"', navbar_html) + navbar_html = gsub('href="manual.html"', 'href="../manual.html"', navbar_html) + navbar_html = gsub('href="articles/', 'href="../articles/', navbar_html) + navbar_html = gsub('href="assets/', 'href="../assets/', navbar_html) + + wrapped_html = c( + '', + '', + '', + '', + '', + '', + sprintf('%s — %s • data.table', paste(info$aliases, collapse = ", "), info$title), + '', + '', + '', + '', + '', + '', + navbar_html, + '
', + html_content, + '
', + '', + '', + '' + ) + + writeLines(wrapped_html, out_file) + unlink(temp_html) + + cat(sprintf(" Generated %s\n", out_file)) +} + +all_info = all_info[order(sapply(all_info, function(x) x$name), method = "radix")] + +saveRDS(all_info, "site/reference_index.rds") + +cat(sprintf("\nGenerated %d reference pages in %s/\n", length(all_info), ref_dir)) \ No newline at end of file diff --git a/site/index.Rmd b/site/index.Rmd new file mode 100644 index 000000000..9b7215fc4 --- /dev/null +++ b/site/index.Rmd @@ -0,0 +1,31 @@ +--- +title: "" +--- + +```{r setup, include=FALSE} +pkg_name = litedown:::detect_pkg() +pkg_dir = find.package(pkg_name) +``` + +```{r, results='asis', echo=FALSE} +# Read README.md from repository root (one directory up from site/) +readme_file = normalizePath(file.path('..', 'README.md'), mustWork = FALSE) +if (file.exists(readme_file)) { + readme = readLines(readme_file, warn = FALSE) + cat(readme, sep = '\n') +} else { + cat('README.md not found.\n') +} +``` + +## Links + +- [CRAN-like website](web/packages/data.table/index.html) +- [CRAN-like checks](web/checks/check_results_data.table.html) +- [Community blog](https://rdatatable-community.github.io/The-Raft/) + +## Citation + +```{r, echo=FALSE, results='asis'} +litedown::pkg_citation(pkg_name) +``` diff --git a/site/manual.Rmd b/site/manual.Rmd new file mode 100644 index 000000000..dd0175949 --- /dev/null +++ b/site/manual.Rmd @@ -0,0 +1,35 @@ +--- +title: "Function Reference" +--- + +```{r, echo=FALSE, results='asis'} +# Load the reference index +ref_info = readRDS("reference_index.rds") + +# Print function list +cat("
\n") + +for (info in ref_info) { + # Create a link to the individual page + link = sprintf("reference/%s.html", info$name) + + # Format aliases as function names + if (length(info$aliases) > 0) { + formatted_aliases = sapply(info$aliases, function(alias) { + # Don't add () to operators and special symbols: + if (grepl("^[%:.[]|<-$|^\\.\\.?$|^\\.$|^\\.\\.\\.$", alias)) { + sprintf("%s", alias) + } else { + sprintf("%s()", alias) + } + }) + alias_text = paste(formatted_aliases, collapse = " ") + } else { + alias_text = sprintf("%s", info$name) + } + + cat(sprintf('
\n

%s

\n

%s

\n
\n\n', link, alias_text, info$title)) +} + +cat("
\n") +``` diff --git a/site/news.Rmd b/site/news.Rmd new file mode 100644 index 000000000..cc394b9d2 --- /dev/null +++ b/site/news.Rmd @@ -0,0 +1,11 @@ +--- +title: "News" +--- + +```{r, echo=FALSE, results='asis'} +cat(litedown::raw_text('')) +``` + +```{r, echo=FALSE, results='asis'} +litedown::pkg_news(recent = 0, number_sections = FALSE) +``` diff --git a/vignettes/_translation_links.R b/vignettes/_translation_links.R index d384f43e3..f01f5f72a 100644 --- a/vignettes/_translation_links.R +++ b/vignettes/_translation_links.R @@ -1,7 +1,7 @@ # build a link list of alternative languages (may be character(0)) # idea is to look like 'Other languages: en | fr | de' -.write.translation.links <- function(fmt) { - url = "https://rdatatable.gitlab.io/data.table/articles" +.write.translation.links = function(fmt) { + url = "https://rdatatable.gitlab.io/data.table/doc" path = dirname(litedown::get_context("input")) if (basename(path) == "vignettes") { lang = "en"