diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..2851fe1 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,44 @@ +name: Build and deploy + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: pages-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: npm run build + - uses: actions/upload-pages-artifact@v3 + if: github.ref == 'refs/heads/main' + with: + path: _site + + deploy: + needs: build + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 13bbe5b..a176443 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea/ - +node_modules/ +_site/ diff --git a/README.md b/README.md index 33babf0..ae23b5a 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,49 @@ -This projects contains the documentation website [docs.tactility.one](https://docs.tactilityproject.org). +This project contains the documentation website at [docs.tactilityproject.org](https://docs.tactilityproject.org). -It is built with [Docsify](https://docsify.js.org), which installs as follows: +The pages are markdown files in `docs/`. [Eleventy](https://www.11ty.dev) turns them into static HTML. The setup and the theme are the same as those of the [Tactility website](https://github.com/TactilityProject/TactilityWebsite). + +Install the dependencies (Node.js 20 or newer): ```sh -npm i docsify-cli -g +npm install ``` -Develop with: +Develop with a local server that reloads on changes: ```sh -docsify serve docs +npm start ``` + +Build the site into `_site/`: + +```sh +npm run build +``` + +## Where things are + +- `docs//`: the documentation of one Tactility version, e.g. `docs/main/` and `docs/v0.7.0/`. A page at `docs/main/app-development/ui.md` gets the URL `/main/app-development/ui/`. +- `docs//index.md`: the start page of a version. +- `docs//_sidebar.md`: the menu of a version, as a list of links with optional group titles. +- `docs/index.md`: the landing page with the list of versions. +- `docs/*.md`: pages that are shared by all versions (support, licenses). +- `docs/_data/`: site name, community links and navigation. +- `docs/_includes/`: page layouts. +- `docs/assets/`: the stylesheet and the script (menus, copy buttons, search). + +## Versions + +Every folder in `docs/` that has a `_sidebar.md` is a version. `main` is the development version. The other folders are releases, and the one with the highest number is shown as the latest release. + +To add a release, copy `docs/main/` to a folder with the name of the release tag (e.g. `docs/v0.8.0/`). The landing page, the version menu and the search pick it up. + +## Writing pages + +- A page starts with a `# Title` heading. +- Link to other pages with a path relative to the current file, e.g. `[Simulator](../os-development/simulator.md)` or `[Assets](fundamentals.md#assets)`. +- `{{versionLabel}}` is replaced with the name of the version folder, which is useful for links to a branch or tag on GitHub. +- Alerts: `> [!NOTE]` and `> [!WARNING]` blocks. +- Tabbed card: wrap `::: tab ` blocks in `:::: tabs` ... `::::`. +- Diagrams: a `plantuml` code block is shown as an image, rendered by the PlantUML server. + +Pushing to `main` builds and publishes the site with GitHub Actions (`.github/workflows/deploy.yml`). For this, the repository's Pages settings must use "GitHub Actions" as the source. diff --git a/docs/.nojekyll b/docs/.nojekyll deleted file mode 100644 index e69de29..0000000 diff --git a/docs/404.md b/docs/404.md new file mode 100644 index 0000000..01e33aa --- /dev/null +++ b/docs/404.md @@ -0,0 +1,7 @@ +--- +title: Page not found +permalink: /404.html +eleventyExcludeFromCollections: true +--- + +This page doesn't exist. Go to the [home page](index.md) to choose a version of the documentation. diff --git a/docs/_data/nav.json b/docs/_data/nav.json new file mode 100644 index 0000000..6f8a46a --- /dev/null +++ b/docs/_data/nav.json @@ -0,0 +1,12 @@ +{ + "main": [ + { "label": "Support", "url": "/support/" }, + { "label": "Website", "url": "https://tactilityproject.org", "external": true } + ], + "footer": [ + { "label": "Licenses & Copyrights", "url": "/licenses-and-copyrights/" }, + { "label": "Support", "url": "/support/" }, + { "label": "Tactility website", "url": "https://tactilityproject.org" }, + { "label": "GitHub", "url": "https://github.com/TactilityProject" } + ] +} diff --git a/docs/_data/site.json b/docs/_data/site.json new file mode 100644 index 0000000..92a735c --- /dev/null +++ b/docs/_data/site.json @@ -0,0 +1,13 @@ +{ + "name": "Tactility Docs", + "tagline": "Documentation for the Tactility operating system", + "description": "Documentation for Tactility, an operating system for ESP32 devices: using it, developing apps, adding devices and drivers, and working on the OS itself.", + "url": "https://docs.tactilityproject.org", + "website": "https://tactilityproject.org", + "repo": "https://github.com/TactilityProject", + "community": [ + { "name": "Matrix", "url": "https://matrix.to/#/#tactility:matrix.org" }, + { "name": "Stoat", "url": "https://stt.gg/xwRTZEpX" }, + { "name": "Discord", "url": "https://discord.gg/pXs2xGS6fs" } + ] +} diff --git a/docs/_includes/base.njk b/docs/_includes/base.njk new file mode 100644 index 0000000..56cad32 --- /dev/null +++ b/docs/_includes/base.njk @@ -0,0 +1,84 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + {%- if home %} + {%- set pageTitle = site.name + ": " + site.tagline %} + {%- elif docTitle %} + {%- set pageTitle = docTitle + ((" (" + version.id + ")") if (version and not version.development) else "") + " | " + site.name %} + {%- else %} + {%- set pageTitle = site.name %} + {%- endif %} + {%- set pageDescription = description or site.description %} + <title>{{ pageTitle }} + + + + + + + + + + + + + + + {%- if home %} + + {%- endif %} + + + + + + +
+ {{ content | safe }} +
+ + + + diff --git a/docs/_includes/home.njk b/docs/_includes/home.njk new file mode 100644 index 0000000..89bba97 --- /dev/null +++ b/docs/_includes/home.njk @@ -0,0 +1,34 @@ +--- +layout: base +home: true +--- +
+
+

{{ tagline }}

+

{{ lead }}

+
+
+ +
+

Choose a version

+ +

Older releases

+
    + {%- for version in versions %}{% if not version.development and not version.latest %} +
  • {{ version.id }}
  • + {%- endif %}{% endfor %} +
+
+ +{%- if content | trim %} +
+ {{ content | safe }} +
+{%- endif %} diff --git a/docs/_includes/page.njk b/docs/_includes/page.njk new file mode 100644 index 0000000..dacf0b5 --- /dev/null +++ b/docs/_includes/page.njk @@ -0,0 +1,63 @@ +--- +layout: base +--- +{%- if version %} +{%- set headings = content | headings %} +{%- set around = version | neighbours(page.url) %} +
+ + + {%- if headings.length > 1 %} + + {%- endif %} +
+{%- else %} +
+
+ {%- if title %}

{{ title }}

{% endif %} + {{ content | safe }} +
+
+{%- endif %} diff --git a/docs/assets/site.css b/docs/assets/site.css new file mode 100644 index 0000000..93c4aaa --- /dev/null +++ b/docs/assets/site.css @@ -0,0 +1,304 @@ +@font-face { + font-family: "Inter"; + src: url("/assets/fonts/inter.woff2") format("woff2"); + font-weight: 100 900; + font-display: swap; +} +@font-face { + font-family: "JetBrains Mono"; + src: url("/assets/fonts/jetbrains-mono.woff2") format("woff2"); + font-weight: 100 800; + font-display: swap; +} + +:root { + --bg: #16171d; + --surface: #1e1f28; + --surface-2: #272934; + --border: #33353f; + --text: #cfd3da; + --muted: #9298a5; + --heading: #ffffff; + --blue: #33bbff; + --pink: #ee81c3; + --green: #5fd38d; + --orange: #f0a35e; + --red: #f0707a; + --radius: 10px; + --wrap: 1120px; + --font: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif; + --mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; + color-scheme: dark; +} + +*, *::before, *::after { box-sizing: border-box; } + +html { + scroll-behavior: smooth; + scroll-padding-top: 5rem; + -webkit-text-size-adjust: 100%; +} + +body { + margin: 0; + min-height: 100vh; + display: flex; + flex-direction: column; + background: var(--bg); + color: var(--text); + font: 400 1rem/1.65 var(--font); + -webkit-font-smoothing: antialiased; +} + +main { flex: 1; } +img { max-width: 100%; height: auto; } +a { color: var(--blue); text-decoration: none; } +a:hover { color: var(--pink); } +:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; border-radius: 4px; } +[hidden] { display: none !important; } + +h1, h2, h3, h4 { color: var(--heading); line-height: 1.2; letter-spacing: -0.015em; } +h1 { font-size: clamp(2rem, 1.5rem + 2vw, 2.75rem); font-weight: 700; margin: 0 0 1.25rem; } + +.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: 1.25rem; } + +.skip { position: absolute; left: -999px; top: 0; padding: 0.5rem 1rem; background: var(--blue); color: #000; z-index: 20; } +.skip:focus { left: 0; } + +/* Header */ + +.site-header { + position: sticky; + top: 0; + z-index: 10; + background: color-mix(in srgb, var(--bg) 85%, transparent); + backdrop-filter: blur(12px); + border-bottom: 1px solid var(--border); +} +.site-header .wrap { display: flex; align-items: center; flex-wrap: wrap; gap: 0.25rem 1.5rem; min-height: 3.75rem; } +.brand { display: inline-flex; align-items: center; gap: 0.6rem; margin-right: auto; color: var(--heading); font: 700 1.15rem var(--mono); } +.brand:hover { color: var(--heading); } +.site-header nav { display: flex; align-items: center; flex-wrap: wrap; gap: 0.15rem; } +.site-header nav a { padding: 0.4rem 0.7rem; border-radius: 6px; color: var(--text); font-size: 0.95rem; font-weight: 500; } +.site-header nav a:hover { color: var(--heading); background: var(--surface-2); } +.site-header nav a[aria-current] { color: var(--blue); } +.site-header nav .nav-repo { margin-left: 0.5rem; border: 1px solid var(--border); } +.nav-toggle { display: none; padding: 0.4rem 0.8rem; border: 1px solid var(--border); border-radius: 6px; background: none; color: var(--text); font: inherit; font-size: 0.9rem; cursor: pointer; } + +@media (max-width: 760px) { + .nav-toggle { display: block; } + .site-header nav.is-collapsible { display: none; } + .site-header nav.is-collapsible.is-open { display: flex; flex-direction: column; align-items: stretch; width: 100%; padding-bottom: 0.75rem; } + .site-header nav .nav-repo { margin-left: 0; } +} + +/* Footer */ + +.site-footer { margin-top: 5rem; border-top: 1px solid var(--border); font-size: 0.9rem; } +.site-footer .wrap { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.5rem 2rem; padding-block: 1.75rem; } +.site-footer ul { display: flex; flex-wrap: wrap; gap: 0.25rem 1.5rem; margin: 0; padding: 0; list-style: none; } +.site-footer a { color: var(--muted); } +.site-footer a:hover { color: var(--heading); } + +/* Badges, cards */ + +.badges { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0; } +.badge { + display: inline-block; + padding: 0.1rem 0.5rem; + border: 1px solid var(--border); + border-radius: 999px; + color: var(--muted); + font: 500 0.72rem/1.6 var(--mono); + white-space: nowrap; +} +.badge-yes { color: var(--green); border-color: color-mix(in srgb, var(--green) 35%, transparent); } +.badge-warn { color: var(--orange); border-color: color-mix(in srgb, var(--orange) 35%, transparent); } +.badge-no { color: var(--red); border-color: color-mix(in srgb, var(--red) 35%, transparent); } + +/* Home */ + +.hero { + padding-block: clamp(3rem, 8vw, 5rem); + background: + radial-gradient(40rem 22rem at 85% 30%, color-mix(in srgb, var(--pink) 14%, transparent), transparent 70%), + radial-gradient(40rem 22rem at 60% 80%, color-mix(in srgb, var(--blue) 14%, transparent), transparent 70%); + border-bottom: 1px solid var(--border); +} +.hero h1 { font-size: clamp(2.25rem, 1.4rem + 3vw, 3.1rem); letter-spacing: -0.03em; text-wrap: balance; } +.lead { max-width: 40rem; margin: 0; font-size: 1.15rem; color: var(--text); } + +.card { + display: block; + padding: 1.4rem; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--surface); + color: var(--text); + transition: border-color 0.15s, transform 0.15s; +} +.card:hover { border-color: var(--blue); color: var(--text); transform: translateY(-2px); } +.card :is(h2, h3) { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin: 0 0 0.5rem; font-size: 1.1rem; } +.card p { margin: 0; font-size: 0.95rem; color: var(--muted); } + +.versions { margin-top: 3.5rem; } +.versions h2 { margin: 0 0 1.25rem; font-size: 1.6rem; } +.versions > h3 { margin: 2rem 0 0.75rem; color: var(--muted); font: 500 0.8rem var(--mono); letter-spacing: 0.12em; text-transform: uppercase; } +.version-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 1rem; } +.version-cards .card h3 { font-family: var(--mono); } +.version-list { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0; padding: 0; list-style: none; } +.version-list a { display: block; padding: 0.4rem 0.9rem; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); color: var(--text); font: 500 0.9rem var(--mono); transition: border-color 0.15s; } +.version-list a:hover { border-color: var(--blue); color: var(--heading); } + +.home-body { margin-top: 3.5rem; } +.home-body.prose { max-width: var(--wrap); } +.home-body.prose h2 { margin-top: 0; } + +/* Search */ + +.search { position: relative; flex: 0 1 17rem; } +.search input { width: 100%; padding: 0.45rem 2.2rem 0.45rem 0.8rem; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--heading); font: inherit; font-size: 0.9rem; } +.search input::placeholder { color: var(--muted); } +.search input:focus { outline: none; border-color: var(--blue); } +.search kbd { position: absolute; right: 0.5rem; top: 50%; translate: 0 -50%; padding: 0 0.4rem; border: 1px solid var(--border); border-radius: 4px; color: var(--muted); font: 0.75rem/1.5 var(--mono); pointer-events: none; } +.search input:focus + kbd, .search input:not(:placeholder-shown) + kbd { display: none; } +.search-results { position: absolute; top: calc(100% + 0.5rem); left: 0; width: min(32rem, calc(100vw - 2.5rem)); max-height: min(32rem, calc(100vh - 6rem)); overflow-y: auto; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); box-shadow: 0 20px 50px -20px #000; } +.search-results a { display: block; padding: 0.7rem 1rem; border-bottom: 1px solid var(--border); color: var(--text); font-size: 0.9rem; line-height: 1.5; } +.search-results a:last-child { border-bottom: 0; } +.search-results a:hover, .search-results a:focus-visible { background: var(--surface-2); color: var(--text); outline: none; } +.search-results strong { display: block; color: var(--heading); font-weight: 600; } +.search-results span { display: block; color: var(--muted); overflow-wrap: anywhere; } +.search-results mark { background: none; color: var(--blue); font-weight: 600; } +.search-results p { margin: 0; padding: 0.8rem 1rem; color: var(--muted); font-size: 0.9rem; } +@media (max-width: 760px) { + .search { order: 1; flex: 1 0 100%; margin-bottom: 0.6rem; } + .search kbd { display: none; } + .site-header nav { order: 2; } +} + +/* Pages */ + +.page { padding-top: clamp(2rem, 5vw, 3.5rem); } +.prose { max-width: 46rem; } + +/* Documentation pages: sidebar, content, "On this page" */ + +.wrap-docs { max-width: 88rem; } +.docs { display: grid; grid-template-columns: 15rem minmax(0, 1fr); gap: 3rem; } +.docs.has-toc { grid-template-columns: 15rem minmax(0, 1fr) 13rem; } +.docs > .prose { width: 100%; max-width: 48rem; padding-top: clamp(2rem, 4vw, 3rem); } + +.sidebar { position: sticky; top: 3.8rem; align-self: start; max-height: calc(100vh - 3.8rem); padding-block: clamp(2rem, 4vw, 3rem) 2rem; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--border) transparent; font-size: 0.92rem; } +.sidebar nav p { margin: 1.5rem 0 0.4rem; color: var(--heading); font-weight: 600; } +.sidebar ul { margin: 0; padding: 0; list-style: none; } +.sidebar nav a { display: block; padding: 0.28rem 0.7rem; border-left: 1px solid var(--border); color: var(--muted); } +.sidebar nav a:hover { color: var(--heading); } +.sidebar nav a[aria-current] { border-left-color: var(--blue); color: var(--blue); } +.sidebar-toggle { display: none; width: 100%; padding: 0.55rem 0.9rem; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--heading); font: inherit; font-weight: 600; text-align: left; cursor: pointer; } +.sidebar-toggle::after { content: "▾"; float: right; color: var(--muted); } +.sidebar-toggle[aria-expanded="true"]::after { content: "▴"; } + +.version-picker { margin-bottom: 1.25rem; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); } +.version-picker summary { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0.8rem; color: var(--muted); cursor: pointer; list-style: none; } +.version-picker summary::-webkit-details-marker { display: none; } +.version-picker summary::after { content: "▾"; margin-left: auto; } +.version-picker[open] summary::after { content: "▴"; } +.version-picker summary strong { color: var(--heading); font: 600 0.9rem var(--mono); } +.version-picker ul { padding: 0.3rem; border-top: 1px solid var(--border); } +.version-picker a { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; padding: 0.3rem 0.5rem; border-radius: 5px; color: var(--text); font: 500 0.88rem var(--mono); } +.version-picker a:hover { background: var(--surface-2); color: var(--heading); } +.version-picker a[aria-current] { color: var(--blue); } +.version-picker small { color: var(--muted); font: 0.75rem var(--font); } + +.toc { position: sticky; top: 3.8rem; align-self: start; max-height: calc(100vh - 3.8rem); padding-block: clamp(2rem, 4vw, 3rem) 2rem; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--border) transparent; font-size: 0.88rem; } +.toc p { margin: 0 0 0.6rem; color: var(--muted); font: 500 0.75rem var(--mono); letter-spacing: 0.1em; text-transform: uppercase; } +.toc ul { margin: 0; padding: 0; list-style: none; border-left: 1px solid var(--border); } +.toc a { display: block; padding: 0.22rem 0 0.22rem 1rem; color: var(--muted); } +.toc .toc-sub a { padding-left: 1.9rem; } +.toc a:hover { color: var(--heading); } + +.version-note { margin: 0 0 1.75rem !important; padding: 0.7rem 1rem; border: 1px solid color-mix(in srgb, var(--orange) 40%, transparent); border-left: 3px solid var(--orange); border-radius: var(--radius); background: color-mix(in srgb, var(--orange) 8%, transparent); font-size: 0.95rem; } + +.pager { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-top: 3.5rem; padding-top: 1.5rem; border-top: 1px solid var(--border); } +.pager a { display: block; padding: 0.8rem 1rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--heading); font-weight: 600; transition: border-color 0.15s; } +.pager a:hover { border-color: var(--blue); } +.pager small { display: block; color: var(--muted); font-size: 0.8rem; font-weight: 400; } +.pager-next { grid-column: 2; text-align: right; } + +@media (max-width: 1200px) { + .docs.has-toc { grid-template-columns: 15rem minmax(0, 1fr); } + .toc { display: none; } +} +@media (max-width: 860px) { + .docs, .docs.has-toc { display: block; } + .sidebar { position: static; max-height: none; padding-block: 1rem 0; overflow: visible; } + .sidebar-toggle { display: block; } + .sidebar-body.is-collapsible { display: none; } + .sidebar-body.is-collapsible.is-open { display: block; margin-top: 1rem; padding-bottom: 1rem; border-bottom: 1px solid var(--border); } + .docs > .prose { padding-top: 1.75rem; } +} + +.prose h2 { margin: 2.75rem 0 0.9rem; font-size: 1.6rem; } +.prose h3 { margin: 2rem 0 0.6rem; font-size: 1.2rem; } +.prose h4 { margin: 1.5rem 0 0.5rem; font-size: 1rem; } +.prose :is(h2, h3, h4) a { color: inherit; } +.prose :is(h2, h3, h4) a:hover::after { content: " #"; color: var(--muted); } +.prose p, .prose ul, .prose ol { margin: 0 0 1.1rem; } +.prose ul, .prose ol { padding-left: 1.4rem; } +.prose li { margin: 0.3rem 0; } +.prose li::marker { color: var(--muted); } +.prose strong { color: var(--heading); font-weight: 600; } +.prose p a, .prose li a, .prose td a { text-decoration: underline; text-decoration-color: color-mix(in srgb, currentColor 40%, transparent); text-underline-offset: 3px; } +.prose p > a:hover > img { opacity: 1; } +.prose hr { border: 0; border-top: 1px solid var(--border); margin: 2.5rem 0; } +.prose blockquote { margin: 1.5rem 0; padding-left: 1rem; border-left: 3px solid var(--border); color: var(--muted); } + +.prose code { padding: 0.15em 0.4em; border-radius: 5px; background: var(--surface-2); color: var(--heading); font: 0.875em var(--mono); } +.prose pre { margin: 1.25rem 0; padding: 1rem 1.2rem; overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); font: 0.875rem/1.6 var(--mono); } +.prose pre code { padding: 0; background: none; color: var(--text); font: inherit; } +.token.comment { color: var(--muted); } +.token.string, .token.attr-value { color: var(--green); } +.token.function, .token.builtin, .token.keyword { color: var(--blue); } +.token.variable, .token.parameter, .token.number, .token.boolean { color: var(--pink); } +.token.operator, .token.punctuation { color: var(--muted); } + +.prose table { display: block; width: max-content; max-width: 100%; margin: 1.25rem 0; overflow-x: auto; border-collapse: collapse; font-size: 0.95rem; } +@media (max-width: 480px) { .prose th, .prose td { padding-inline: 0.55rem !important; } } +.prose th, .prose td { padding: 0.5rem 0.9rem; border-bottom: 1px solid var(--border); text-align: left; vertical-align: top; } +.prose th { color: var(--muted); font: 500 0.75rem var(--mono); letter-spacing: 0.08em; text-transform: uppercase; white-space: nowrap; } +.prose tr:last-child td { border-bottom: 0; } + +.markdown-alert { margin: 1.5rem 0; padding: 0.9rem 1.1rem; border: 1px solid color-mix(in srgb, var(--alert) 40%, transparent); border-left-width: 3px; border-left-color: var(--alert); border-radius: var(--radius); background: color-mix(in srgb, var(--alert) 8%, transparent); --alert: var(--blue); } +.markdown-alert > :last-child { margin-bottom: 0; } +.markdown-alert-title { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.35rem !important; color: var(--alert); font-weight: 600; } +.markdown-alert-title svg { fill: currentColor; flex: none; } +.markdown-alert-warning, .markdown-alert-caution { --alert: var(--orange); } +.markdown-alert-tip { --alert: var(--green); } +.markdown-alert-important { --alert: var(--pink); } + +/* Tabbed card (":::: tabs" in markdown): radio buttons pick the visible panel */ +.tabs { display: flex; flex-wrap: wrap; margin: 1.5rem 0; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); overflow: hidden; } +.tab-input { position: absolute; opacity: 0; pointer-events: none; } +.tab-label { padding: 0.7rem 1.2rem; border-bottom: 2px solid transparent; color: var(--muted); font-weight: 600; font-size: 0.95rem; cursor: pointer; } +.tab-label:hover { color: var(--heading); } +.tab-input:checked + .tab-label { border-bottom-color: var(--blue); color: var(--heading); } +.tab-input:focus-visible + .tab-label { outline: 2px solid var(--blue); outline-offset: -2px; } +.tab-panel { display: none; order: 1; width: 100%; padding: 1.25rem 1.4rem 0.4rem; border-top: 1px solid var(--border); } +.tab-input:checked + .tab-label + .tab-panel { display: block; } +.tab-panel pre { background: var(--bg); } + + +/* Diagrams are drawn in dark colors, so they get a light card */ +.prose .diagram { margin: 1.5rem 0; padding: 1rem; overflow-x: auto; border-radius: var(--radius); background: #e9ebf0; text-align: center; } +.prose .diagram img { display: inline-block; max-width: none; vertical-align: middle; } + +/* Copy button of a code block */ +.code { position: relative; } +.copy { position: absolute; top: 0.5rem; right: 0.5rem; padding: 0.2rem 0.6rem; border: 1px solid var(--border); border-radius: 6px; background: var(--surface-2); color: var(--muted); font: 500 0.75rem var(--mono); cursor: pointer; opacity: 0; transition: opacity 0.15s; } +.code:hover .copy, .copy:focus-visible { opacity: 1; } +.copy:hover { color: var(--heading); } + +@media (prefers-reduced-motion: reduce) { + html { scroll-behavior: auto; } + * { transition: none !important; } +} diff --git a/docs/assets/site.js b/docs/assets/site.js new file mode 100644 index 0000000..a9f2f8c --- /dev/null +++ b/docs/assets/site.js @@ -0,0 +1,133 @@ +(function () { + // Collapsible menus on small screens + function collapsible(buttonSelector, target) { + var button = document.querySelector(buttonSelector); + if (!button || !target) return; + button.hidden = false; + target.classList.add("is-collapsible"); + button.addEventListener("click", function () { + var open = target.classList.toggle("is-open"); + button.setAttribute("aria-expanded", open); + }); + } + collapsible(".nav-toggle", document.getElementById("site-nav")); + collapsible(".sidebar-toggle", document.getElementById("doc-nav")); + + // Keep the current page visible in a long sidebar + var sidebar = document.querySelector(".sidebar"); + var current = sidebar && sidebar.querySelector("nav a[aria-current]"); + if (current && sidebar.scrollHeight > sidebar.clientHeight) { + sidebar.scrollTop = current.offsetTop - sidebar.clientHeight / 2; + } + + // Copy button on code blocks + if (navigator.clipboard) { + [].forEach.call(document.querySelectorAll(".prose pre"), function (pre) { + var box = document.createElement("div"); + box.className = "code"; + pre.parentNode.insertBefore(box, pre); + box.appendChild(pre); + var button = document.createElement("button"); + button.type = "button"; + button.className = "copy"; + button.textContent = "Copy"; + button.addEventListener("click", function () { + navigator.clipboard.writeText(pre.textContent.replace(/\n$/, "")).then(function () { + button.textContent = "Copied"; + setTimeout(function () { button.textContent = "Copy"; }, 1500); + }); + }); + box.appendChild(button); + }); + } + + // Search in the pages of one version, with the Pagefind index of the build + var search = document.querySelector(".search"); + if (!search) return; + var input = search.querySelector("input"); + var panel = search.querySelector(".search-results"); + var version = search.dataset.version; + var pagefind; + search.hidden = false; + + function load() { + if (!pagefind) { + pagefind = import("/pagefind/pagefind.js").then(function (module) { + module.init(); + return module; + }); + } + return pagefind; + } + + function message(text) { + var p = document.createElement("p"); + p.textContent = text; + panel.replaceChildren(p); + panel.hidden = false; + } + + function show(items) { + if (!items.length) return message("No results in " + version + "."); + panel.replaceChildren.apply(panel, items.map(function (item) { + var link = document.createElement("a"); + link.href = item.url; + var title = document.createElement("strong"); + title.textContent = item.title; + var excerpt = document.createElement("span"); + excerpt.innerHTML = item.excerpt; // escaped text with from Pagefind + link.append(title, excerpt); + return link; + })); + panel.hidden = false; + } + + input.addEventListener("focus", function () { + load().catch(function () {}); + if (input.value.trim() && panel.childNodes.length) panel.hidden = false; + }); + + input.addEventListener("input", function () { + var query = input.value.trim(); + if (!query) { + panel.hidden = true; + return; + } + load() + .then(function (module) { + return module.debouncedSearch(query, { filters: { version: version } }, 150); + }) + .then(function (result) { + if (!result) return; // a newer search replaced this one + return Promise.all(result.results.slice(0, 8).map(function (r) { return r.data(); })).then(function (pages) { + if (input.value.trim() !== query) return; + var items = []; + pages.forEach(function (page) { + // One entry per matching section of a page, at most 3 per page + var sections = page.sub_results.slice(0, 3); + sections.forEach(function (section) { + var title = section.title === page.meta.title ? page.meta.title : page.meta.title + " › " + section.title; + items.push({ url: section.url, title: title, excerpt: section.excerpt }); + }); + }); + show(items.slice(0, 12)); + }); + }) + .catch(function () { + message("Search isn't available."); + }); + }); + + document.addEventListener("keydown", function (event) { + if (event.key === "Escape") { + panel.hidden = true; + if (document.activeElement === input) input.blur(); + } else if (event.key === "/" && !/^(INPUT|TEXTAREA|SELECT)$/.test(document.activeElement.tagName)) { + event.preventDefault(); + input.focus(); + } + }); + document.addEventListener("click", function (event) { + if (!search.contains(event.target)) panel.hidden = true; + }); +})(); diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 7317c70..0000000 --- a/docs/index.html +++ /dev/null @@ -1,76 +0,0 @@ - - - - - Tactility - - - - - - - - - - - -
Loading...
- - - - - - - - - - - - diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..1583651 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,20 @@ +--- +layout: home +tagline: Tactility documentation +lead: >- + Tactility is an operating system for devices with a SoC from the ESP32 family. + These pages explain how to use it, how to build apps for it, how to add devices + and drivers, and how to work on the operating system itself. +developmentText: >- + Follows the main branch of Tactility. It has the newest pages, and it can + describe features that aren't in a release yet. +latestText: Matches the firmware of the latest release. +--- + +## About these docs + +The documentation is written per Tactility version. Pick the version that matches the firmware on your device or the SDK that you build with. + +- To install Tactility or to see which devices it runs on, go to [tactilityproject.org](https://tactilityproject.org). +- Bugs and suggestions can be reported on [GitHub](https://github.com/TactilityProject/Tactility/issues). +- For questions, see [Support](support.md). diff --git a/docs/versions/licenses-and-copyrights.md b/docs/licenses-and-copyrights.md similarity index 100% rename from docs/versions/licenses-and-copyrights.md rename to docs/licenses-and-copyrights.md diff --git a/docs/versions/main/_sidebar.md b/docs/main/_sidebar.md similarity index 100% rename from docs/versions/main/_sidebar.md rename to docs/main/_sidebar.md diff --git a/docs/versions/main/app-development/fundamentals.md b/docs/main/app-development/fundamentals.md similarity index 96% rename from docs/versions/main/app-development/fundamentals.md rename to docs/main/app-development/fundamentals.md index 562cf24..6d4cb8c 100644 --- a/docs/versions/main/app-development/fundamentals.md +++ b/docs/main/app-development/fundamentals.md @@ -11,7 +11,7 @@ The app refers to these symbols, but they don't actually exist inside its binary The device firmware contains the main operating system, including symbol mappings for apps: -It exposes a limited set of functions to the apps. It stores a map that ties a function pointer to a function name. Function names are stripped by the ESP-IDF build system, so we have to manually map them. Each [kernel module](kernel-concepts/module.md) declares its own symbol table (a `ModuleSymbol` array built with `DEFINE_MODULE_SYMBOL`), which is hard-wired into the firmware - see [Symbols](app-development/symbols.md) for how to add one. +It exposes a limited set of functions to the apps. It stores a map that ties a function pointer to a function name. Function names are stripped by the ESP-IDF build system, so we have to manually map them. Each [kernel module](../kernel-concepts/module.md) declares its own symbol table (a `ModuleSymbol` array built with `DEFINE_MODULE_SYMBOL`), which is hard-wired into the firmware - see [Symbols](symbols.md) for how to add one. ## TactilitySDK @@ -28,7 +28,7 @@ Apps are usually distributed as a package: a file with an `.app` extension. This A package can contain more than one app. For example, a game and its settings app can be installed and updated together. -A package can also contain kernel modules, for functionality that runs in the background and that apps can use. See [Modules in a Package](app-development/modules.md). +A package can also contain kernel modules, for functionality that runs in the background and that apps can use. See [Modules in a Package](modules.md). App executables only contain the symbols defined by its own source files. All other symbols must be present in the main firmware **and** exposed explicitly by a kernel module's symbol table. @@ -193,7 +193,7 @@ The window manager deletes and recreates the window's widgets when another windo ## Developing with the simulator -The [simulator](os-development/simulator.md) runs Tactility on your computer (Linux, or WSL on Windows). Building and testing an app there is much faster than flashing and testing on a device, and it's easier to debug. +The [simulator](../os-development/simulator.md) runs Tactility on your computer (Linux, or WSL on Windows). Building and testing an app there is much faster than flashing and testing on a device, and it's easier to debug. 1. Add `posix-x86_64` to the app's `target.platforms` in `manifest.properties`. 2. Start the simulator, and turn on the development server in its Settings app (Settings -> Development). @@ -236,7 +236,7 @@ app.0.binary=helloworld ### Module properties -A package can have modules (see [Modules in a Package](app-development/modules.md)). Each one has a `module..` property, numbered from `0` without gaps: +A package can have modules (see [Modules in a Package](modules.md)). Each one has a `module..` property, numbered from `0` without gaps: - `module..binary`: required name of the module's binary, without extension. It's installed as `bin//.elf`. diff --git a/docs/versions/main/app-development/getting-started.md b/docs/main/app-development/getting-started.md similarity index 78% rename from docs/versions/main/app-development/getting-started.md rename to docs/main/app-development/getting-started.md index 684c9bb..4f6f146 100644 --- a/docs/versions/main/app-development/getting-started.md +++ b/docs/main/app-development/getting-started.md @@ -21,7 +21,7 @@ HelloWorld/ └── main.c ``` -- `manifest.properties` describes the package and its apps (see [Manifest.properties](app-development/fundamentals.md?id=manifestproperties)). +- `manifest.properties` describes the package and its apps (see [Manifest.properties](fundamentals.md#manifestproperties)). - The top-level `CMakeLists.txt` sets up the project with the SDK: ```cmake cmake_minimum_required(VERSION 3.20) @@ -40,19 +40,19 @@ HelloWorld/ file(GLOB_RECURSE SOURCE_FILES Source/*.c*) tactility_component_register(SRCS ${SOURCE_FILES}) ``` -- `assets/` holds files that are packaged with the app (see [Assets](app-development/fundamentals.md?id=assets)). +- `assets/` holds files that are packaged with the app (see [Assets](fundamentals.md#assets)). -The easiest way to start a new app is to let `tt` generate one (see [Starting a new app](app-development/tt.md?id=starting-a-new-app)), or to copy one of the examples and change its manifest. +The easiest way to start a new app is to let `tt` generate one (see [Starting a new app](tt.md#starting-a-new-app)), or to copy one of the examples and change its manifest. ## Installing the tt tool -Apps are built, installed and run with the [tt tool](app-development/tt.md). Download [tt.zip](https://cdn.tactilityproject.org/sdk/tt.zip) from `https://cdn.tactilityproject.org/sdk/tt.zip` and extract it into your project's root folder. It needs Python 3. +Apps are built, installed and run with the [tt tool](tt.md). Download [tt.zip](https://cdn.tactilityproject.org/sdk/tt.zip) from `https://cdn.tactilityproject.org/sdk/tt.zip` and extract it into your project's root folder. It needs Python 3. The example apps in [TactilityApps](https://github.com/TactilityProject/TactilityApps) already include it. ## Building -After you went through the [Fundamentals](app-development/fundamentals.md) to set up a new application, +After you went through the [Fundamentals](fundamentals.md) to set up a new application, or if you downloaded one of the example apps, you can build it from the app's folder: ```bash @@ -62,7 +62,7 @@ or if you downloaded one of the example apps, you can build it from the app's fo ./tt app build --architecture esp32s3 ``` -On Windows, use `.\tt` in PowerShell, or `py tt.py` (see [The tt Tool](app-development/tt.md)). +On Windows, use `.\tt` in PowerShell, or `py tt.py` (see [The tt Tool](tt.md)). `tt` downloads the TactilitySDK version from the manifest's `target.sdk` automatically. It builds the app for every platform and creates the package in `build/.app`. @@ -73,15 +73,15 @@ If you have more than one app in your repository, you can specify its path: ./tt app build --path Apps/OtherApp ``` -Run `./tt help` for all commands, and see [The tt Tool](app-development/tt.md) for more documentation. +Run `./tt help` for all commands, and see [The tt Tool](tt.md) for more documentation. -To build against a firmware that you built yourself (e.g. with new symbols), see [Building TactilitySDK locally](app-development/symbols.md?id=building-tactilitysdk-locally). +To build against a firmware that you built yourself (e.g. with new symbols), see [Building TactilitySDK locally](symbols.md#building-tactilitysdk-locally). ## Installing and running First, make sure to set up the Tactility device: - Wi-Fi should be on, and connected to the same network as your computer -- The development server should be on (via Settings -> Development, see [Remote Access](user-guide/remote-access.md)) +- The development server should be on (via Settings -> Development, see [Remote Access](../user-guide/remote-access.md)) Tell `tt` which device to use. The Development app shows its IP address: @@ -97,7 +97,7 @@ Install the app on your device as follows: ./tt app install ``` -The first time, `tt` shows the device's fingerprint and asks whether to trust it, and the device asks whether to accept your computer. See [Remote Access](user-guide/remote-access.md). The package contains the binaries for all of its platforms, so the device picks its own. +The first time, `tt` shows the device's fingerprint and asks whether to trust it, and the device asks whether to accept your computer. See [Remote Access](../user-guide/remote-access.md). The package contains the binaries for all of its platforms, so the device picks its own. To install a package that you already have, pass the file. This works outside of an app project too: @@ -133,7 +133,7 @@ To remove the app again: ### Remote shell -`./tt shell` opens a shell on the device, to look at files and run commands while you test your app. `./tt push` and `./tt pull` copy files to and from the device. See [The tt Tool](app-development/tt.md). +`./tt shell` opens a shell on the device, to look at files and run commands while you test your app. `./tt push` and `./tt pull` copy files to and from the device. See [The tt Tool](tt.md). ### Serial monitor @@ -143,7 +143,7 @@ To use `idf.py` you must have the [main firmware repository](https://github.com/ To connect faster, run `idf.py -p [PORT] monitor` -When an app fails to load, the log names the missing symbol (see [Symbols](app-development/symbols.md?id=testing)). +When an app fails to load, the log names the missing symbol (see [Symbols](symbols.md#testing)). ### Attaching a debugger diff --git a/docs/versions/main/app-development/input-devices.md b/docs/main/app-development/input-devices.md similarity index 89% rename from docs/versions/main/app-development/input-devices.md rename to docs/main/app-development/input-devices.md index ec0697c..507b651 100644 --- a/docs/versions/main/app-development/input-devices.md +++ b/docs/main/app-development/input-devices.md @@ -1,10 +1,10 @@ # App Development - Input Devices -Most apps get their input through LVGL (see [Input and Focus](app-development/input.md)). Apps that take over the whole screen, like games and demos, can also read the input devices directly through their drivers: a touchscreen, a keyboard or a trackball. +Most apps get their input through LVGL (see [Input and Focus](input.md)). Apps that take over the whole screen, like games and demos, can also read the input devices directly through their drivers: a touchscreen, a keyboard or a trackball. ## Finding devices -Input devices are [devices](kernel-concepts/device.md) of a specific type (``): +Input devices are [devices](../kernel-concepts/device.md) of a specific type (``): | Type | Header | Hardware | |---|---|---| @@ -44,7 +44,7 @@ if (!module_is_started(&lvgl_module)) { } ``` -While LVGL is stopped, your app owns the screen: draw it yourself through the display driver (see the [Display](driver-development/display.md) driver API), as there's no window manager or statusbar. +While LVGL is stopped, your app owns the screen: draw it yourself through the display driver (see the [Display](../driver-development/display.md) driver API), as there's no window manager or statusbar. ## Touchscreen diff --git a/docs/versions/main/app-development/input.md b/docs/main/app-development/input.md similarity index 98% rename from docs/versions/main/app-development/input.md rename to docs/main/app-development/input.md index 17fd208..c9f92a3 100644 --- a/docs/versions/main/app-development/input.md +++ b/docs/main/app-development/input.md @@ -62,7 +62,7 @@ Most apps only need LVGL's events (`LV_EVENT_CLICKED`, `LV_EVENT_VALUE_CHANGED`, ## Reading input devices directly -Full-screen apps like games can also read the touchscreen, keyboards and trackballs through their drivers, without LVGL. See [Input Devices](app-development/input-devices.md). +Full-screen apps like games can also read the touchscreen, keyboards and trackballs through their drivers, without LVGL. See [Input Devices](input-devices.md). ## Testing diff --git a/docs/versions/main/app-development/insets.md b/docs/main/app-development/insets.md similarity index 100% rename from docs/versions/main/app-development/insets.md rename to docs/main/app-development/insets.md diff --git a/docs/versions/main/app-development/mesh.md b/docs/main/app-development/mesh.md similarity index 98% rename from docs/versions/main/app-development/mesh.md rename to docs/main/app-development/mesh.md index 5681119..ababa5b 100644 --- a/docs/versions/main/app-development/mesh.md +++ b/docs/main/app-development/mesh.md @@ -2,7 +2,7 @@ Apps can send and receive text messages over the mesh network with ``. The API is the same for every mesh protocol (MeshCore, Meshtastic): the mesh service runs the protocol, stores the messages and reports events. -See [Mesh Networking](user-guide/mesh.md) for what the user sets up, and which devices support it. +See [Mesh Networking](../user-guide/mesh.md) for what the user sets up, and which devices support it. ## Availability @@ -196,4 +196,4 @@ Settings are persisted before they are used, so a setter that fails to save leav ## Raw frames -To use protocol features that the API doesn't cover, `mesh_raw_send(backend, data, length)` sends a protocol-specific frame, when that protocol is active. Received frames are available as raw radio packets through `radio_rx_event_subscribe()` on the radio device (see the [Radio](driver-development/radio.md) driver page). +To use protocol features that the API doesn't cover, `mesh_raw_send(backend, data, length)` sends a protocol-specific frame, when that protocol is active. Received frames are available as raw radio packets through `radio_rx_event_subscribe()` on the radio device (see the [Radio](../driver-development/radio.md) driver page). diff --git a/docs/versions/main/app-development/modules.md b/docs/main/app-development/modules.md similarity index 88% rename from docs/versions/main/app-development/modules.md rename to docs/main/app-development/modules.md index 7d9b0a5..99976e3 100644 --- a/docs/versions/main/app-development/modules.md +++ b/docs/main/app-development/modules.md @@ -1,6 +1,6 @@ # Modules in a Package -A package can contain [kernel modules](kernel-concepts/module.md) next to its apps, or instead of them. A module has no window. It runs in the background and can export functions to apps, like the modules in the firmware do (see [Symbols](app-development/symbols.md)). The web server of the [TactilityApps](https://github.com/TactilityProject/TactilityApps) project is an example: its module serves the pages and its app changes the settings. +A package can contain [kernel modules](../kernel-concepts/module.md) next to its apps, or instead of them. A module has no window. It runs in the background and can export functions to apps, like the modules in the firmware do (see [Symbols](symbols.md)). The web server of the [TactilityApps](https://github.com/TactilityProject/TactilityApps) project is an example: its module serves the pages and its app changes the settings. ## Manifest @@ -67,9 +67,9 @@ struct Module* module_main(int argc, char* argv[]) { } ``` -[Start a new project](app-development/tt.md?id=starting-a-new-app) and choose `module` to get this template. +[Start a new project](tt.md#starting-a-new-app) and choose `module` to get this template. -To let apps call into the module, list its functions in `.symbols`, like the [firmware modules do](app-development/symbols.md?id=how-symbols-are-exported). +To let apps call into the module, list its functions in `.symbols`, like the [firmware modules do](symbols.md#how-symbols-are-exported). ## When modules are loaded @@ -119,12 +119,12 @@ The Kernel settings app (Settings → Kernel) lists these modules. A button star ## Limits -An external module can only use the symbols the firmware exports (see [Symbols](app-development/symbols.md)), and it can't count on the C++ standard library being complete: +An external module can only use the symbols the firmware exports (see [Symbols](symbols.md)), and it can't count on the C++ standard library being complete: - No streams (``, ``, ``). Use `stdio` and `std::string`. - No static or global objects with a destructor, not even a function-local `static const std::string`. The destructor needs `__dso_handle`, and the build fails. Use a local variable or `new` and `delete`. - No `std::from_chars` or `std::to_chars` on RISC-V targets, which need a function of the compiler runtime that isn't exported. Use `strtol()`. - Call `vSemaphoreDelete()` on a kernel `Mutex`'s `handle`, instead of `mutex_destruct()`. -To find what is missing, list the undefined symbols of the built `.elf` and compare them with the symbol tables, or load the module on a device and read the log (see [Symbols](app-development/symbols.md?id=testing)). +To find what is missing, list the undefined symbols of the built `.elf` and compare them with the symbol tables, or load the module on a device and read the log (see [Symbols](symbols.md#testing)). On a device with PSRAM, the code and data of a module are loaded into PSRAM. A task that a module creates still gets its stack from internal RAM, so keep the stack sizes small. diff --git a/docs/versions/main/app-development/storage.md b/docs/main/app-development/storage.md similarity index 97% rename from docs/versions/main/app-development/storage.md rename to docs/main/app-development/storage.md index a119685..d191392 100644 --- a/docs/versions/main/app-development/storage.md +++ b/docs/main/app-development/storage.md @@ -66,4 +66,4 @@ Keep in mind that: - Writing to flash is slow, and wears it out. Don't write files in a tight loop. - An SD card can be removed at any time. Handle failing file operations gracefully. -See [Filesystems](kernel-concepts/filesystems.md) for how storage is mounted. +See [Filesystems](../kernel-concepts/filesystems.md) for how storage is mounted. diff --git a/docs/versions/main/app-development/symbols.md b/docs/main/app-development/symbols.md similarity index 87% rename from docs/versions/main/app-development/symbols.md rename to docs/main/app-development/symbols.md index e6555e6..23980c0 100644 --- a/docs/versions/main/app-development/symbols.md +++ b/docs/main/app-development/symbols.md @@ -4,7 +4,7 @@ Apps can only call the functions that the firmware exports. This page explains h ## How symbols are exported -Every [kernel module](kernel-concepts/module.md) can export symbols through the `symbols` field of its `struct Module`: a table of `ModuleSymbol` entries, terminated by `MODULE_SYMBOL_TERMINATOR`. When an app is loaded, its undefined symbols are looked up in the tables of the modules that were added to the system with `module_add()`. A module doesn't need to be started for this. The [modules from a package](app-development/modules.md) are looked up the same way. +Every [kernel module](../kernel-concepts/module.md) can export symbols through the `symbols` field of its `struct Module`: a table of `ModuleSymbol` entries, terminated by `MODULE_SYMBOL_TERMINATOR`. When an app is loaded, its undefined symbols are looked up in the tables of the modules that were added to the system with `module_add()`. A module doesn't need to be started for this. The [modules from a package](modules.md) are looked up the same way. ```c const struct ModuleSymbol lvgl_module_symbols[] = { @@ -55,7 +55,7 @@ When you export new symbols, you'll need to build your own SDK locally to build 1. Open a terminal and navigate to the Tactility project root folder. 2. Build the firmware for the relevant target (e.g. ESP32-S3) with `idf.py build`. -3. Run `python Buildscripts/release-current.py`. It creates the SDK in `release/sdk//platform-/TactilitySDK`, and the [tt](app-development/tt.md) tools in `release/sdk//tools`, where `` comes from `version.txt`. +3. Run `python Buildscripts/release-current.py`. It creates the SDK in `release/sdk//platform-/TactilitySDK`, and the [tt](tt.md) tools in `release/sdk//tools`, where `` comes from `version.txt`. - With an activated ESP-IDF environment, it builds the SDK for the target in `sdkconfig`. - Without one, it builds the SDK for the simulator (`posix-`). 4. Set the environment variable `TACTILITY_SDK_PATH` to the `release` folder, for example: `export TACTILITY_SDK_PATH=/path/to/Tactility/release` diff --git a/docs/versions/main/app-development/tt.md b/docs/main/app-development/tt.md similarity index 86% rename from docs/versions/main/app-development/tt.md rename to docs/main/app-development/tt.md index 9865af0..622edf2 100644 --- a/docs/versions/main/app-development/tt.md +++ b/docs/main/app-development/tt.md @@ -25,7 +25,7 @@ If you still use `tactility.py`: run `python tactility.py updateself`, then run 1. `tt` picks the SDK version, in this order: 1. `target.sdk` from `manifest.properties` in the current folder, or else from the first `Apps/*/manifest.properties` or `apps/*/manifest.properties`. A project always decides: when one is found, the other sources are not read or asked. 2. The version that you set with `tt sdk `, stored in `.tactility/sdk_version` in the current folder. - 3. The Tactility version of the device that you set with `tt host`. `tt` asks the device for it (see [Choosing the device](app-development/tt.md?id=choosing-the-device)). + 3. The Tactility version of the device that you set with `tt host`. `tt` asks the device for it (see [Choosing the device](tt.md#choosing-the-device)). 4. A question for the SDK version. The answer is stored in `.tactility/sdk_version`, so it's asked once per folder. 2. It downloads that SDK version's tools, and the SDK for each of the manifest's `target.platforms` (every platform of the SDK for `target.platforms=all`), into `~/.tactility/sdk//`. This only happens once per version. 3. It runs the command with that version's tools. @@ -59,7 +59,7 @@ Commands that don't depend on an app (`shell`, `push`, `pull`, `screenshot`, `ho | Action | Purpose | |---|---| | `build` | Build the app and create its package in `build/.app` | -| `create` | Create a new app or module project in the current folder (see [Starting a new app](app-development/tt.md?id=starting-a-new-app)) | +| `create` | Create a new app or module project in the current folder (see [Starting a new app](tt.md#starting-a-new-app)) | | `install` | Install the package on the device | | `install .app` | Install an existing app package on the device. This needs no app project | | `run` | Start the app on the device. A package with only modules has no app to start | @@ -85,7 +85,7 @@ Run `tt app create` in an empty folder: ./tt app create ``` -The SDK version comes from `tt sdk` or from the device (see [How it works](app-development/tt.md?id=how-it-works)), so run `./tt sdk ` first if you have no device. Every question shows its default in brackets, and Enter accepts it: +The SDK version comes from `tt sdk` or from the device (see [How it works](tt.md#how-it-works)), so run `./tt sdk ` first if you have no device. Every question shows its default in brackets, and Enter accepts it: | Question | Default | |---|---| @@ -97,13 +97,13 @@ The SDK version comes from `tt sdk` or from the device (see [How it works](app-d The project types are: - `gui`: an app with a window, that shows "Hello, world!" and a toolbar. - `terminal`: a headless app that prints "Hello, world!" to its output and runs as a shell command. -- `module`: a [kernel module](app-development/modules.md) that prints "Module started" and "Module stopped". It has no app, so the app id and app name aren't asked. +- `module`: a [kernel module](modules.md) that prints "Module started" and "Module stopped". It has no app, so the app id and app name aren't asked. -`tt app create` fills in your answers in the project templates that come with the tools of the SDK version (`NewProject/` in `tools.zip`, see [Releases](os-development/releases.md)). It refuses to run when the folder already has a `manifest.properties` or `main/`. The root `CMakeLists.txt` is created by the first build. +`tt app create` fills in your answers in the project templates that come with the tools of the SDK version (`NewProject/` in `tools.zip`, see [Releases](../os-development/releases.md)). It refuses to run when the folder already has a `manifest.properties` or `main/`. The root `CMakeLists.txt` is created by the first build. ## Choosing the device -Commands that talk to a device need its host name or IP address (shown in the device's Development app, see [Remote Access](user-guide/remote-access.md)). Either pass it with `-h`, or store it once: +Commands that talk to a device need its host name or IP address (shown in the device's Development app, see [Remote Access](../user-guide/remote-access.md)). Either pass it with `-h`, or store it once: ```bash ./tt host 192.168.1.123 @@ -112,7 +112,7 @@ Commands that talk to a device need its host name or IP address (shown in the de `tt host` stores the host in `.tactility/device_host` in the current folder, not globally. `tt host` without an argument shows it, or `none`. -After storing the host, `tt host ` asks the device for its manufacturer, name, id and Tactility version, and prints them. That needs the device's development server to be enabled. When the device answers, its Tactility version is also the SDK version that `tt` uses outside an app project (see [How it works](app-development/tt.md?id=how-it-works)). +After storing the host, `tt host ` asks the device for its manufacturer, name, id and Tactility version, and prints them. That needs the device's development server to be enabled. When the device answers, its Tactility version is also the SDK version that `tt` uses outside an app project (see [How it works](tt.md#how-it-works)). ## Choosing the SDK version outside an app @@ -120,7 +120,7 @@ Outside an app project, `tt sdk ` stores the SDK version in `.tactility ## Using a local SDK -To build against an SDK that you built yourself (see [Building TactilitySDK locally](app-development/symbols.md?id=building-tactilitysdk-locally)), point `TACTILITY_SDK_PATH` to its `release` folder: +To build against an SDK that you built yourself (see [Building TactilitySDK locally](symbols.md#building-tactilitysdk-locally)), point `TACTILITY_SDK_PATH` to its `release` folder: ```bash export TACTILITY_SDK_PATH=/path/to/Tactility/release diff --git a/docs/versions/main/app-development/ui.md b/docs/main/app-development/ui.md similarity index 96% rename from docs/versions/main/app-development/ui.md rename to docs/main/app-development/ui.md index b2ea046..01f1d00 100644 --- a/docs/versions/main/app-development/ui.md +++ b/docs/main/app-development/ui.md @@ -4,7 +4,7 @@ Tactility uses [LVGL](https://lvgl.io/) (version 9) for its user interface. Apps ## Windows -An app gets a window with `window_manager_create()` (see [Writing an app with a window](app-development/fundamentals.md?id=writing-an-app-with-a-window)): +An app gets a window with `window_manager_create()` (see [Writing an app with a window](fundamentals.md#writing-an-app-with-a-window)): ```c WindowId window = window_manager_create(app_instance_id, create_widgets, ctx); @@ -112,7 +112,7 @@ It also works on a card that has the items as its direct children, without a sep ### Chips -Chips don't select themselves: give the selected one `LV_STATE_CHECKED`. Put a group of chips in a container with grid navigation, so the arrow keys move between the chips and up/down move to the next group (see [Input and Focus](app-development/input.md)): +Chips don't select themselves: give the selected one `LV_STATE_CHECKED`. Put a group of chips in a container with grid navigation, so the arrow keys move between the chips and up/down move to the next group (see [Input and Focus](input.md)): ```c lv_obj_t* chips = lv_obj_create(parent); @@ -179,7 +179,7 @@ Tactility runs on displays from 135×240 to 1280×720 pixels, in portrait and la - Use LVGL's flex layout (`lv_obj_set_flex_flow()`, `lv_obj_set_flex_grow()`) and percentages (`LV_PCT(100)`). - Size things relative to the font size (e.g. `lvgl_get_text_font_height(FONT_SIZE_DEFAULT)`) instead of in pixels. - Use `lvgl_theme_get_spacing()` and `lvgl_theme_get_size()` (``) for custom paddings and sizes. They range from 0 (compact) to 100 (regular), and users set them in the Appearance app. `lvgl_theme_lerp(compact, regular, percent)` interpolates between the two values. -- On round or rounded displays, use the [Insets](app-development/insets.md) API for content near the edges. +- On round or rounded displays, use the [Insets](insets.md) API for content near the edges. ## Fonts and icons @@ -192,7 +192,7 @@ lv_obj_set_style_text_font(label, lvgl_get_text_font(FONT_SIZE_LARGE), LV_STATE_ - Text: `FONT_SIZE_SMALL`, `FONT_SIZE_DEFAULT` and `FONT_SIZE_LARGE`. - Icons: [Material Symbols](https://fonts.google.com/icons), with defines in `` (e.g. `LVGL_ICON_SHARED_SETTINGS`). Use them as label text with `lvgl_get_shared_icon_default_font()` or `lvgl_get_shared_icon_large_font()`. -Only the icons that Tactility uses are included. See [Icons and Fonts](os-development/icons-and-fonts.md) for how to add one. +Only the icons that Tactility uses are included. See [Icons and Fonts](../os-development/icons-and-fonts.md) for how to add one. ## Theme @@ -204,4 +204,4 @@ To look right in all of them: ## Input -Not every device has a touchscreen: some have a keyboard, a trackball, an encoder wheel or a few buttons. See [Input and Focus](app-development/input.md) to make your app work with all of them. +Not every device has a touchscreen: some have a keyboard, a trackball, an encoder wheel or a few buttons. See [Input and Focus](input.md) to make your app work with all of them. diff --git a/docs/versions/main/device-development/device-properties.md b/docs/main/device-development/device-properties.md similarity index 99% rename from docs/versions/main/device-development/device-properties.md rename to docs/main/device-development/device-properties.md index 5032ccb..cf7ad5d 100644 --- a/docs/versions/main/device-development/device-properties.md +++ b/docs/main/device-development/device-properties.md @@ -59,7 +59,7 @@ The USB device classes are enabled separately. They're all optional booleans tha - `storage.path`: the mount path of the storage that holds the user data, e.g. `/data` for the internal data partition or `/sdcard` for an SD card. Defaults to `/sdcard` or `/data` when the older `storage.userDataLocation` (`SD` or `Internal`) is set instead. -The device's `.dts` file must provide the storage at that path. For `/data` it needs an `espressif,esp32-flash-fatfs` node with `partition-label = "data"` and `mount-path = "/data"`. Any other path is provided by the driver of the storage, such as an SD card. See [Filesystems](kernel-concepts/filesystems.md). +The device's `.dts` file must provide the storage at that path. For `/data` it needs an `espressif,esp32-flash-fatfs` node with `partition-label = "data"` and `mount-path = "/data"`. Any other path is provided by the driver of the storage, such as an SD card. See [Filesystems](../kernel-concepts/filesystems.md). ## display diff --git a/docs/versions/main/device-development/guide.md b/docs/main/device-development/guide.md similarity index 85% rename from docs/versions/main/device-development/guide.md rename to docs/main/device-development/guide.md index ed76cd4..f903476 100644 --- a/docs/versions/main/device-development/guide.md +++ b/docs/main/device-development/guide.md @@ -33,16 +33,16 @@ The `sdkconfig` is generated from `device.properties` file in `Devices/your-devi Run `python device.py your-device-identifier` to generate the `sdkconfig` file for the device. You can add `--dev` to generate partitions with a `4 MB` footprint for faster flashing. -See the [device.properties documentation](device-development/device-properties.md) for more info. +See the [device.properties documentation](device-properties.md) for more info. ## Implementation Use `Devices/lilygo-tdeck/` as a reference. -You'll also need a `module.yaml` file and a `.dts` file (see [Devicetree](kernel-concepts/devicetree.md)). +You'll also need a `module.yaml` file and a `.dts` file (see [Devicetree](../kernel-concepts/devicetree.md)). Create a `source/module.cpp` or an equivalent `.c` file. -This file must contain a `struct Module your_device_identifier_module = { ... }` (see [Module](kernel-concepts/module.md)) +This file must contain a `struct Module your_device_identifier_module = { ... }` (see [Module](../kernel-concepts/module.md)) If your device doesn't need any module start/stop logic (an "empty" module, with `.start`/`.stop` set to `nullptr`), you can skip the `source/` folder and `CMakeLists.txt` entirely and set @@ -51,7 +51,7 @@ module symbol for this device, and keeps it out of the CMake component tree. Drivers based on the `TactilityKernel` subproject. The drivers are found in `Platforms/` and `Drivers/`. -Explore the [Kernel Concepts](kernel-concepts/driver.md) for how [drivers](kernel-concepts/driver.md), [devices](kernel-concepts/device.md) and [modules](kernel-concepts/module.md) work. +Explore the [Kernel Concepts](../kernel-concepts/driver.md) for how [drivers](../kernel-concepts/driver.md), [devices](../kernel-concepts/device.md) and [modules](../kernel-concepts/module.md) work. ## Overriding system assets diff --git a/docs/versions/main/device-development/hardware-considerations.md b/docs/main/device-development/hardware-considerations.md similarity index 100% rename from docs/versions/main/device-development/hardware-considerations.md rename to docs/main/device-development/hardware-considerations.md diff --git a/docs/versions/main/device-development/wiring.md b/docs/main/device-development/wiring.md similarity index 100% rename from docs/versions/main/device-development/wiring.md rename to docs/main/device-development/wiring.md diff --git a/docs/versions/main/driver-development/display.md b/docs/main/driver-development/display.md similarity index 96% rename from docs/versions/main/driver-development/display.md rename to docs/main/driver-development/display.md index abd27ff..db1706e 100644 --- a/docs/versions/main/driver-development/display.md +++ b/docs/main/driver-development/display.md @@ -2,7 +2,7 @@ A display driver connects a display panel to Tactility. The driver only talks to the panel: LVGL, the window manager and the apps use it through the generic display API, so a new panel works with the whole OS once its driver implements that API. -Read the [Kernel Concepts](kernel-concepts/driver.md) first for how [drivers](kernel-concepts/driver.md), [devices](kernel-concepts/device.md) and [modules](kernel-concepts/module.md) work in general. +Read the [Kernel Concepts](../kernel-concepts/driver.md) first for how [drivers](../kernel-concepts/driver.md), [devices](../kernel-concepts/device.md) and [modules](../kernel-concepts/module.md) work in general. ## Fundamentals @@ -114,7 +114,7 @@ The LVGL bridge in `lvgl-module` (`source/devices/devices.cpp`, `source/devices/ ### Shared SPI buses -A panel on a shared SPI bus (its parent is an `SPI_CONTROLLER_TYPE` device) must lock the bus around each logical operation, e.g. a whole `draw_bitmap()`, with `spi_controller_lock()` and `spi_controller_unlock()`, or `spi_controller_lock_bus_of()` and `spi_controller_unlock_bus_of()` for a direct child of the controller. The generic display API doesn't do it for you, because only the driver knows which calls use the bus. SD cards often share the bus with the display. See [SPI Controller](driver-development/spi-controller.md). +A panel on a shared SPI bus (its parent is an `SPI_CONTROLLER_TYPE` device) must lock the bus around each logical operation, e.g. a whole `draw_bitmap()`, with `spi_controller_lock()` and `spi_controller_unlock()`, or `spi_controller_lock_bus_of()` and `spi_controller_unlock_bus_of()` for a direct child of the controller. The generic display API doesn't do it for you, because only the driver knows which calls use the bus. SD cards often share the bus with the display. See [SPI Controller](spi-controller.md). ## Monochrome (1 bpp) panels @@ -144,7 +144,7 @@ Examples: `Drivers/gdeq031t10-module` (1 bpp, full frame), `Drivers/esp-epaper-m ## Display shape (insets) -Displays with rounded corners or a circular visible area report their shape, so that the statusbar, the toolbar and the apps keep their content inside it (see [Insets](app-development/insets.md) for how apps use it). +Displays with rounded corners or a circular visible area report their shape, so that the statusbar, the toolbar and the apps keep their content inside it (see [Insets](../app-development/insets.md) for how apps use it). To support it in a driver: 1. Include the shared `display.yaml` binding, which adds the optional `shape` and `corner-radius` properties: diff --git a/docs/versions/main/driver-development/io-expander.md b/docs/main/driver-development/io-expander.md similarity index 97% rename from docs/versions/main/driver-development/io-expander.md rename to docs/main/driver-development/io-expander.md index 3498d5a..1d74980 100644 --- a/docs/versions/main/driver-development/io-expander.md +++ b/docs/main/driver-development/io-expander.md @@ -4,7 +4,7 @@ An IO expander is a chip that adds GPIO pins to a device, usually over I2C. Boar In Tactility, an IO expander driver is a **GPIO controller**, just like the ESP32's own GPIO. Other drivers use its pins through the same GPIO API, so they don't need to know whether a pin is on the ESP32 or on an expander. -Read the [Kernel Concepts](kernel-concepts/driver.md) first for how [drivers](kernel-concepts/driver.md), [devices](kernel-concepts/device.md) and [modules](kernel-concepts/module.md) work in general. +Read the [Kernel Concepts](../kernel-concepts/driver.md) first for how [drivers](../kernel-concepts/driver.md), [devices](../kernel-concepts/device.md) and [modules](../kernel-concepts/module.md) work in general. ## Fundamentals diff --git a/docs/versions/main/driver-development/keyboard.md b/docs/main/driver-development/keyboard.md similarity index 94% rename from docs/versions/main/driver-development/keyboard.md rename to docs/main/driver-development/keyboard.md index 5f69f6a..40556fa 100644 --- a/docs/versions/main/driver-development/keyboard.md +++ b/docs/main/driver-development/keyboard.md @@ -2,7 +2,7 @@ A keyboard driver reports key presses: from a full keyboard, a matrix of buttons, an encoder wheel or a few GPIO buttons. Tactility uses the same driver type for all of them. -Read the [Kernel Concepts](kernel-concepts/driver.md) first for how [drivers](kernel-concepts/driver.md), [devices](kernel-concepts/device.md) and [modules](kernel-concepts/module.md) work in general. +Read the [Kernel Concepts](../kernel-concepts/driver.md) first for how [drivers](../kernel-concepts/driver.md), [devices](../kernel-concepts/device.md) and [modules](../kernel-concepts/module.md) work in general. ## The driver diff --git a/docs/versions/main/driver-development/pointer.md b/docs/main/driver-development/pointer.md similarity index 89% rename from docs/versions/main/driver-development/pointer.md rename to docs/main/driver-development/pointer.md index b0cd74f..bd95363 100644 --- a/docs/versions/main/driver-development/pointer.md +++ b/docs/main/driver-development/pointer.md @@ -2,7 +2,7 @@ A pointer driver connects a touch controller (or another pointing device) to Tactility. It reports the coordinates of the touched points, and the LVGL bridge in `lvgl-module` turns them into touch input for the UI. -Read the [Kernel Concepts](kernel-concepts/driver.md) first for how [drivers](kernel-concepts/driver.md), [devices](kernel-concepts/device.md) and [modules](kernel-concepts/module.md) work in general. +Read the [Kernel Concepts](../kernel-concepts/driver.md) first for how [drivers](../kernel-concepts/driver.md), [devices](../kernel-concepts/device.md) and [modules](../kernel-concepts/module.md) work in general. ## The driver @@ -40,7 +40,7 @@ The driver applies `swap-xy`, `mirror-x` and `mirror-y` to the raw coordinates. Resistive touch screens (e.g. the XPT2046 on CYD boards) need a calibration: the raw values don't cover the full range, and differ per device. The LVGL bridge can apply a linear calibration per axis (`lvgl_pointer_set_calibration()`), which the Touch Calibration app measures and stores. -Enable it for a device with these [device properties](device-development/device-properties.md): +Enable it for a device with these [device properties](../device-development/device-properties.md): - `touch.calibrationSupported=true` adds the Touch Calibration app. - `touch.calibrationRequired=true` also runs it during the first setup. @@ -53,4 +53,4 @@ On displays with `DISPLAY_CAPABILITY_SLOW_REFRESH` (e.g. e-paper), the LVGL brid - `device_type` is `&POINTER_TYPE`, and the API's functions match the controller's capabilities (`NULL` for no sleep mode). - `read_data()` is fast, and returns `ERROR_TIMEOUT` or another error when the controller doesn't respond. - Coordinates are in the display's native orientation, after applying the devicetree's `swap-xy`/`mirror-*`. -- A controller on a shared SPI bus locks the bus (see [SPI Controller](driver-development/spi-controller.md)). +- A controller on a shared SPI bus locks the bus (see [SPI Controller](spi-controller.md)). diff --git a/docs/versions/main/driver-development/radio.md b/docs/main/driver-development/radio.md similarity index 95% rename from docs/versions/main/driver-development/radio.md rename to docs/main/driver-development/radio.md index 319de3e..c152c95 100644 --- a/docs/versions/main/driver-development/radio.md +++ b/docs/main/driver-development/radio.md @@ -2,7 +2,7 @@ A radio driver controls a packet radio transceiver, such as a Semtech SX1262 (LoRa, FSK), a TI CC1101 (FSK, OOK) or the ESP-NOW transport of the Wi-Fi radio. Consumers like the mesh service use the same API for every radio: they pick a modulation, set its parameters, turn the radio on, and then send and receive packets. -Read the [Kernel Concepts](kernel-concepts/driver.md) first for how [drivers](kernel-concepts/driver.md), [devices](kernel-concepts/device.md) and [modules](kernel-concepts/module.md) work in general. +Read the [Kernel Concepts](../kernel-concepts/driver.md) first for how [drivers](../kernel-concepts/driver.md), [devices](../kernel-concepts/device.md) and [modules](../kernel-concepts/module.md) work in general. Existing drivers to use as a reference: - `Drivers/sx126x-module`: SX1262 (LoRa, FSK, LR-FHSS transmit only), built on RadioLib @@ -107,7 +107,7 @@ RadioLib and most modem libraries are not thread-safe. Use one radio thread per ## SPI bus -Radios usually share an SPI bus with a display or an SD card. Lock the bus for each logical operation, as described in [SPI Controller](driver-development/spi-controller.md). A modem library that drives the chip-select line itself spreads one command over several transfers, so each command needs the bus lock for its whole duration. +Radios usually share an SPI bus with a display or an SD card. Lock the bus for each logical operation, as described in [SPI Controller](spi-controller.md). A modem library that drives the chip-select line itself spreads one command over several transfers, so each command needs the bus lock for its whole duration. ## What the kernel handles diff --git a/docs/versions/main/driver-development/spi-controller.md b/docs/main/driver-development/spi-controller.md similarity index 80% rename from docs/versions/main/driver-development/spi-controller.md rename to docs/main/driver-development/spi-controller.md index 728865f..779f207 100644 --- a/docs/versions/main/driver-development/spi-controller.md +++ b/docs/main/driver-development/spi-controller.md @@ -2,7 +2,7 @@ An SPI controller (`SPI_CONTROLLER_TYPE`) is often shared by several devices on the same bus, such as a display and an SD card. Their drivers take turns on the bus by locking it. -Read the [Kernel Concepts](kernel-concepts/driver.md) first for how [drivers](kernel-concepts/driver.md), [devices](kernel-concepts/device.md) and [modules](kernel-concepts/module.md) work in general. +Read the [Kernel Concepts](../kernel-concepts/driver.md) first for how [drivers](../kernel-concepts/driver.md), [devices](../kernel-concepts/device.md) and [modules](../kernel-concepts/module.md) work in general. ## SPI bus locking diff --git a/docs/versions/main/driver-development/wifi.md b/docs/main/driver-development/wifi.md similarity index 100% rename from docs/versions/main/driver-development/wifi.md rename to docs/main/driver-development/wifi.md diff --git a/docs/versions/v0.6.0/README.md b/docs/main/index.md similarity index 95% rename from docs/versions/v0.6.0/README.md rename to docs/main/index.md index 2b07e1b..5be2fe7 100644 --- a/docs/versions/v0.6.0/README.md +++ b/docs/main/index.md @@ -1,4 +1,4 @@ -## Overview +# Overview This is the documentation for [Tactility](https://tactilityproject.org) development. diff --git a/docs/versions/main/kernel-concepts/device.md b/docs/main/kernel-concepts/device.md similarity index 93% rename from docs/versions/main/kernel-concepts/device.md rename to docs/main/kernel-concepts/device.md index 15a79f7..89a26bd 100644 --- a/docs/versions/main/kernel-concepts/device.md +++ b/docs/main/kernel-concepts/device.md @@ -1,6 +1,6 @@ # Device -A device generally relates to a specific piece of hardware. A [driver](kernel-concepts/driver.md) is bound to it to make it usable. +A device generally relates to a specific piece of hardware. A [driver](driver.md) is bound to it to make it usable. There can also more abstract instances of devices that help facilitate other devices. ```c @@ -32,7 +32,7 @@ static struct Device i2c_internal = { ``` Devices are part of a devicetree. This tree starts with a root node and all devices are attached to it. -This means that all devices have a parent, except for the root node itself. More on that in [Devicetree](kernel-concepts/devicetree.md). +This means that all devices have a parent, except for the root node itself. More on that in [Devicetree](devicetree.md). So its lifecycle is as follows: @@ -96,4 +96,4 @@ if (device_get(device) == ERROR_NONE) { ## Devicetree -Devices are usually defined in a device's `.dts` file, which the build turns into `struct Device` instances. See [Devicetree](kernel-concepts/devicetree.md). +Devices are usually defined in a device's `.dts` file, which the build turns into `struct Device` instances. See [Devicetree](devicetree.md). diff --git a/docs/versions/main/kernel-concepts/devicetree.md b/docs/main/kernel-concepts/devicetree.md similarity index 95% rename from docs/versions/main/kernel-concepts/devicetree.md rename to docs/main/kernel-concepts/devicetree.md index 172c38f..7ac9334 100644 --- a/docs/versions/main/kernel-concepts/devicetree.md +++ b/docs/main/kernel-concepts/devicetree.md @@ -1,6 +1,6 @@ # Devicetree -A devicetree describes the hardware of a device: which chips it has, how they're connected and how they're configured. Every device in `Devices/` has a `.dts` file. At build time, the devicetree compiler (`Buildscripts/DevicetreeCompiler/`) turns it into C code with a [`struct Device`](kernel-concepts/device.md) for every node. At startup, the kernel adds those devices and binds each one to its [driver](kernel-concepts/driver.md). +A devicetree describes the hardware of a device: which chips it has, how they're connected and how they're configured. Every device in `Devices/` has a `.dts` file. At build time, the devicetree compiler (`Buildscripts/DevicetreeCompiler/`) turns it into C code with a [`struct Device`](device.md) for every node. At startup, the kernel adds those devices and binds each one to its [driver](driver.md). The format is based on the Linux and Zephyr devicetree formats, but it's a simplified subset with its own compiler. @@ -147,6 +147,6 @@ Devices with a driver that can detect the hardware (e.g. a removable SD card) ar ## Where to find more -- [Device](kernel-concepts/device.md) and [Driver](kernel-concepts/driver.md) for the device and driver lifecycle +- [Device](device.md) and [Driver](driver.md) for the device and driver lifecycle - The `.dts` files in `Devices/` for complete examples - The bindings in `Drivers/*/bindings/`, `Platforms/*/bindings/` and `TactilityKernel/bindings/` for the properties of each driver diff --git a/docs/versions/main/kernel-concepts/driver.md b/docs/main/kernel-concepts/driver.md similarity index 86% rename from docs/versions/main/kernel-concepts/driver.md rename to docs/main/kernel-concepts/driver.md index ecd8ef3..9c922ff 100644 --- a/docs/versions/main/kernel-concepts/driver.md +++ b/docs/main/kernel-concepts/driver.md @@ -1,6 +1,6 @@ # Driver -A driver contains the code for a type of hardware, like an I2C controller or a display panel. Drivers bind to [devices](kernel-concepts/device.md), and are registered by [modules](kernel-concepts/module.md). +A driver contains the code for a type of hardware, like an I2C controller or a display panel. Drivers bind to [devices](device.md), and are registered by [modules](module.md). ## Driver definition @@ -48,7 +48,7 @@ Fields that aren't used can be left out of the initializer, so they're `NULL`. A module becomes the owner of the drivers it registers when it starts. `driver_get_owner()` returns that module, or `NULL` for a driver that wasn't registered by a module. -`driver_is_in_use()` tells if a device is bound to the driver. A device counts from the moment `driver_bind()` starts, so a driver is in use while `start_device` runs. A module with a driver in use can't be stopped: `module_stop()` returns `ERROR_RESOURCE_BUSY`. Unbind the devices first (see [Module](kernel-concepts/module.md?id=references)). +`driver_is_in_use()` tells if a device is bound to the driver. A device counts from the moment `driver_bind()` starts, so a driver is in use while `start_device` runs. A module with a driver in use can't be stopped: `module_stop()` returns `ERROR_RESOURCE_BUSY`. Unbind the devices first (see [Module](module.md#references)). ## Starting and stopping devices @@ -64,7 +64,7 @@ When anything fails, it releases what it already acquired, and returns an error. ## Binding to devices -The `compatible` strings connect drivers to devices: a device in the devicetree names its `compatible` driver, and the driver's YAML binding describes the properties that device can set. When a device is added, the kernel finds the driver with a matching `compatible` string (`driver_find_compatible()`) and binds it. See [Devicetree](kernel-concepts/devicetree.md). +The `compatible` strings connect drivers to devices: a device in the devicetree names its `compatible` driver, and the driver's YAML binding describes the properties that device can set. When a device is added, the kernel finds the driver with a matching `compatible` string (`driver_find_compatible()`) and binds it. See [Devicetree](devicetree.md). ## Hotplug @@ -76,7 +76,7 @@ The kernel calls `probe` regularly, and starts or stops the device when its pres ## SPI and other shared buses -A driver whose device is on a bus that other devices share, such as SPI, must lock the bus around its operations. See [SPI Controller](driver-development/spi-controller.md). +A driver whose device is on a bus that other devices share, such as SPI, must lock the bus around its operations. See [SPI Controller](../driver-development/spi-controller.md). ## Lifecycle @@ -92,8 +92,8 @@ skinparam padding 2 @enduml ``` -Modules usually do this for you: list the drivers in the module's `drivers` array, and `module_start()`/`module_stop()` construct, add, remove and destruct them (see [Module](kernel-concepts/module.md)). +Modules usually do this for you: list the drivers in the module's `drivers` array, and `module_start()`/`module_stop()` construct, add, remove and destruct them (see [Module](module.md)). ## Writing drivers -The [Driver Development](driver-development/display.md) section describes specific types of drivers: [Display](driver-development/display.md), [IO Expander](driver-development/io-expander.md), [Keyboard](driver-development/keyboard.md), [Pointer](driver-development/pointer.md) and [SPI Controller](driver-development/spi-controller.md). +The [Driver Development](../driver-development/display.md) section describes specific types of drivers: [Display](../driver-development/display.md), [IO Expander](../driver-development/io-expander.md), [Keyboard](../driver-development/keyboard.md), [Pointer](../driver-development/pointer.md) and [SPI Controller](../driver-development/spi-controller.md). diff --git a/docs/versions/main/kernel-concepts/events.md b/docs/main/kernel-concepts/events.md similarity index 100% rename from docs/versions/main/kernel-concepts/events.md rename to docs/main/kernel-concepts/events.md diff --git a/docs/versions/main/kernel-concepts/filesystems.md b/docs/main/kernel-concepts/filesystems.md similarity index 98% rename from docs/versions/main/kernel-concepts/filesystems.md rename to docs/main/kernel-concepts/filesystems.md index 60e462b..3a2a7c9 100644 --- a/docs/versions/main/kernel-concepts/filesystems.md +++ b/docs/main/kernel-concepts/filesystems.md @@ -29,7 +29,7 @@ The path functions don't check the storage: they return the path even when the S `paths_get_data_root_path()` and `paths_is_data_root_mounted()` are deprecated aliases of the functions above. -Apps use `` instead, which gives each app its own folders (see [Storage and Settings](app-development/storage.md)). +Apps use `` instead, which gives each app its own folders (see [Storage and Settings](../app-development/storage.md)). ## Adding a filesystem diff --git a/docs/versions/main/kernel-concepts/logging-and-errors.md b/docs/main/kernel-concepts/logging-and-errors.md similarity index 100% rename from docs/versions/main/kernel-concepts/logging-and-errors.md rename to docs/main/kernel-concepts/logging-and-errors.md diff --git a/docs/versions/main/kernel-concepts/module.md b/docs/main/kernel-concepts/module.md similarity index 81% rename from docs/versions/main/kernel-concepts/module.md rename to docs/main/kernel-concepts/module.md index d34ce19..9f6d2ae 100644 --- a/docs/versions/main/kernel-concepts/module.md +++ b/docs/main/kernel-concepts/module.md @@ -1,6 +1,6 @@ # Module -A module is a collection of [drivers](kernel-concepts/driver.md) or other functionality that can be loaded and unloaded at runtime. Modules can also export symbols for apps (see [Symbols](app-development/symbols.md)). +A module is a collection of [drivers](driver.md) or other functionality that can be loaded and unloaded at runtime. Modules can also export symbols for apps (see [Symbols](../app-development/symbols.md)). ```c struct Module { @@ -77,13 +77,13 @@ check(module_destruct(&hal_device_module) == ERROR_NONE); ## Resolving symbols -`module_resolve_symbol()` looks up a symbol in the `symbols` table of one module. `module_resolve_symbol_global()` looks in all modules. Both work for a module that was added with `module_add()`, also when it hasn't started yet. This is what lets the modules and apps of an [app package](app-development/modules.md), that load early during boot, find the symbols of firmware modules that are added already but start later. A module that isn't added yet, like `lvgl-module` at that moment, can't be found. +`module_resolve_symbol()` looks up a symbol in the `symbols` table of one module. `module_resolve_symbol_global()` looks in all modules. Both work for a module that was added with `module_add()`, also when it hasn't started yet. This is what lets the modules and apps of an [app package](../app-development/modules.md), that load early during boot, find the symbols of firmware modules that are added already but start later. A module that isn't added yet, like `lvgl-module` at that moment, can't be found. Calling a function of a module that hasn't started is up to the caller: its code is there, but its state isn't set up yet. ## References -A reference keeps a module from being stopped, removed or destructed. It matters for modules that are loaded at runtime, the [modules from an app package](app-development/modules.md): their binary is unloaded when the package is uninstalled, and code that still points into it would crash. Hold a reference for as long as you use the functions or data of such a module. +A reference keeps a module from being stopped, removed or destructed. It matters for modules that are loaded at runtime, the [modules from an app package](../app-development/modules.md): their binary is unloaded when the package is uninstalled, and code that still points into it would crash. Hold a reference for as long as you use the functions or data of such a module. Built-in modules are linked into the firmware, so their code is always in memory. Code that calls their functions directly doesn't need a reference. They can still be stopped, and it is up to the code that stops them to deal with their users (for example, the Terminal app stops LVGL on purpose). @@ -93,7 +93,7 @@ The ELF loader of the ESP32 takes one reference on every module whose symbols an `module_stop()` returns `ERROR_RESOURCE_BUSY` and leaves the module running when: - something holds a reference on it, also when the module hasn't started, or -- one of its [drivers](kernel-concepts/driver.md) is bound to a device. +- one of its [drivers](driver.md) is bound to a device. The check and the refusal of new references or binds are one atomic step. Nothing can use the module in between, and a refused stop doesn't leave a driver removed. @@ -101,4 +101,4 @@ A module that hasn't started, or has stopped, is still added, so its symbols can ## Modules from packages -A [package](app-development/modules.md) can contain modules. They are marked as external, which `module_is_external()` returns. `module_set_external()` sets the flag on a constructed module, until it is destructed. The modules of the firmware are never external. +A [package](../app-development/modules.md) can contain modules. They are marked as external, which `module_is_external()` returns. `module_set_external()` sets the flag on a constructed module, until it is destructed. The modules of the firmware are never external. diff --git a/docs/versions/main/kernel-concepts/services.md b/docs/main/kernel-concepts/services.md similarity index 96% rename from docs/versions/main/kernel-concepts/services.md rename to docs/main/kernel-concepts/services.md index 8817539..9c26ed6 100644 --- a/docs/versions/main/kernel-concepts/services.md +++ b/docs/main/kernel-concepts/services.md @@ -5,7 +5,7 @@ A service is a long-running part of the system that works in the background, ind | Service | Purpose | |---|---| | `tactility.statusbar` | Updates the statusbar icons (Wi-Fi, battery, SD card, etc.) | -| `development-server` | The development server (`development-module`): remote shell, file transfer, and installing and running apps over the network (see [Remote Access](user-guide/remote-access.md)) | +| `development-server` | The development server (`development-module`): remote shell, file transfer, and installing and running apps over the network (see [Remote Access](../user-guide/remote-access.md)) | | `tactility.audio` | Audio input and output, volume and mute | | `tactility.rtctime` | Keeps the system time in sync with a real-time clock | | `tactility.displayidle`, `tactility.keyboardidle` | Turn off the display and keyboard backlight after a period without activity | @@ -59,7 +59,7 @@ The firmware registers its services at startup (`Tactility/Source/InitServices.c ## Doing work in a service As a service has no task of its own, `on_start` must return quickly. For the actual work: -- **Events:** create a task that waits on a `TaskEventGroup` with your subscriptions, as described in [Event Design](kernel-concepts/events.md). +- **Events:** create a task that waits on a `TaskEventGroup` with your subscriptions, as described in [Event Design](events.md). - **Periodic work:** use a timer (``). Keep the timer callback short: it runs on the shared timer task, with a small stack. - **Requests from apps:** offer functions that apps call. They run on the calling app's task, so protect the service's data with a mutex. diff --git a/docs/versions/main/os-development/architecture.md b/docs/main/os-development/architecture.md similarity index 79% rename from docs/versions/main/os-development/architecture.md rename to docs/main/os-development/architecture.md index 6a12f88..8657b5e 100644 --- a/docs/versions/main/os-development/architecture.md +++ b/docs/main/os-development/architecture.md @@ -3,12 +3,12 @@ ## Layer Stack - **Tactility**: The main project which is used to build the firmware and simulator builds. It ties everything together with wrappers and some basic applications. -- **TactilityKernel**: The kernel with C API: device/driver/module lifecycle, concurrency primitives (thread, mutex, timer, dispatcher), filesystem, logging. Header convention: `` (lowercase snake_case). See the Kernel Concepts ([Driver](kernel-concepts/driver.md), [Device](kernel-concepts/device.md), [Module](kernel-concepts/module.md)) for the device/driver/module system. +- **TactilityKernel**: The kernel with C API: device/driver/module lifecycle, concurrency primitives (thread, mutex, timer, dispatcher), filesystem, logging. Header convention: `` (lowercase snake_case). See the Kernel Concepts ([Driver](../kernel-concepts/driver.md), [Device](../kernel-concepts/device.md), [Module](../kernel-concepts/module.md)) for the device/driver/module system. - **TactilityFreeRtos**: Thin C++ wrappers around FreeRTOS primitives. ## App Framework -Apps implement `tt::app::App` (or provide plain callbacks). Each app has an `AppManifest` with `appId`, `appName`, `appCategory` and a factory function `createApp`. Internal apps are registered at startup in `Tactility.cpp`. External apps can be loaded from SD card via `manifest.properties` files, or side-loaded as ELF binaries on ESP32 — see [App Development](app-development/fundamentals.md). +Apps implement `tt::app::App` (or provide plain callbacks). Each app has an `AppManifest` with `appId`, `appName`, `appCategory` and a factory function `createApp`. Internal apps are registered at startup in `Tactility.cpp`. External apps can be loaded from SD card via `manifest.properties` files, or side-loaded as ELF binaries on ESP32 — see [App Development](../app-development/fundamentals.md). ## Service Framework @@ -19,7 +19,7 @@ Services implement `tt::service::Service` with a `ServiceManifest`. Services are The main driver abstractions are in TactilityKernel. Implementations of drivers are found in `Drivers/`. Secondary driver modules are located at `Modules/`. -Drivers are based on the Linux driver subsystem model described in [Driver](kernel-concepts/driver.md): modules register drivers, drivers bind to [devices](kernel-concepts/device.md) via `compatible` strings from the [devicetree](kernel-concepts/devicetree.md). +Drivers are based on the Linux driver subsystem model described in [Driver](../kernel-concepts/driver.md): modules register drivers, drivers bind to [devices](../kernel-concepts/device.md) via `compatible` strings from the [devicetree](../kernel-concepts/devicetree.md). ## Platform Abstraction @@ -30,7 +30,7 @@ Drivers are based on the Linux driver subsystem model described in [Driver](kern The `tactility_add_module()` CMake macro (in `Buildscripts/module.cmake`) wraps ESP-IDF's `idf_component_register` on ESP32 and a standard `add_library`/`add_executable` on POSIX, allowing the same source to build for both targets. -`device.py` reads `Devices//device.properties` and generates the `sdkconfig` file with all necessary ESP-IDF config (target chip, flash size, SPIRAM, LVGL fonts, Bluetooth, USB, etc.) — see [Device Properties](device-development/device-properties.md). +`device.py` reads `Devices//device.properties` and generates the `sdkconfig` file with all necessary ESP-IDF config (target chip, flash size, SPIRAM, LVGL fonts, Bluetooth, USB, etc.) — see [Device Properties](../device-development/device-properties.md). ## LVGL diff --git a/docs/versions/main/os-development/building-tactility.md b/docs/main/os-development/building-tactility.md similarity index 98% rename from docs/versions/main/os-development/building-tactility.md rename to docs/main/os-development/building-tactility.md index d3ddd64..f24363d 100644 --- a/docs/versions/main/os-development/building-tactility.md +++ b/docs/main/os-development/building-tactility.md @@ -1,6 +1,6 @@ # Building Tactility -Make sure you looked at the [requirements](requirements.md) first. +Make sure you looked at the [requirements](../requirements.md) first. ## Cloning from git diff --git a/docs/versions/main/os-development/icons-and-fonts.md b/docs/main/os-development/icons-and-fonts.md similarity index 100% rename from docs/versions/main/os-development/icons-and-fonts.md rename to docs/main/os-development/icons-and-fonts.md diff --git a/docs/versions/main/os-development/mesh-backends.md b/docs/main/os-development/mesh-backends.md similarity index 99% rename from docs/versions/main/os-development/mesh-backends.md rename to docs/main/os-development/mesh-backends.md index c8603bf..c7023a3 100644 --- a/docs/versions/main/os-development/mesh-backends.md +++ b/docs/main/os-development/mesh-backends.md @@ -10,7 +10,7 @@ mesh-module service, store, radio management, settings TactilityKernel radio.h radio drivers (SX1262, CC1101, ESP-NOW, mock) ``` -See [Mesh](app-development/mesh.md) for the app API, and [Radio](driver-development/radio.md) for the driver layer. +See [Mesh](../app-development/mesh.md) for the app API, and [Radio](../driver-development/radio.md) for the driver layer. ## Licensing diff --git a/docs/versions/main/os-development/project-structure.md b/docs/main/os-development/project-structure.md similarity index 92% rename from docs/versions/main/os-development/project-structure.md rename to docs/main/os-development/project-structure.md index 869ba3f..1ee52d4 100644 --- a/docs/versions/main/os-development/project-structure.md +++ b/docs/main/os-development/project-structure.md @@ -13,5 +13,5 @@ - `Tests`: Doctest-based unit tests that run on the simulator (POSIX) target - `Translations`: Localization CSV files, compiled via `generate.py` -See [Architecture](os-development/architecture.md) for how these layers fit together. +See [Architecture](architecture.md) for how these layers fit together. diff --git a/docs/versions/main/os-development/releases.md b/docs/main/os-development/releases.md similarity index 82% rename from docs/versions/main/os-development/releases.md rename to docs/main/os-development/releases.md index 9368452..ea89838 100644 --- a/docs/versions/main/os-development/releases.md +++ b/docs/main/os-development/releases.md @@ -18,11 +18,11 @@ The "Build Firmware" workflow (`.github/workflows/build.yml`) runs for pull requ 4. **Publish:** uploads the firmware and SDKs to the CDN (`https://cdn.tactilityproject.org`): - a push to `main` publishes a **snapshot** firmware, and the SDKs when `version.txt` ends with `-dev` - a tag that starts with `v` (e.g. `v0.8.0`) publishes a **stable** firmware, and the SDKs when `version.txt` doesn't end with `-dev` - - a push to `main` also publishes the [tt tool](app-development/tt.md) (`sdk/tt.zip` and `sdk/tt.json`) + - a push to `main` also publishes the [tt tool](../app-development/tt.md) (`sdk/tt.zip` and `sdk/tt.json`) So a release's SDKs come only from its tag, and later pushes to `main` can't overwrite them. -Every SDK version on the CDN (`sdk//`) has the SDK of each platform, and `tools.zip` (the tools that `tt` runs for that version, with the project templates of `tt app create` in `NewProject/`: a `gui`, a `terminal` and a `module` variant). `index.json` lists them all. The CDN feeds the [Web Installer](user-guide/installing-firmware.md), and `tt` downloads the SDKs and tools from it. +Every SDK version on the CDN (`sdk//`) has the SDK of each platform, and `tools.zip` (the tools that `tt` runs for that version, with the project templates of `tt app create` in `NewProject/`: a `gui`, a `terminal` and a `module` variant). `index.json` lists them all. The CDN feeds the [Web Installer](../user-guide/installing-firmware.md), and `tt` downloads the SDKs and tools from it. "Build Simulator" builds the simulator for Linux and macOS, and "Tests" runs the unit tests. @@ -35,7 +35,7 @@ Every SDK version on the CDN (`sdk//`) has the SDK of each platform, an ## Device metadata -`Buildscripts/CDN/generate-firmware-files.py` creates the CDN's metadata for each device from its [device properties](device-development/device-properties.md): `general.vendor`, `general.name`, `general.incubating`, `cdn.warningMessage` and `cdn.infoMessage`. Incubating devices and messages are shown in the Web Installer. +`Buildscripts/CDN/generate-firmware-files.py` creates the CDN's metadata for each device from its [device properties](../device-development/device-properties.md): `general.vendor`, `general.name`, `general.incubating`, `cdn.warningMessage` and `cdn.infoMessage`. Incubating devices and messages are shown in the Web Installer. ## Local builds @@ -44,12 +44,12 @@ The `Buildscripts/` folder has scripts to package local builds: | Script | Purpose | |---|---| | `release.sh ` | Copies the current firmware build (`build/`) into `release/Tactility-`, with the flash scripts, and the `.elf` files with debug symbols into `release/Tactility--symbols` | -| `release-current.py` | Builds the TactilitySDK for the current build and exports the tools, into `release/sdk//` (see [Building TactilitySDK locally](app-development/symbols.md?id=building-tactilitysdk-locally)) | +| `release-current.py` | Builds the TactilitySDK for the current build and exports the tools, into `release/sdk//` (see [Building TactilitySDK locally](../app-development/symbols.md#building-tactilitysdk-locally)) | | `release-sdk-current.py` | Only builds the TactilitySDK for the current build | | `release-tools-current.py` | Only exports the tools that `tt` runs, with `NewProject/`, into `release/sdk//tools` | | `release-tt.py` | Creates `tt.zip` and `tt.json` for the CDN in `release/tt/` | | `release-simulator.sh ` | Packages a simulator build with its `Data/` folders | -A firmware release contains `flash.sh` and `flash.ps1` to flash it with esptool, and `merge.sh`/`merge.ps1` to merge the binaries into a single image (see [Installing Firmware](user-guide/installing-firmware.md)). +A firmware release contains `flash.sh` and `flash.ps1` to flash it with esptool, and `merge.sh`/`merge.ps1` to merge the binaries into a single image (see [Installing Firmware](../user-guide/installing-firmware.md)). Keep the `-symbols` folder of every release you distribute: it's needed to decode crash backtraces from that firmware. diff --git a/docs/versions/main/os-development/simulator.md b/docs/main/os-development/simulator.md similarity index 100% rename from docs/versions/main/os-development/simulator.md rename to docs/main/os-development/simulator.md diff --git a/docs/versions/main/os-development/testing.md b/docs/main/os-development/testing.md similarity index 95% rename from docs/versions/main/os-development/testing.md rename to docs/main/os-development/testing.md index 8215b24..2dde14e 100644 --- a/docs/versions/main/os-development/testing.md +++ b/docs/main/os-development/testing.md @@ -32,7 +32,7 @@ Each suite is its own executable, registered in `Tests/CMakeLists.txt`: - `TactilityTests` — the `Tactility` layer itself - `CryptModuleTests` — `crypt-module` - `AppModuleTests` — `app-module` -- `AppPosixModuleTests` — `app-posix-module`, including loading [modules from a package](app-development/modules.md) with a fixture module +- `AppPosixModuleTests` — `app-posix-module`, including loading [modules from a package](../app-development/modules.md) with a fixture module - `HttpModuleTests` — `http-module` ## SDK integration test @@ -51,7 +51,7 @@ Run it after any change to `Buildscripts/TactilitySDK/`, `tactility_component_re ## tt tests -The [tt tool](app-development/tt.md) and its update tools have Python tests, which use a local fake CDN: +The [tt tool](../app-development/tt.md) and its update tools have Python tests, which use a local fake CDN: ```bash python3 -m unittest discover -s Tests/Python -p '*_test.py' diff --git a/docs/versions/main/requirements.md b/docs/main/requirements.md similarity index 100% rename from docs/versions/main/requirements.md rename to docs/main/requirements.md diff --git a/docs/versions/main/user-guide/configuration.md b/docs/main/user-guide/configuration.md similarity index 85% rename from docs/versions/main/user-guide/configuration.md rename to docs/main/user-guide/configuration.md index ead413c..ab89003 100644 --- a/docs/versions/main/user-guide/configuration.md +++ b/docs/main/user-guide/configuration.md @@ -6,7 +6,7 @@ The `/data` partition is only used on devices that don't keep their data on an S ## Boot -During the boot process, the system tries to load `/{user_data}/settings/boot.properties`. `{user_data}` resolves to the device's storage path (e.g. the SD card or internal storage), set by its `storage.path` property (see [Device Properties](device-development/device-properties.md)). When the file is missing, or doesn't override a property, the system defaults are used. +During the boot process, the system tries to load `/{user_data}/settings/boot.properties`. `{user_data}` resolves to the device's storage path (e.g. the SD card or internal storage), set by its `storage.path` property (see [Device Properties](../device-development/device-properties.md)). When the file is missing, or doesn't override a property, the system defaults are used. Properties: - `launcherAppId`: The application identifier for the launcher app. This parameter is required. The default is "Launcher". @@ -25,7 +25,7 @@ Properties: ## Kernel -Settings → Kernel lists the kernel modules that came from installed app packages (see [Modules in a Package](app-development/modules.md)), with their status under the name. The button next to a module starts or stops it. When a module can't be started or stopped, the reason shows in the status. A module that is in use by an app, or one that has a driver in use, can't be stopped. +Settings → Kernel lists the kernel modules that came from installed app packages (see [Modules in a Package](../app-development/modules.md)), with their status under the name. The button next to a module starts or stops it. When a module can't be started or stopped, the reason shows in the status. A module that is in use by an app, or one that has a driver in use, can't be stopped. Modules that are part of the firmware aren't listed. diff --git a/docs/versions/main/user-guide/installing-firmware.md b/docs/main/user-guide/installing-firmware.md similarity index 90% rename from docs/versions/main/user-guide/installing-firmware.md rename to docs/main/user-guide/installing-firmware.md index e789afd..c3f9c27 100644 --- a/docs/versions/main/user-guide/installing-firmware.md +++ b/docs/main/user-guide/installing-firmware.md @@ -34,7 +34,7 @@ If you get an error like `function: not found` on Linux, your `sh` is probably a ## Building it yourself -To build and flash Tactility from source, see [Building Tactility](os-development/building-tactility.md): +To build and flash Tactility from source, see [Building Tactility](../os-development/building-tactility.md): ```bash python device.py @@ -43,6 +43,6 @@ idf.py build flash monitor ## After installing -On the first start, Tactility runs its setup. Devices with an SD card store their data on it, so insert one before you start (see the device's `storage.path` in its [device properties](device-development/device-properties.md)). +On the first start, Tactility runs its setup. Devices with an SD card store their data on it, so insert one before you start (see the device's `storage.path` in its [device properties](../device-development/device-properties.md)). -To install apps from your computer, connect the device to Wi-Fi and turn on the development server in Settings → Development (see [Remote Access](user-guide/remote-access.md)). +To install apps from your computer, connect the device to Wi-Fi and turn on the development server in Settings → Development (see [Remote Access](remote-access.md)). diff --git a/docs/versions/main/user-guide/mesh.md b/docs/main/user-guide/mesh.md similarity index 96% rename from docs/versions/main/user-guide/mesh.md rename to docs/main/user-guide/mesh.md index 0034b60..85eda9c 100644 --- a/docs/versions/main/user-guide/mesh.md +++ b/docs/main/user-guide/mesh.md @@ -104,7 +104,7 @@ A QR code opens full screen in the **QR Code** app. Touch the screen, press a ke ## Inspecting traffic -The `mesh inspect` command in the [Terminal](user-guide/shell-and-terminal.md) shows what the mesh network receives and sends, line by line. `mesh inspect 50` prints the latest 50 messages (20 without a number) and ends. `mesh inspect -f` (or `--follow`) prints them and then keeps following new traffic until you press **Ctrl+C** or **q**: +The `mesh inspect` command in the [Terminal](shell-and-terminal.md) shows what the mesh network receives and sends, line by line. `mesh inspect 50` prints the latest 50 messages (20 without a number) and ends. `mesh inspect -f` (or `--follow`) prints them and then keeps following new traffic until you press **Ctrl+C** or **q**: - Messages, with their channel or sender, the signal strength and, for sent messages, the delivery state - Adverts: nodes announcing themselves diff --git a/docs/versions/main/user-guide/navigation.md b/docs/main/user-guide/navigation.md similarity index 99% rename from docs/versions/main/user-guide/navigation.md rename to docs/main/user-guide/navigation.md index d3617e9..ebb32d8 100644 --- a/docs/versions/main/user-guide/navigation.md +++ b/docs/main/user-guide/navigation.md @@ -27,7 +27,7 @@ Favourites are only for apps, not for settings. ## System menu -The system menu has quick toggles for Wi-Fi, GPS, Bluetooth and Mesh, and a power off button. It only shows the ones that your device has (Mesh appears once it's set up, see [Mesh Networking](user-guide/mesh.md)). +The system menu has quick toggles for Wi-Fi, GPS, Bluetooth and Mesh, and a power off button. It only shows the ones that your device has (Mesh appears once it's set up, see [Mesh Networking](mesh.md)). To open it: - **Swipe down** on the statusbar. diff --git a/docs/versions/main/user-guide/remote-access.md b/docs/main/user-guide/remote-access.md similarity index 97% rename from docs/versions/main/user-guide/remote-access.md rename to docs/main/user-guide/remote-access.md index 7e76374..f42bc99 100644 --- a/docs/versions/main/user-guide/remote-access.md +++ b/docs/main/user-guide/remote-access.md @@ -1,6 +1,6 @@ # Remote Access -The development server lets a computer on the same network control a Tactility device: install and run apps, copy files, and open a remote shell. The [tt tool](app-development/tt.md) is its client. +The development server lets a computer on the same network control a Tactility device: install and run apps, copy files, and open a remote shell. The [tt tool](../app-development/tt.md) is its client. ## Turning it on diff --git a/docs/versions/main/user-guide/shell-and-terminal.md b/docs/main/user-guide/shell-and-terminal.md similarity index 93% rename from docs/versions/main/user-guide/shell-and-terminal.md rename to docs/main/user-guide/shell-and-terminal.md index cb9e8ad..315b33d 100644 --- a/docs/versions/main/user-guide/shell-and-terminal.md +++ b/docs/main/user-guide/shell-and-terminal.md @@ -75,7 +75,7 @@ Installed apps that are command-line tools (headless apps) are also available as ### Files and folders -The storage is mounted at `/sdcard` (SD card), `/data` (internal storage) and `/system` (read-only system files). See [Storage and Settings](app-development/storage.md). +The storage is mounted at `/sdcard` (SD card), `/data` (internal storage) and `/system` (read-only system files). See [Storage and Settings](../app-development/storage.md). ### Scripts and programs @@ -85,7 +85,7 @@ The storage is mounted at `/sdcard` (SD card), `/data` (internal storage) and `/ ### Boot scripts -Tactility runs shell scripts at boot. Put them in the `boot` folder of the user data folder: `/sdcard/tactility/boot/` or `/data/tactility/boot/` (see [Configuration](user-guide/configuration.md)). Without that folder, nothing runs. +Tactility runs shell scripts at boot. Put them in the `boot` folder of the user data folder: `/sdcard/tactility/boot/` or `/data/tactility/boot/` (see [Configuration](configuration.md)). Without that folder, nothing runs. - Every file that ends in `.sh` runs, one after the other, in alphabetical order. Prefix the names with numbers to control the order, e.g. `10-wifi.sh` and `20-mesh.sh`. - Each script runs with `sh`, like `sh script.sh`, so it can use the [shell commands](#commands) such as `wifi` and `mesh`. @@ -101,12 +101,12 @@ wifi connect -s MyNetwork -p secret ## Remote shell -You can also use the shell from a computer on the same network, with the [tt tool](app-development/tt.md): +You can also use the shell from a computer on the same network, with the [tt tool](../app-development/tt.md): - `./tt shell` opens an interactive shell. - `./tt shell ` runs one command and returns its exit code, e.g. `./tt shell ls /sdcard`. Quote a command with pipes or `;`, e.g. `./tt shell 'ls | wc'`. -This needs the development server on the device, see [Remote Access](user-guide/remote-access.md). +This needs the development server on the device, see [Remote Access](remote-access.md). ## Writing command-line apps @@ -115,4 +115,4 @@ An app becomes a shell command when its manifest marks it as headless (`app.0.he - Arguments arrive in `main(argc, argv)`, like in a regular C program. - Return `0` from `main()` for success. -See [Fundamentals](app-development/fundamentals.md) for the manifest, and the CoreUtils example in [TactilityApps](https://github.com/TactilityProject/TactilityApps). +See [Fundamentals](../app-development/fundamentals.md) for the manifest, and the CoreUtils example in [TactilityApps](https://github.com/TactilityProject/TactilityApps). diff --git a/docs/sitemap.njk b/docs/sitemap.njk new file mode 100644 index 0000000..8a774b4 --- /dev/null +++ b/docs/sitemap.njk @@ -0,0 +1,11 @@ +--- +permalink: /sitemap.xml +layout: false +eleventyExcludeFromCollections: true +--- + + +{%- for item in collections.all %} + {{ site.url }}{{ item.url }} +{%- endfor %} + diff --git a/docs/versions/support.md b/docs/support.md similarity index 100% rename from docs/versions/support.md rename to docs/support.md diff --git a/docs/theme/tactility.css b/docs/theme/tactility.css deleted file mode 100644 index d8e229d..0000000 --- a/docs/theme/tactility.css +++ /dev/null @@ -1,868 +0,0 @@ -/*! - * Tactility theme, based on Darcula > Darkly > Dark theme for docsify - */ -@import url('https://fonts.googleapis.com/css?family=Open+Sans|Source+Sans+Pro:300,600'); -* { - -webkit-font-smoothing: antialiased; - -webkit-overflow-scrolling: touch; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); - -webkit-text-size-adjust: none; - -webkit-touch-callout: none; - box-sizing: border-box; -} -:root { - --docsifytabs-border-color:#303d4a; - --docsifytabs-border-px:1px; - --docsifytabs-border-radius-px: ; - --docsifytabs-margin:1.5em 0; - --docsifytabs-tab-background:#303d4a; - --docsifytabs-tab-background--active:var(--docsifytabs-content-background); - --docsifytabs-tab-color:#999; - --docsifytabs-tab-color--active:#ee81c3; - --docsifytabs-tab-highlight-px:3px; - --docsifytabs-tab-highlight-color:var(--theme-color,currentColor); - --docsifytabs-tab-padding:0.6em 1em; - --docsifytabs-content-background:inherit; - --docsifytabs-content-padding:1.5rem -} -body:not(.ready) { - overflow: hidden; -} -body:not(.ready) .app-nav, -body:not(.ready) > nav, -body:not(.ready) [data-cloak] { - display: none; -} -div#app { - font-size: 30px; - font-weight: lighter; - margin: 40vh auto; - text-align: center; -} -div#app:empty:before { - content: 'Loading...'; -} -.emoji { - height: 1.2rem; - vertical-align: middle; -} -.progress { - background-color: var(--theme-color, #33bbff); - height: 2px; - left: 0; - position: fixed; - right: 0; - top: 0; - transition: width 0.2s, opacity 0.4s; - width: 0; - z-index: 5; -} -input[type='search'] { - background: #21222a; - border-color: #21222a; - color: rgb(192, 192, 192); -} -.version-selector { - display: grid!important; - align-items: center!important; - justify-content: center!important; - margin: 15px auto 10xp auto!important; -} -.version-selector-label { - margin: 0 0 4px 0; -} -.search { - border-bottom: 1px solid #191919; -} -.search .search-keyword, -.search a:hover { - color: var(--theme-color, #33bbff); -} -.search .search-keyword { - color: var(--theme-color, #33bbff); - font-style: normal; - font-weight: 700; -} -.search .results-panel { - color: rgb(192, 192, 192); -} -.search .matching-post { - border-bottom: 1px solid #444444 !important; -} -body, -html { - height: 100%; -} -body { - -moz-osx-font-smoothing: grayscale; - -webkit-font-smoothing: antialiased; - /* color: #34495e; */ - color: #bbc0c4; - font-family: "Open Sans", "Courier New", monospace; - font-size: 16px; - letter-spacing: 0; - margin: 0; - overflow-x: hidden; - z-index: 10; -} -img { - max-width: 100%; -} -a[disabled] { - cursor: not-allowed; - opacity: 0.6; -} -kbd { - border: 1px solid #a3a3a3; - border-radius: 3px; - display: inline-block; - font-size: 12px !important; - line-height: 12px; - margin-bottom: 3px; - padding: 3px 5px; - vertical-align: middle; -} -.task-list-item { - list-style-type: none; -} -li input[type='checkbox'] { - margin: 0 0.2em 0.25em -1.6em; - vertical-align: middle; -} -.app-nav { - margin: 25px 60px 0 0; - position: absolute; - right: 0; - text-align: right; - z-index: 2; -} -.app-nav.no-badge { - margin-right: 25px; -} -.app-nav p { - margin: 0; -} -.app-nav > a { - margin: 0 1rem; - padding: 5px 0; -} -.app-nav li, -.app-nav ul { - display: inline-block; - list-style: none; - margin: 0; -} -.app-nav a { - color: inherit; - font-size: 16px; - text-decoration: none; - transition: color 0.3s; -} -.app-nav a.active, -.app-nav a:hover { - color: var(--theme-color, #33bbff); -} -.app-nav a.active { - border-bottom: 2px solid var(--theme-color, #33bbff); - color: var(--theme-color, #33bbff); -} -.app-nav li { - display: inline-block; - margin: 0 1rem; - padding: 5px 0; - position: relative; -} -.app-nav li ul { - background-color: #fff; - border: 1px solid #b0b0b0; - border-bottom-color: #a3a3a3; - border-radius: 4px; - box-sizing: border-box; - display: none; - max-height: calc(100vh - 61px); - overflow-y: auto; - padding: 10px 0; - position: absolute; - right: -15px; - text-align: left; - top: 100%; - white-space: nowrap; -} -.app-nav li ul li { - display: block; - font-size: 14px; - line-height: 1rem; - margin: 0; - margin: 8px 14px; - white-space: nowrap; -} -.app-nav li ul a { - display: block; - font-size: inherit; - margin: 0; - padding: 0; -} -.app-nav li ul a.active { - border-bottom: 0; -} -.app-nav li:hover ul { - display: block; -} -.github-corner { - border-bottom: 0; - position: fixed; - right: 0; - text-decoration: none; - top: 0; - z-index: 1; -} -.github-corner:hover .octo-arm { - animation: a 0.56s ease-in-out; -} -.github-corner svg { - color: #21222b; - fill: var(--theme-color, #33bbff); - height: 80px; - width: 80px; -} -main { - display: block; - position: relative; - width: 100vw; - height: 100%; - z-index: 0; -} -main.hidden { - display: none; -} -.anchor { - display: inline-block; - text-decoration: none; - transition: all 0.3s; -} -.anchor span { - color: #33bbff; -} -.anchor:hover { - text-decoration: underline; -} -.sidebar { - border-right: 1px solid rgba(0, 0, 0, 0.4); - overflow-y: auto; - padding: 40px 0 0; - position: absolute; - top: 0; - bottom: 0; - left: 0; - transition: transform 0.25s ease-out; - width: 300px; - z-index: 3; -} -.sidebar > h1 { - margin: 0 auto 1rem; - font-size: 1.5rem; - font-weight: 300; - text-align: center; -} -.sidebar > h1 a { - color: inherit; - text-decoration: none; - font-weight: 600; - color: #ee81c3; -} -.sidebar > h1 .app-nav { - display: block; - position: static; -} -.sidebar .sidebar-nav { - line-height: 2em; - padding-bottom: 40px; -} -.sidebar li.collapse .app-sub-sidebar { - display: none; -} -.sidebar ul { - margin: 0 0 0 15px; - padding: 0; -} -.sidebar li > p { - font-weight: 700; - margin: 0; - color: #ee81c3; -} -.sidebar ul, -.sidebar ul li { - list-style: none; -} -.sidebar ul li a { - border-bottom: none; - display: block; -} -.sidebar ul li ul { - padding-left: 20px; -} -.sidebar::-webkit-scrollbar { - width: 4px; -} -.sidebar::-webkit-scrollbar-thumb { - background: transparent; - border-radius: 4px; -} -.sidebar:hover::-webkit-scrollbar-thumb { - background: hsla(0, 0%, 53%, 0.4); -} -.sidebar:hover::-webkit-scrollbar-track { - background: hsla(0, 0%, 53%, 0.1); -} -.sidebar-toggle { - background-color: transparent; - background-color: #21222b; - border: 0; - outline: none; - padding: 10px; - position: absolute; - bottom: 0; - left: 0; - text-align: center; - transition: opacity 0.3s; - width: 284px; - z-index: 4; -} -.sidebar-toggle .sidebar-toggle-button:hover { - opacity: 0.8; - -} -.sidebar-toggle span { - background-color: var(--theme-color, #33bbff); - display: block; - margin-bottom: 4px; - width: 24px; - height: 4px; - text-shadow: 2px 2px; -} -body.sticky .sidebar, -body.sticky .sidebar-toggle { - position: fixed; -} -.content { - padding-top: 60px; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 300px; - transition: left 0.25s ease; -} -.markdown-section { - margin: 0 auto; - max-width: 800px; - padding: 30px 15px 40px; - position: relative; -} -.markdown-section > * { - box-sizing: border-box; - font-size: inherit; -} -.markdown-section > :first-child { - margin-top: 0 !important; -} -.markdown-section hr { - border: none; - border-bottom: 1px solid #444444; - margin: 2em 0; -} -.markdown-section iframe { - border: 1px solid #444444; -} -.markdown-section table { - border-collapse: collapse; - border-spacing: 0; - display: block; - margin-bottom: 1rem; - overflow: auto; - width: 100%; -} -.markdown-section th { - font-weight: 700; -} -.markdown-section td, -.markdown-section th { - border: 1px solid #b0b0b0; - padding: 6px 13px; -} -.markdown-section tr { - border-top: 1px solid #a3a3a3; -} -.markdown-section p.tip, -.markdown-section tr:nth-child(2n) { - background-color: #1e1e25; -} -.markdown-section p.tip { - border-bottom-right-radius: 2px; - border-left: 4px solid #33bbff; - border-top-right-radius: 2px; - margin: 2em 0; - padding: 12px 24px 12px 30px; - position: relative; -} -.markdown-section p.tip:before { - background-color: #33bbff; - border-radius: 100%; - color: #fff; - content: '!'; - font-family: Dosis, Source Sans Pro, Helvetica Neue, Arial, sans-serif; - font-size: 14px; - font-weight: 700; - left: -12px; - line-height: 20px; - position: absolute; - height: 20px; - width: 20px; - text-align: center; - top: 14px; -} -.markdown-section p.tip code { - background-color: #191919; -} -.markdown-section p.tip em { - color: #34495e; -} -.markdown-section p.warn { - background: rgba(66, 185, 131, 0.2); - border-radius: 2px; - padding: 1rem; -} -body.close .sidebar { - transform: translateX(-300px); -} -body.close .sidebar-toggle { - width: auto; -} -body.close .content { - left: 0; -} -@media print { - .app-nav, - .github-corner, - .sidebar, - .sidebar-toggle { - display: none; - } -} -@media screen and (max-width: 768px) { - .github-corner, - .sidebar, - .sidebar-toggle { - position: fixed; - } - .app-nav { - margin-top: 16px; - } - .app-nav li ul { - top: 30px; - } - main { - height: auto; - overflow-x: hidden; - } - .sidebar { - left: -300px; - transition: transform 0.25s ease-out; - } - .content { - left: 0; - max-width: 100vw; - position: static; - padding-top: 20px; - transition: transform 0.25s ease; - } - .app-nav, - .github-corner { - transition: transform 0.25s ease-out; - } - .sidebar-toggle { - background-color: transparent; - width: auto; - padding: 30px 30px 10px 10px; - } - body.close .sidebar { - transform: translateX(300px); - } - body.close .sidebar-toggle { - background-color: #282a35; - transition: background-color 1s; - width: 284px; - padding: 10px; - } - body.close .content { - transform: translateX(300px); - } - body.close .app-nav, - body.close .github-corner { - display: none; - } - .github-corner:hover .octo-arm { - animation: none; - } - .github-corner .octo-arm { - animation: a 0.56s ease-in-out; - } -} -@keyframes a { - 0%, - to { - transform: rotate(0); - } - 20%, - 60% { - transform: rotate(-25deg); - } - 40%, - 80% { - transform: rotate(10deg); - } -} -section.cover { - -ms-flex-align: center; - align-items: center; - background-position: 50%; - background-repeat: no-repeat; - background-size: cover; - height: 100vh; - display: none; - background: - linear-gradient(180deg, #341D65 0%, rgba(52, 29, 101, 0) 100%), - radial-gradient(94.51% 124.88% at 94.32% 94.43%, rgba(65, 244, 255, 0.78) 0%, rgba(128, 217, 255, 0.78) 46.52%, rgba(65, 198, 255, 0) 100%), - linear-gradient(41deg, #fffce673 4.33%, rgba(255, 252, 230, 0) 73.7%); - background-blend-mode: normal, normal, normal; - /* filter: blur(0.5px); */ - z-index: 0; /* 设置较小的 z-index */ -} - - -section.cover.show { - display: -ms-flexbox; - display: flex; -} -section.cover.has-mask .mask { - background-color: #fff; - opacity: 0.8; - position: absolute; - top: 0; - height: 100%; - width: 100%; -} -section.cover .cover-main { - -ms-flex: 1; - flex: 1; - margin: -20px 16px 0; - text-align: center; - z-index: 10; -} -section.cover a { - color: inherit; -} -section.cover a, -section.cover a:hover { - text-decoration: none; -} -section.cover p { - line-height: 1.5rem; - margin: 1em 0; -} -section.cover h1 { - color: inherit; - font-size: 2.5rem; - font-weight: 300; - margin: 0.625rem 0 2.5rem; - position: relative; - text-align: center; -} -section.cover h1 a { - display: block; -} -section.cover h1 small { - bottom: -0.4375rem; - font-size: 1rem; - position: absolute; -} -section.cover blockquote { - font-size: 1.5rem; - text-align: center; -} -section.cover ul { - line-height: 1.8; - list-style-type: none; - margin: 1em auto; - max-width: 500px; - padding: 0; -} -section.cover .cover-main > p:last-child a { - border: 1px solid var(--theme-color, #33bbff); - border-radius: 2rem; - box-sizing: border-box; - color: var(--theme-color, #33bbff); - display: inline-block; - font-size: 1.05rem; - letter-spacing: 0.1rem; - margin: 0.5rem 1rem; - padding: 0.75em 2rem; - text-decoration: none; - transition: all 0.15s ease; -} -section.cover .cover-main > p:last-child a:last-child { - background-color: var(--theme-color, #33bbff); - color: #e5e5e5; -} -section.cover .cover-main > p:last-child a:last-child:hover { - color: inherit; - opacity: 0.8; - color: #e5e5e5; -} -section.cover .cover-main > p:last-child a:hover { - color: inherit; -} -section.cover blockquote > p > a { - border-bottom: 2px solid var(--theme-color, #33bbff); - transition: color 0.3s; -} -section.cover blockquote > p > a:hover { - color: var(--theme-color, #33bbff); -} -.sidebar, -body { - background-color: #21222b; -} -.sidebar { - color: #bbc0c4; -} -.sidebar li { - margin: 6px 0; -} -.sidebar ul li a { - color: #33bbff; - font-size: 14px; - font-weight: 600; - overflow: hidden; - text-decoration: none; - text-overflow: ellipsis; - white-space: nowrap; -} -.sidebar ul li a:hover { - text-decoration: underline; -} -.sidebar ul li ul { - padding: 0; -} -.sidebar ul li.active > a { - border-right: 2px solid; - color: var(--theme-color, #33bbff); - font-size: 16px; - font-weight: 600; -} -.app-sub-sidebar li:before { - content: '-'; - padding-right: 4px; - float: left; -} -.markdown-section h1, -.markdown-section h2, -.markdown-section h3, -.markdown-section h4, -.markdown-section strong { - color: #fff; - font-weight: 600; -} -.markdown-section a { - color: #ee81c3; - font-weight: 600; -} -.markdown-section h1 { - font-size: 2rem; - margin: 0 0 1rem; -} -.markdown-section h2 { - font-size: 1.75rem; - margin: 45px 0 0.8rem; -} -.markdown-section h3 { - font-size: 1.5rem; - margin: 40px 0 0.6rem; -} -.markdown-section h4 { - font-size: 1.25rem; -} -.markdown-section h5 { - font-size: 1rem; -} -.markdown-section h6 { - color: #777; - font-size: 1rem; -} -.markdown-section figure, -.markdown-section p { - margin: 1.2em 0; -} -.markdown-section ol, -.markdown-section p, -.markdown-section ul { - line-height: 1.6rem; - word-spacing: 0.05rem; -} -.markdown-section ol, -.markdown-section ul { - padding-left: 1.5rem; -} -.markdown-section blockquote { - border-left: 4px solid var(--theme-color, #33bbff); - color: #858585; - margin: 2em 0; - padding-left: 20px; -} -.markdown-section blockquote p { - font-weight: 600; - margin-left: 0; -} -.markdown-section iframe { - margin: 1em 0; -} -.markdown-section em { - color: #7f8c8d; -} -.markdown-section code { - border-radius: 2px; - color: #eee; - font-size: 0.9rem; - margin: 0 2px; - padding: 3px 5px; - white-space: pre-wrap; -} -.markdown-section code, -.markdown-section pre { - background-color: #1e1e25; - font-family: Source Sans Pro, courier, monospace; - font-size: 16px; -} -.markdown-section pre { - -moz-osx-font-smoothing: initial; - -webkit-font-smoothing: initial; - line-height: 1.5rem; - margin: 1.2em 0; - overflow: auto; - padding: 0 1.4rem; - position: relative; - word-wrap: normal; -} -.token.cdata, -.token.comment, -.token.doctype, -.token.prolog { - color: #8e908c; -} -.token.namespace { - opacity: 0.7; -} -.token.boolean, -.token.number { - color: #f3fa9a; -} -.token.punctuation { - color: #0078c8; -} -.token.property { - color: #f3fa9a; -} -.token.tag { - color: #ee81c3; -} -.token.string { - color: var(--theme-color, #33bbff); -} -.token.selector { - color: #6679cc; -} -.token.attr-name { - color: #ee81c3; -} -.language-css .token.string, -.style .token.string, -.token.entity, -.token.url { - color: #22a2c9; -} -.token.attr-value, -.token.control, -.token.directive, -.token.unit { - color: var(--theme-color, #33bbff); -} -.token.keyword { - color: #f3fa9a; -} -.token.atrule, -.token.regex, -.token.statement { - color: #22a2c9; -} -.token.placeholder, -.token.variable { - color: #3d8fd1; -} -.token.deleted { - text-decoration: line-through; -} -.token.inserted { - border-bottom: 1px dotted #202746; - text-decoration: none; -} -.token.italic { - font-style: italic; -} -.token.bold, -.token.important { - font-weight: 700; -} -.token.important { - color: #f3fa9a; -} -.token.entity { - cursor: help; -} -.markdown-section pre > code { - -moz-osx-font-smoothing: initial; - -webkit-font-smoothing: initial; - background-color: #1e1e25; - border-radius: 2px; - color: #c0c0c0; - display: block; - font-family: Roboto Mono, Monaco, courier, monospace; - font-size: 0.9rem; - line-height: inherit; - margin: 0 2px; - max-width: inherit; - overflow: inherit; - padding: 2.2em 5px; - white-space: inherit; -} -.markdown-section code:after, -.markdown-section code:before { - letter-spacing: 0.05rem; -} -code .token { - -moz-osx-font-smoothing: initial; - -webkit-font-smoothing: initial; - min-height: 1.5rem; -} -pre:after { - color: #a3a3a3; - content: attr(data-lang); - font-size: 0.6rem; - font-weight: 600; - height: 15px; - line-height: 15px; - padding: 5px 10px 0; - position: absolute; - right: 0; - text-align: right; - top: 0; -} diff --git a/docs/versions/v0.1.0/_sidebar.md b/docs/v0.1.0/_sidebar.md similarity index 100% rename from docs/versions/v0.1.0/_sidebar.md rename to docs/v0.1.0/_sidebar.md diff --git a/docs/versions/v0.1.0/application-lifecycle.md b/docs/v0.1.0/application-lifecycle.md similarity index 100% rename from docs/versions/v0.1.0/application-lifecycle.md rename to docs/v0.1.0/application-lifecycle.md diff --git a/docs/versions/v0.1.0/build-scripts.md b/docs/v0.1.0/build-scripts.md similarity index 100% rename from docs/versions/v0.1.0/build-scripts.md rename to docs/v0.1.0/build-scripts.md diff --git a/docs/versions/v0.1.0/building-external-apps.md b/docs/v0.1.0/building-external-apps.md similarity index 100% rename from docs/versions/v0.1.0/building-external-apps.md rename to docs/v0.1.0/building-external-apps.md diff --git a/docs/versions/v0.1.0/building-internal-apps.md b/docs/v0.1.0/building-internal-apps.md similarity index 100% rename from docs/versions/v0.1.0/building-internal-apps.md rename to docs/v0.1.0/building-internal-apps.md diff --git a/docs/versions/v0.1.0/building-tactility.md b/docs/v0.1.0/building-tactility.md similarity index 100% rename from docs/versions/v0.1.0/building-tactility.md rename to docs/v0.1.0/building-tactility.md diff --git a/docs/versions/v0.1.0/README.md b/docs/v0.1.0/index.md similarity index 96% rename from docs/versions/v0.1.0/README.md rename to docs/v0.1.0/index.md index e01a949..19e282d 100644 --- a/docs/versions/v0.1.0/README.md +++ b/docs/v0.1.0/index.md @@ -1,4 +1,4 @@ -## Overview +# Overview This is the documentation for [Tactility](https://tactilityproject.org) development. diff --git a/docs/versions/v0.1.0/project-structure.md b/docs/v0.1.0/project-structure.md similarity index 100% rename from docs/versions/v0.1.0/project-structure.md rename to docs/v0.1.0/project-structure.md diff --git a/docs/versions/v0.1.0/simulator.md b/docs/v0.1.0/simulator.md similarity index 100% rename from docs/versions/v0.1.0/simulator.md rename to docs/v0.1.0/simulator.md diff --git a/docs/versions/v0.2.0/_sidebar.md b/docs/v0.2.0/_sidebar.md similarity index 100% rename from docs/versions/v0.2.0/_sidebar.md rename to docs/v0.2.0/_sidebar.md diff --git a/docs/versions/v0.2.0/app-concepts.md b/docs/v0.2.0/app-concepts.md similarity index 93% rename from docs/versions/v0.2.0/app-concepts.md rename to docs/v0.2.0/app-concepts.md index 96a0f1b..c735dd4 100644 --- a/docs/versions/v0.2.0/app-concepts.md +++ b/docs/v0.2.0/app-concepts.md @@ -52,15 +52,15 @@ Apps must declare a manifest. The manifest specifies the app identifier, the hum where the class or functions of the app are and more. The app manifest is then registered, so the apps can be launched (e.g. from the Launcher app or other apps) - - -#### **Tactility** +:::: tabs +::: tab Tactility Internal apps are specified in the `tt::Configuration` such as [here](https://github.com/ByteWelder/Tactility/blob/{{versionLabel}}/App/Source/Main.cpp). They are automatically added to the internal app registry. -#### **TactilitySDK** +::: +::: tab TactilitySDK External apps must manually register their manifest: @@ -76,7 +76,8 @@ int main(int argc, char* argv[]) { } ``` - +::: +:::: ## Showing a user interface @@ -87,9 +88,8 @@ When the application's `onStart()` function is called, you get access to `lv_obj This parent is the root widget that is used to attach other widgets too. It's the root of the application's window. - - -#### **Tactility** +:::: tabs +::: tab Tactility ```cpp void onShow(AppContext& context, lv_obj_t* parent) override { @@ -102,7 +102,8 @@ void onShow(AppContext& context, lv_obj_t* parent) override { } ``` -#### **TactilitySDK** +::: +::: tab TactilitySDK ```c static void onShow(AppHandle app, void* data, lv_obj_t* parent) { @@ -115,13 +116,13 @@ static void onShow(AppHandle app, void* data, lv_obj_t* parent) { } ``` - +::: +:::: ## Starting app with parameters - - -#### **Tactility** +:::: tabs +::: tab Tactility There are generally 2 ways to start an app with parameters: - By calling `void tt::app::start(string appId, Bundle parameters)` @@ -130,13 +131,15 @@ There are generally 2 ways to start an app with parameters: When apps require parameters, it's desirable to have an app-specific function that takes care of calling `tt::app::start()`. This way, the caller doesn't have to know about the internal formatting of the `Bundle`. -#### **TactilitySDK** +::: +::: tab TactilitySDK TactilitySDK has the following functions via `tt_app.h`: - `void tt_app_start(const char* appId)` - `void tt_app_start_with_bundle(string id, BundleHandle parameters)` - +::: +:::: ## App results @@ -151,23 +154,23 @@ it can report back which item was selected by setting the item index in the resu The application that launched the result-setting application is the one that receives the result. - - -#### **Tactility** +:::: tabs +::: tab Tactility The launching application will have `App::onResult()` called right after `App::onShow()` when it resumes operation. -#### **TactilitySDK** +::: +::: tab TactilitySDK The launching application will have `ExternalAppManifest::onResult` called right after `ExternalAppManifest::onShow` when it resumes operation. - +::: +:::: ## Return a result to an app - - -#### **Tactility** +:::: tabs +::: tab Tactility The `App` class has these methods: - `void setResult(Result, std::unique_ptr)` @@ -175,7 +178,8 @@ The `App` class has these methods: They can be called at any time, but after `App::onStop()` is called, any calls to `setResult()` won't have an effect. -#### **TactilitySDK** +::: +::: tab TactilitySDK TactilityC has these functions related to `AppHandle`: - `void tt_app_set_result(AppHandle, Result, BundleHandle)` @@ -183,5 +187,6 @@ TactilityC has these functions related to `AppHandle`: They can be called at any time, but after `ExternalManifest::onStop` is called, any calls to `tt_app_context_set_result()` won't have an effect. - +::: +:::: diff --git a/docs/versions/v0.2.0/board-development.md b/docs/v0.2.0/board-development.md similarity index 100% rename from docs/versions/v0.2.0/board-development.md rename to docs/v0.2.0/board-development.md diff --git a/docs/versions/v0.2.0/build-scripts.md b/docs/v0.2.0/build-scripts.md similarity index 100% rename from docs/versions/v0.2.0/build-scripts.md rename to docs/v0.2.0/build-scripts.md diff --git a/docs/versions/v0.2.0/building-tactility.md b/docs/v0.2.0/building-tactility.md similarity index 100% rename from docs/versions/v0.2.0/building-tactility.md rename to docs/v0.2.0/building-tactility.md diff --git a/docs/versions/v0.2.0/README.md b/docs/v0.2.0/index.md similarity index 96% rename from docs/versions/v0.2.0/README.md rename to docs/v0.2.0/index.md index e01a949..19e282d 100644 --- a/docs/versions/v0.2.0/README.md +++ b/docs/v0.2.0/index.md @@ -1,4 +1,4 @@ -## Overview +# Overview This is the documentation for [Tactility](https://tactilityproject.org) development. diff --git a/docs/versions/v0.2.0/project-structure.md b/docs/v0.2.0/project-structure.md similarity index 100% rename from docs/versions/v0.2.0/project-structure.md rename to docs/v0.2.0/project-structure.md diff --git a/docs/versions/v0.2.0/simulator.md b/docs/v0.2.0/simulator.md similarity index 100% rename from docs/versions/v0.2.0/simulator.md rename to docs/v0.2.0/simulator.md diff --git a/docs/versions/v0.3.0/_sidebar.md b/docs/v0.3.0/_sidebar.md similarity index 100% rename from docs/versions/v0.3.0/_sidebar.md rename to docs/v0.3.0/_sidebar.md diff --git a/docs/versions/v0.4.0/app-fundamentals.md b/docs/v0.3.0/app-fundamentals.md similarity index 93% rename from docs/versions/v0.4.0/app-fundamentals.md rename to docs/v0.3.0/app-fundamentals.md index b7e3ed5..611c754 100644 --- a/docs/versions/v0.4.0/app-fundamentals.md +++ b/docs/v0.3.0/app-fundamentals.md @@ -53,15 +53,15 @@ Apps must declare a manifest. The manifest specifies the app identifier, the hum where the class or functions of the app are and more. The app manifest is then registered, so the apps can be launched (e.g. from the Launcher app or other apps) - - -#### **Tactility** +:::: tabs +::: tab Tactility Internal apps are specified in the `tt::Configuration` such as [here](https://github.com/ByteWelder/Tactility/blob/{{versionLabel}}/App/Source/Main.cpp). They are automatically added to the internal app registry. -#### **TactilitySDK** +::: +::: tab TactilitySDK External apps must manually register their manifest: @@ -77,7 +77,8 @@ int main(int argc, char* argv[]) { } ``` - +::: +:::: ## Showing a user interface @@ -88,9 +89,8 @@ When the application's `onCreate()` function is called, you get access to `lv_ob This parent is the root widget that is used to attach other widgets too. It's the root of the application's window. - - -#### **Tactility** +:::: tabs +::: tab Tactility ```cpp void onShow(AppContext& context, lv_obj_t* parent) override { @@ -103,7 +103,8 @@ void onShow(AppContext& context, lv_obj_t* parent) override { } ``` -#### **TactilitySDK** +::: +::: tab TactilitySDK ```c static void onShow(AppHandle app, void* data, lv_obj_t* parent) { @@ -116,13 +117,13 @@ static void onShow(AppHandle app, void* data, lv_obj_t* parent) { } ``` - +::: +:::: ## Starting app with parameters - - -#### **Tactility** +:::: tabs +::: tab Tactility There are generally 2 ways to start an app with parameters: - By calling `void tt::app::start(string appId, Bundle parameters)` @@ -131,13 +132,15 @@ There are generally 2 ways to start an app with parameters: When apps require parameters, it's desirable to have an app-specific function that takes care of calling `tt::app::start()`. This way, the caller doesn't have to know about the internal formatting of the `Bundle`. -#### **TactilitySDK** +::: +::: tab TactilitySDK TactilitySDK has the following functions via `tt_app.h`: - `void tt_app_start(const char* appId)` - `void tt_app_start_with_bundle(string id, BundleHandle parameters)` - +::: +:::: ## App results @@ -152,23 +155,23 @@ it can report back which item was selected by setting the item index in the resu The application that launched the result-setting application is the one that receives the result. - - -#### **Tactility** +:::: tabs +::: tab Tactility The launching application will have `App::onResult()` called right after `App::onShow()` when it resumes operation. -#### **TactilitySDK** +::: +::: tab TactilitySDK The launching application will have `ExternalAppManifest::onResult` called right after `ExternalAppManifest::onShow` when it resumes operation. - +::: +:::: ## Return a result to an app - - -#### **Tactility** +:::: tabs +::: tab Tactility The `App` class has these methods: - `void setResult(Result, std::unique_ptr)` @@ -176,7 +179,8 @@ The `App` class has these methods: They can be called at any time, but after `App::onDestroy()` is called, any calls to `setResult()` won't have an effect. -#### **TactilitySDK** +::: +::: tab TactilitySDK TactilityC has these functions related to `AppHandle`: - `void tt_app_set_result(AppHandle, Result, BundleHandle)` @@ -184,5 +188,6 @@ TactilityC has these functions related to `AppHandle`: They can be called at any time, but after `ExternalManifest::onDestroy` is called, any calls to `tt_app_context_set_result()` won't have an effect. - +::: +:::: diff --git a/docs/versions/v0.3.0/board-development.md b/docs/v0.3.0/board-development.md similarity index 100% rename from docs/versions/v0.3.0/board-development.md rename to docs/v0.3.0/board-development.md diff --git a/docs/versions/v0.3.0/build-scripts.md b/docs/v0.3.0/build-scripts.md similarity index 100% rename from docs/versions/v0.3.0/build-scripts.md rename to docs/v0.3.0/build-scripts.md diff --git a/docs/versions/v0.3.0/building-tactility.md b/docs/v0.3.0/building-tactility.md similarity index 100% rename from docs/versions/v0.3.0/building-tactility.md rename to docs/v0.3.0/building-tactility.md diff --git a/docs/versions/v0.3.0/create-external-app.md b/docs/v0.3.0/create-external-app.md similarity index 100% rename from docs/versions/v0.3.0/create-external-app.md rename to docs/v0.3.0/create-external-app.md diff --git a/docs/versions/v0.3.0/create-internal-app.md b/docs/v0.3.0/create-internal-app.md similarity index 100% rename from docs/versions/v0.3.0/create-internal-app.md rename to docs/v0.3.0/create-internal-app.md diff --git a/docs/versions/v0.3.0/hal-fundamentals.md b/docs/v0.3.0/hal-fundamentals.md similarity index 100% rename from docs/versions/v0.3.0/hal-fundamentals.md rename to docs/v0.3.0/hal-fundamentals.md diff --git a/docs/versions/v0.3.0/README.md b/docs/v0.3.0/index.md similarity index 96% rename from docs/versions/v0.3.0/README.md rename to docs/v0.3.0/index.md index e01a949..19e282d 100644 --- a/docs/versions/v0.3.0/README.md +++ b/docs/v0.3.0/index.md @@ -1,4 +1,4 @@ -## Overview +# Overview This is the documentation for [Tactility](https://tactilityproject.org) development. diff --git a/docs/versions/v0.3.0/project-structure.md b/docs/v0.3.0/project-structure.md similarity index 100% rename from docs/versions/v0.3.0/project-structure.md rename to docs/v0.3.0/project-structure.md diff --git a/docs/versions/v0.3.0/simulator.md b/docs/v0.3.0/simulator.md similarity index 100% rename from docs/versions/v0.3.0/simulator.md rename to docs/v0.3.0/simulator.md diff --git a/docs/versions/v0.4.0/_sidebar.md b/docs/v0.4.0/_sidebar.md similarity index 100% rename from docs/versions/v0.4.0/_sidebar.md rename to docs/v0.4.0/_sidebar.md diff --git a/docs/versions/v0.5.0/app-fundamentals.md b/docs/v0.4.0/app-fundamentals.md similarity index 93% rename from docs/versions/v0.5.0/app-fundamentals.md rename to docs/v0.4.0/app-fundamentals.md index b7e3ed5..611c754 100644 --- a/docs/versions/v0.5.0/app-fundamentals.md +++ b/docs/v0.4.0/app-fundamentals.md @@ -53,15 +53,15 @@ Apps must declare a manifest. The manifest specifies the app identifier, the hum where the class or functions of the app are and more. The app manifest is then registered, so the apps can be launched (e.g. from the Launcher app or other apps) - - -#### **Tactility** +:::: tabs +::: tab Tactility Internal apps are specified in the `tt::Configuration` such as [here](https://github.com/ByteWelder/Tactility/blob/{{versionLabel}}/App/Source/Main.cpp). They are automatically added to the internal app registry. -#### **TactilitySDK** +::: +::: tab TactilitySDK External apps must manually register their manifest: @@ -77,7 +77,8 @@ int main(int argc, char* argv[]) { } ``` - +::: +:::: ## Showing a user interface @@ -88,9 +89,8 @@ When the application's `onCreate()` function is called, you get access to `lv_ob This parent is the root widget that is used to attach other widgets too. It's the root of the application's window. - - -#### **Tactility** +:::: tabs +::: tab Tactility ```cpp void onShow(AppContext& context, lv_obj_t* parent) override { @@ -103,7 +103,8 @@ void onShow(AppContext& context, lv_obj_t* parent) override { } ``` -#### **TactilitySDK** +::: +::: tab TactilitySDK ```c static void onShow(AppHandle app, void* data, lv_obj_t* parent) { @@ -116,13 +117,13 @@ static void onShow(AppHandle app, void* data, lv_obj_t* parent) { } ``` - +::: +:::: ## Starting app with parameters - - -#### **Tactility** +:::: tabs +::: tab Tactility There are generally 2 ways to start an app with parameters: - By calling `void tt::app::start(string appId, Bundle parameters)` @@ -131,13 +132,15 @@ There are generally 2 ways to start an app with parameters: When apps require parameters, it's desirable to have an app-specific function that takes care of calling `tt::app::start()`. This way, the caller doesn't have to know about the internal formatting of the `Bundle`. -#### **TactilitySDK** +::: +::: tab TactilitySDK TactilitySDK has the following functions via `tt_app.h`: - `void tt_app_start(const char* appId)` - `void tt_app_start_with_bundle(string id, BundleHandle parameters)` - +::: +:::: ## App results @@ -152,23 +155,23 @@ it can report back which item was selected by setting the item index in the resu The application that launched the result-setting application is the one that receives the result. - - -#### **Tactility** +:::: tabs +::: tab Tactility The launching application will have `App::onResult()` called right after `App::onShow()` when it resumes operation. -#### **TactilitySDK** +::: +::: tab TactilitySDK The launching application will have `ExternalAppManifest::onResult` called right after `ExternalAppManifest::onShow` when it resumes operation. - +::: +:::: ## Return a result to an app - - -#### **Tactility** +:::: tabs +::: tab Tactility The `App` class has these methods: - `void setResult(Result, std::unique_ptr)` @@ -176,7 +179,8 @@ The `App` class has these methods: They can be called at any time, but after `App::onDestroy()` is called, any calls to `setResult()` won't have an effect. -#### **TactilitySDK** +::: +::: tab TactilitySDK TactilityC has these functions related to `AppHandle`: - `void tt_app_set_result(AppHandle, Result, BundleHandle)` @@ -184,5 +188,6 @@ TactilityC has these functions related to `AppHandle`: They can be called at any time, but after `ExternalManifest::onDestroy` is called, any calls to `tt_app_context_set_result()` won't have an effect. - +::: +:::: diff --git a/docs/versions/v0.4.0/board-development.md b/docs/v0.4.0/board-development.md similarity index 100% rename from docs/versions/v0.4.0/board-development.md rename to docs/v0.4.0/board-development.md diff --git a/docs/versions/v0.4.0/build-scripts.md b/docs/v0.4.0/build-scripts.md similarity index 100% rename from docs/versions/v0.4.0/build-scripts.md rename to docs/v0.4.0/build-scripts.md diff --git a/docs/versions/v0.4.0/building-tactility.md b/docs/v0.4.0/building-tactility.md similarity index 100% rename from docs/versions/v0.4.0/building-tactility.md rename to docs/v0.4.0/building-tactility.md diff --git a/docs/versions/v0.4.0/create-external-app.md b/docs/v0.4.0/create-external-app.md similarity index 100% rename from docs/versions/v0.4.0/create-external-app.md rename to docs/v0.4.0/create-external-app.md diff --git a/docs/versions/v0.4.0/create-internal-app.md b/docs/v0.4.0/create-internal-app.md similarity index 100% rename from docs/versions/v0.4.0/create-internal-app.md rename to docs/v0.4.0/create-internal-app.md diff --git a/docs/versions/v0.4.0/hal-fundamentals.md b/docs/v0.4.0/hal-fundamentals.md similarity index 100% rename from docs/versions/v0.4.0/hal-fundamentals.md rename to docs/v0.4.0/hal-fundamentals.md diff --git a/docs/versions/v0.4.0/README.md b/docs/v0.4.0/index.md similarity index 96% rename from docs/versions/v0.4.0/README.md rename to docs/v0.4.0/index.md index e01a949..19e282d 100644 --- a/docs/versions/v0.4.0/README.md +++ b/docs/v0.4.0/index.md @@ -1,4 +1,4 @@ -## Overview +# Overview This is the documentation for [Tactility](https://tactilityproject.org) development. diff --git a/docs/versions/v0.4.0/project-structure.md b/docs/v0.4.0/project-structure.md similarity index 100% rename from docs/versions/v0.4.0/project-structure.md rename to docs/v0.4.0/project-structure.md diff --git a/docs/versions/v0.4.0/requirements.md b/docs/v0.4.0/requirements.md similarity index 100% rename from docs/versions/v0.4.0/requirements.md rename to docs/v0.4.0/requirements.md diff --git a/docs/versions/v0.4.0/simulator.md b/docs/v0.4.0/simulator.md similarity index 100% rename from docs/versions/v0.4.0/simulator.md rename to docs/v0.4.0/simulator.md diff --git a/docs/versions/v0.5.0/_sidebar.md b/docs/v0.5.0/_sidebar.md similarity index 100% rename from docs/versions/v0.5.0/_sidebar.md rename to docs/v0.5.0/_sidebar.md diff --git a/docs/versions/v0.3.0/app-fundamentals.md b/docs/v0.5.0/app-fundamentals.md similarity index 93% rename from docs/versions/v0.3.0/app-fundamentals.md rename to docs/v0.5.0/app-fundamentals.md index b7e3ed5..611c754 100644 --- a/docs/versions/v0.3.0/app-fundamentals.md +++ b/docs/v0.5.0/app-fundamentals.md @@ -53,15 +53,15 @@ Apps must declare a manifest. The manifest specifies the app identifier, the hum where the class or functions of the app are and more. The app manifest is then registered, so the apps can be launched (e.g. from the Launcher app or other apps) - - -#### **Tactility** +:::: tabs +::: tab Tactility Internal apps are specified in the `tt::Configuration` such as [here](https://github.com/ByteWelder/Tactility/blob/{{versionLabel}}/App/Source/Main.cpp). They are automatically added to the internal app registry. -#### **TactilitySDK** +::: +::: tab TactilitySDK External apps must manually register their manifest: @@ -77,7 +77,8 @@ int main(int argc, char* argv[]) { } ``` - +::: +:::: ## Showing a user interface @@ -88,9 +89,8 @@ When the application's `onCreate()` function is called, you get access to `lv_ob This parent is the root widget that is used to attach other widgets too. It's the root of the application's window. - - -#### **Tactility** +:::: tabs +::: tab Tactility ```cpp void onShow(AppContext& context, lv_obj_t* parent) override { @@ -103,7 +103,8 @@ void onShow(AppContext& context, lv_obj_t* parent) override { } ``` -#### **TactilitySDK** +::: +::: tab TactilitySDK ```c static void onShow(AppHandle app, void* data, lv_obj_t* parent) { @@ -116,13 +117,13 @@ static void onShow(AppHandle app, void* data, lv_obj_t* parent) { } ``` - +::: +:::: ## Starting app with parameters - - -#### **Tactility** +:::: tabs +::: tab Tactility There are generally 2 ways to start an app with parameters: - By calling `void tt::app::start(string appId, Bundle parameters)` @@ -131,13 +132,15 @@ There are generally 2 ways to start an app with parameters: When apps require parameters, it's desirable to have an app-specific function that takes care of calling `tt::app::start()`. This way, the caller doesn't have to know about the internal formatting of the `Bundle`. -#### **TactilitySDK** +::: +::: tab TactilitySDK TactilitySDK has the following functions via `tt_app.h`: - `void tt_app_start(const char* appId)` - `void tt_app_start_with_bundle(string id, BundleHandle parameters)` - +::: +:::: ## App results @@ -152,23 +155,23 @@ it can report back which item was selected by setting the item index in the resu The application that launched the result-setting application is the one that receives the result. - - -#### **Tactility** +:::: tabs +::: tab Tactility The launching application will have `App::onResult()` called right after `App::onShow()` when it resumes operation. -#### **TactilitySDK** +::: +::: tab TactilitySDK The launching application will have `ExternalAppManifest::onResult` called right after `ExternalAppManifest::onShow` when it resumes operation. - +::: +:::: ## Return a result to an app - - -#### **Tactility** +:::: tabs +::: tab Tactility The `App` class has these methods: - `void setResult(Result, std::unique_ptr)` @@ -176,7 +179,8 @@ The `App` class has these methods: They can be called at any time, but after `App::onDestroy()` is called, any calls to `setResult()` won't have an effect. -#### **TactilitySDK** +::: +::: tab TactilitySDK TactilityC has these functions related to `AppHandle`: - `void tt_app_set_result(AppHandle, Result, BundleHandle)` @@ -184,5 +188,6 @@ TactilityC has these functions related to `AppHandle`: They can be called at any time, but after `ExternalManifest::onDestroy` is called, any calls to `tt_app_context_set_result()` won't have an effect. - +::: +:::: diff --git a/docs/versions/v0.5.0/board-development.md b/docs/v0.5.0/board-development.md similarity index 100% rename from docs/versions/v0.5.0/board-development.md rename to docs/v0.5.0/board-development.md diff --git a/docs/versions/v0.5.0/build-scripts.md b/docs/v0.5.0/build-scripts.md similarity index 100% rename from docs/versions/v0.5.0/build-scripts.md rename to docs/v0.5.0/build-scripts.md diff --git a/docs/versions/v0.5.0/building-tactility.md b/docs/v0.5.0/building-tactility.md similarity index 100% rename from docs/versions/v0.5.0/building-tactility.md rename to docs/v0.5.0/building-tactility.md diff --git a/docs/versions/v0.5.0/configuration.md b/docs/v0.5.0/configuration.md similarity index 100% rename from docs/versions/v0.5.0/configuration.md rename to docs/v0.5.0/configuration.md diff --git a/docs/versions/v0.5.0/create-external-app.md b/docs/v0.5.0/create-external-app.md similarity index 100% rename from docs/versions/v0.5.0/create-external-app.md rename to docs/v0.5.0/create-external-app.md diff --git a/docs/versions/v0.5.0/create-internal-app.md b/docs/v0.5.0/create-internal-app.md similarity index 100% rename from docs/versions/v0.5.0/create-internal-app.md rename to docs/v0.5.0/create-internal-app.md diff --git a/docs/versions/v0.5.0/hal-fundamentals.md b/docs/v0.5.0/hal-fundamentals.md similarity index 100% rename from docs/versions/v0.5.0/hal-fundamentals.md rename to docs/v0.5.0/hal-fundamentals.md diff --git a/docs/v0.5.0/index.md b/docs/v0.5.0/index.md new file mode 100644 index 0000000..19e282d --- /dev/null +++ b/docs/v0.5.0/index.md @@ -0,0 +1,11 @@ +# Overview + +This is the documentation for [Tactility](https://tactilityproject.org) development. + +Tactility is an operating system for devices with a SOC from the ESP32 family. + +This website is a programming manual for: +- Developing Tactility itself +- Developing applications with Tactility SDK (external applications) +- Developing internal applications + diff --git a/docs/versions/v0.5.0/installation.md b/docs/v0.5.0/installation.md similarity index 100% rename from docs/versions/v0.5.0/installation.md rename to docs/v0.5.0/installation.md diff --git a/docs/versions/v0.5.0/project-structure.md b/docs/v0.5.0/project-structure.md similarity index 100% rename from docs/versions/v0.5.0/project-structure.md rename to docs/v0.5.0/project-structure.md diff --git a/docs/versions/v0.5.0/requirements.md b/docs/v0.5.0/requirements.md similarity index 100% rename from docs/versions/v0.5.0/requirements.md rename to docs/v0.5.0/requirements.md diff --git a/docs/versions/v0.5.0/simulator.md b/docs/v0.5.0/simulator.md similarity index 100% rename from docs/versions/v0.5.0/simulator.md rename to docs/v0.5.0/simulator.md diff --git a/docs/versions/v0.6.0/_sidebar.md b/docs/v0.6.0/_sidebar.md similarity index 100% rename from docs/versions/v0.6.0/_sidebar.md rename to docs/v0.6.0/_sidebar.md diff --git a/docs/versions/v0.6.0/app-development/fundamentals.md b/docs/v0.6.0/app-development/fundamentals.md similarity index 100% rename from docs/versions/v0.6.0/app-development/fundamentals.md rename to docs/v0.6.0/app-development/fundamentals.md diff --git a/docs/versions/v0.6.0/app-development/getting-started.md b/docs/v0.6.0/app-development/getting-started.md similarity index 94% rename from docs/versions/v0.6.0/app-development/getting-started.md rename to docs/v0.6.0/app-development/getting-started.md index 9c6e0b0..32427b6 100644 --- a/docs/versions/v0.6.0/app-development/getting-started.md +++ b/docs/v0.6.0/app-development/getting-started.md @@ -8,7 +8,7 @@ Each app in the [Apps/](https://github.com/ByteWelder/TactilityApps/tree/main/Ap ## Building -After you went through the [Fundamentals](app-development/fundamentals.md) to set up a new application, +After you went through the [Fundamentals](fundamentals.md) to set up a new application, or if you downloaded one of the example apps, you can build it: ```bash diff --git a/docs/versions/v0.6.0/app-development/symbols.md b/docs/v0.6.0/app-development/symbols.md similarity index 100% rename from docs/versions/v0.6.0/app-development/symbols.md rename to docs/v0.6.0/app-development/symbols.md diff --git a/docs/versions/v0.6.0/configuration.md b/docs/v0.6.0/configuration.md similarity index 100% rename from docs/versions/v0.6.0/configuration.md rename to docs/v0.6.0/configuration.md diff --git a/docs/versions/v0.7.0/README.md b/docs/v0.6.0/index.md similarity index 95% rename from docs/versions/v0.7.0/README.md rename to docs/v0.6.0/index.md index 2b07e1b..5be2fe7 100644 --- a/docs/versions/v0.7.0/README.md +++ b/docs/v0.6.0/index.md @@ -1,4 +1,4 @@ -## Overview +# Overview This is the documentation for [Tactility](https://tactilityproject.org) development. diff --git a/docs/versions/v0.6.0/installation.md b/docs/v0.6.0/installation.md similarity index 100% rename from docs/versions/v0.6.0/installation.md rename to docs/v0.6.0/installation.md diff --git a/docs/versions/v0.6.0/requirements.md b/docs/v0.6.0/requirements.md similarity index 100% rename from docs/versions/v0.6.0/requirements.md rename to docs/v0.6.0/requirements.md diff --git a/docs/versions/v0.6.0/tactility-development/board-development.md b/docs/v0.6.0/tactility-development/board-development.md similarity index 100% rename from docs/versions/v0.6.0/tactility-development/board-development.md rename to docs/v0.6.0/tactility-development/board-development.md diff --git a/docs/versions/v0.6.0/tactility-development/build-scripts.md b/docs/v0.6.0/tactility-development/build-scripts.md similarity index 100% rename from docs/versions/v0.6.0/tactility-development/build-scripts.md rename to docs/v0.6.0/tactility-development/build-scripts.md diff --git a/docs/versions/v0.6.0/tactility-development/building-tactility.md b/docs/v0.6.0/tactility-development/building-tactility.md similarity index 98% rename from docs/versions/v0.6.0/tactility-development/building-tactility.md rename to docs/v0.6.0/tactility-development/building-tactility.md index 5489b3a..ddcdfe2 100644 --- a/docs/versions/v0.6.0/tactility-development/building-tactility.md +++ b/docs/v0.6.0/tactility-development/building-tactility.md @@ -1,6 +1,6 @@ # Building Tactility -Make sure you looked at the [requirements](requirements.md) first. +Make sure you looked at the [requirements](../requirements.md) first. ## Cloning from git diff --git a/docs/versions/v0.6.0/tactility-development/hal-fundamentals.md b/docs/v0.6.0/tactility-development/hal-fundamentals.md similarity index 100% rename from docs/versions/v0.6.0/tactility-development/hal-fundamentals.md rename to docs/v0.6.0/tactility-development/hal-fundamentals.md diff --git a/docs/versions/v0.6.0/tactility-development/project-structure.md b/docs/v0.6.0/tactility-development/project-structure.md similarity index 100% rename from docs/versions/v0.6.0/tactility-development/project-structure.md rename to docs/v0.6.0/tactility-development/project-structure.md diff --git a/docs/versions/v0.6.0/tactility-development/simulator.md b/docs/v0.6.0/tactility-development/simulator.md similarity index 100% rename from docs/versions/v0.6.0/tactility-development/simulator.md rename to docs/v0.6.0/tactility-development/simulator.md diff --git a/docs/versions/v0.7.0/_sidebar.md b/docs/v0.7.0/_sidebar.md similarity index 100% rename from docs/versions/v0.7.0/_sidebar.md rename to docs/v0.7.0/_sidebar.md diff --git a/docs/versions/v0.7.0/app-development/fundamentals.md b/docs/v0.7.0/app-development/fundamentals.md similarity index 100% rename from docs/versions/v0.7.0/app-development/fundamentals.md rename to docs/v0.7.0/app-development/fundamentals.md diff --git a/docs/versions/v0.7.0/app-development/getting-started.md b/docs/v0.7.0/app-development/getting-started.md similarity index 95% rename from docs/versions/v0.7.0/app-development/getting-started.md rename to docs/v0.7.0/app-development/getting-started.md index b75bb9f..81ad387 100644 --- a/docs/versions/v0.7.0/app-development/getting-started.md +++ b/docs/v0.7.0/app-development/getting-started.md @@ -8,7 +8,7 @@ Each app in the [Apps/](https://github.com/ByteWelder/TactilityApps/tree/main/Ap ## Building -After you went through the [Fundamentals](app-development/fundamentals.md) to set up a new application, +After you went through the [Fundamentals](fundamentals.md) to set up a new application, or if you downloaded one of the example apps, you can build it: ```bash diff --git a/docs/versions/v0.7.0/app-development/symbols.md b/docs/v0.7.0/app-development/symbols.md similarity index 100% rename from docs/versions/v0.7.0/app-development/symbols.md rename to docs/v0.7.0/app-development/symbols.md diff --git a/docs/versions/v0.7.0/configuration.md b/docs/v0.7.0/configuration.md similarity index 100% rename from docs/versions/v0.7.0/configuration.md rename to docs/v0.7.0/configuration.md diff --git a/docs/versions/v0.7.0/device-development/device-properties.md b/docs/v0.7.0/device-development/device-properties.md similarity index 100% rename from docs/versions/v0.7.0/device-development/device-properties.md rename to docs/v0.7.0/device-development/device-properties.md diff --git a/docs/versions/v0.7.0/device-development/drivers-legacy.md b/docs/v0.7.0/device-development/drivers-legacy.md similarity index 100% rename from docs/versions/v0.7.0/device-development/drivers-legacy.md rename to docs/v0.7.0/device-development/drivers-legacy.md diff --git a/docs/versions/v0.7.0/device-development/drivers.md b/docs/v0.7.0/device-development/drivers.md similarity index 100% rename from docs/versions/v0.7.0/device-development/drivers.md rename to docs/v0.7.0/device-development/drivers.md diff --git a/docs/versions/v0.7.0/device-development/guide.md b/docs/v0.7.0/device-development/guide.md similarity index 90% rename from docs/versions/v0.7.0/device-development/guide.md rename to docs/v0.7.0/device-development/guide.md index db5c3c0..8dbcc61 100644 --- a/docs/versions/v0.7.0/device-development/guide.md +++ b/docs/v0.7.0/device-development/guide.md @@ -33,7 +33,7 @@ The `sdkconfig` is generated from `device.properties` file in `Devices/your-devi Run `python device.py your-device-identifier` to generate the `sdkconfig` file for the device. You can add `--dev` to generate partitions with a `4 MB` footprint for faster flashing. -See the [device.properties documentation](device-development/device-properties.md) for more info. +See the [device.properties documentation](device-properties.md) for more info. ## Implementation @@ -57,7 +57,7 @@ When possible, re-use existing drivers: New drivers should be written as kernel drivers. Look at `Platforms/PlatformEsp32` for examples. -Explore the [drivers documentation](device-development/drivers.md) or the [legacy drivers documentation](device-development/drivers-legacy.md) for more details. +Explore the [drivers documentation](drivers.md) or the [legacy drivers documentation](drivers-legacy.md) for more details. ## Continuous Integration diff --git a/docs/versions/v0.7.0/device-development/hardware-considerations.md b/docs/v0.7.0/device-development/hardware-considerations.md similarity index 100% rename from docs/versions/v0.7.0/device-development/hardware-considerations.md rename to docs/v0.7.0/device-development/hardware-considerations.md diff --git a/docs/versions/main/README.md b/docs/v0.7.0/index.md similarity index 95% rename from docs/versions/main/README.md rename to docs/v0.7.0/index.md index 2b07e1b..5be2fe7 100644 --- a/docs/versions/main/README.md +++ b/docs/v0.7.0/index.md @@ -1,4 +1,4 @@ -## Overview +# Overview This is the documentation for [Tactility](https://tactilityproject.org) development. diff --git a/docs/versions/v0.7.0/os-development/building-tactility.md b/docs/v0.7.0/os-development/building-tactility.md similarity index 98% rename from docs/versions/v0.7.0/os-development/building-tactility.md rename to docs/v0.7.0/os-development/building-tactility.md index 3dddc5d..9b2642b 100644 --- a/docs/versions/v0.7.0/os-development/building-tactility.md +++ b/docs/v0.7.0/os-development/building-tactility.md @@ -1,6 +1,6 @@ # Building Tactility -Make sure you looked at the [requirements](requirements.md) first. +Make sure you looked at the [requirements](../requirements.md) first. ## Cloning from git diff --git a/docs/versions/v0.7.0/os-development/project-structure.md b/docs/v0.7.0/os-development/project-structure.md similarity index 100% rename from docs/versions/v0.7.0/os-development/project-structure.md rename to docs/v0.7.0/os-development/project-structure.md diff --git a/docs/versions/v0.7.0/os-development/simulator.md b/docs/v0.7.0/os-development/simulator.md similarity index 100% rename from docs/versions/v0.7.0/os-development/simulator.md rename to docs/v0.7.0/os-development/simulator.md diff --git a/docs/versions/v0.7.0/requirements.md b/docs/v0.7.0/requirements.md similarity index 100% rename from docs/versions/v0.7.0/requirements.md rename to docs/v0.7.0/requirements.md diff --git a/docs/versions/v0.5.0/README.md b/docs/versions/v0.5.0/README.md deleted file mode 100644 index e01a949..0000000 --- a/docs/versions/v0.5.0/README.md +++ /dev/null @@ -1,11 +0,0 @@ -## Overview - -This is the documentation for [Tactility](https://tactilityproject.org) development. - -Tactility is an operating system for devices with a SOC from the ESP32 family. - -This website is a programming manual for: -- Developing Tactility itself -- Developing applications with Tactility SDK (external applications) -- Developing internal applications - diff --git a/eleventy.config.js b/eleventy.config.js new file mode 100644 index 0000000..15bf3c1 --- /dev/null +++ b/eleventy.config.js @@ -0,0 +1,234 @@ +import { InputPathToUrlTransformPlugin } from "@11ty/eleventy"; +import syntaxHighlight from "@11ty/eleventy-plugin-syntaxhighlight"; +import fs from "node:fs"; +import path from "node:path"; +import zlib from "node:zlib"; +import markdownIt from "markdown-it"; +import anchor from "markdown-it-anchor"; +import alerts from "markdown-it-github-alerts"; +import container from "markdown-it-container"; +import * as pagefind from "pagefind"; + +const input = "docs"; +const fonts = "node_modules/@fontsource-variable"; + +// A version is a folder in docs/ with a _sidebar.md. "main" comes first, then +// the releases from new to old. +function loadVersions() { + const release = (id) => id.replace(/^v/, "").split(".").map(Number); + const ids = fs + .readdirSync(input) + .filter((name) => fs.existsSync(path.join(input, name, "_sidebar.md"))) + .sort((a, b) => { + if (a === "main" || b === "main") return a === "main" ? -1 : b === "main" ? 1 : 0; + const [x, y] = [release(a), release(b)]; + for (let i = 0; i < Math.max(x.length, y.length); i++) { + if ((x[i] || 0) !== (y[i] || 0)) return (y[i] || 0) - (x[i] || 0); + } + return 0; + }); + const latest = ids.find((id) => id !== "main"); + return ids.map((id) => { + const sections = parseSidebar(id); + return { + id, + url: `/${id}/`, + development: id === "main", + latest: id === latest, + sections, + pages: sections.flatMap((section) => section.items).filter((item) => item.url.startsWith(`/${id}/`)), + }; + }); +} + +// _sidebar.md is a list of links, optionally grouped under a title: +// - Group title +// - [Page](folder/page.md) +function parseSidebar(id) { + const sections = [{ title: null, items: [{ label: "Overview", url: `/${id}/` }] }]; + const lines = fs.readFileSync(path.join(input, id, "_sidebar.md"), "utf8").split("\n"); + for (const line of lines) { + const entry = /^(\s*)[-*]\s+(.*\S)\s*$/.exec(line); + if (!entry) continue; + const link = /^\[(.+)\]\((.+)\)$/.exec(entry[2]); + if (!link) { + sections.push({ title: entry[2], items: [] }); + continue; + } + if (!entry[1] && sections.at(-1).title) sections.push({ title: null, items: [] }); + const file = path.posix.normalize(path.posix.join(id, link[2])); + if (!fs.existsSync(path.join(input, file))) { + throw new Error(`${id}/_sidebar.md links to ${link[2]}, which doesn't exist`); + } + sections.at(-1).items.push({ label: link[1], url: fileUrl(file) }); + } + return sections.filter((section) => section.items.length); +} + +// "main/app-development/ui.md" -> "/main/app-development/ui/" +function fileUrl(file) { + const url = "/" + file.replace(/\.md$/, "/").replace(/(^|\/)index\/$/, "$1"); + return url; +} + +// Path of a page inside docs/, e.g. "main/app-development/ui.md" +const relativeInput = (inputPath) => path.posix.relative(input, inputPath.replaceAll(path.sep, "/")); + +function versionOf(versions, inputPath) { + const id = relativeInput(inputPath).split("/")[0]; + return versions.find((version) => version.id === id); +} + +// PlantUML diagrams are rendered by the PlantUML server: the image URL +// contains the compressed source. +function plantUmlUrl(source) { + if (!/skinparam\s+backgroundColor/i.test(source)) { + source = source.replace(/@startuml[^\n]*\n/, "$&skinparam backgroundColor transparent\n"); + } + const from = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + const to = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_"; + const encoded = zlib + .deflateRawSync(Buffer.from(source, "utf8"), { level: 9 }) + .toString("base64") + .replace(/=+$/, "") + .replace(/[A-Za-z0-9+/]/g, (char) => to[from.indexOf(char)]); + return `https://www.plantuml.com/plantuml/svg/${encoded}`; +} + +export default function (eleventyConfig) { + // Lets markdown link to other pages by file name, e.g. [Simulator](simulator.md) + eleventyConfig.addPlugin(InputPathToUrlTransformPlugin); + eleventyConfig.addPlugin(syntaxHighlight); + + // The sidebars are data, not pages + eleventyConfig.ignores.add(`${input}/**/_sidebar.md`); + eleventyConfig.addWatchTarget(`${input}/**/_sidebar.md`); + + eleventyConfig.addGlobalData("versions", loadVersions); + eleventyConfig.addGlobalData("layout", "page"); + eleventyConfig.addGlobalData("eleventyComputed", { + version: (data) => (data.page.inputPath ? versionOf(data.versions, data.page.inputPath) : undefined), + // Title for the browser tab: the frontmatter title, else the first + // heading of the page, else the label in the sidebar + docTitle: (data) => { + if (data.title) return data.title; + if (!data.page.inputPath?.endsWith(".md")) return undefined; + const source = fs.readFileSync(data.page.inputPath, "utf8").split("```")[0]; + const heading = /^# +(.+)$/m.exec(source); + if (heading) return heading[1].trim(); + const version = versionOf(data.versions, data.page.inputPath); + const url = fileUrl(relativeInput(data.page.inputPath)); + return version?.pages.find((item) => item.url === url)?.label; + }, + }); + + // {{versionLabel}} in a page is the name of its version folder, which is + // also the name of the branch or tag in the Tactility repository + eleventyConfig.addPreprocessor("versionLabel", "md", (data, content) => { + if (!content.includes("{{versionLabel}}")) return content; + return content.replaceAll("{{versionLabel}}", relativeInput(data.page.inputPath).split("/")[0]); + }); + + // Tabbed card in markdown, without JavaScript (radio buttons + CSS): + // :::: tabs + // ::: tab Linux / macOS + // ... + // ::: + // ::: tab Windows + // ... + // ::: + // :::: + let tabGroup = 0; + let tabIndex = 0; + const escapeHtml = markdownIt().utils.escapeHtml; + + const md = markdownIt({ html: true, linkify: true }) + .use(anchor, { + level: 2, + // Same ids as the old Docsify site, e.g. "Manifest.properties" -> "manifestproperties" + slugify: (text) => + text + .trim() + .toLowerCase() + .replace(/[^\p{L}\p{N}\s_-]/gu, "") + .replace(/\s+/g, "-"), + permalink: anchor.permalink.headerLink({ safariReaderFix: true }), + }) + .use(alerts) + .use(container, "tabs", { + render(tokens, idx) { + if (tokens[idx].nesting !== 1) return "\n"; + tabGroup++; + tabIndex = 0; + return '
\n'; + }, + }) + .use(container, "tab", { + render(tokens, idx) { + if (tokens[idx].nesting !== 1) return "
\n"; + const title = escapeHtml(tokens[idx].info.trim().replace(/^tab\s*/, "")); + const id = `tab-${tabGroup}-${++tabIndex}`; + const checked = tabIndex === 1 ? " checked" : ""; + return ( + `` + + `\n
\n` + ); + }, + }); + + // ```plantuml code blocks become diagrams + const fence = md.renderer.rules.fence; + md.renderer.rules.fence = (tokens, idx, options, env, self) => { + const token = tokens[idx]; + if (token.info.trim() !== "plantuml") return fence(tokens, idx, options, env, self); + return `

Diagram

\n`; + }; + + eleventyConfig.setLibrary("md", md); + + eleventyConfig.addPassthroughCopy(`${input}/images`); + eleventyConfig.addPassthroughCopy(`${input}/assets`); + eleventyConfig.addPassthroughCopy(`${input}/CNAME`); + eleventyConfig.addPassthroughCopy({ + [`${fonts}/inter/files/inter-latin-wght-normal.woff2`]: "assets/fonts/inter.woff2", + [`${fonts}/jetbrains-mono/files/jetbrains-mono-latin-wght-normal.woff2`]: "assets/fonts/jetbrains-mono.woff2", + }); + + // URL of the same page in another version, or the start page of that + // version when it doesn't have the page + eleventyConfig.addFilter("inVersion", (inputPath, version) => { + const [, ...rest] = relativeInput(inputPath).split("/"); + const file = [version.id, ...rest].join("/"); + return fs.existsSync(path.join(input, file)) ? fileUrl(file) : version.url; + }); + + // The pages before and after a page, in the order of the sidebar + eleventyConfig.addFilter("neighbours", (version, url) => { + const index = version.pages.findIndex((item) => item.url === url); + if (index < 0) return {}; + return { previous: version.pages[index - 1], next: version.pages[index + 1] }; + }); + + // Second and third level headings of rendered content, for the "On this page" list + eleventyConfig.addFilter("headings", (html) => + [...String(html).matchAll(/]*>(.*?)<\/h\1>/gs)].map((m) => ({ + level: Number(m[1]), + id: m[2], + text: m[3].replace(/<[^>]+>/g, ""), + })), + ); + + // Search index (Pagefind), written next to the built pages + eleventyConfig.on("eleventy.after", async ({ directories }) => { + const { index } = await pagefind.createIndex(); + const { page_count } = await index.addDirectory({ path: directories.output }); + await index.writeFiles({ outputPath: path.join(directories.output, "pagefind") }); + await pagefind.close(); + console.log(`[pagefind] Indexed ${page_count} pages`); + }); + + return { + dir: { input, output: "_site" }, + markdownTemplateEngine: false, + }; +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..29f0582 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2002 @@ +{ + "name": "tactility-docs", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tactility-docs", + "devDependencies": { + "@11ty/eleventy": "^3.1.8", + "@11ty/eleventy-plugin-syntaxhighlight": "^5.0.2", + "@fontsource-variable/inter": "^5.3.0", + "@fontsource-variable/jetbrains-mono": "^5.3.0", + "markdown-it": "^15.0.3", + "markdown-it-anchor": "^10.1.0", + "markdown-it-container": "^4.0.0", + "markdown-it-github-alerts": "^1.0.1", + "pagefind": "^1.3.0" + } + }, + "node_modules/@11ty/chokidar-with-glob-support": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@11ty/chokidar-with-glob-support/-/chokidar-with-glob-support-1.1.1.tgz", + "integrity": "sha512-wyugY5VKwQ9OabCBNwa3BLwei54aV36jy8K3O762m/ZVKiczLvnKJN+JYGFL5Ct2faDoBr3Bp0AjNbZpz0xo3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "picomatch": "^4.0.7" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/11ty" + } + }, + "node_modules/@11ty/chokidar-with-glob-support/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@11ty/chokidar-with-glob-support/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@11ty/dependency-tree": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@11ty/dependency-tree/-/dependency-tree-4.0.2.tgz", + "integrity": "sha512-RTF6VTZHatYf7fSZBUN3RKwiUeJh5dhWV61gDPrHhQF2/gzruAkYz8yXuvGLx3w3ZBKreGrR+MfYpSVkdbdbLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@11ty/eleventy-utils": "^2.0.1" + } + }, + "node_modules/@11ty/dependency-tree-esm": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@11ty/dependency-tree-esm/-/dependency-tree-esm-2.0.4.tgz", + "integrity": "sha512-MYKC0Ac77ILr1HnRJalzKDlb9Z8To3kXQCltx299pUXXUFtJ1RIONtULlknknqW8cLe19DLVgmxVCtjEFm7h0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@11ty/eleventy-utils": "^2.0.7", + "acorn": "^8.15.0", + "dependency-graph": "^1.0.0", + "normalize-path": "^3.0.0" + } + }, + "node_modules/@11ty/eleventy": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@11ty/eleventy/-/eleventy-3.1.8.tgz", + "integrity": "sha512-nzwVWco4TBETroX7yARBtHlX8P1TWfqHZtQKc5LQ4vyBSbE8dw8lA5gsMWs9VpWqWoLdfnHCLl/r8hT8qOFvwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@11ty/chokidar-with-glob-support": "^1.1.0", + "@11ty/dependency-tree": "^4.0.2", + "@11ty/dependency-tree-esm": "^2.0.4", + "@11ty/eleventy-dev-server": "^2.1.2", + "@11ty/eleventy-plugin-bundle": "^3.0.7", + "@11ty/eleventy-utils": "^2.0.7", + "@11ty/lodash-custom": "^4.17.21", + "@11ty/posthtml-urls": "^1.0.3", + "@11ty/recursive-copy": "^4.0.6", + "@sindresorhus/slugify": "^2.2.1", + "bcp-47-normalize": "^2.3.0", + "debug": "^4.4.3", + "dependency-graph": "^1.0.0", + "entities": "^6.0.1", + "filesize": "^10.1.6", + "gray-matter": "npm:@11ty/gray-matter@^1.0.0", + "iso-639-1": "^3.1.6", + "js-yaml": "^4.1.1", + "kleur": "^4.1.5", + "liquidjs": "^10.30.0", + "luxon": "^3.7.2", + "markdown-it": "^14.3.3", + "minimist": "^1.2.8", + "moo": "0.5.2", + "node-retrieve-globals": "^6.0.1", + "nunjucks": "^3.2.4", + "picomatch": "^4.0.7", + "please-upgrade-node": "^3.2.0", + "posthtml": "^0.16.7", + "posthtml-match-helper": "^2.0.3", + "semver": "^7.8.5", + "slugify": "^1.6.9", + "tinyglobby": "^0.2.16" + }, + "bin": { + "eleventy": "cmd.cjs" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/11ty" + } + }, + "node_modules/@11ty/eleventy-dev-server": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@11ty/eleventy-dev-server/-/eleventy-dev-server-2.1.2.tgz", + "integrity": "sha512-cwReJlQtIZsFuwMhm2fXwwjj+uUVvRkuoESgDtR5Mg9tY2qRj8UbNC8xhYRBuYVpACd3E09CxGPkk/Qj15Zo+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@11ty/chokidar-with-glob-support": "^1.1.0", + "@11ty/eleventy-utils": "^2.0.1", + "debug": "^4.4.0", + "finalhandler": "^1.3.1", + "mime": "^3.0.0", + "minimist": "^1.2.8", + "morphdom": "^2.7.4", + "please-upgrade-node": "^3.2.0", + "send": "^1.1.0", + "ssri": "^11.0.0", + "urlpattern-polyfill": "^10.0.0", + "ws": "^8.18.1" + }, + "bin": { + "eleventy-dev-server": "cmd.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/11ty" + } + }, + "node_modules/@11ty/eleventy-plugin-bundle": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@11ty/eleventy-plugin-bundle/-/eleventy-plugin-bundle-3.0.7.tgz", + "integrity": "sha512-QK1tRFBhQdZASnYU8GMzpTdsMMFLVAkuU0gVVILqNyp09xJJZb81kAS3AFrNrwBCsgLxTdWHJ8N64+OTTsoKkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@11ty/eleventy-utils": "^2.0.2", + "debug": "^4.4.0", + "posthtml-match-helper": "^2.0.3" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/11ty" + } + }, + "node_modules/@11ty/eleventy-plugin-syntaxhighlight": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@11ty/eleventy-plugin-syntaxhighlight/-/eleventy-plugin-syntaxhighlight-5.0.2.tgz", + "integrity": "sha512-T6xVVRDJuHlrFMHbUiZkHjj5o1IlLzZW+1IL9eUsyXFU7rY2ztcYhZew/64vmceFFpQwzuSfxQOXxTJYmKkQ+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "prismjs": "^1.30.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/11ty" + } + }, + "node_modules/@11ty/eleventy-utils": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@11ty/eleventy-utils/-/eleventy-utils-2.0.7.tgz", + "integrity": "sha512-6QE+duqSQ0GY9rENXYb4iPR4AYGdrFpqnmi59tFp9VrleOl0QSh8VlBr2yd6dlhkdtj7904poZW5PvGr9cMiJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/11ty" + } + }, + "node_modules/@11ty/eleventy/node_modules/linkify-it": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/@11ty/eleventy/node_modules/markdown-it": { + "version": "14.3.3", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.3.tgz", + "integrity": "sha512-L//tDAkjTjJCU6pBUSBTgs3Kg4InVaZuUZog/b6V12UKhcDQ7S78877ibbCanRExwPuYP1CZD7Gd2dxSy1GXRg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.5.0", + "linkify-it": "^5.0.2", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/@11ty/eleventy/node_modules/markdown-it/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@11ty/eleventy/node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@11ty/lodash-custom": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@11ty/lodash-custom/-/lodash-custom-4.17.21.tgz", + "integrity": "sha512-Mqt6im1xpb1Ykn3nbcCovWXK3ggywRJa+IXIdoz4wIIK+cvozADH63lexcuPpGS/gJ6/m2JxyyXDyupkMr5DHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/11ty" + } + }, + "node_modules/@11ty/posthtml-urls": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@11ty/posthtml-urls/-/posthtml-urls-1.0.3.tgz", + "integrity": "sha512-1YvhnkaNlFnnJic1rBMWmTC2adbuy+JQiBfl1Hecr1Wjjik1pQZmGyk/eC9zKX/FQv52s2Nht1Gi/UwhYqrBeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "evaluate-value": "^2.0.0", + "http-equiv-refresh": "^2.0.1", + "list-to-array": "^1.1.0", + "parse-srcset": "^1.0.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@11ty/recursive-copy": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@11ty/recursive-copy/-/recursive-copy-4.0.6.tgz", + "integrity": "sha512-+mdqG79jXufZnY0nD/ZWOsT7i8lxqXRtaO0TotpV4fD3qrHlPonEz2izTsbENAlzpsq5+QpwuHnH4RZoalx44g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.21", + "errno": "^1.0.0", + "junk": "^3.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@fontsource-variable/inter": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource-variable/inter/-/inter-5.3.0.tgz", + "integrity": "sha512-OupL48va4JNofb97w6NYeF9S7W/kHNKM0Er8Dem5nqi4jeOLrVJDoE8tZEpnMJmtkvNbB1EIPPwHcdkF6b1oUA==", + "dev": true, + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource-variable/jetbrains-mono": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource-variable/jetbrains-mono/-/jetbrains-mono-5.3.0.tgz", + "integrity": "sha512-F32xpS2NsGYoQi2ADSkKTgpJj7ozajsGgDJ8woTnqjmIB+dxDIqImjl4pXZVEExu8UFZ2ndhmX18EBS/hdz3Lw==", + "dev": true, + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@pagefind/darwin-arm64": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.6.0.tgz", + "integrity": "sha512-611gl64aqN12UcMyKPtZ8+vRWBKlJFSh2JgO8uScxa1Eyjo1dXz9r4wZuRno0yCUE/KN0FauEoKZ3KEwUYHCCg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@pagefind/darwin-x64": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.6.0.tgz", + "integrity": "sha512-rd3ZbtuxyYqjASKZAPuIV7eeQQHJ7VYpoNKKG8CvglaD3g30eT5Nb7XGNLYwVReBsCccpkURwrGwWjSwr8nyQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@pagefind/freebsd-x64": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.6.0.tgz", + "integrity": "sha512-d+sXqsaThszmPWGVDSRKqIz9GpZcj/bAksNZfg19asSxU0ogNPOMND4kV4giQvOjPFMsltUFf6hQJfoHQ6+hSQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@pagefind/linux-arm64": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.6.0.tgz", + "integrity": "sha512-SLB2I+XqCZma8NvIGIH8nyKblgKIxw5PmqsVtofWOKxNHhEtOEsddbqU+hsrNcT3EV0faM6UmKDHVIxcPYq29w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@pagefind/linux-x64": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.6.0.tgz", + "integrity": "sha512-IbMSx/+Vr/Xk0TTBlLkjgK+rvnbOXGmmb2/MKxQu99AR5DsqQoV3NKEA9PQz2tAhuCZjeD5xJBp4w5N5DHIC7Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@pagefind/windows-arm64": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@pagefind/windows-arm64/-/windows-arm64-1.6.0.tgz", + "integrity": "sha512-wUrniW3REr7VWJHz6xE6F/p/AQgHDFpGZiY9sbWT71KfEijDE5Jnc37V8zc7cilV9h/QkwJGXoEP2+ZLwDmhtw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@pagefind/windows-x64": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.6.0.tgz", + "integrity": "sha512-cUA2+yXfbymdJllyMjY5YTBwuOgh0/H53Pn5fYKjDnDjk154Ft4U5RVj3TenWySPL/jG6zJfARUWSlyUyXHlZw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sindresorhus/slugify": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/slugify/-/slugify-2.2.1.tgz", + "integrity": "sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/transliterate": "^1.0.0", + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sindresorhus/transliterate": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/transliterate/-/transliterate-1.6.0.tgz", + "integrity": "sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/a-sync-waterfall": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", + "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz", + "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bcp-47": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.2.tgz", + "integrity": "sha512-wsv86UuNFuPnVieKLyECTT+CP1WSAHeoIg5P3xu2kA9Z+7fVPoKt7dxzSeEhtJel0AI+NW3fIW3KtTKQKUCngg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/bcp-47-match": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz", + "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/bcp-47-normalize": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/bcp-47-normalize/-/bcp-47-normalize-2.3.0.tgz", + "integrity": "sha512-8I/wfzqQvttUFz7HVJgIZ7+dj3vUaIyIxYXaTRP1YWoSDfzt6TUmxaKZeuXR62qBmYr+nvuWINFRl6pZ5DlN4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "bcp-47": "^2.0.0", + "bcp-47-match": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.22.tgz", + "integrity": "sha512-CKLDTuSsN0+r1ThM0WkzzBI7dnPI6xEGfa/JnVYIK9JNBA1N1NyPwXVHor//NKD/y3sDxp764sZdLhFK7pH8XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dependency-graph": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz", + "integrity": "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.1.tgz", + "integrity": "sha512-Ev/hIyePLZtpL98x562lvPtenrUfGr0lfaf44HFZ+hDEwso7ccV9QAZrEVLg0hxNAzygaAFxy2dYaAKKM0G06A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/errno": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/errno/-/errno-1.0.0.tgz", + "integrity": "sha512-3zV5mFS1E8/1bPxt/B0xxzI1snsg3uSCIh6Zo1qKg6iMw93hzPANk9oBFzSFBFrwuVoQuE3rLoouAUfwOAj1wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esm-import-transformer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/esm-import-transformer/-/esm-import-transformer-3.0.5.tgz", + "integrity": "sha512-1GKLvfuMnnpI75l8c6sHoz0L3Z872xL5akGuBudgqTDPv4Vy6f2Ec7jEMKTxlqWl/3kSvNbHELeimJtnqgYniw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/evaluate-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/evaluate-value/-/evaluate-value-2.0.0.tgz", + "integrity": "sha512-VonfiuDJc0z4sOO7W0Pd130VLsXN6vmBWZlrog1mCb/o7o/Nl5Lr25+Kj/nkCCAhG+zqeeGjxhkK9oHpkgTHhQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/filesize": { + "version": "10.1.6", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.1.6.tgz", + "integrity": "sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 10.4.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gray-matter": { + "name": "@11ty/gray-matter", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@11ty/gray-matter/-/gray-matter-1.0.0.tgz", + "integrity": "sha512-7mJJl+wf1AByoT0PknQiQfOPnVNT4fevGrUBVWO4HXsnYn1aQPyRyrELYrNUFleUBM++KzMKN6QaxHPk0t/6/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-yaml": "^4.1.0", + "kind-of": "^6.0.3", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=11" + } + }, + "node_modules/htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-equiv-refresh": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-equiv-refresh/-/http-equiv-refresh-2.0.1.tgz", + "integrity": "sha512-XJpDL/MLkV3dKwLzHwr2dY05dYNfBNlyPu4STQ8WvKCFdc6vC5tPXuq28of663+gHVg03C+16pHHs/+FmmDjcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-json": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz", + "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/iso-639-1": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/iso-639-1/-/iso-639-1-3.1.6.tgz", + "integrity": "sha512-ZFar/L4ngX7wZh2QX+Fiftmuf0igWJsrJtfizrovWifF1gAWkfmRa5Z1m0LQZbm0hKCHRDYhLRSLFrSqNe4EJA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/junk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", + "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/linkify-it": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-6.1.0.tgz", + "integrity": "sha512-wJ/TwpSDTLepCrQoYWYIExIKg5Zchex2Nn5yk2mFnB+6PtdkHtyLx742md9csRjjOnGkKIS/RrbY7l8D6gT9Vw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "uc.micro": "^3.0.0" + } + }, + "node_modules/liquidjs": { + "version": "10.30.1", + "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.30.1.tgz", + "integrity": "sha512-P1Iz14WUHZdfbOYwHOIhtl/iAWIOYk1ks5l9rPZlDM6vZo+6BLeYIuWHAPkIz+U8Mm8HKZKITIrjX7DQJO2fqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^10.0.0" + }, + "bin": { + "liquid": "bin/liquid.js", + "liquidjs": "bin/liquid.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/liquidjs" + } + }, + "node_modules/list-to-array": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/list-to-array/-/list-to-array-1.1.0.tgz", + "integrity": "sha512-+dAZZ2mM+/m+vY9ezfoueVvrgnHIGi5FvgSymbIgJOFwiznWyA59mav95L+Mc6xPtL3s9gm5eNTlNtxJLbNM1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/luxon": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", + "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/markdown-it": { + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-15.0.3.tgz", + "integrity": "sha512-+Az7Ojz3iRGu8KYMvJ6xVhvrxP3eE+vEtJX75Ow88XBpcQNV/0lGX5ZynIIBftREo4DC2jsveAMDaeltmm8QvQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^3.0.3", + "entities": "^8.1.0", + "linkify-it": "^6.1.0", + "mdurl": "^2.1.0", + "punycode.js": "^2.3.1", + "uc.micro": "^3.0.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-anchor": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-10.1.0.tgz", + "integrity": "sha512-htMTOUnoGqoWguddWO+NBrOEdh3dt9k1RIhTlr2CQTJOPiLZglDDGADu+Ch+47roTO6nxl2aJfSYykmOhdJCew==", + "dev": true, + "license": "Unlicense", + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + }, + "peerDependenciesMeta": { + "@types/markdown-it": { + "optional": true + } + } + }, + "node_modules/markdown-it-container": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/markdown-it-container/-/markdown-it-container-4.0.0.tgz", + "integrity": "sha512-HaNccxUH0l7BNGYbFbjmGpf5aLHAMTinqRZQAEQbMr2cdD3z91Q6kIo1oUn1CQndkT03jat6ckrdRYuwwqLlQw==", + "dev": true, + "license": "MIT" + }, + "node_modules/markdown-it-github-alerts": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/markdown-it-github-alerts/-/markdown-it-github-alerts-1.0.1.tgz", + "integrity": "sha512-NNATF4QdoGI07hyCitoB2YqJ1YcNVCKT89ut2VtfFY9rkeFCXe/V2lOonKQLpJiq5DjiZZepf97BJx5xOjFIAw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "markdown-it": ">= 13.0.0" + } + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-3.0.4.tgz", + "integrity": "sha512-fwWK3Zq22l4XJJjx5mcBHGikIk+hJqO+32dPm0Jur7+rtG5x1scOjSfdT9ZKeWHPpsya/l0HHK4tDImBkTrtvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "PSF-2.0" + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz", + "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/mdurl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.1.0.tgz", + "integrity": "sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/moo": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", + "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/morphdom": { + "version": "2.7.8", + "resolved": "https://registry.npmjs.org/morphdom/-/morphdom-2.7.8.tgz", + "integrity": "sha512-D/fR4xgGUyVRbdMGU6Nejea1RFzYxYtyurG4Fbv2Fi/daKlWKuXGLOdXtl+3eIwL110cI2hz1ZojGICjjFLgTg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-retrieve-globals": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/node-retrieve-globals/-/node-retrieve-globals-6.0.1.tgz", + "integrity": "sha512-j0DeFuZ/Wg3VlklfbxUgZF/mdHMTEiEipBb3q0SpMMbHaV3AVfoUQF8UGxh1s/yjqO0TgRZd4Pi/x2yRqoQ4Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.14.1", + "acorn-walk": "^8.3.4", + "esm-import-transformer": "^3.0.3" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nunjucks": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", + "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "commander": "^5.1.0" + }, + "bin": { + "nunjucks-precompile": "bin/precompile" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "chokidar": "^3.3.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/nunjucks/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/on-finished": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.2.tgz", + "integrity": "sha512-ATWUv387i+nLD49EiDoOft5scf32Vsdz3ktrQJG9bi7jRFmaXTssvyvj1EmeKVVgvesbqUtvQQvaS2TpTukAZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pagefind": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.6.0.tgz", + "integrity": "sha512-yeOd9EA0TUE1PUfbQIluBxzPadBG5/0BV48FAxwMVLkhZcOvwCz6TX4/a/Ofz97pF9/FwwT7CVbTC97BfmI+LQ==", + "dev": true, + "license": "MIT", + "bin": { + "pagefind": "lib/runner/bin.cjs" + }, + "optionalDependencies": { + "@pagefind/darwin-arm64": "1.6.0", + "@pagefind/darwin-x64": "1.6.0", + "@pagefind/freebsd-x64": "1.6.0", + "@pagefind/linux-arm64": "1.6.0", + "@pagefind/linux-x64": "1.6.0", + "@pagefind/windows-arm64": "1.6.0", + "@pagefind/windows-x64": "1.6.0" + } + }, + "node_modules/parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver-compare": "^1.0.0" + } + }, + "node_modules/posthtml": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.7.tgz", + "integrity": "sha512-7Hc+IvlQ7hlaIfQFZnxlRl0jnpWq2qwibORBhQYIb0QbNtuicc5ZxvKkVT71HJ4Py1wSZ/3VR1r8LfkCtoCzhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "posthtml-parser": "^0.11.0", + "posthtml-render": "^3.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/posthtml-match-helper": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/posthtml-match-helper/-/posthtml-match-helper-2.0.3.tgz", + "integrity": "sha512-p9oJgTdMF2dyd7WE54QI1LvpBIkNkbSiiECKezNnDVYhGhD1AaOnAkw0Uh0y5TW+OHO8iBdSqnd8Wkpb6iUqmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "posthtml": "^0.16.6" + } + }, + "node_modules/posthtml-parser": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz", + "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "htmlparser2": "^7.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/posthtml-render": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz", + "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-json": "^2.0.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/semver": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.6.tgz", + "integrity": "sha512-9lpSSU4Mly3mrtruwLs1eLK6+gzbzqeBFwRNjIjE+9LuDpWdYctrQlQ3ej1Ilgi7gknpsTRnPoLP8DpCHqHIJw==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slugify": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.9.tgz", + "integrity": "sha512-vZ7rfeehZui7wQs438JXBckYLkIIdfHOXsaVEUMyS5fHo1483l1bMdo0EDSWYclY0yZKFOipDy4KHuKs6ssvdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ssri": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-11.0.0.tgz", + "integrity": "sha512-aZpUoMN/Jj2MqA4vMCeiKGnc/8SuSyHbGSBdgFbZxP8OJGF/lFkIuElzPxsN0q8TQQ+prw3P4EDfB3TBHHgfXw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/uc.micro": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-3.0.0.tgz", + "integrity": "sha512-U3PppEkleoTnIfi8BozMx3yju3qc/L6SwqWo2Sw+54PX+PX0q9I+r1Um5HCmqD7n9VDX5/v3vQH/AjA6deDdtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/urlpattern-polyfill": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz", + "integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ws": { + "version": "8.22.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.2.tgz", + "integrity": "sha512-/rEZ2ejown2N5gxVTHUu9bVVC7ttu0/Tgzf5hbTOixR3Fs7mbbXgn2DbdbcKgJOmpQMtrIUWivtYuVMnp/W/yA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..ac8c8d0 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "tactility-docs", + "private": true, + "type": "module", + "scripts": { + "start": "eleventy --serve", + "build": "eleventy" + }, + "devDependencies": { + "@11ty/eleventy": "^3.1.8", + "@11ty/eleventy-plugin-syntaxhighlight": "^5.0.2", + "@fontsource-variable/inter": "^5.3.0", + "@fontsource-variable/jetbrains-mono": "^5.3.0", + "markdown-it": "^15.0.3", + "markdown-it-anchor": "^10.1.0", + "markdown-it-container": "^4.0.0", + "markdown-it-github-alerts": "^1.0.1", + "pagefind": "^1.3.0" + } +}