upcontent 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +61 -0
- package/.github/workflows/deploy.yml +59 -0
- package/.github/workflows/reusable-pages.yml +74 -0
- package/.upcontent/config.json +82 -0
- package/.upcontent/favicon.svg +4 -0
- package/.upcontent/logo.svg +6 -0
- package/.upcontent/portal.css +139 -0
- package/Makefile +29 -0
- package/README.md +114 -2
- package/SHOWCASE.mdx +163 -0
- package/astro.config.mjs +125 -0
- package/customization/config-json.md +92 -0
- package/customization/content.md +48 -0
- package/customization/environment.md +43 -0
- package/customization/index.md +33 -0
- package/customization/navigation.md +57 -0
- package/customization/site-identity.md +43 -0
- package/customization/theme.md +47 -0
- package/deployment/github-pages.md +46 -0
- package/deployment/index.md +24 -0
- package/deployment/static-hosts.md +34 -0
- package/getting-started/consumer-repository.md +156 -0
- package/getting-started/first-build.md +78 -0
- package/guides/authoring-content.md +105 -0
- package/guides/validate-your-site.md +53 -0
- package/package.json +33 -3
- package/scripts/upcontent-cli.mjs +113 -0
- package/scripts/verify-external-build.mjs +24 -0
- package/src/components/MermaidLoader.astro +290 -0
- package/src/components/PaletteShowcase.astro +122 -0
- package/src/components/StructuredDataCopy.astro +22 -0
- package/src/content/__mocks__/astro-content.ts +7 -0
- package/src/content/__mocks__/astro-loaders.ts +3 -0
- package/src/content.config.test.ts +163 -0
- package/src/content.config.ts +90 -0
- package/src/lib/content-blocklist.test.ts +47 -0
- package/src/lib/content-blocklist.ts +55 -0
- package/src/lib/doc-links.test.ts +62 -0
- package/src/lib/doc-links.ts +31 -0
- package/src/lib/mermaid-render.test.ts +42 -0
- package/src/lib/mermaid-render.ts +24 -0
- package/src/lib/portal-config.test.ts +143 -0
- package/src/lib/portal-config.ts +176 -0
- package/src/lib/product-identity.ts +2 -0
- package/src/lib/rehype-callouts.test.ts +69 -0
- package/src/lib/rehype-callouts.ts +61 -0
- package/src/lib/remark-strip-duplicate-title.test.ts +73 -0
- package/src/lib/remark-strip-duplicate-title.ts +37 -0
- package/src/lib/remark-structured-data-preview.test.ts +104 -0
- package/src/lib/remark-structured-data-preview.ts +66 -0
- package/src/lib/remark-wiki-links.test.ts +102 -0
- package/src/lib/remark-wiki-links.ts +110 -0
- package/src/lib/sidebar.test.ts +142 -0
- package/src/lib/sidebar.ts +113 -0
- package/src/lib/structured-data-tree.test.ts +75 -0
- package/src/lib/structured-data-tree.ts +55 -0
- package/src/overrides/Footer.astro +114 -0
- package/src/overrides/Head.astro +20 -0
- package/src/pages/index.astro +20 -0
- package/src/styles/callouts.css +29 -0
- package/src/styles/structured-data-preview.css +95 -0
- package/test-fixtures/external-consumer/.upcontent/config.json +26 -0
- package/test-fixtures/external-consumer/.upcontent/favicon.svg +4 -0
- package/test-fixtures/external-consumer/.upcontent/logo.svg +4 -0
- package/test-fixtures/external-consumer/.upcontent/theme.css +4 -0
- package/test-fixtures/external-consumer/.upcontent-renderer/README.md +3 -0
- package/test-fixtures/external-consumer/README.md +6 -0
- package/test-fixtures/external-consumer/forbidden.md +5 -0
- package/tsconfig.json +7 -0
- package/vitest.config.ts +18 -0
package/SHOWCASE.mdx
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
heading: Capability showcase
|
|
3
|
+
description: One practical page showing what a consumer repository can render, configure, and publish with Upcontent.
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 1
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
import PaletteShowcase from './src/components/PaletteShowcase.astro';
|
|
9
|
+
|
|
10
|
+
Upcontent turns Markdown into a searchable static documentation site. Configure the consumer repository, add the content formats your team needs, and publish the generated `dist/` directory to a static host.
|
|
11
|
+
|
|
12
|
+
## Rendered content
|
|
13
|
+
|
|
14
|
+
The portal accepts ordinary Markdown and adds a small set of useful content features.
|
|
15
|
+
|
|
16
|
+
### Callouts
|
|
17
|
+
|
|
18
|
+
:::tip[Use callouts for decisions]
|
|
19
|
+
Keep callouts short. Use them for a warning, a recommendation, or a next action that should not disappear in the surrounding text.
|
|
20
|
+
:::
|
|
21
|
+
|
|
22
|
+
:::caution
|
|
23
|
+
Static output is public wherever the selected host is public. A private source repository does not automatically make the published site private.
|
|
24
|
+
:::
|
|
25
|
+
|
|
26
|
+
### Code and diagrams
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
export function buildPortal(contentPath: string) {
|
|
30
|
+
return `make build CONTENT_PATH=${contentPath}`
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```mermaid
|
|
35
|
+
flowchart LR
|
|
36
|
+
Content[Consumer repository] --> Config[.upcontent/config.json]
|
|
37
|
+
Config --> Build[Static build]
|
|
38
|
+
Content --> Build
|
|
39
|
+
Build --> Output[Searchable portal]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Mermaid diagrams support theme synchronization, fullscreen, zoom, pan, source copying, and an invalid-source fallback.
|
|
43
|
+
|
|
44
|
+
### Structured examples
|
|
45
|
+
|
|
46
|
+
JSON and YAML blocks remain readable as collapsible data:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"portal": "static",
|
|
51
|
+
"search": "pagefind",
|
|
52
|
+
"themes": ["light", "dark"]
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
contentPath: ./docs
|
|
58
|
+
publish: github-pages
|
|
59
|
+
sourceOfTruth: consumer-repository
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
CSV becomes a table when the content is tabular:
|
|
63
|
+
|
|
64
|
+
```csv
|
|
65
|
+
capability,status
|
|
66
|
+
custom-css,available
|
|
67
|
+
blocklist,available
|
|
68
|
+
pagefind,available
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Links and navigation
|
|
72
|
+
|
|
73
|
+
Regular Markdown links are best for stable routes, such as [site validation](../guides/validate-your-site/). Wiki links are useful when the source repository refers to files by name, such as `[[README]]`. Missing wiki-link targets fail the build instead of producing broken navigation.
|
|
74
|
+
|
|
75
|
+
## Consumer customization
|
|
76
|
+
|
|
77
|
+
The consumer changes identity and presentation in `.upcontent/`, without editing the portal renderer:
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
.upcontent/
|
|
81
|
+
├── config.json
|
|
82
|
+
├── favicon.svg
|
|
83
|
+
├── logo.svg
|
|
84
|
+
└── theme.css
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The configuration controls:
|
|
88
|
+
|
|
89
|
+
- Site title, description, URL, logo, and favicon
|
|
90
|
+
- Repository links and local custom CSS
|
|
91
|
+
- Social links, table of contents, pagination, last-updated metadata, and code styling
|
|
92
|
+
- Sidebar roots, labels, and content blocklists
|
|
93
|
+
- The frontmatter field used as a page title
|
|
94
|
+
|
|
95
|
+
Example identity configuration:
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"site": {
|
|
100
|
+
"title": "Engineering Docs",
|
|
101
|
+
"description": "Documentation for the engineering team.",
|
|
102
|
+
"logo": {
|
|
103
|
+
"src": ".upcontent/logo.svg",
|
|
104
|
+
"alt": "Engineering Docs"
|
|
105
|
+
},
|
|
106
|
+
"favicon": ".upcontent/favicon.svg"
|
|
107
|
+
},
|
|
108
|
+
"repo": {
|
|
109
|
+
"url": "https://github.com/acme/engineering-docs"
|
|
110
|
+
},
|
|
111
|
+
"theme": {
|
|
112
|
+
"customCss": [".upcontent/theme.css"]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The [configuration reference](../customization/config-json/) explains every supported field and its fallback behavior.
|
|
118
|
+
|
|
119
|
+
### Palette variations
|
|
120
|
+
|
|
121
|
+
The same Starlight surface can carry different consumer identities through tokens and custom CSS:
|
|
122
|
+
|
|
123
|
+
<PaletteShowcase />
|
|
124
|
+
|
|
125
|
+
These are examples for a consumer repository, not a runtime theme picker that every published portal needs to expose.
|
|
126
|
+
|
|
127
|
+
## Content boundaries
|
|
128
|
+
|
|
129
|
+
The build excludes protected internal paths and consumer blocklists before parsing. This keeps excluded files out of both the content collection and the sidebar.
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"navigation": {
|
|
134
|
+
"blocklist": {
|
|
135
|
+
"exact": ["notes.md"],
|
|
136
|
+
"prefixes": ["drafts/", "internal/"]
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Broken wiki links and traversal attempts fail with the source file and invalid reference. The same pipeline also works when `CONTENT_PATH` points to a separate consumer repository.
|
|
143
|
+
|
|
144
|
+
## Validation and publishing
|
|
145
|
+
|
|
146
|
+
The repeatable local contract is:
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
pnpm test
|
|
150
|
+
pnpm check
|
|
151
|
+
make build CONTENT_PATH=.
|
|
152
|
+
make check-external
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The reference CI workflow validates the site and an external consumer fixture. The deployment workflow publishes `dist/` to a static host such as GitHub Pages.
|
|
156
|
+
|
|
157
|
+
## Explore the product
|
|
158
|
+
|
|
159
|
+
- [Your first build](../getting-started/first-build/)
|
|
160
|
+
- [Set up a consumer repository](../getting-started/consumer-repository/)
|
|
161
|
+
- [Authoring content](../guides/authoring-content/)
|
|
162
|
+
- [Validate your site](../guides/validate-your-site/)
|
|
163
|
+
- [Deployment](../deployment/)
|
package/astro.config.mjs
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url'
|
|
2
|
+
import { cpSync, existsSync, mkdirSync, statSync } from 'node:fs'
|
|
3
|
+
import { basename, resolve, sep } from 'node:path'
|
|
4
|
+
import { unified } from '@astrojs/markdown-remark'
|
|
5
|
+
import starlight from '@astrojs/starlight'
|
|
6
|
+
import { defineConfig } from 'astro/config'
|
|
7
|
+
import { visit } from 'unist-util-visit'
|
|
8
|
+
import { rehypeCallouts } from './src/lib/rehype-callouts.ts'
|
|
9
|
+
import { remarkStripDuplicateTitle } from './src/lib/remark-strip-duplicate-title.ts'
|
|
10
|
+
import { remarkStructuredDataPreview } from './src/lib/remark-structured-data-preview.ts'
|
|
11
|
+
import { remarkWikiLinks } from './src/lib/remark-wiki-links.ts'
|
|
12
|
+
import { getPortalConfig } from './src/lib/portal-config.ts'
|
|
13
|
+
import { buildSidebar } from './src/lib/sidebar.ts'
|
|
14
|
+
import { PRODUCT_NAME, PRODUCT_TAGLINE } from './src/lib/product-identity.ts'
|
|
15
|
+
|
|
16
|
+
const docsRoot = fileURLToPath(new URL('./src/content/docs', import.meta.url))
|
|
17
|
+
const portalConfig = getPortalConfig()
|
|
18
|
+
|
|
19
|
+
function resolvePortalAsset(assetPath) {
|
|
20
|
+
if (!assetPath || assetPath.startsWith('http')) return assetPath
|
|
21
|
+
if (assetPath.startsWith('/')) return assetPath
|
|
22
|
+
|
|
23
|
+
const source = resolve(docsRoot, assetPath)
|
|
24
|
+
if (!source.startsWith(`${resolve(docsRoot)}${sep}`) || !existsSync(source) || !statSync(source).isFile()) {
|
|
25
|
+
console.warn(`[${PRODUCT_NAME}] Portal asset not found: ${source}`)
|
|
26
|
+
return undefined
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const targetDir = resolve(process.cwd(), 'public/upcontent-assets')
|
|
30
|
+
mkdirSync(targetDir, { recursive: true })
|
|
31
|
+
const targetName = basename(source)
|
|
32
|
+
cpSync(source, resolve(targetDir, targetName))
|
|
33
|
+
return `/upcontent-assets/${targetName}`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function resolvePortalLogo(logo) {
|
|
37
|
+
if (!logo) return undefined
|
|
38
|
+
if (logo.src.startsWith('http') || logo.src.startsWith('/')) return logo
|
|
39
|
+
const source = resolve(docsRoot, logo.src)
|
|
40
|
+
if (!source.startsWith(`${resolve(docsRoot)}${sep}`) || !existsSync(source) || !statSync(source).isFile()) {
|
|
41
|
+
console.warn(`[${PRODUCT_NAME}] Portal logo not found: ${source}`)
|
|
42
|
+
return undefined
|
|
43
|
+
}
|
|
44
|
+
const targetDir = resolve(process.cwd(), 'src/assets')
|
|
45
|
+
mkdirSync(targetDir, { recursive: true })
|
|
46
|
+
const target = resolve(targetDir, 'consumer-logo.svg')
|
|
47
|
+
cpSync(source, target)
|
|
48
|
+
return { ...logo, src: './src/assets/consumer-logo.svg' }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const consumerCss = (portalConfig.theme?.customCss ?? [])
|
|
52
|
+
.map(cssPath => resolve(docsRoot, cssPath))
|
|
53
|
+
.filter(cssPath => {
|
|
54
|
+
if (existsSync(cssPath)) return true
|
|
55
|
+
console.warn(`[${PRODUCT_NAME}] Custom CSS file not found: ${cssPath}`)
|
|
56
|
+
return false
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
const customCss = ['./src/styles/callouts.css', './src/styles/structured-data-preview.css', ...consumerCss]
|
|
60
|
+
const starlightOptions = portalConfig.starlight ?? {}
|
|
61
|
+
|
|
62
|
+
// Remark plugin: converts ```mermaid blocks to <div class="mermaid"> BEFORE Shiki runs
|
|
63
|
+
function remarkMermaid() {
|
|
64
|
+
return (tree) => {
|
|
65
|
+
visit(tree, 'code', (node, index, parent) => {
|
|
66
|
+
if (node.lang !== 'mermaid') return
|
|
67
|
+
// <br/> inside a div becomes a DOM element before Mermaid parses the text, breaking the parser
|
|
68
|
+
const safe = node.value.replace(/<br\s*\/?>/gi, ' ')
|
|
69
|
+
parent.children.splice(index, 1, {
|
|
70
|
+
type: 'html',
|
|
71
|
+
value: `<div class="mermaid">${safe}</div>`,
|
|
72
|
+
})
|
|
73
|
+
return index + 1
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Rehype plugin: strip .md suffix from internal hrefs so links resolve correctly
|
|
79
|
+
function rehypeStripMdLinks() {
|
|
80
|
+
return (tree) => {
|
|
81
|
+
visit(tree, 'element', (node) => {
|
|
82
|
+
if (node.tagName !== 'a') return
|
|
83
|
+
const href = node.properties?.href
|
|
84
|
+
if (typeof href !== 'string') return
|
|
85
|
+
if (href.startsWith('http://') || href.startsWith('https://') || href.startsWith('#')) return
|
|
86
|
+
if (href.endsWith('.md')) node.properties.href = href.slice(0, -3)
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export default defineConfig({
|
|
92
|
+
output: 'static',
|
|
93
|
+
site: process.env.SITE_URL || portalConfig.site?.url || undefined,
|
|
94
|
+
base: process.env.BASE_PATH || undefined,
|
|
95
|
+
integrations: [
|
|
96
|
+
starlight({
|
|
97
|
+
title: portalConfig.site?.title ?? PRODUCT_NAME,
|
|
98
|
+
description: portalConfig.site?.description ?? PRODUCT_TAGLINE,
|
|
99
|
+
logo: resolvePortalLogo(portalConfig.site?.logo),
|
|
100
|
+
favicon: resolvePortalAsset(portalConfig.site?.favicon),
|
|
101
|
+
components: {
|
|
102
|
+
Head: './src/overrides/Head.astro',
|
|
103
|
+
Footer: './src/overrides/Footer.astro',
|
|
104
|
+
},
|
|
105
|
+
customCss,
|
|
106
|
+
social: starlightOptions.social,
|
|
107
|
+
tableOfContents: starlightOptions.tableOfContents,
|
|
108
|
+
lastUpdated: starlightOptions.lastUpdated,
|
|
109
|
+
pagination: starlightOptions.pagination,
|
|
110
|
+
expressiveCode: starlightOptions.expressiveCode,
|
|
111
|
+
sidebar: buildSidebar(docsRoot),
|
|
112
|
+
}),
|
|
113
|
+
],
|
|
114
|
+
markdown: {
|
|
115
|
+
processor: unified({
|
|
116
|
+
remarkPlugins: [
|
|
117
|
+
remarkStripDuplicateTitle,
|
|
118
|
+
[remarkWikiLinks, { contentRoot: docsRoot, failOnBrokenLinks: true }],
|
|
119
|
+
remarkMermaid,
|
|
120
|
+
remarkStructuredDataPreview,
|
|
121
|
+
],
|
|
122
|
+
rehypePlugins: [rehypeCallouts, rehypeStripMdLinks],
|
|
123
|
+
}),
|
|
124
|
+
},
|
|
125
|
+
})
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: JSON configuration reference
|
|
3
|
+
description: Complete reference for the consumer-owned .upcontent/config.json file.
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 6
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
The `.upcontent/config.json` file is optional. An omitted field uses the portal or Starlight default.
|
|
9
|
+
|
|
10
|
+
## Start with the minimum
|
|
11
|
+
|
|
12
|
+
You only need a title and repository link to get started. Add a logo and custom CSS when you are ready to make the portal yours:
|
|
13
|
+
|
|
14
|
+
```json
|
|
15
|
+
{
|
|
16
|
+
"site": {
|
|
17
|
+
"title": "Engineering Docs",
|
|
18
|
+
"description": "Documentation for the engineering team."
|
|
19
|
+
},
|
|
20
|
+
"repo": {
|
|
21
|
+
"url": "https://github.com/acme/engineering-docs"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Relative asset paths resolve from the consumer repository. Absolute URLs are also supported for externally hosted assets.
|
|
27
|
+
|
|
28
|
+
## Complete example
|
|
29
|
+
|
|
30
|
+
The optional fields below cover navigation, Starlight presentation, and content conventions. You can add them one group at a time.
|
|
31
|
+
|
|
32
|
+
<details>
|
|
33
|
+
<summary>Show the complete configuration</summary>
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"site": {
|
|
38
|
+
"title": "Engineering Docs",
|
|
39
|
+
"description": "Documentation for the engineering team.",
|
|
40
|
+
"url": "https://docs.example.com",
|
|
41
|
+
"logo": {
|
|
42
|
+
"src": ".upcontent/logo.svg",
|
|
43
|
+
"alt": "Engineering Docs",
|
|
44
|
+
"replacesTitle": false
|
|
45
|
+
},
|
|
46
|
+
"favicon": ".upcontent/favicon.svg"
|
|
47
|
+
},
|
|
48
|
+
"repo": {
|
|
49
|
+
"url": "https://github.com/acme/engineering-docs"
|
|
50
|
+
},
|
|
51
|
+
"theme": {
|
|
52
|
+
"customCss": [".upcontent/theme.css"]
|
|
53
|
+
},
|
|
54
|
+
"starlight": {
|
|
55
|
+
"social": [
|
|
56
|
+
{ "icon": "github", "label": "GitHub", "href": "https://github.com/acme/engineering-docs" }
|
|
57
|
+
],
|
|
58
|
+
"tableOfContents": { "minHeadingLevel": 2, "maxHeadingLevel": 3 },
|
|
59
|
+
"lastUpdated": true,
|
|
60
|
+
"pagination": true,
|
|
61
|
+
"expressiveCode": {
|
|
62
|
+
"styleOverrides": { "borderRadius": "0.6rem" }
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"navigation": {
|
|
66
|
+
"roots": ["README.md", "guides"],
|
|
67
|
+
"labelOverrides": { "api": "API reference" },
|
|
68
|
+
"blocklist": {
|
|
69
|
+
"exact": ["notes.md"],
|
|
70
|
+
"prefixes": ["drafts/"]
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"content": {
|
|
74
|
+
"titleField": "title"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
</details>
|
|
80
|
+
|
|
81
|
+
## Field groups
|
|
82
|
+
|
|
83
|
+
| Group | Controls |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| `site` | Name, description, URL, logo, and favicon. |
|
|
86
|
+
| `repo` | Source repository links. |
|
|
87
|
+
| `theme` | Consumer-owned local CSS. |
|
|
88
|
+
| `starlight` | Safe layout, social, table of contents, pagination, and code options. |
|
|
89
|
+
| `navigation` | Sidebar roots, labels, and blocklists. |
|
|
90
|
+
| `content` | Frontmatter title field selection. |
|
|
91
|
+
|
|
92
|
+
Invalid curated values are ignored or fall back safely. Heading levels must be integers from 1 through 6, and the minimum cannot exceed the maximum.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Content behavior
|
|
3
|
+
description: Configure page titles, metadata, and the Markdown features available to authors.
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 5
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Upcontent accepts Markdown and MDX files from the consumer repository. Starlight provides the page layout and frontmatter schema; Upcontent adds title fallback, wiki links, callouts, Mermaid, and structured previews.
|
|
9
|
+
|
|
10
|
+
## Choose a title field
|
|
11
|
+
|
|
12
|
+
If an existing repository uses a field such as `heading` instead of `title`, configure it once:
|
|
13
|
+
|
|
14
|
+
```json
|
|
15
|
+
{
|
|
16
|
+
"content": {
|
|
17
|
+
"titleField": "heading"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
When the configured field is missing, the portal falls back to the filename. A file named `first-build.md` becomes `First Build`.
|
|
23
|
+
|
|
24
|
+
## Page metadata
|
|
25
|
+
|
|
26
|
+
Use frontmatter at the top of a page:
|
|
27
|
+
|
|
28
|
+
```md
|
|
29
|
+
---
|
|
30
|
+
heading: Configure a consumer repository
|
|
31
|
+
sidebar:
|
|
32
|
+
order: 2
|
|
33
|
+
---
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Use `##` for body sections because the page title is rendered as the top-level heading.
|
|
37
|
+
|
|
38
|
+
## Supported content features
|
|
39
|
+
|
|
40
|
+
- Obsidian-style callouts for notes, tips, cautions, and dangers
|
|
41
|
+
- Mermaid diagrams with theme-aware rendering and interaction controls
|
|
42
|
+
- JSON and YAML data previews
|
|
43
|
+
- CSV tables
|
|
44
|
+
- `[[wiki links]]` with build-time target validation
|
|
45
|
+
- Syntax-highlighted code blocks
|
|
46
|
+
- Standard Markdown links, tables, lists, and images
|
|
47
|
+
|
|
48
|
+
See [Authoring content](../guides/authoring-content/) for writing rules and the [capability showcase](../showcase/) for rendered examples.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Environment variables
|
|
3
|
+
description: Configure repository links and hosting paths at build time.
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 7
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
The portal is static, but a few values belong to the build environment rather than consumer content.
|
|
9
|
+
|
|
10
|
+
## Supported variables
|
|
11
|
+
|
|
12
|
+
| Variable | Purpose | Example |
|
|
13
|
+
| --- | --- | --- |
|
|
14
|
+
| `CONTENT_PATH` | Filesystem path passed to `make dev` or `make build`. | `./` |
|
|
15
|
+
| `REPO_URL` | Overrides the configured source repository URL for generated GitHub links. | `https://github.com/acme/docs` |
|
|
16
|
+
| `BASE_PATH` | URL prefix for a project site hosted below the domain root. | `/engineering-docs` |
|
|
17
|
+
| `SITE_URL` | Canonical site origin used by Astro integrations such as the sitemap. | `https://docs.example.com` |
|
|
18
|
+
|
|
19
|
+
## Local development
|
|
20
|
+
|
|
21
|
+
Pass the content path to Make:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
make dev CONTENT_PATH=.
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`CONTENT_PATH` is a Make variable, not a value read from `.upcontent/config.json`.
|
|
28
|
+
|
|
29
|
+
## Static hosting
|
|
30
|
+
|
|
31
|
+
For GitHub Pages project sites, set a base path and site URL in the deployment workflow:
|
|
32
|
+
|
|
33
|
+
```yaml
|
|
34
|
+
env:
|
|
35
|
+
BASE_PATH: /engineering-docs
|
|
36
|
+
SITE_URL: https://acme.github.io/engineering-docs
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
For a custom domain or a host serving the site at `/`, leave `BASE_PATH` empty and set `SITE_URL` to the canonical origin.
|
|
40
|
+
|
|
41
|
+
## Precedence
|
|
42
|
+
|
|
43
|
+
`REPO_URL` takes precedence over `repo.url` for generated source links. `BASE_PATH` and `SITE_URL` are build inputs; they do not change the consumer configuration file.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Customization
|
|
3
|
+
description: Configure the portal from the consumer repository without editing the renderer.
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 1
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Customization belongs to the consumer repository. The renderer stays shared; the consumer decides what the site is called, how it looks, what appears in navigation, and where it is published.
|
|
9
|
+
|
|
10
|
+
## Choose a path
|
|
11
|
+
|
|
12
|
+
- [Site identity](site-identity/): title, description, logo, favicon, and repository links.
|
|
13
|
+
- [Theme and CSS](theme/): colors, typography, spacing, and dark mode using Starlight tokens.
|
|
14
|
+
- [Navigation](navigation/): sidebar roots, labels, and content blocklists.
|
|
15
|
+
- [Content behavior](content/): title fields, page metadata, and supported rendered content.
|
|
16
|
+
- [JSON configuration](config-json/): the complete `.upcontent/config.json` guide.
|
|
17
|
+
- [Environment variables](environment/): build-time values for repository URLs and hosting paths.
|
|
18
|
+
|
|
19
|
+
The [configuration reference](config-json/) is the source of truth for supported fields. The [capability showcase](../showcase/) demonstrates the result in one page.
|
|
20
|
+
|
|
21
|
+
## The customization boundary
|
|
22
|
+
|
|
23
|
+
Consumer-owned files normally live here:
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
.upcontent/
|
|
27
|
+
├── config.json
|
|
28
|
+
├── favicon.svg
|
|
29
|
+
├── logo.svg
|
|
30
|
+
└── theme.css
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
If a customization requires changing `astro.config.mjs`, it is not part of the normal consumer configuration surface.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Navigation
|
|
3
|
+
description: Shape the sidebar and keep internal material out of the published portal.
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 4
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
The sidebar is generated from the consumer file structure. Use configuration when the default filesystem order is not the right experience for readers.
|
|
9
|
+
|
|
10
|
+
## Select top-level roots
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"navigation": {
|
|
15
|
+
"roots": [
|
|
16
|
+
"README.md",
|
|
17
|
+
"getting-started",
|
|
18
|
+
"guides",
|
|
19
|
+
"reference"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`roots` limits the files and folders shown at the top level. It does not move files or change their URLs.
|
|
26
|
+
|
|
27
|
+
## Rename generated labels
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"navigation": {
|
|
32
|
+
"labelOverrides": {
|
|
33
|
+
"api": "API reference",
|
|
34
|
+
"runbooks": "Runbooks"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Keys are matched case-insensitively. Use labels that describe the reader's destination, not the team's internal shorthand.
|
|
41
|
+
|
|
42
|
+
## Exclude content before parsing
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"navigation": {
|
|
47
|
+
"blocklist": {
|
|
48
|
+
"exact": ["notes.md"],
|
|
49
|
+
"prefixes": ["drafts/", "internal/"]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Blocklisted paths are excluded from the content collection and sidebar before Markdown is parsed. The portal also protects internal paths such as `.upcontent/`, `.github/`, `src/`, and `node_modules/`.
|
|
56
|
+
|
|
57
|
+
Blocklisting is not access control. Do not put secrets in a repository that will be published to a public host.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Site identity
|
|
3
|
+
description: Give a consumer portal its own name, assets, and repository links.
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 2
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
The consumer controls the visible identity of the published portal.
|
|
9
|
+
|
|
10
|
+
## Configuration
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"site": {
|
|
15
|
+
"title": "Engineering Docs",
|
|
16
|
+
"description": "Documentation for the engineering team.",
|
|
17
|
+
"url": "https://docs.example.com",
|
|
18
|
+
"logo": {
|
|
19
|
+
"src": ".upcontent/logo.svg",
|
|
20
|
+
"alt": "Engineering Docs",
|
|
21
|
+
"replacesTitle": false
|
|
22
|
+
},
|
|
23
|
+
"favicon": ".upcontent/favicon.svg"
|
|
24
|
+
},
|
|
25
|
+
"repo": {
|
|
26
|
+
"url": "https://github.com/acme/engineering-docs"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Logo behavior
|
|
32
|
+
|
|
33
|
+
Use a compact logo mark when the header should display the site title beside it. Set `replacesTitle` to `true` when the logo already contains the full wordmark.
|
|
34
|
+
|
|
35
|
+
Always provide useful alternative text when the logo communicates identity. Use an empty `alt` when the adjacent visible site title already provides the accessible name.
|
|
36
|
+
|
|
37
|
+
Relative asset paths resolve from the consumer repository. Logo and favicon assets are copied into the static output during the build.
|
|
38
|
+
|
|
39
|
+
## Repository links
|
|
40
|
+
|
|
41
|
+
`repo.url` powers links that let readers view or edit the source document on GitHub. Set it to the repository containing the content, not the repository containing the shared portal renderer.
|
|
42
|
+
|
|
43
|
+
If the content repository is private, confirm that the links are appropriate for the readers who will receive the published site.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Theme and CSS
|
|
3
|
+
description: Customize color, typography, and spacing with the official Starlight CSS seam.
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 3
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Use `theme.customCss` to refine the Starlight surface without replacing its layout or accessibility behavior.
|
|
9
|
+
|
|
10
|
+
## Load a consumer stylesheet
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"theme": {
|
|
15
|
+
"customCss": [".upcontent/theme.css"]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The path is relative to the consumer repository. CSS is bundled during the build, so keep the stylesheet in the content repository rather than depending on a remote stylesheet.
|
|
21
|
+
|
|
22
|
+
## Use Starlight tokens
|
|
23
|
+
|
|
24
|
+
```css
|
|
25
|
+
:root {
|
|
26
|
+
--sl-color-accent: #0f766e;
|
|
27
|
+
--sl-color-accent-high: #115e59;
|
|
28
|
+
--sl-font: 'Poppins', sans-serif;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
:root[data-theme='dark'] {
|
|
32
|
+
--sl-color-accent: #5eead4;
|
|
33
|
+
--sl-color-accent-high: #99f6e4;
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Tokens keep custom colors aligned with callouts, links, code blocks, and theme switching.
|
|
38
|
+
|
|
39
|
+
## Keep the interface coherent
|
|
40
|
+
|
|
41
|
+
- Define both light and dark values for strong colors.
|
|
42
|
+
- Prefer tokens over hard-coded colors in component selectors.
|
|
43
|
+
- Keep body text readable before adjusting decorative styles.
|
|
44
|
+
- Use one display or body family consistently instead of styling every section differently.
|
|
45
|
+
- Check keyboard focus and reduced-motion behavior after adding transitions.
|
|
46
|
+
|
|
47
|
+
The site's stylesheet is a working example of this seam.
|