Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/Documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ on:
push:
branches:
- main
tags: '*'
tags:
- 'v[0-9]+\.[0-9]+\.[0-9]+' # v0.5.0 yes, v0.5.0-beta no
pull_request:
types: [labeled, opened, synchronize, reopened]

Expand Down
4 changes: 4 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
build/
node_modules/
package-lock.json
Manifest.toml
4 changes: 4 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
[deps]
CTBase = "54762871-cc72-4466-b8e8-f6c8b58076cd"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
DocumenterVitepress = "4710194d-e776-4893-9690-8d956a29c365"
JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
LiveServer = "16fef848-5104-11e9-1b77-fb7a48bbb589"
MarkdownAST = "d0879d2d-cac2-40c8-9cee-1863dc0c7391"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"

[compat]
CTBase = "0.26"
Documenter = "1"
DocumenterVitepress = "0.3"
JLD2 = "0.6"
JSON3 = "1"
LiveServer = "1"
MarkdownAST = "0.1"
Plots = "1"
julia = "1.10"
397 changes: 142 additions & 255 deletions docs/api_reference.jl

Large diffs are not rendered by default.

94 changes: 42 additions & 52 deletions docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,47 +1,33 @@
# to run the documentation generation:
# julia --project=. docs/make.jl
# to run the documentation generation: julia --project=. docs/make.jl
# to serve the documentation (option 1 — handles clean URLs natively):
# npx serve docs/build/1 --listen 5173
# to serve the documentation (option 2 — Julia only):
# julia --project=docs -e 'using LiveServer; LiveServer.serve(dir="docs/build/1", single_page=true)'
# note: single_page=true is required so that reloading /getting-started serves the correct HTML
pushfirst!(LOAD_PATH, joinpath(@__DIR__))
pushfirst!(LOAD_PATH, joinpath(@__DIR__, ".."))

using Documenter
using DocumenterVitepress
using CTModels
using CTBase
using Plots
using JSON3
using JLD2
using Markdown
using MarkdownAST: MarkdownAST

# ═══════════════════════════════════════════════════════════════════════════════
# Configuration
# ═══════════════════════════════════════════════════════════════════════════════
# if draft is true, then the julia code from .md is not executed
# to disable the draft mode in a specific markdown file, use the following:
#=
```@meta
Draft = false
```
=#
draft = false # Draft mode: if true, @example blocks in markdown are not executed
draft = false # Draft mode: if true, @example blocks in markdown are not executed

# ═══════════════════════════════════════════════════════════════════════════════
# Load extensions
# Extensions
# ═══════════════════════════════════════════════════════════════════════════════
const CTModelsPlots = Base.get_extension(CTModels, :CTModelsPlots)
const CTModelsJSON = Base.get_extension(CTModels, :CTModelsJSON)
const CTModelsJLD = Base.get_extension(CTModels, :CTModelsJLD)
const DocumenterReference = Base.get_extension(CTBase, :DocumenterReference)

if !isnothing(DocumenterReference)
DocumenterReference.reset_config!()
end

Modules = [Plots, CTModelsPlots, CTModelsJSON, CTModelsJLD]
for Module in Modules
isnothing(DocMeta.getdocmeta(Module, :DocTestSetup)) &&
DocMeta.setdocmeta!(Module, :DocTestSetup, :(using $Module); recursive=true)
end

# ═══════════════════════════════════════════════════════════════════════════════
# Paths
# ═══════════════════════════════════════════════════════════════════════════════
Expand All @@ -61,45 +47,49 @@ with_api_reference(src_dir, ext_dir) do api_pages
remotes=nothing,
warnonly=[:cross_references],
sitename="CTModels.jl",
format=Documenter.HTML(;
repolink="https://" * repo_url,
prettyurls=false,
assets=[
asset("https://control-toolbox.org/assets/css/documentation.css"),
asset("https://control-toolbox.org/assets/js/documentation.js"),
],
size_threshold_ignore=["api_ocp_building_public.md"],
format=DocumenterVitepress.MarkdownVitepress(;
repo=repo_url, devbranch="main", devurl="dev", sidebar_drawer=true
),
pages=[
"Introduction" => "index.md",
"Optimal control problems" => [
"Overview" => "model/index.md",
"Types and traits" => "model/types_and_traits.md",
"Components" => "model/components.md",
"Dynamics and objective" => "model/dynamics_objective.md",
"Constraints" => "model/constraints.md",
"Building a model" => "model/building.md",
# index.md is the VitePress root — not listed here
"Getting Started" => "getting-started.md",
"OCP Model" => [
"Overview" => "model/index.md",
"Types & Traits" => "model/types_and_traits.md",
"Components" => "model/components.md",
"Dynamics & Objective" => "model/dynamics_objective.md",
"Constraints" => "model/constraints.md",
"Building a Model" => "model/building.md",
],
"Solutions" => [
"Overview" => "solution/index.md",
"Time grids" => "solution/time_grids.md",
"Trajectories" => "solution/trajectories.md",
"Duals & diagnostics" => "solution/duals.md",
"Overview" => "solution/index.md",
"Time Grids" => "solution/time_grids.md",
"Trajectories" => "solution/trajectories.md",
"Duals & Diagnostics" => "solution/duals.md",
],
"Initial guesses" => [
"Overview" => "initial_guess/index.md",
"Input formats" => "initial_guess/formats.md",
"Validation & warm-start" => "initial_guess/validation.md",
"Initial Guesses" => [
"Overview" => "initial_guess/index.md",
"Input Formats" => "initial_guess/formats.md",
"Validation" => "initial_guess/validation.md",
],
"Serialization & extensions" => [
"Overview" => "serialization/index.md",
"Export & import" => "serialization/export_import.md",
"Plotting" => "serialization/plotting.md",
"Extensions" => [
"Overview" => "serialization/index.md",
"Export & Import" => "serialization/export_import.md",
"Plotting" => "serialization/plotting.md",
],
"API Reference" => api_pages,
],
)
end

# ═══════════════════════════════════════════════════════════════════════════════
deploydocs(; repo=repo_url * ".git", devbranch="main")
# Deploy documentation to GitHub Pages
# ═══════════════════════════════════════════════════════════════════════════════
bases_file = joinpath(@__DIR__, "build", "bases.txt")
if isfile(bases_file)
DocumenterVitepress.deploydocs(;
repo=repo_url * ".git", devbranch="main", push_preview=true
)
else
@info "Skipping deployment: no bases were built (prerelease with existing higher stable release)."
end
20 changes: 20 additions & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"devDependencies": {
"@types/node": "^25.3.5",
"@types/markdown-it-footnote": "^3.0.4"
},
"scripts": {
"docs:dev": "vitepress dev build/.documenter",
"docs:build": "vitepress build build/.documenter",
"docs:preview": "vitepress preview build/.documenter"
},
"dependencies": {
"@nolebase/vitepress-plugin-enhanced-readabilities": "^2.18.2",
"@mdit/plugin-mathjax": "^0.26.1",
"@mdit/plugin-tex": "^0.24.1",
"markdown-it-footnote": "^4.0.0",
"markdown-it": "^14.1.0",
"vitepress": "^1.6.4",
"vitepress-plugin-tabs": "^0.8.0"
}
}
101 changes: 101 additions & 0 deletions docs/src/.vitepress/config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { defineConfig } from 'vitepress'
import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'
import { mathjaxPlugin } from './mathjax-plugin'
import { juliaReplTransformer } from './julia-repl-transformer'
import footnote from "markdown-it-footnote";
import path from 'path'

const mathjax = mathjaxPlugin()

function getBaseRepository(base: string): string {
if (!base || base === '/') return '/';
const parts = base.split('/').filter(Boolean);
return parts.length > 0 ? `/${parts[0]}/` : '/';
}

const baseTemp = {
base: 'REPLACE_ME_DOCUMENTER_VITEPRESS',// TODO: replace this in makedocs!
}

const nav = [
{ text: 'Home', link: '/index' },
{ component: 'VersionPicker' }
]

// https://vitepress.dev/reference/site-config
export default defineConfig({
base: 'REPLACE_ME_DOCUMENTER_VITEPRESS',// TODO: replace this in makedocs!
title: 'REPLACE_ME_DOCUMENTER_VITEPRESS',
description: 'REPLACE_ME_DOCUMENTER_VITEPRESS',
lastUpdated: true,
cleanUrls: true,
outDir: 'REPLACE_ME_DOCUMENTER_VITEPRESS', // This is required for MarkdownVitepress to work correctly...
head: [
['link', { rel: 'icon', href: 'REPLACE_ME_DOCUMENTER_VITEPRESS_FAVICON' }],
['link', { rel: 'stylesheet', href: 'https://control-toolbox.org/assets/css/vitepress-documentation.css' }],
['script', {src: `${getBaseRepository(baseTemp.base)}versions.js`}],
['script', {src: 'https://control-toolbox.org/assets/js/vitepress-documentation.js'}],
['script', {src: `${baseTemp.base}siteinfo.js`}]
],

markdown: {
codeTransformers: [juliaReplTransformer()],
config(md) {
md.use(tabsMarkdownPlugin);
md.use(footnote);
mathjax.markdownConfig(md);
},
theme: {
light: "github-light",
dark: "github-dark"
},
},
vite: {
plugins: [
mathjax.vitePlugin,
],
define: {
__DEPLOY_ABSPATH__: JSON.stringify('REPLACE_ME_DOCUMENTER_VITEPRESS_DEPLOY_ABSPATH'),
},
resolve: {
alias: {
'@': path.resolve(__dirname, '../components')
}
},
optimizeDeps: {
exclude: [
'@nolebase/vitepress-plugin-enhanced-readabilities/client',
'vitepress',
'@nolebase/ui',
],
},
ssr: {
noExternal: [
// If there are other packages that need to be processed by Vite, you can add them here.
'@nolebase/vitepress-plugin-enhanced-readabilities',
'@nolebase/ui',
],
},
},
themeConfig: {
outline: 'deep',
logo: 'REPLACE_ME_DOCUMENTER_VITEPRESS',
search: {
provider: 'local',
options: {
detailedView: true
}
},
nav,
sidebar: 'REPLACE_ME_DOCUMENTER_VITEPRESS',
sidebarDrawer: 'REPLACE_ME_DOCUMENTER_VITEPRESS_SIDEBAR_DRAWER',
editLink: 'REPLACE_ME_DOCUMENTER_VITEPRESS',
socialLinks: [
{ icon: 'github', link: 'REPLACE_ME_DOCUMENTER_VITEPRESS' }
],
footer: {
message: 'Made with <a href="https://luxdl.github.io/DocumenterVitepress.jl/dev/" target="_blank"><strong>DocumenterVitepress.jl</strong></a><br>',
copyright: `© Copyright ${new Date().getUTCFullYear()}.`
}
}
})
54 changes: 54 additions & 0 deletions docs/src/.vitepress/julia-repl-transformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { ShikiTransformer } from "shiki"

type PromptKind = "julia" | "pkg" | null

export function juliaReplTransformer(): ShikiTransformer {
let promptInfoByLine: Array<{ len: number; kind: PromptKind }> = []
let isJuliaBlock = false
const rules: Array<{ kind: PromptKind; re: RegExp }> = [
{ kind: "julia", re: /^julia>/ },
{ kind: "pkg", re: /^(\([^)]*\)\s*)?pkg>/ }, // handles (@v1.9) pkg>
]

function classify(line: string): { len: number; kind: PromptKind } {
for (const r of rules) {
const m = line.match(r.re)
if (m) return { len: m[0].length, kind: r.kind }
}

return { len: 0, kind: null }
}

return {
name: "julia-repl-prompts",

preprocess(code, options) {
isJuliaBlock = options.lang === "julia"
return code
},

tokens(tokens) {
if (!isJuliaBlock) {
promptInfoByLine = []
return
}

promptInfoByLine = tokens.map((lineTokens) => {
const line = lineTokens.map((t) => t.content).join("")
return classify(line)
})
},

span(node, line, col) {
if (!isJuliaBlock) return

const info = promptInfoByLine[line - 1]
if (!info || !info.kind || info.len <= 0) return

if (col < info.len) {
this.addClassToHast(node, "repl-prompt")
this.addClassToHast(node, `repl-prompt-${info.kind}`)
}
},
}
}
Loading
Loading