Compare commits
33 Commits
module-plu
...
docs-build
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ae5e0fc4f | ||
|
|
0c1956adb7 | ||
|
|
10638d02f5 | ||
|
|
277bb4d199 | ||
|
|
71da1cfe64 | ||
|
|
dd4cf21aa4 | ||
|
|
02728eb3b5 | ||
|
|
f7509103d7 | ||
|
|
1d52e0d8ff | ||
|
|
7adf1612b7 | ||
|
|
2aa2f81896 | ||
|
|
2c63502d7a | ||
|
|
1fed0529b9 | ||
|
|
658116a581 | ||
|
|
b0300a70f2 | ||
|
|
5cfd48d41e | ||
|
|
61c66cd220 | ||
|
|
44786aaea4 | ||
|
|
37225eb514 | ||
|
|
9dd8396fa6 | ||
|
|
ab1e43f394 | ||
|
|
3f01b063fd | ||
|
|
a4c2363c6f | ||
|
|
81a7436a9b | ||
|
|
617125d4e4 | ||
|
|
9967046df3 | ||
|
|
6a684d3647 | ||
|
|
459aac171f | ||
|
|
d8b5e0e38b | ||
|
|
2003298bf2 | ||
|
|
db4b6fb58e | ||
|
|
fd4f236f63 | ||
|
|
219e0c5799 |
94
.claude/skills/docs-build/SKILL.md
Normal file
@@ -0,0 +1,94 @@
|
||||
---
|
||||
name: docs-build
|
||||
description: Build, preview, and orchestrate the Metabase docs site. Use when working on the Astro renderer under docs-build/, the mage tasks in mage.docs, the auto-generated content (env vars, config, API, CLI commands, analytics, country codes, SDK typedoc), versioned/release builds, or the page chrome (header, footer, search).
|
||||
allowed-tools: Read, Write, Edit, Grep, Bash, Glob
|
||||
---
|
||||
|
||||
# Docs build skill
|
||||
|
||||
This skill is about *building* the docs site. For *writing* docs prose, use the `docs-write` skill. For the user-facing developer guide (audience: humans editing pages), see `docs/developers-guide/docs.md`.
|
||||
|
||||
## Architecture
|
||||
|
||||
Markdown under `docs/` is rendered by a self-contained Astro app under `docs-build/` (entry route `src/pages/[...slug].astro`; Scalar API viewer at `src/pages/api.astro` over `docs/api.json`). Everything that crosses the repo — full builds, regenerating auto-derived content, building from a release branch via worktree — is orchestrated by `mage/src/mage/docs.clj`, registered as `docs-*` tasks in `bb.edn`. The `bun run docs:*` aliases in the root `package.json` are thin wrappers around those mage tasks.
|
||||
|
||||
## Decision tree: which command do I run?
|
||||
|
||||
| I want to... | Run | Notes |
|
||||
|---|---|---|
|
||||
| Edit a doc and see it live | `bun run docs:dev` | Hot reload. Does **not** regenerate auto-derived content — transcluded snippets render placeholders. |
|
||||
| Preview a page that transcludes generated content | `bun run docs:preview` | Calls `docs-build --preview --lazy`: regens only *missing* artifacts, then production build + preview server. |
|
||||
| Build the production site | `bun run docs:build` | Full SDK + OpenAPI rebuild from scratch. Multi-minute. |
|
||||
| Build a different release version | `./bin/mage docs-build-branch release-x.NN.x` | Builds via git worktree; output → `build/docs/v0.NN/`. |
|
||||
| Regenerate auto-derived markdown | `bun run docs:generate` | Updates committed files; commit the diff alongside the code change. |
|
||||
| Regenerate one slice | `./bin/mage docs-generate --env-vars` (or `--config` / `--api` / `--commands` / `--analytics` / `--country-codes`) | Same as above, scoped. |
|
||||
| Regenerate SDK typedoc | `bun run docs:generate:embedding` | Full SDK rebuild + typedoc. Pass `--pure` to skip the SDK rebuild. |
|
||||
| Kill an orphaned dev server | `bun run docs:dev:clean` | Astro pins port 4321; force-quit terminals reparent `node` to launchd. |
|
||||
| List every docs command | `bun run docs:help` | `./bin/mage <task> --help` for flag-level detail. |
|
||||
|
||||
## Generated artifacts
|
||||
|
||||
**The load-bearing distinction:** some auto-derived content is gitignored and lazily regenerated; the rest is committed and must be regenerated by hand.
|
||||
|
||||
| Path | Generator | Commit? |
|
||||
|---|---|---|
|
||||
| `docs/api.json` | `./bin/mage docs-generate --api` | gitignored; lazy-regen if missing |
|
||||
| `docs/embedding/sdk/api/snippets/index.md` | `bun run docs:generate:embedding` | gitignored; lazy-regen if missing |
|
||||
| `docs-build/public/embedding/sdk/api/index.html` | `bun run docs:generate:embedding` | gitignored; lazy-regen if missing |
|
||||
| `docs/embedding/eajs/snippets/` | `bun run docs:generate:embedding` | gitignored |
|
||||
| `docs/configuring-metabase/environment-variables.md` | `--env-vars` | committed |
|
||||
| `docs/configuring-metabase/config-template.md` | `--config` | committed |
|
||||
| `docs/installation-and-operation/commands.md` | `--commands` | committed |
|
||||
| `docs/usage-and-performance-tools/usage-analytics-reference.md` | `--analytics` | committed |
|
||||
| `docs/questions/visualizations/country-codes.md` | `--country-codes` | committed |
|
||||
|
||||
The "lazy-regen if missing" path runs through `mage.docs/artifact-present?` (`mage/src/mage/docs.clj:156`) — freshness is **existence only**, not source-mtime. If you edit an SDK type and want the typedoc refreshed, you must run `bun run docs:generate:embedding` explicitly; merely re-running the build won't pick up the change.
|
||||
|
||||
The committed files are **not** part of the lazy-regen path. If you add or rename a `defsetting`, run `docs:generate --env-vars` and commit the diff — otherwise the published docs drift from the code.
|
||||
|
||||
## Versioned builds
|
||||
|
||||
`mage.docs/base-path-for-branch` maps `release-x.NN.x` → `/docs/v0.NN`; everything else → `/docs/latest`. `docs-build-branch <branch>` builds via a git worktree at `__worktrees/docs-<slug>/`, rsyncs `dist/` into `build/docs/<base-tail>/`, removes the worktree on success, retains it on failure. `./bin/mage docs-clean-worktrees [--force]` lists or bulk-removes leftovers. **The build hard-resets the worktree to the resolved ref — uncommitted work inside it will be discarded.**
|
||||
|
||||
## Custom plugins
|
||||
|
||||
In `docs-build/src/plugins/` — each plugin has a `.test.mjs` next to it (the Node test runner glob is `src/plugins/**/*.test.mjs`):
|
||||
|
||||
- `remark-include-file.mjs` — `{% include_file "path" %}` transclusion (syntax documented in the dev guide). Markdown is spliced inline; source files render as syntax-highlighted code fences. Snippet markers: `<!-- [<snippet NAME>] -->` for markdown, `// [<snippet NAME>]` for source.
|
||||
- `remark-docs-version.mjs` — substitutes `{SAMPLE_APP_BRANCH}` (`<NN>-stable` on release branches, `master` elsewhere).
|
||||
- `rehype-internal-links.mjs` — rewrites relative `.md` paths to the right URL under `DOCS_BASE_PATH`.
|
||||
- `rehype-blockquote-classes.mjs` — tags callout blockquotes by their leading `> **Marker:**`. Recognized markers (colon optional, case-insensitive): `Tip`/`Hint` → `.tip`, `Note` → `.note`, `Warning`/`Caution` → `.warning`, `Danger` → `.danger`, `Plans` → `.plans-callout`. Blockquotes that already carry a `*-callout` class are left untouched so author overrides win.
|
||||
|
||||
`nav.yml` parsing lives in `docs-build/src/lib/nav.ts` and is `?raw`-imported by Vite so the file is inlined at build time.
|
||||
|
||||
## Header and footer
|
||||
|
||||
Markup: `docs-build/src/data/{header,footer}.html` (raw-imported by `Header.astro` / `Footer.astro`). Styles: `docs-build/src/styles/chrome.css`. Client behavior (mobile hamburger, GitHub stars, promo-banner dismiss, Inkeep search): `docs-build/public/js/`. Edit those files directly.
|
||||
|
||||
## Pre-PR checklist
|
||||
|
||||
```
|
||||
bun run docs:check # Astro/TS type check + nav.yml reference validator
|
||||
bun run docs:test # remark + rehype plugin unit tests (node --test)
|
||||
bun run lint-docs-links # verifies in-product docsUrl() references resolve
|
||||
./bin/test-agent :only '[mage.docs-test]' # if you touched mage.docs
|
||||
```
|
||||
|
||||
`docs:check` is the closest thing to a build-time linter for the markdown layer — frontmatter errors, plugin failures, broken `{% include_file %}` targets, and misspelled `nav.yml` slugs all surface here.
|
||||
|
||||
## Common pitfalls
|
||||
|
||||
- **Transclusions render placeholders in `docs:dev`** — by design, but easy to forget. If a page `{% include_file %}`s the SDK typedoc and looks broken, run `docs:preview` once to confirm it's not just stale generated content.
|
||||
- **SDK type changes don't appear after a rebuild** — lazy-regen is existence-based. Existing typedoc output won't refresh on its own; run `bun run docs:generate:embedding` to update it.
|
||||
- **Plugin edits look like no-ops** — Astro caches plugin output across `dev` restarts. Run `bun run docs:dev:clean` to clear the caches, then `bun run docs:dev`.
|
||||
- **Port 4321 is busy** — Astro pins it (`strictPort: true`). On macOS, force-quitting a terminal reparents `node` to launchd. `bun run docs:dev:clean` kills the orphan.
|
||||
- **Worktree leftover** — `docs-build-branch` refuses to overwrite an existing `__worktrees/docs-<slug>/`. Run `./bin/mage docs-clean-worktrees --force` to bulk-remove, or pass `--worktree-dir` to use a different path. Anything uncommitted *inside* a worktree is discarded by the next build's hard reset.
|
||||
- **Editing the dev-guide command table directly fails CI** — `docs/developers-guide/docs.md` contains a `<!-- BEGIN docs-help-table --> … <!-- END docs-help-table -->` block generated from `help-rows` in `mage/src/mage/docs.clj`. To add/rename a command, edit `help-rows`, then run `./bin/mage docs-help --write` to regenerate the block. `mage.docs-test/dev-guide-table-in-sync-test` asserts the two stay in sync, so hand-edits silently drift until CI catches them.
|
||||
|
||||
## Where things live
|
||||
|
||||
- **Markdown content**: `docs/`
|
||||
- **Astro app**: `docs-build/` — `astro.config.mjs` (markdown pipeline, base path), `nav.yml` (sidebar + topbar), `src/pages/[...slug].astro` and `src/pages/api.astro` (routes), `src/pages/llms.txt.ts` + `llms-[section]-full.txt.ts` (LLM index endpoints, emitted by Astro — no post-build step), `src/lib/nav.ts` (nav parser), `src/layouts/DocsLayout.astro`, `src/components/`, `src/plugins/`, `src/data/{header,footer}.html`, `src/styles/chrome.css`, `scripts/validate-nav.mjs` (run by `docs:check`).
|
||||
- **Orchestrator**: `mage/src/mage/docs.clj`; task registration in `bb.edn` (search `docs-`).
|
||||
- **Tests**: `mage/test/mage/docs_test.clj`, `docs-build/src/plugins/*.test.mjs`.
|
||||
- **User-facing dev guide**: `docs/developers-guide/docs.md`.
|
||||
4
.github/workflows/docs-generate-base.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.automation_token }}
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
uses: ./.github/actions/prepare-backend
|
||||
|
||||
- name: Set up Babashka
|
||||
uses: turtlequeue/setup-babashka@d78ec6570aea3b614bc695cafcceb82eb45c2af9 # v1.8.0
|
||||
uses: turtlequeue/setup-babashka@2d4df498c7a578b4f3e906283f9af913590bdec7 # v1.7.0
|
||||
with:
|
||||
babashka-version: 1.12.197
|
||||
|
||||
|
||||
6
.github/workflows/docs-links.yml
vendored
@@ -13,21 +13,21 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
sparse-checkout: |
|
||||
docs
|
||||
.lychee
|
||||
|
||||
- name: Cache lychee
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/lychee
|
||||
key: lychee-${{ runner.os }}-${{ hashFiles('.lychee/config.toml') }}
|
||||
|
||||
- name: Run link checker
|
||||
id: link-check
|
||||
uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2.8.0
|
||||
uses: lycheeverse/lychee-action@f613c4a64e50d792e0b31ec34bbcbba12263c6a6 # v2.3.0
|
||||
with:
|
||||
fail: true
|
||||
args: docs --config ./.lychee/config.toml --offline
|
||||
|
||||
4
.github/workflows/docs-release-generate.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
target_branch:
|
||||
description: "Target branch for documentation update"
|
||||
required: false
|
||||
default: "release-x.62.x"
|
||||
default: "release-x.60.x"
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
@@ -17,6 +17,6 @@ jobs:
|
||||
name: Call documentation update for release branch
|
||||
uses: ./.github/workflows/docs-generate-base.yml
|
||||
with:
|
||||
target_branch: ${{ inputs.target_branch || 'release-x.62.x' }}
|
||||
target_branch: ${{ inputs.target_branch || 'release-x.60.x' }}
|
||||
secrets:
|
||||
automation_token: ${{ secrets.METABASE_AUTOMATION_USER_TOKEN }}
|
||||
|
||||
5
.gitignore
vendored
@@ -48,8 +48,12 @@ cypress.env.json
|
||||
**/node_modules
|
||||
/docs/**/package.json
|
||||
/docs/**/yarn.lock
|
||||
# Generated by `bun run docs:generate:embedding` (typedoc).
|
||||
/docs/embedding/sdk/api/
|
||||
/docs/embedding/eajs/snippets/
|
||||
/docs-build/public/embedding/sdk/api/
|
||||
# Generated by `bun run docs:generate -- --api` (clojure -M:ee:doc api-documentation).
|
||||
/docs/api.json
|
||||
/frontend/test/snapshots/*
|
||||
/e2e/snapshots/*
|
||||
/e2e/tmp/*
|
||||
@@ -189,5 +193,6 @@ __pycache__/
|
||||
.config/wt.toml
|
||||
.claude/worktrees
|
||||
.claude/scheduled_tasks.lock
|
||||
/__worktrees/
|
||||
.playwright-mcp
|
||||
.ci_reports/
|
||||
|
||||
@@ -3,18 +3,19 @@ const commonConfig = require("./typedoc.common.config.js");
|
||||
/** @type {import("typedoc").TypeDocOptions} */
|
||||
const htmlConfig = {
|
||||
...commonConfig,
|
||||
plugin: [...commonConfig.plugin, "./typedoc-plugin-frontmatter.js"],
|
||||
out: "../docs/embedding/sdk/api",
|
||||
out: "../docs-build/public/embedding/sdk/api",
|
||||
favicon: "../resources/frontend_client/favicon.ico",
|
||||
customJs: "./page-custom-logic.js",
|
||||
customCss: "./page-custom-styles.css",
|
||||
pretty: true,
|
||||
visibilityFilters: {},
|
||||
frontmatterGlobals: {
|
||||
title: "Embedded analytics SDK documentation",
|
||||
layout: "docs-api",
|
||||
},
|
||||
hideGenerator: true,
|
||||
// Override commonConfig.replaceText: drop the Jekyll Liquid include that
|
||||
// it injects into <meta name="description"> (would render as literal
|
||||
// `{% include %}` text under Astro), keep the data-refl scrubber.
|
||||
replaceText: {
|
||||
' data-refl="[^"]*"': "",
|
||||
},
|
||||
customFooterHtml:
|
||||
'<script type="text/javascript" src="/gdpr-cookie-notice/dist/script.js"></script>' +
|
||||
'<script type="text/javascript" src="/js/cookie-consent.js"></script>' +
|
||||
|
||||
63
bb.edn
@@ -186,6 +186,69 @@
|
||||
[:string {:name "description" :description "Lowercase alphanumeric/underscore name (e.g., mq_indexes)"}]]
|
||||
:task (task! (new-migration/new-migration parsed))}
|
||||
|
||||
;;; Docs build / generate
|
||||
|
||||
docs-build
|
||||
{:doc "Build the docs site to docs-build/dist/ (with --preview, run the Astro preview server after)"
|
||||
:examples [["./bin/mage docs-build" "Build the site for the current branch's base path"]
|
||||
["./bin/mage docs-build --preview --lazy" "Inner-loop preview: lazy-regen build + preview server"]
|
||||
["./bin/mage docs-build --base-path /docs/v0.55" "Build with an explicit base path"]
|
||||
["DOCS_BASE_PATH=/docs/v0.55 ./bin/mage docs-build" "Same, via environment variable"]]
|
||||
:requires [[mage.docs :as docs]]
|
||||
:options [[nil "--preview" "After building, start the Astro preview server"]
|
||||
[nil "--lazy" "Skip the full SDK + OpenAPI rebuild; regenerate only missing artifacts"]
|
||||
[nil "--base-path PATH" "URL prefix (default: /docs/latest, or /docs/v0.NN inferred from a release-x.NN.x branch)"]
|
||||
[nil "--site-url URL" "Absolute site URL for canonical/sitemap"]]
|
||||
:task (task! (docs/build parsed))}
|
||||
|
||||
docs-build-branch
|
||||
{:doc "Build the docs site for an arbitrary git branch via a worktree"
|
||||
:examples [["./bin/mage docs-build-branch release-x.55.x" "Build v0.55 docs into build/docs/v0.55/"]
|
||||
["./bin/mage docs-build-branch master" "Build master docs into build/docs/latest/"]]
|
||||
:requires [[mage.docs :as docs]]
|
||||
:arg-schema [:tuple [:string {:name "branch"}]]
|
||||
:task (task! (docs/build-branch parsed))}
|
||||
|
||||
docs-generate
|
||||
{:doc "Regenerate auto-derived backend docs (env vars, config, REST API, CLI commands, usage analytics)"
|
||||
:examples [["./bin/mage docs-generate" "Run every generator (default)"]
|
||||
["./bin/mage docs-generate --api" "Regenerate only the REST API reference"]
|
||||
["./bin/mage docs-generate --env-vars --config" "Regenerate env vars + config template"]]
|
||||
:requires [[mage.docs :as docs]]
|
||||
:options [[nil "--env-vars" "Generate env vars doc -> docs/configuring-metabase/environment-variables.md"]
|
||||
[nil "--config" "Generate config template doc -> docs/configuring-metabase/config-template.md"]
|
||||
[nil "--api" "Generate REST API doc -> docs/api.json"]
|
||||
[nil "--commands" "Generate CLI command doc -> docs/installation-and-operation/commands.md"]
|
||||
[nil "--analytics" "Generate usage analytics doc -> docs/usage-and-performance-tools/usage-analytics-reference.md"]
|
||||
[nil "--country-codes" "Generate country codes reference -> docs/questions/visualizations/country-codes.md"]]
|
||||
:task (task! (docs/generate parsed))}
|
||||
|
||||
docs-generate-embedding
|
||||
{:doc "Regenerate embedding SDK and Embed.js typedoc reference docs"
|
||||
:examples [["./bin/mage docs-generate-embedding" "Full SDK rebuild + typedoc"]
|
||||
["./bin/mage docs-generate-embedding --pure" "Typedoc only (SDK d.ts files already current)"]]
|
||||
:requires [[mage.docs :as docs]]
|
||||
:options [[nil "--pure" "Skip rebuilding the SDK package (typedoc-only)"]]
|
||||
:task (task! (docs/generate-embedding parsed))}
|
||||
|
||||
docs-clean-worktrees
|
||||
{:doc "List or remove leftover __worktrees/docs-* directories from docs-build-branch runs"
|
||||
:examples [["./bin/mage docs-clean-worktrees" "List leftover worktrees (dry-run)"]
|
||||
["./bin/mage docs-clean-worktrees --force" "Remove all leftover worktrees"]]
|
||||
:requires [[mage.docs :as docs]]
|
||||
:options [[nil "--force" "Actually remove the worktrees (default: list only)"]]
|
||||
:task (task! (docs/clean-worktrees parsed))}
|
||||
|
||||
docs-help
|
||||
{:doc "Print the list of docs commands (bun aliases and what they do)"
|
||||
:examples [["./bin/mage docs-help" "List all docs:* commands with descriptions"]
|
||||
["./bin/mage docs-help --format md" "Print the dev-guide markdown table to stdout"]
|
||||
["./bin/mage docs-help --write" "Rewrite the bracketed table in docs/developers-guide/docs.md"]]
|
||||
:requires [[mage.docs :as docs]]
|
||||
:options [[nil "--format FMT" "Output format. Omit for terminal (default), 'md' for the dev-guide markdown table."]
|
||||
[nil "--write" "Rewrite the docs-help-table block in docs/developers-guide/docs.md in place"]]
|
||||
:task (task! (docs/print-help! parsed))}
|
||||
|
||||
jar-download
|
||||
{:doc "Download (and optionally run) jar for a metabase version or branch"
|
||||
:examples [["./bin/mage jar-download 50" "Download the latest enterprise version of release 50 to ~/path/to/my/metabase/jars"]
|
||||
|
||||
5
docs-build/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.astro/
|
||||
*.log
|
||||
.DS_Store
|
||||
23
docs-build/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# docs-build
|
||||
|
||||
Astro renderer for [`../docs/`](../docs). The markdown lives next door; this
|
||||
directory is everything that turns it into a static site.
|
||||
|
||||
See [`../docs/developers-guide/docs.md`](../docs/developers-guide/docs.md) for
|
||||
the full guide: previewing, building, generators, multi-version builds, and
|
||||
where each piece lives.
|
||||
|
||||
For most things you don't need to be in this directory — the build is
|
||||
orchestrated from the repo root via mage:
|
||||
|
||||
```
|
||||
bun run docs:help # list every docs command with a one-line description
|
||||
bun run docs:dev # hot-reload preview (skips auto-generated content)
|
||||
bun run docs:preview # full preview with lazy regen of generated artifacts
|
||||
bun run docs:build # production build (what CI runs)
|
||||
bun run docs:check # type check + nav.yml validation
|
||||
bun run docs:test # unit tests for the custom remark/rehype plugins
|
||||
```
|
||||
|
||||
For agents working in this area, see
|
||||
[`.claude/skills/docs-build/SKILL.md`](../.claude/skills/docs-build/SKILL.md).
|
||||
66
docs-build/astro.config.mjs
Normal file
@@ -0,0 +1,66 @@
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { defineConfig } from "astro/config";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import rehypeSlug from "rehype-slug";
|
||||
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
||||
import rehypeExternalLinks from "rehype-external-links";
|
||||
import { remarkDocsVersion } from "./src/plugins/remark-docs-version.mjs";
|
||||
import { remarkIncludeFile } from "./src/plugins/remark-include-file.mjs";
|
||||
import { rehypeBlockquoteClasses } from "./src/plugins/rehype-blockquote-classes.mjs";
|
||||
import { rehypeInternalLinks } from "./src/plugins/rehype-internal-links.mjs";
|
||||
import { loadRedirects } from "./src/redirects.mjs";
|
||||
|
||||
const base = process.env.DOCS_BASE_PATH ?? "/docs/latest";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const docsDir = path.resolve(__dirname, "../docs");
|
||||
// Sidecar file for redirects whose source path is outside `/docs/`
|
||||
// (marketing-site URLs). Lives under public/ so Astro copies it into
|
||||
// dist/ at build time; the delivery hop consumes it to wire those into
|
||||
// the marketing site's redirect layer.
|
||||
const marketingSidecar = path.resolve(__dirname, "public/marketing-redirects.json");
|
||||
const { redirects, inDocsCount, marketingCount } = loadRedirects({
|
||||
docsDir,
|
||||
sidecarPath: marketingSidecar,
|
||||
base,
|
||||
});
|
||||
console.log(
|
||||
`[redirects] ${inDocsCount} in-docs redirects, ${marketingCount} marketing-site redirects (sidecar: public/marketing-redirects.json)`,
|
||||
);
|
||||
|
||||
export default defineConfig({
|
||||
site: process.env.DOCS_SITE_URL ?? "https://www.metabase.com",
|
||||
base,
|
||||
outDir: "./dist",
|
||||
trailingSlash: "ignore",
|
||||
redirects,
|
||||
server: {
|
||||
port: 4321,
|
||||
},
|
||||
vite: {
|
||||
server: {
|
||||
// Hard-fail if 4321 is in use instead of silently bumping to 4322 — keeps
|
||||
// orphaned `astro dev` processes from stacking unnoticed.
|
||||
strictPort: true,
|
||||
},
|
||||
},
|
||||
build: {
|
||||
format: "file",
|
||||
},
|
||||
markdown: {
|
||||
remarkPlugins: [remarkGfm, remarkDocsVersion, remarkIncludeFile],
|
||||
rehypePlugins: [
|
||||
rehypeSlug,
|
||||
[rehypeAutolinkHeadings, { behavior: "wrap" }],
|
||||
[rehypeInternalLinks, { base }],
|
||||
[rehypeExternalLinks, { target: "_blank", rel: ["noopener", "noreferrer"] }],
|
||||
rehypeBlockquoteClasses,
|
||||
],
|
||||
shikiConfig: {
|
||||
// Light theme to match the live docs (white code blocks with a soft shadow).
|
||||
theme: "github-light",
|
||||
wrap: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
888
docs-build/bun.lock
Normal file
@@ -0,0 +1,888 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "@metabase/docs-build",
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.9.9",
|
||||
"astro": "^6.3.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"mdast-util-from-markdown": "^2.0.2",
|
||||
"mdast-util-gfm": "^3.0.0",
|
||||
"micromark-extension-gfm": "^3.0.0",
|
||||
"rehype-autolink-headings": "^7.1.0",
|
||||
"rehype-external-links": "^3.0.0",
|
||||
"rehype-slug": "^6.0.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"typescript": "^6.0.3",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^25.8.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@astrojs/check": ["@astrojs/check@0.9.9", "", { "dependencies": { "@astrojs/language-server": "^2.16.7", "chokidar": "^4.0.3", "kleur": "^4.1.5", "yargs": "^17.7.2" }, "peerDependencies": { "typescript": "^5.0.0 || ^6.0.0" }, "bin": { "astro-check": "bin/astro-check.js" } }, "sha512-A5UW8uIuErLWEoRQvzgXpO1gTjUFtK8r7nU2Z7GewAMxUb7bPvpk11qaKKgxqXlHJWlAvaaxy+Xg28A6bmQ1Tg=="],
|
||||
|
||||
"@astrojs/compiler": ["@astrojs/compiler@4.0.0", "", {}, "sha512-eouss7G8ygdZqHuke033VMcVw5HTZUu+PXd/h06DGDUg/jt5btPYPqh66ENWw/mU78rBrf/oeC4oqoBwMtDMNA=="],
|
||||
|
||||
"@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.9.0", "", { "dependencies": { "picomatch": "^4.0.4" } }, "sha512-GdYkzR26re8izmyYlBqf4z2s7zNngmWLFuxw0UKiPNqHraZGS6GKWIwSHgS22RDlu2ePFJ8bzmpBcUszut/SDg=="],
|
||||
|
||||
"@astrojs/language-server": ["@astrojs/language-server@2.16.8", "", { "dependencies": { "@astrojs/compiler": "^2.13.1", "@astrojs/yaml2ts": "^0.2.3", "@jridgewell/sourcemap-codec": "^1.5.5", "@volar/kit": "~2.4.28", "@volar/language-core": "~2.4.28", "@volar/language-server": "~2.4.28", "@volar/language-service": "~2.4.28", "muggle-string": "^0.4.1", "tinyglobby": "^0.2.16", "volar-service-css": "0.0.70", "volar-service-emmet": "0.0.70", "volar-service-html": "0.0.70", "volar-service-prettier": "0.0.70", "volar-service-typescript": "0.0.70", "volar-service-typescript-twoslash-queries": "0.0.70", "volar-service-yaml": "0.0.70", "vscode-html-languageservice": "^5.6.2", "vscode-uri": "^3.1.0" }, "peerDependencies": { "prettier": "^3.0.0", "prettier-plugin-astro": ">=0.11.0" }, "optionalPeers": ["prettier", "prettier-plugin-astro"], "bin": { "astro-ls": "bin/nodeServer.js" } }, "sha512-yg1pZF6hs9FaKr2fgXMOGbW7pDLgFexFjuhWilPAc8VybTU+WSnbfbhYaUL1exm6dAK4sM3aKXGcfVwss+HXbg=="],
|
||||
|
||||
"@astrojs/markdown-remark": ["@astrojs/markdown-remark@7.1.1", "", { "dependencies": { "@astrojs/internal-helpers": "0.9.0", "@astrojs/prism": "4.0.1", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "js-yaml": "^4.1.1", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "retext-smartypants": "^6.2.0", "shiki": "^4.0.0", "smol-toml": "^1.6.0", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.1.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-C6e9BnLGlbdv6bV8MYGeHpHxsUHrCrB4OuRLqi5LI7oiBVcBcqfUN06zpwFQdHgV48QCCrMmLpyqBr7VqC+swA=="],
|
||||
|
||||
"@astrojs/prism": ["@astrojs/prism@4.0.1", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-nksZQVjlferuWzhPsBpQ1JE5XuKAf1id1/9Hj4a9KG4+ofrlzxUUwX4YGQF/SuDiuiGKEnzopGOt38F3AnVWsQ=="],
|
||||
|
||||
"@astrojs/telemetry": ["@astrojs/telemetry@3.3.2", "", { "dependencies": { "ci-info": "^4.4.0", "dset": "^3.1.4", "is-docker": "^4.0.0", "is-wsl": "^3.1.1", "which-pm-runs": "^1.1.0" } }, "sha512-j8DNruA8ors99Al39RYZPJK4DC1bKkoNm93mAMuBhY9TCNC4R8n1q7ovFnJ5qhGh5Lsh7pa1gpQVpYpsJPeTHQ=="],
|
||||
|
||||
"@astrojs/yaml2ts": ["@astrojs/yaml2ts@0.2.3", "", { "dependencies": { "yaml": "^2.8.2" } }, "sha512-PJzRmgQzUxI2uwpdX2lXSHtP4G8ocp24/t+bZyf5Fy0SZLSF9f9KXZoMlFM/XCGue+B0nH/2IZ7FpBYQATBsCg=="],
|
||||
|
||||
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
|
||||
|
||||
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
|
||||
|
||||
"@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="],
|
||||
|
||||
"@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
|
||||
|
||||
"@capsizecss/unpack": ["@capsizecss/unpack@4.0.0", "", { "dependencies": { "fontkitten": "^1.0.0" } }, "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA=="],
|
||||
|
||||
"@clack/core": ["@clack/core@1.3.0", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-xJPHpAmEQUBrXSLx0gF+q5K/IyihXpsHZcha+jB+tyahsKRK3Dxo4D0coZDewHo12NhiuzC3dTtMPbm53GEAAA=="],
|
||||
|
||||
"@clack/prompts": ["@clack/prompts@1.3.0", "", { "dependencies": { "@clack/core": "1.3.0", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-GgcWwRCs/xPtaqlMy8qRhPnZf9vlWcWZNHAitnVQ3yk7JmSralSiq5q07yaffYE8SogtDm7zFeKccx1QNVARpw=="],
|
||||
|
||||
"@emmetio/abbreviation": ["@emmetio/abbreviation@2.3.3", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA=="],
|
||||
|
||||
"@emmetio/css-abbreviation": ["@emmetio/css-abbreviation@2.1.8", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw=="],
|
||||
|
||||
"@emmetio/css-parser": ["@emmetio/css-parser@0.4.1", "", { "dependencies": { "@emmetio/stream-reader": "^2.2.0", "@emmetio/stream-reader-utils": "^0.1.0" } }, "sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ=="],
|
||||
|
||||
"@emmetio/html-matcher": ["@emmetio/html-matcher@1.3.0", "", { "dependencies": { "@emmetio/scanner": "^1.0.0" } }, "sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ=="],
|
||||
|
||||
"@emmetio/scanner": ["@emmetio/scanner@1.0.4", "", {}, "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA=="],
|
||||
|
||||
"@emmetio/stream-reader": ["@emmetio/stream-reader@2.2.0", "", {}, "sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw=="],
|
||||
|
||||
"@emmetio/stream-reader-utils": ["@emmetio/stream-reader-utils@0.1.0", "", {}, "sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A=="],
|
||||
|
||||
"@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="],
|
||||
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.7", "", { "os": "android", "cpu": "arm64" }, "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ=="],
|
||||
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.27.7", "", { "os": "android", "cpu": "x64" }, "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg=="],
|
||||
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw=="],
|
||||
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ=="],
|
||||
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.7", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w=="],
|
||||
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ=="],
|
||||
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.7", "", { "os": "linux", "cpu": "arm" }, "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA=="],
|
||||
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A=="],
|
||||
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.7", "", { "os": "linux", "cpu": "ia32" }, "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg=="],
|
||||
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q=="],
|
||||
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw=="],
|
||||
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ=="],
|
||||
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ=="],
|
||||
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw=="],
|
||||
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.7", "", { "os": "linux", "cpu": "x64" }, "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA=="],
|
||||
|
||||
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w=="],
|
||||
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.7", "", { "os": "none", "cpu": "x64" }, "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw=="],
|
||||
|
||||
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.7", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A=="],
|
||||
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.7", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg=="],
|
||||
|
||||
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw=="],
|
||||
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.7", "", { "os": "sunos", "cpu": "x64" }, "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA=="],
|
||||
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA=="],
|
||||
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.7", "", { "os": "win32", "cpu": "ia32" }, "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw=="],
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="],
|
||||
|
||||
"@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="],
|
||||
|
||||
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="],
|
||||
|
||||
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="],
|
||||
|
||||
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="],
|
||||
|
||||
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="],
|
||||
|
||||
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="],
|
||||
|
||||
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="],
|
||||
|
||||
"@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="],
|
||||
|
||||
"@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="],
|
||||
|
||||
"@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="],
|
||||
|
||||
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="],
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="],
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="],
|
||||
|
||||
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="],
|
||||
|
||||
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="],
|
||||
|
||||
"@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="],
|
||||
|
||||
"@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="],
|
||||
|
||||
"@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="],
|
||||
|
||||
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="],
|
||||
|
||||
"@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="],
|
||||
|
||||
"@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="],
|
||||
|
||||
"@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="],
|
||||
|
||||
"@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="],
|
||||
|
||||
"@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="],
|
||||
|
||||
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="],
|
||||
|
||||
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||
|
||||
"@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="],
|
||||
|
||||
"@parcel/watcher": ["@parcel/watcher@2.5.6", "", { "dependencies": { "detect-libc": "^2.0.3", "is-glob": "^4.0.3", "node-addon-api": "^7.0.0", "picomatch": "^4.0.3" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.6", "@parcel/watcher-darwin-arm64": "2.5.6", "@parcel/watcher-darwin-x64": "2.5.6", "@parcel/watcher-freebsd-x64": "2.5.6", "@parcel/watcher-linux-arm-glibc": "2.5.6", "@parcel/watcher-linux-arm-musl": "2.5.6", "@parcel/watcher-linux-arm64-glibc": "2.5.6", "@parcel/watcher-linux-arm64-musl": "2.5.6", "@parcel/watcher-linux-x64-glibc": "2.5.6", "@parcel/watcher-linux-x64-musl": "2.5.6", "@parcel/watcher-win32-arm64": "2.5.6", "@parcel/watcher-win32-ia32": "2.5.6", "@parcel/watcher-win32-x64": "2.5.6" } }, "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ=="],
|
||||
|
||||
"@parcel/watcher-android-arm64": ["@parcel/watcher-android-arm64@2.5.6", "", { "os": "android", "cpu": "arm64" }, "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A=="],
|
||||
|
||||
"@parcel/watcher-darwin-arm64": ["@parcel/watcher-darwin-arm64@2.5.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA=="],
|
||||
|
||||
"@parcel/watcher-darwin-x64": ["@parcel/watcher-darwin-x64@2.5.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg=="],
|
||||
|
||||
"@parcel/watcher-freebsd-x64": ["@parcel/watcher-freebsd-x64@2.5.6", "", { "os": "freebsd", "cpu": "x64" }, "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng=="],
|
||||
|
||||
"@parcel/watcher-linux-arm-glibc": ["@parcel/watcher-linux-arm-glibc@2.5.6", "", { "os": "linux", "cpu": "arm" }, "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ=="],
|
||||
|
||||
"@parcel/watcher-linux-arm-musl": ["@parcel/watcher-linux-arm-musl@2.5.6", "", { "os": "linux", "cpu": "arm" }, "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg=="],
|
||||
|
||||
"@parcel/watcher-linux-arm64-glibc": ["@parcel/watcher-linux-arm64-glibc@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA=="],
|
||||
|
||||
"@parcel/watcher-linux-arm64-musl": ["@parcel/watcher-linux-arm64-musl@2.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA=="],
|
||||
|
||||
"@parcel/watcher-linux-x64-glibc": ["@parcel/watcher-linux-x64-glibc@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ=="],
|
||||
|
||||
"@parcel/watcher-linux-x64-musl": ["@parcel/watcher-linux-x64-musl@2.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg=="],
|
||||
|
||||
"@parcel/watcher-win32-arm64": ["@parcel/watcher-win32-arm64@2.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q=="],
|
||||
|
||||
"@parcel/watcher-win32-ia32": ["@parcel/watcher-win32-ia32@2.5.6", "", { "os": "win32", "cpu": "ia32" }, "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g=="],
|
||||
|
||||
"@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw=="],
|
||||
|
||||
"@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="],
|
||||
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.60.3", "", { "os": "android", "cpu": "arm" }, "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw=="],
|
||||
|
||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.60.3", "", { "os": "android", "cpu": "arm64" }, "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw=="],
|
||||
|
||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.60.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g=="],
|
||||
|
||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.60.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw=="],
|
||||
|
||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.60.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ=="],
|
||||
|
||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.60.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.60.3", "", { "os": "linux", "cpu": "arm" }, "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.60.3", "", { "os": "linux", "cpu": "arm" }, "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.60.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.60.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg=="],
|
||||
|
||||
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.60.3", "", { "os": "linux", "cpu": "none" }, "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA=="],
|
||||
|
||||
"@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.60.3", "", { "os": "linux", "cpu": "none" }, "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg=="],
|
||||
|
||||
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.60.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ=="],
|
||||
|
||||
"@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.60.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.60.3", "", { "os": "linux", "cpu": "none" }, "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.60.3", "", { "os": "linux", "cpu": "none" }, "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ=="],
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.60.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.60.3", "", { "os": "linux", "cpu": "x64" }, "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.60.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA=="],
|
||||
|
||||
"@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.60.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q=="],
|
||||
|
||||
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.60.3", "", { "os": "none", "cpu": "arm64" }, "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg=="],
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.60.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg=="],
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.60.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.60.3", "", { "os": "win32", "cpu": "x64" }, "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.60.3", "", { "os": "win32", "cpu": "x64" }, "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA=="],
|
||||
|
||||
"@shikijs/core": ["@shikijs/core@4.0.2", "", { "dependencies": { "@shikijs/primitive": "4.0.2", "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw=="],
|
||||
|
||||
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag=="],
|
||||
|
||||
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg=="],
|
||||
|
||||
"@shikijs/langs": ["@shikijs/langs@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2" } }, "sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg=="],
|
||||
|
||||
"@shikijs/primitive": ["@shikijs/primitive@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw=="],
|
||||
|
||||
"@shikijs/themes": ["@shikijs/themes@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2" } }, "sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA=="],
|
||||
|
||||
"@shikijs/types": ["@shikijs/types@4.0.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg=="],
|
||||
|
||||
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
|
||||
|
||||
"@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="],
|
||||
|
||||
"@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
|
||||
|
||||
"@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="],
|
||||
|
||||
"@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
|
||||
|
||||
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
|
||||
|
||||
"@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="],
|
||||
|
||||
"@types/node": ["@types/node@25.8.0", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-TCFSk8IZh+iLX1xtksoBVtdmgL+1IX0fC9BeU4QqFSuNdN/K+HUlhqOzEmSYYpZUVsLYcPqc9KX+60iDuninSQ=="],
|
||||
|
||||
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
||||
|
||||
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.1", "", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="],
|
||||
|
||||
"@volar/kit": ["@volar/kit@2.4.28", "", { "dependencies": { "@volar/language-service": "2.4.28", "@volar/typescript": "2.4.28", "typesafe-path": "^0.2.2", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "typescript": "*" } }, "sha512-cKX4vK9dtZvDRaAzeoUdaAJEew6IdxHNCRrdp5Kvcl6zZOqb6jTOfk3kXkIkG3T7oTFXguEMt5+9ptyqYR84Pg=="],
|
||||
|
||||
"@volar/language-core": ["@volar/language-core@2.4.28", "", { "dependencies": { "@volar/source-map": "2.4.28" } }, "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ=="],
|
||||
|
||||
"@volar/language-server": ["@volar/language-server@2.4.28", "", { "dependencies": { "@volar/language-core": "2.4.28", "@volar/language-service": "2.4.28", "@volar/typescript": "2.4.28", "path-browserify": "^1.0.1", "request-light": "^0.7.0", "vscode-languageserver": "^9.0.1", "vscode-languageserver-protocol": "^3.17.5", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" } }, "sha512-NqcLnE5gERKuS4PUFwlhMxf6vqYo7hXtbMFbViXcbVkbZ905AIVWhnSo0ZNBC2V127H1/2zP7RvVOVnyITFfBw=="],
|
||||
|
||||
"@volar/language-service": ["@volar/language-service@2.4.28", "", { "dependencies": { "@volar/language-core": "2.4.28", "vscode-languageserver-protocol": "^3.17.5", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" } }, "sha512-Rh/wYCZJrI5vCwMk9xyw/Z+MsWxlJY1rmMZPsxUoJKfzIRjS/NF1NmnuEcrMbEVGja00aVpCsInJfixQTMdvLw=="],
|
||||
|
||||
"@volar/source-map": ["@volar/source-map@2.4.28", "", {}, "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ=="],
|
||||
|
||||
"@volar/typescript": ["@volar/typescript@2.4.28", "", { "dependencies": { "@volar/language-core": "2.4.28", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw=="],
|
||||
|
||||
"@vscode/emmet-helper": ["@vscode/emmet-helper@2.11.0", "", { "dependencies": { "emmet": "^2.4.3", "jsonc-parser": "^2.3.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.15.1", "vscode-uri": "^3.0.8" } }, "sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw=="],
|
||||
|
||||
"@vscode/l10n": ["@vscode/l10n@0.0.18", "", {}, "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ=="],
|
||||
|
||||
"ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="],
|
||||
|
||||
"ajv-draft-04": ["ajv-draft-04@1.0.0", "", { "peerDependencies": { "ajv": "^8.5.0" }, "optionalPeers": ["ajv"] }, "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw=="],
|
||||
|
||||
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
|
||||
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
|
||||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
|
||||
|
||||
"array-iterate": ["array-iterate@2.0.1", "", {}, "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg=="],
|
||||
|
||||
"astro": ["astro@6.3.1", "", { "dependencies": { "@astrojs/compiler": "^4.0.0", "@astrojs/internal-helpers": "0.9.0", "@astrojs/markdown-remark": "7.1.1", "@astrojs/telemetry": "3.3.2", "@capsizecss/unpack": "^4.0.0", "@clack/prompts": "^1.1.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", "clsx": "^2.1.1", "common-ancestor-path": "^2.0.0", "cookie": "^1.1.1", "devalue": "^5.6.3", "diff": "^8.0.3", "dset": "^3.1.4", "es-module-lexer": "^2.0.0", "esbuild": "^0.27.3", "flattie": "^1.1.1", "fontace": "~0.4.1", "get-tsconfig": "5.0.0-beta.4", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "js-yaml": "^4.1.1", "jsonc-parser": "^3.3.1", "magic-string": "^0.30.21", "magicast": "^0.5.2", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "obug": "^2.1.1", "p-limit": "^7.3.0", "p-queue": "^9.1.0", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.4", "rehype": "^13.0.2", "semver": "^7.7.4", "shiki": "^4.0.2", "smol-toml": "^1.6.0", "svgo": "^4.0.1", "tinyclip": "^0.1.12", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", "ultrahtml": "^1.6.0", "unifont": "~0.7.4", "unist-util-visit": "^5.1.0", "unstorage": "^1.17.5", "vfile": "^6.0.3", "vite": "^7.3.2", "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", "zod": "^4.3.6" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "bin/astro.mjs" } }, "sha512-atz6dmkE3Gu24bDgb7g2RE/BYnKqPYIHd6hTUM1UXvu/i7qNZOKLAqEHvgYpv9PQVcgWsXpk4/OOXZ0E/FzvSQ=="],
|
||||
|
||||
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
||||
|
||||
"bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="],
|
||||
|
||||
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
|
||||
|
||||
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
|
||||
|
||||
"character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
|
||||
|
||||
"character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
|
||||
|
||||
"character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
|
||||
|
||||
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
||||
|
||||
"ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="],
|
||||
|
||||
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
||||
|
||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
|
||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||
|
||||
"comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
|
||||
|
||||
"commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="],
|
||||
|
||||
"common-ancestor-path": ["common-ancestor-path@2.0.0", "", {}, "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng=="],
|
||||
|
||||
"cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
|
||||
|
||||
"cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="],
|
||||
|
||||
"crossws": ["crossws@0.3.5", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA=="],
|
||||
|
||||
"css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
|
||||
|
||||
"css-tree": ["css-tree@3.2.1", "", { "dependencies": { "mdn-data": "2.27.1", "source-map-js": "^1.2.1" } }, "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA=="],
|
||||
|
||||
"css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
|
||||
|
||||
"csso": ["csso@5.0.5", "", { "dependencies": { "css-tree": "~2.2.0" } }, "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="],
|
||||
|
||||
"defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="],
|
||||
|
||||
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
||||
|
||||
"destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="],
|
||||
|
||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||
|
||||
"devalue": ["devalue@5.8.0", "", {}, "sha512-2zA9pFEsnp7vWBZbXF5JAgAq0fsUIt/1XPbRiAmRV3lp/2C3upzH+sADiyy66aFCihoLEsrQHxNM5w1gIDfsBg=="],
|
||||
|
||||
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
|
||||
|
||||
"diff": ["diff@8.0.4", "", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="],
|
||||
|
||||
"dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
|
||||
|
||||
"domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
|
||||
|
||||
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
|
||||
|
||||
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
|
||||
|
||||
"dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="],
|
||||
|
||||
"emmet": ["emmet@2.4.11", "", { "dependencies": { "@emmetio/abbreviation": "^2.3.3", "@emmetio/css-abbreviation": "^2.1.8" } }, "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
|
||||
|
||||
"es-module-lexer": ["es-module-lexer@2.1.0", "", {}, "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ=="],
|
||||
|
||||
"esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="],
|
||||
|
||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||
|
||||
"escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
|
||||
|
||||
"estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||
|
||||
"eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="],
|
||||
|
||||
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
|
||||
|
||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||
|
||||
"fast-string-truncated-width": ["fast-string-truncated-width@3.0.3", "", {}, "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g=="],
|
||||
|
||||
"fast-string-width": ["fast-string-width@3.0.2", "", { "dependencies": { "fast-string-truncated-width": "^3.0.2" } }, "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg=="],
|
||||
|
||||
"fast-uri": ["fast-uri@3.1.2", "", {}, "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ=="],
|
||||
|
||||
"fast-wrap-ansi": ["fast-wrap-ansi@0.2.0", "", { "dependencies": { "fast-string-width": "^3.0.2" } }, "sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w=="],
|
||||
|
||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||
|
||||
"flattie": ["flattie@1.1.1", "", {}, "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ=="],
|
||||
|
||||
"fontace": ["fontace@0.4.1", "", { "dependencies": { "fontkitten": "^1.0.2" } }, "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw=="],
|
||||
|
||||
"fontkitten": ["fontkitten@1.0.3", "", { "dependencies": { "tiny-inflate": "^1.0.3" } }, "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw=="],
|
||||
|
||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||
|
||||
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
|
||||
|
||||
"get-tsconfig": ["get-tsconfig@5.0.0-beta.4", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ=="],
|
||||
|
||||
"github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
|
||||
|
||||
"h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="],
|
||||
|
||||
"hast-util-from-html": ["hast-util-from-html@2.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.1.0", "hast-util-from-parse5": "^8.0.0", "parse5": "^7.0.0", "vfile": "^6.0.0", "vfile-message": "^4.0.0" } }, "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw=="],
|
||||
|
||||
"hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="],
|
||||
|
||||
"hast-util-heading-rank": ["hast-util-heading-rank@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA=="],
|
||||
|
||||
"hast-util-is-element": ["hast-util-is-element@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g=="],
|
||||
|
||||
"hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="],
|
||||
|
||||
"hast-util-raw": ["hast-util-raw@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-from-parse5": "^8.0.0", "hast-util-to-parse5": "^8.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "parse5": "^7.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw=="],
|
||||
|
||||
"hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
|
||||
|
||||
"hast-util-to-parse5": ["hast-util-to-parse5@8.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA=="],
|
||||
|
||||
"hast-util-to-string": ["hast-util-to-string@3.0.1", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A=="],
|
||||
|
||||
"hast-util-to-text": ["hast-util-to-text@4.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "hast-util-is-element": "^3.0.0", "unist-util-find-after": "^5.0.0" } }, "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A=="],
|
||||
|
||||
"hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
|
||||
|
||||
"hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="],
|
||||
|
||||
"html-escaper": ["html-escaper@3.0.3", "", {}, "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="],
|
||||
|
||||
"html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
|
||||
|
||||
"http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="],
|
||||
|
||||
"immutable": ["immutable@5.1.5", "", {}, "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A=="],
|
||||
|
||||
"iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="],
|
||||
|
||||
"is-absolute-url": ["is-absolute-url@4.0.1", "", {}, "sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A=="],
|
||||
|
||||
"is-docker": ["is-docker@4.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA=="],
|
||||
|
||||
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
||||
|
||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||
|
||||
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
||||
|
||||
"is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
|
||||
|
||||
"is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
|
||||
|
||||
"is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="],
|
||||
|
||||
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
||||
|
||||
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||
|
||||
"jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="],
|
||||
|
||||
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
||||
|
||||
"longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
|
||||
|
||||
"lru-cache": ["lru-cache@11.3.6", "", {}, "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A=="],
|
||||
|
||||
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||
|
||||
"magicast": ["magicast@0.5.2", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "source-map-js": "^1.2.1" } }, "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ=="],
|
||||
|
||||
"markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
|
||||
|
||||
"mdast-util-definitions": ["mdast-util-definitions@6.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ=="],
|
||||
|
||||
"mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="],
|
||||
|
||||
"mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="],
|
||||
|
||||
"mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="],
|
||||
|
||||
"mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="],
|
||||
|
||||
"mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="],
|
||||
|
||||
"mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="],
|
||||
|
||||
"mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="],
|
||||
|
||||
"mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="],
|
||||
|
||||
"mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="],
|
||||
|
||||
"mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="],
|
||||
|
||||
"mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="],
|
||||
|
||||
"mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="],
|
||||
|
||||
"mdn-data": ["mdn-data@2.27.1", "", {}, "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ=="],
|
||||
|
||||
"micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="],
|
||||
|
||||
"micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="],
|
||||
|
||||
"micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="],
|
||||
|
||||
"micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="],
|
||||
|
||||
"micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="],
|
||||
|
||||
"micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="],
|
||||
|
||||
"micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="],
|
||||
|
||||
"micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="],
|
||||
|
||||
"micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="],
|
||||
|
||||
"micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="],
|
||||
|
||||
"micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="],
|
||||
|
||||
"micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="],
|
||||
|
||||
"micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="],
|
||||
|
||||
"micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="],
|
||||
|
||||
"micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
|
||||
|
||||
"micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="],
|
||||
|
||||
"micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="],
|
||||
|
||||
"micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="],
|
||||
|
||||
"micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="],
|
||||
|
||||
"micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="],
|
||||
|
||||
"micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="],
|
||||
|
||||
"micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="],
|
||||
|
||||
"micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="],
|
||||
|
||||
"micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="],
|
||||
|
||||
"micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="],
|
||||
|
||||
"micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="],
|
||||
|
||||
"micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="],
|
||||
|
||||
"micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="],
|
||||
|
||||
"mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"muggle-string": ["muggle-string@0.4.1", "", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="],
|
||||
|
||||
"nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="],
|
||||
|
||||
"neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="],
|
||||
|
||||
"nlcst-to-string": ["nlcst-to-string@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0" } }, "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA=="],
|
||||
|
||||
"node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
|
||||
|
||||
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
|
||||
|
||||
"node-mock-http": ["node-mock-http@1.0.4", "", {}, "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ=="],
|
||||
|
||||
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
||||
|
||||
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
|
||||
|
||||
"obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="],
|
||||
|
||||
"ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="],
|
||||
|
||||
"ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
|
||||
|
||||
"oniguruma-parser": ["oniguruma-parser@0.12.2", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="],
|
||||
|
||||
"oniguruma-to-es": ["oniguruma-to-es@4.3.6", "", { "dependencies": { "oniguruma-parser": "^0.12.2", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA=="],
|
||||
|
||||
"p-limit": ["p-limit@7.3.0", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw=="],
|
||||
|
||||
"p-queue": ["p-queue@9.2.0", "", { "dependencies": { "eventemitter3": "^5.0.4", "p-timeout": "^7.0.0" } }, "sha512-dWgLE8AH0HjQ9fe74pUkKkvzzYT18Inp4zra3lKHnnwqGvcfcUBrvF2EAVX+envufDNBOzpPq/IBUONDbI7+3g=="],
|
||||
|
||||
"p-timeout": ["p-timeout@7.0.1", "", {}, "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg=="],
|
||||
|
||||
"package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="],
|
||||
|
||||
"parse-latin": ["parse-latin@7.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "@types/unist": "^3.0.0", "nlcst-to-string": "^4.0.0", "unist-util-modify-children": "^4.0.0", "unist-util-visit-children": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ=="],
|
||||
|
||||
"parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
|
||||
|
||||
"path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
|
||||
|
||||
"piccolore": ["piccolore@0.1.3", "", {}, "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
|
||||
|
||||
"postcss": ["postcss@8.5.14", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg=="],
|
||||
|
||||
"prettier": ["prettier@3.8.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw=="],
|
||||
|
||||
"prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="],
|
||||
|
||||
"property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
|
||||
|
||||
"radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="],
|
||||
|
||||
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||
|
||||
"regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="],
|
||||
|
||||
"regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="],
|
||||
|
||||
"regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="],
|
||||
|
||||
"rehype": ["rehype@13.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "rehype-parse": "^9.0.0", "rehype-stringify": "^10.0.0", "unified": "^11.0.0" } }, "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A=="],
|
||||
|
||||
"rehype-autolink-headings": ["rehype-autolink-headings@7.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-heading-rank": "^3.0.0", "hast-util-is-element": "^3.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-rItO/pSdvnvsP4QRB1pmPiNHUskikqtPojZKJPPPAVx9Hj8i8TwMBhofrrAYRhYOOBZH9tgmG5lPqDLuIWPWmw=="],
|
||||
|
||||
"rehype-external-links": ["rehype-external-links@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-is-element": "^3.0.0", "is-absolute-url": "^4.0.0", "space-separated-tokens": "^2.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw=="],
|
||||
|
||||
"rehype-parse": ["rehype-parse@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-from-html": "^2.0.0", "unified": "^11.0.0" } }, "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag=="],
|
||||
|
||||
"rehype-raw": ["rehype-raw@7.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-raw": "^9.0.0", "vfile": "^6.0.0" } }, "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww=="],
|
||||
|
||||
"rehype-slug": ["rehype-slug@6.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "github-slugger": "^2.0.0", "hast-util-heading-rank": "^3.0.0", "hast-util-to-string": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A=="],
|
||||
|
||||
"rehype-stringify": ["rehype-stringify@10.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-to-html": "^9.0.0", "unified": "^11.0.0" } }, "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA=="],
|
||||
|
||||
"remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="],
|
||||
|
||||
"remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="],
|
||||
|
||||
"remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="],
|
||||
|
||||
"remark-smartypants": ["remark-smartypants@3.0.2", "", { "dependencies": { "retext": "^9.0.0", "retext-smartypants": "^6.0.0", "unified": "^11.0.4", "unist-util-visit": "^5.0.0" } }, "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA=="],
|
||||
|
||||
"remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="],
|
||||
|
||||
"request-light": ["request-light@0.7.0", "", {}, "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q=="],
|
||||
|
||||
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
|
||||
|
||||
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
||||
|
||||
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
|
||||
|
||||
"retext": ["retext@9.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "retext-latin": "^4.0.0", "retext-stringify": "^4.0.0", "unified": "^11.0.0" } }, "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA=="],
|
||||
|
||||
"retext-latin": ["retext-latin@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "parse-latin": "^7.0.0", "unified": "^11.0.0" } }, "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA=="],
|
||||
|
||||
"retext-smartypants": ["retext-smartypants@6.2.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "nlcst-to-string": "^4.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ=="],
|
||||
|
||||
"retext-stringify": ["retext-stringify@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "nlcst-to-string": "^4.0.0", "unified": "^11.0.0" } }, "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA=="],
|
||||
|
||||
"rollup": ["rollup@4.60.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.3", "@rollup/rollup-android-arm64": "4.60.3", "@rollup/rollup-darwin-arm64": "4.60.3", "@rollup/rollup-darwin-x64": "4.60.3", "@rollup/rollup-freebsd-arm64": "4.60.3", "@rollup/rollup-freebsd-x64": "4.60.3", "@rollup/rollup-linux-arm-gnueabihf": "4.60.3", "@rollup/rollup-linux-arm-musleabihf": "4.60.3", "@rollup/rollup-linux-arm64-gnu": "4.60.3", "@rollup/rollup-linux-arm64-musl": "4.60.3", "@rollup/rollup-linux-loong64-gnu": "4.60.3", "@rollup/rollup-linux-loong64-musl": "4.60.3", "@rollup/rollup-linux-ppc64-gnu": "4.60.3", "@rollup/rollup-linux-ppc64-musl": "4.60.3", "@rollup/rollup-linux-riscv64-gnu": "4.60.3", "@rollup/rollup-linux-riscv64-musl": "4.60.3", "@rollup/rollup-linux-s390x-gnu": "4.60.3", "@rollup/rollup-linux-x64-gnu": "4.60.3", "@rollup/rollup-linux-x64-musl": "4.60.3", "@rollup/rollup-openbsd-x64": "4.60.3", "@rollup/rollup-openharmony-arm64": "4.60.3", "@rollup/rollup-win32-arm64-msvc": "4.60.3", "@rollup/rollup-win32-ia32-msvc": "4.60.3", "@rollup/rollup-win32-x64-gnu": "4.60.3", "@rollup/rollup-win32-x64-msvc": "4.60.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A=="],
|
||||
|
||||
"sass": ["sass@1.99.0", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.1.5", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q=="],
|
||||
|
||||
"sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
|
||||
|
||||
"semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="],
|
||||
|
||||
"sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="],
|
||||
|
||||
"shiki": ["shiki@4.0.2", "", { "dependencies": { "@shikijs/core": "4.0.2", "@shikijs/engine-javascript": "4.0.2", "@shikijs/engine-oniguruma": "4.0.2", "@shikijs/langs": "4.0.2", "@shikijs/themes": "4.0.2", "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ=="],
|
||||
|
||||
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
|
||||
|
||||
"smol-toml": ["smol-toml@1.6.1", "", {}, "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
|
||||
"space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
|
||||
|
||||
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
|
||||
|
||||
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"svgo": ["svgo@4.0.1", "", { "dependencies": { "commander": "^11.1.0", "css-select": "^5.1.0", "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.1.1", "sax": "^1.5.0" }, "bin": "./bin/svgo.js" }, "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w=="],
|
||||
|
||||
"tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="],
|
||||
|
||||
"tinyclip": ["tinyclip@0.1.12", "", {}, "sha512-Ae3OVUqifDw0wBriIBS7yVaW44Dp6eSHQcyq4Igc7eN2TJH/2YsicswaW+J/OuMvhpDPOKEgpAZCjkb4hpoyeA=="],
|
||||
|
||||
"tinyexec": ["tinyexec@1.1.2", "", {}, "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA=="],
|
||||
|
||||
"tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
|
||||
|
||||
"trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
|
||||
|
||||
"trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"typesafe-path": ["typesafe-path@0.2.2", "", {}, "sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA=="],
|
||||
|
||||
"typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="],
|
||||
|
||||
"typescript-auto-import-cache": ["typescript-auto-import-cache@0.3.6", "", { "dependencies": { "semver": "^7.3.8" } }, "sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ=="],
|
||||
|
||||
"ufo": ["ufo@1.6.4", "", {}, "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA=="],
|
||||
|
||||
"ultrahtml": ["ultrahtml@1.6.0", "", {}, "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw=="],
|
||||
|
||||
"uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="],
|
||||
|
||||
"undici-types": ["undici-types@7.24.6", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="],
|
||||
|
||||
"unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="],
|
||||
|
||||
"unifont": ["unifont@0.7.4", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg=="],
|
||||
|
||||
"unist-util-find-after": ["unist-util-find-after@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ=="],
|
||||
|
||||
"unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="],
|
||||
|
||||
"unist-util-modify-children": ["unist-util-modify-children@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "array-iterate": "^2.0.0" } }, "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw=="],
|
||||
|
||||
"unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="],
|
||||
|
||||
"unist-util-remove-position": ["unist-util-remove-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q=="],
|
||||
|
||||
"unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="],
|
||||
|
||||
"unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="],
|
||||
|
||||
"unist-util-visit-children": ["unist-util-visit-children@3.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA=="],
|
||||
|
||||
"unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="],
|
||||
|
||||
"unstorage": ["unstorage@1.17.5", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.10", "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6 || ^7 || ^8", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1 || ^2 || ^3", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg=="],
|
||||
|
||||
"vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
|
||||
|
||||
"vfile-location": ["vfile-location@5.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg=="],
|
||||
|
||||
"vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
|
||||
|
||||
"vite": ["vite@7.3.3", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA=="],
|
||||
|
||||
"vitefu": ["vitefu@1.1.3", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="],
|
||||
|
||||
"volar-service-css": ["volar-service-css@0.0.70", "", { "dependencies": { "vscode-css-languageservice": "^6.3.0", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-K1qyOvBpE3rzdAv3e4/6Rv5yizrYPy5R/ne3IWCAzLBuMO4qBMV3kSqWzj6KUVe6S0AnN6wxF7cRkiaKfYMYJw=="],
|
||||
|
||||
"volar-service-emmet": ["volar-service-emmet@0.0.70", "", { "dependencies": { "@emmetio/css-parser": "^0.4.1", "@emmetio/html-matcher": "^1.3.0", "@vscode/emmet-helper": "^2.9.3", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-xi5bC4m/VyE3zy/n2CXspKeDZs3qA41tHLTw275/7dNWM/RqE2z3BnDICQybHIVp/6G1iOQj5c1qXMgQC08TNg=="],
|
||||
|
||||
"volar-service-html": ["volar-service-html@0.0.70", "", { "dependencies": { "vscode-html-languageservice": "^5.3.0", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-eR6vCgMdmYAo4n+gcT7DSyBQbwB8S3HZZvSagTf0sxNaD4WppMCFfpqWnkrlGStPKMZvMiejRRVmqsX9dYcTvQ=="],
|
||||
|
||||
"volar-service-prettier": ["volar-service-prettier@0.0.70", "", { "dependencies": { "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0", "prettier": "^2.2 || ^3.0" }, "optionalPeers": ["@volar/language-service", "prettier"] }, "sha512-Z6BCFSpGVCd8BPAsZ785Kce1BGlWd5ODqmqZGVuB14MJvrR4+CYz6cDy4F+igmE1gMifqfvMhdgT8Aud4M5ngg=="],
|
||||
|
||||
"volar-service-typescript": ["volar-service-typescript@0.0.70", "", { "dependencies": { "path-browserify": "^1.0.1", "semver": "^7.6.2", "typescript-auto-import-cache": "^0.3.5", "vscode-languageserver-textdocument": "^1.0.11", "vscode-nls": "^5.2.0", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-l46Bx4cokkUedTd74ojO5H/zqHZJ8SUuyZ0IB8JN4jfRqUM3bQFBHoOwlZCyZmOeO0A3RQNkMnFclxO4c++gsg=="],
|
||||
|
||||
"volar-service-typescript-twoslash-queries": ["volar-service-typescript-twoslash-queries@0.0.70", "", { "dependencies": { "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-IdD13Z9N2Bu8EM6CM0fDV1E69olEYGHDU25X51YXmq8Y0CmJ2LNj6gOiBJgpS5JGUqFzECVhMNBW7R0sPdRTMQ=="],
|
||||
|
||||
"volar-service-yaml": ["volar-service-yaml@0.0.70", "", { "dependencies": { "vscode-uri": "^3.0.8", "yaml-language-server": "~1.20.0" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-0c8bXDBeoATF9F6iPIlOuYTuZAC4c+yi0siQo920u7eiBJk8oQmUmg9cDUbR4+Gl++bvGP4plj3fErbJuPqdcQ=="],
|
||||
|
||||
"vscode-css-languageservice": ["vscode-css-languageservice@6.3.10", "", { "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "3.17.5", "vscode-uri": "^3.1.0" } }, "sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA=="],
|
||||
|
||||
"vscode-html-languageservice": ["vscode-html-languageservice@5.6.2", "", { "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "^3.17.5", "vscode-uri": "^3.1.0" } }, "sha512-ulCrSnFnfQ16YzvwnYUgEbUEl/ZG7u2eV27YhvLObSHKkb8fw1Z9cgsnUwjTEeDIdJDoTDTDpxuhQwoenoLNMg=="],
|
||||
|
||||
"vscode-json-languageservice": ["vscode-json-languageservice@4.1.8", "", { "dependencies": { "jsonc-parser": "^3.0.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.16.0", "vscode-nls": "^5.0.0", "vscode-uri": "^3.0.2" } }, "sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg=="],
|
||||
|
||||
"vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="],
|
||||
|
||||
"vscode-languageserver": ["vscode-languageserver@9.0.1", "", { "dependencies": { "vscode-languageserver-protocol": "3.17.5" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g=="],
|
||||
|
||||
"vscode-languageserver-protocol": ["vscode-languageserver-protocol@3.17.5", "", { "dependencies": { "vscode-jsonrpc": "8.2.0", "vscode-languageserver-types": "3.17.5" } }, "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg=="],
|
||||
|
||||
"vscode-languageserver-textdocument": ["vscode-languageserver-textdocument@1.0.12", "", {}, "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA=="],
|
||||
|
||||
"vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="],
|
||||
|
||||
"vscode-nls": ["vscode-nls@5.2.0", "", {}, "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng=="],
|
||||
|
||||
"vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="],
|
||||
|
||||
"web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
|
||||
|
||||
"which-pm-runs": ["which-pm-runs@1.1.0", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="],
|
||||
|
||||
"wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||
|
||||
"xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="],
|
||||
|
||||
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
||||
|
||||
"yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="],
|
||||
|
||||
"yaml-language-server": ["yaml-language-server@1.20.0", "", { "dependencies": { "@vscode/l10n": "^0.0.18", "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "prettier": "^3.5.0", "request-light": "^0.5.7", "vscode-json-languageservice": "4.1.8", "vscode-languageserver": "^9.0.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.16.0", "vscode-uri": "^3.0.2", "yaml": "2.7.1" }, "bin": { "yaml-language-server": "bin/yaml-language-server" } }, "sha512-qhjK/bzSRZ6HtTvgeFvjNPJGWdZ0+x5NREV/9XZWFjIGezew2b4r5JPy66IfOhd5OA7KeFwk1JfmEbnTvev0cA=="],
|
||||
|
||||
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
|
||||
|
||||
"yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="],
|
||||
|
||||
"yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="],
|
||||
|
||||
"zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="],
|
||||
|
||||
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
||||
|
||||
"@astrojs/language-server/@astrojs/compiler": ["@astrojs/compiler@2.13.1", "", {}, "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg=="],
|
||||
|
||||
"@vscode/emmet-helper/jsonc-parser": ["jsonc-parser@2.3.1", "", {}, "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg=="],
|
||||
|
||||
"anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||
|
||||
"csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="],
|
||||
|
||||
"dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||
|
||||
"is-inside-container/is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
|
||||
|
||||
"rollup/@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||
|
||||
"unstorage/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
||||
|
||||
"yaml-language-server/request-light": ["request-light@0.5.8", "", {}, "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg=="],
|
||||
|
||||
"yaml-language-server/yaml": ["yaml@2.7.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ=="],
|
||||
|
||||
"yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
|
||||
|
||||
"csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="],
|
||||
|
||||
"unstorage/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
||||
}
|
||||
}
|
||||
542
docs-build/nav.yml
Normal file
@@ -0,0 +1,542 @@
|
||||
categories:
|
||||
- name: "Analytics"
|
||||
pages:
|
||||
- name: "Getting started"
|
||||
url: "/learn/metabase-basics/getting-started/index"
|
||||
- name: "Metabase concepts"
|
||||
url: "/learn/metabase-basics/overview/concepts"
|
||||
- name: Queries and charts
|
||||
url: "questions/start"
|
||||
pages:
|
||||
- name: "Introduction"
|
||||
url: "questions/introduction"
|
||||
- name: "Query builder"
|
||||
pages:
|
||||
- name: "Editor"
|
||||
url: "questions/query-builder/editor"
|
||||
- name: "Filtering"
|
||||
url: "questions/query-builder/filters"
|
||||
- name: "Summarizing and grouping"
|
||||
url: "questions/query-builder/summarizing-and-grouping"
|
||||
- name: "Joining data"
|
||||
url: "questions/query-builder/join"
|
||||
- name: "Custom expressions"
|
||||
url: "questions/query-builder/expressions"
|
||||
- name: "List of expressions"
|
||||
url: "questions/query-builder/expressions-list"
|
||||
- name: "SQL and native queries"
|
||||
pages:
|
||||
- name: "SQL editor"
|
||||
url: "questions/native-editor/writing-sql"
|
||||
- name: "SQL parameters"
|
||||
url: "questions/native-editor/sql-parameters"
|
||||
- name: "Field filters"
|
||||
url: "questions/native-editor/field-filters"
|
||||
- name: "Basic SQL parameters"
|
||||
url: "questions/native-editor/basic-sql-parameters"
|
||||
- name: "Time grouping parameters"
|
||||
url: "questions/native-editor/time-grouping-parameters"
|
||||
- name: "Table variables"
|
||||
url: "questions/native-editor/table-variables"
|
||||
- name: "Optional variables"
|
||||
url: "questions/native-editor/optional-variables"
|
||||
- name: "Filter widgets"
|
||||
url: "questions/native-editor/filter-widgets"
|
||||
- name: "Referencing models and questions"
|
||||
url: "questions/native-editor/referencing-saved-questions-in-queries"
|
||||
- name: "Snippets"
|
||||
url: "questions/native-editor/snippets"
|
||||
- name: "Snippet folder permissions"
|
||||
url: "permissions/snippets"
|
||||
- name: "Visualizations"
|
||||
pages:
|
||||
- name: "Overview"
|
||||
url: "questions/visualizations/visualizing-results"
|
||||
- name: "Box plot"
|
||||
url: "questions/visualizations/box-plot"
|
||||
- name: "Combo chart"
|
||||
url: "questions/visualizations/combo-chart"
|
||||
- name: "Details chart"
|
||||
url: "questions/visualizations/detail"
|
||||
- name: "Funnel chart"
|
||||
url: "questions/visualizations/funnel"
|
||||
- name: "Gauge chart"
|
||||
url: "questions/visualizations/gauge"
|
||||
- name: "Line, bar, and area charts"
|
||||
url: "questions/visualizations/line-bar-and-area-charts"
|
||||
- name: "Maps"
|
||||
url: "questions/visualizations/map"
|
||||
- name: "Number chart"
|
||||
url: "questions/visualizations/numbers"
|
||||
- name: "Pie and sunburst charts"
|
||||
url: "questions/visualizations/pie-or-donut-chart"
|
||||
- name: "Pivot tables"
|
||||
url: "questions/visualizations/pivot-table"
|
||||
- name: "Progress bar"
|
||||
url: "questions/visualizations/progress-bar"
|
||||
- name: "Sankey chart"
|
||||
url: "questions/visualizations/sankey"
|
||||
- name: "Scatterplot"
|
||||
url: "questions/visualizations/scatterplot-or-bubble-chart"
|
||||
- name: "Table"
|
||||
url: "questions/visualizations/table"
|
||||
- name: "Trend chart"
|
||||
url: "questions/visualizations/trend"
|
||||
- name: "Waterfall chart"
|
||||
url: "questions/visualizations/waterfall-chart"
|
||||
- name: "Tooltips"
|
||||
url: "questions/visualizations/tooltips"
|
||||
|
||||
- name: "Metrics explorer"
|
||||
url: "questions/metrics-explorer"
|
||||
- name: "Alerts"
|
||||
url: "questions/alerts"
|
||||
- name: "Exporting data"
|
||||
url: "questions/exporting-results"
|
||||
- name: "Dashboards"
|
||||
url: "dashboards/start"
|
||||
pages:
|
||||
- name: "Overview"
|
||||
url: "dashboards/introduction"
|
||||
- name: "Dashboard filters"
|
||||
url: "dashboards/filters"
|
||||
- name: "Linked filters"
|
||||
url: "dashboards/linked-filters"
|
||||
- name: "Dashboard interactivity"
|
||||
url: "dashboards/interactive"
|
||||
- name: "Charts with multiple series"
|
||||
url: "dashboards/multiple-series"
|
||||
- name: "Dashboard subscriptions"
|
||||
url: "dashboards/subscriptions"
|
||||
- name: "Actions on dashboards"
|
||||
url: "dashboards/actions"
|
||||
|
||||
- name: "Documents"
|
||||
url: "documents/start"
|
||||
pages:
|
||||
- name: "Overview"
|
||||
url: "documents/introduction"
|
||||
|
||||
- name: "AI"
|
||||
url: "ai/start"
|
||||
pages:
|
||||
- name: "Metabot AI"
|
||||
url: "ai/metabot"
|
||||
- name: "Metabot for Slack"
|
||||
url: "ai/metabot-slack"
|
||||
- name: "Metabot settings"
|
||||
url: "ai/settings"
|
||||
- name: "MCP server"
|
||||
url: "ai/mcp"
|
||||
- name: "Agent API"
|
||||
url: "ai/agent-api"
|
||||
- name: "File-based development"
|
||||
url: "ai/file-based-development"
|
||||
|
||||
- name: "Data modeling"
|
||||
url: "data-modeling/start"
|
||||
pages:
|
||||
- name: "Models"
|
||||
url: "data-modeling/models"
|
||||
- name: "Model persistence"
|
||||
url: "data-modeling/model-persistence"
|
||||
- name: "Metrics"
|
||||
url: "data-modeling/metrics"
|
||||
- name: "Table metadata settings"
|
||||
url: "data-modeling/metadata-editing"
|
||||
- name: "Data and semantic types"
|
||||
url: "data-modeling/semantic-types"
|
||||
- name: "Editable tables"
|
||||
url: "data-modeling/editable-tables"
|
||||
- name: "Formatting defaults"
|
||||
url: "data-modeling/formatting"
|
||||
- name: "Working with JSON"
|
||||
url: "data-modeling/json-unfolding"
|
||||
- name: "Segments"
|
||||
url: "data-modeling/segments"
|
||||
- name: "Actions"
|
||||
pages:
|
||||
- name: "Overview"
|
||||
url: "actions/introduction"
|
||||
- name: "Basic actions"
|
||||
url: "actions/basic"
|
||||
- name: "Custom actions"
|
||||
url: "actions/custom"
|
||||
|
||||
- name: "Data Studio"
|
||||
url: "data-studio/overview"
|
||||
pages:
|
||||
- name: "Overview"
|
||||
url: "data-studio/overview"
|
||||
- name: "Library"
|
||||
url: "data-studio/library"
|
||||
- name: "Data structure"
|
||||
url: "data-studio/managing-tables"
|
||||
- name: "Glossary"
|
||||
url: "exploration-and-organization/data-model-reference#glossary"
|
||||
- name: "Dependency graph"
|
||||
url: "data-studio/dependencies/graph"
|
||||
- name: "Dependency diagnostics"
|
||||
url: "data-studio/dependencies/diagnostics"
|
||||
- name: "Transforms"
|
||||
pages:
|
||||
- name: "Overview"
|
||||
url: "data-studio/transforms/transforms-overview"
|
||||
- name: "Query transforms"
|
||||
url: "data-studio/transforms/query-transforms"
|
||||
- name: "Python transforms"
|
||||
url: "data-studio/transforms/python-transforms"
|
||||
- name: "Python runner"
|
||||
url: "data-studio/transforms/python-runner"
|
||||
- name: "Jobs and runs"
|
||||
url: "data-studio/transforms/jobs-and-runs"
|
||||
- name: "Transform inspector"
|
||||
url: "data-studio/transforms/transform-inspector"
|
||||
|
||||
- name: "Organization"
|
||||
url: "exploration-and-organization/start"
|
||||
pages:
|
||||
- name: "Basic exploration"
|
||||
url: "exploration-and-organization/exploration"
|
||||
- name: "Keyboard shortcuts"
|
||||
url: "exploration-and-organization/keyboard-shortcuts"
|
||||
- name: "Collections"
|
||||
url: "exploration-and-organization/collections"
|
||||
- name: "Data reference"
|
||||
url: "exploration-and-organization/data-model-reference"
|
||||
- name: "Events and timelines"
|
||||
url: "exploration-and-organization/events-and-timelines"
|
||||
- name: "X-rays"
|
||||
url: "exploration-and-organization/x-rays"
|
||||
- name: "Content verification"
|
||||
url: "exploration-and-organization/content-verification"
|
||||
- name: "History"
|
||||
url: "exploration-and-organization/history"
|
||||
- name: "Delete and restore"
|
||||
url: "exploration-and-organization/delete-and-restore"
|
||||
|
||||
- name: "Embedding"
|
||||
url: "embedding/start"
|
||||
pages:
|
||||
- name: "Overview"
|
||||
url: "embedding/introduction"
|
||||
- name: "Modular embedding"
|
||||
pages:
|
||||
- name: "Overview"
|
||||
url: "embedding/modular-embedding"
|
||||
- name: "Components"
|
||||
url: "embedding/components"
|
||||
- name: "Appearance"
|
||||
url: "embedding/appearance"
|
||||
- name: "Authentication"
|
||||
url: "embedding/authentication"
|
||||
- name: "Tenants"
|
||||
url: "embedding/tenants"
|
||||
- name: "Modular embedding SDK"
|
||||
pages:
|
||||
- name: "Overview"
|
||||
url: "embedding/sdk/introduction"
|
||||
- name: "Quickstarts"
|
||||
url: "embedding/sdk/introduction#quickstarts"
|
||||
- name: "Components"
|
||||
pages:
|
||||
- name: "Questions"
|
||||
url: "embedding/sdk/questions"
|
||||
- name: "Dashboards"
|
||||
url: "embedding/sdk/dashboards"
|
||||
- name: "AI chat"
|
||||
url: "embedding/sdk/ai-chat"
|
||||
- name: "Collections"
|
||||
url: "embedding/sdk/collections"
|
||||
- name: "Plugins"
|
||||
url: "embedding/sdk/plugins"
|
||||
- name: "Configuration"
|
||||
pages:
|
||||
- name: "Provider config"
|
||||
url: "embedding/sdk/config"
|
||||
- name: "Working with Next.js"
|
||||
url: "embedding/sdk/next-js"
|
||||
- name: "Versioning"
|
||||
url: "embedding/sdk/version"
|
||||
- name: "Upgrading"
|
||||
url: "embedding/sdk/upgrade"
|
||||
- name: "API"
|
||||
url: "embedding/sdk/api/"
|
||||
- name: "Guest embedding"
|
||||
url: "embedding/guest-embedding"
|
||||
- name: "Translate embeds"
|
||||
url: "embedding/translations"
|
||||
- name: "Full app embedding"
|
||||
pages:
|
||||
- name: "Overview"
|
||||
url: "embedding/full-app-embedding"
|
||||
- name: "Quickstart"
|
||||
url: "embedding/full-app-embedding-quick-start-guide"
|
||||
- name: "Full app UI components"
|
||||
url: "embedding/full-app-ui-components"
|
||||
- name: "Public links and embeds"
|
||||
url: "embedding/public-links"
|
||||
- name: "Securing embeds"
|
||||
url: "embedding/securing-embeds"
|
||||
- name: "AI agent resources"
|
||||
url: "embedding/ai-agent-resources"
|
||||
|
||||
- name: "Administration"
|
||||
pages:
|
||||
- name: "Installation"
|
||||
url: "installation-and-operation/start"
|
||||
pages:
|
||||
- name: "Installing Metabase"
|
||||
pages:
|
||||
- name: "Installation overview"
|
||||
url: "installation-and-operation/installing-metabase"
|
||||
- name: "Metabase Cloud"
|
||||
url: "cloud/start"
|
||||
- name: "Running the JAR file"
|
||||
url: "installation-and-operation/running-the-metabase-jar-file"
|
||||
- name: "Running in Docker"
|
||||
url: "installation-and-operation/running-metabase-on-docker"
|
||||
- name: "Other installation options"
|
||||
url: "installation-and-operation/installing-metabase#other-installation-options"
|
||||
- name: "Upgrading Metabase"
|
||||
url: "installation-and-operation/upgrading-metabase"
|
||||
- name: "Configuring the Metabase application database"
|
||||
url: "installation-and-operation/configuring-application-database"
|
||||
- name: "Activating Enterprise features"
|
||||
url: "installation-and-operation/activating-the-enterprise-edition"
|
||||
- name: "Migrating to a production application database"
|
||||
url: "installation-and-operation/migrating-from-h2"
|
||||
- name: "Data sources"
|
||||
url: "databases/start"
|
||||
pages:
|
||||
- name: "Adding and managing databases"
|
||||
url: "databases/connecting"
|
||||
- name: "Supported databases"
|
||||
pages:
|
||||
- name: "Athena"
|
||||
url: "databases/connections/athena"
|
||||
- name: "Amazon RDS"
|
||||
url: "databases/connections/aws-rds"
|
||||
- name: "BigQuery"
|
||||
url: "databases/connections/bigquery"
|
||||
- name: "ClickHouse"
|
||||
url: "databases/connections/clickhouse"
|
||||
- name: "Databricks"
|
||||
url: "databases/connections/databricks"
|
||||
- name: "Druid"
|
||||
url: "databases/connections/druid"
|
||||
- name: "MariaDB"
|
||||
url: "databases/connections/mariadb"
|
||||
- name: "MongoDB"
|
||||
url: "databases/connections/mongodb"
|
||||
- name: "MySQL"
|
||||
url: "databases/connections/mysql"
|
||||
- name: "Oracle"
|
||||
url: "databases/connections/oracle"
|
||||
- name: "PostgreSQL"
|
||||
url: "databases/connections/postgresql"
|
||||
- name: "Presto"
|
||||
url: "databases/connections/presto"
|
||||
- name: "Redshift"
|
||||
url: "databases/connections/redshift"
|
||||
- name: "Snowflake"
|
||||
url: "databases/connections/snowflake"
|
||||
- name: "SQL Server"
|
||||
url: "databases/connections/sql-server"
|
||||
- name: "SQLite"
|
||||
url: "databases/connections/sqlite"
|
||||
- name: "Spark SQL"
|
||||
url: "databases/connections/sparksql"
|
||||
- name: "Starburst"
|
||||
url: "databases/connections/starburst"
|
||||
- name: "Vertica"
|
||||
url: "databases/connections/vertica"
|
||||
- name: "Community drivers"
|
||||
url: "developers-guide/community-drivers"
|
||||
|
||||
- name: "Database users, roles, and privileges"
|
||||
url: "databases/users-roles-privileges"
|
||||
- name: "Writable connection"
|
||||
url: "databases/writable-connection"
|
||||
- name: "Syncing and scanning databases"
|
||||
url: "databases/sync-scan"
|
||||
- name: "Encrypting your database connection"
|
||||
url: "databases/encrypting-details-at-rest"
|
||||
- name: "SSH tunneling"
|
||||
url: "databases/ssh-tunnel"
|
||||
- name: "SSL certificate"
|
||||
url: "databases/ssl-certificates"
|
||||
- name: "Setting up data uploads"
|
||||
url: "databases/uploads"
|
||||
- name: "Uploading data"
|
||||
url: "exploration-and-organization/uploads"
|
||||
- name: "Metabase Cloud Storage"
|
||||
url: "cloud/storage"
|
||||
- name: "Sync Google Sheets"
|
||||
url: "cloud/google-sheets"
|
||||
- name: "Configuration"
|
||||
url: "configuring-metabase/start"
|
||||
pages:
|
||||
- name: "Setting up Metabase"
|
||||
url: "configuring-metabase/setting-up-metabase"
|
||||
- name: "General settings"
|
||||
url: "configuring-metabase/settings"
|
||||
- name: "Set up email"
|
||||
url: "configuring-metabase/email"
|
||||
- name: "Set up Slack"
|
||||
url: "configuring-metabase/slack"
|
||||
- name: "Webhooks"
|
||||
url: "configuring-metabase/webhooks"
|
||||
- name: "Environment variables"
|
||||
url: "configuring-metabase/environment-variables"
|
||||
- name: "Configuration file"
|
||||
url: "configuring-metabase/config-file"
|
||||
- name: "Config file template"
|
||||
url: "configuring-metabase/config-template"
|
||||
- name: "Metabase log configuration"
|
||||
url: "configuring-metabase/log-configuration"
|
||||
- name: "Timezones"
|
||||
url: "configuring-metabase/timezones"
|
||||
- name: "Languages and localization"
|
||||
url: "configuring-metabase/localization"
|
||||
- name: "Appearance"
|
||||
url: "configuring-metabase/appearance"
|
||||
- name: "Fonts"
|
||||
url: "configuring-metabase/fonts"
|
||||
- name: "Caching query results"
|
||||
url: "configuring-metabase/caching"
|
||||
- name: "Custom maps"
|
||||
url: "configuring-metabase/custom-maps"
|
||||
- name: "Customizing the Metabase Jetty webserver"
|
||||
url: "configuring-metabase/customizing-jetty-webserver"
|
||||
- name: "Operations and monitoring"
|
||||
url: "installation-and-operation/start"
|
||||
pages:
|
||||
- name: "Backing up Metabase"
|
||||
url: "installation-and-operation/backing-up-metabase-application-data"
|
||||
- name: "Development instances"
|
||||
url: "installation-and-operation/development-instance"
|
||||
- name: "Monitoring your Metabase"
|
||||
url: "installation-and-operation/monitoring-metabase"
|
||||
- name: "Observability with Prometheus"
|
||||
url: "installation-and-operation/observability-with-prometheus"
|
||||
- name: "Serialization"
|
||||
url: "installation-and-operation/serialization"
|
||||
- name: "Remote Sync"
|
||||
url: "installation-and-operation/remote-sync"
|
||||
- name: "Commands"
|
||||
url: "installation-and-operation/commands"
|
||||
- name: "Usage analytics"
|
||||
url: "usage-and-performance-tools/usage-analytics"
|
||||
- name: "Admin tools"
|
||||
url: "usage-and-performance-tools/tools"
|
||||
- name: "Authentication"
|
||||
url: "people-and-groups/start"
|
||||
pages:
|
||||
- name: "Account settings"
|
||||
url: "people-and-groups/account-settings"
|
||||
- name: "Password complexity"
|
||||
url: "people-and-groups/changing-password-complexity"
|
||||
- name: "Session expiration"
|
||||
url: "people-and-groups/changing-session-expiration"
|
||||
- name: "Google Sign-In"
|
||||
url: "people-and-groups/google-sign-in"
|
||||
- name: "LDAP"
|
||||
url: "people-and-groups/ldap"
|
||||
- name: "User provisioning"
|
||||
url: "people-and-groups/user-provisioning"
|
||||
- name: "API keys"
|
||||
url: "people-and-groups/api-keys"
|
||||
- name: "Paid SSO options"
|
||||
url: "people-and-groups/start"
|
||||
pages:
|
||||
- name: "JWT-based authentication"
|
||||
url: "people-and-groups/authenticating-with-jwt"
|
||||
- name: "SAML-based authentication"
|
||||
url: "people-and-groups/authenticating-with-saml"
|
||||
- name: "SAML with Auth0"
|
||||
url: "people-and-groups/saml-auth0"
|
||||
- name: "SAML with Microsoft Entra ID"
|
||||
url: "people-and-groups/saml-azure"
|
||||
- name: "SAML with Google"
|
||||
url: "people-and-groups/saml-google"
|
||||
- name: "SAML with Keycloak"
|
||||
url: "people-and-groups/saml-keycloak"
|
||||
- name: "SAML with Okta"
|
||||
url: "people-and-groups/saml-okta"
|
||||
- name: "OIDC-based authentication"
|
||||
url: "people-and-groups/authenticating-with-oidc"
|
||||
- name: "OIDC with Keycloak"
|
||||
url: "people-and-groups/oidc-keycloak"
|
||||
- name: "Permissions"
|
||||
url: "permissions/start"
|
||||
pages:
|
||||
- name: "Permissions introduction"
|
||||
url: "permissions/introduction"
|
||||
- name: "Managing people and groups"
|
||||
url: "people-and-groups/managing"
|
||||
- name: "Data permissions"
|
||||
url: "permissions/data"
|
||||
- name: "Collection permissions"
|
||||
url: "permissions/collections"
|
||||
- name: "Application permissions"
|
||||
url: "permissions/application"
|
||||
- name: "Row and column security"
|
||||
url: "permissions/row-and-column-security"
|
||||
- name: "Row and column security examples"
|
||||
url: "permissions/row-and-column-security-examples"
|
||||
- name: "Database routing"
|
||||
url: "permissions/database-routing"
|
||||
- name: "Impersonation"
|
||||
url: "permissions/impersonation"
|
||||
- name: "Snippets folder permissions"
|
||||
url: "permissions/snippets"
|
||||
- name: "Notification permissions"
|
||||
url: "permissions/notifications"
|
||||
- name: "Configuring permissions for embedding"
|
||||
url: "permissions/embedding"
|
||||
|
||||
- name: Other resources
|
||||
pages:
|
||||
- name: "API"
|
||||
url: "api"
|
||||
- name: "Metabase Cloud"
|
||||
url: "cloud/start"
|
||||
pages:
|
||||
- name: "Cloud vs self-hosted"
|
||||
url: "cloud/cloud-vs-self-hosting"
|
||||
- name: "Custom domain"
|
||||
url: "cloud/custom-domain"
|
||||
- name: "Cloud limitations"
|
||||
url: "cloud/limitations"
|
||||
- name: "Change region"
|
||||
url: "cloud/change-region"
|
||||
- name: "IP addresses to whitelist"
|
||||
url: "cloud/ip-addresses-to-whitelist"
|
||||
- name: "Migrate from self-hosted to Cloud"
|
||||
url: "cloud/migrate/guide"
|
||||
- name: "Migrate from Cloud to self-hosted"
|
||||
url: "cloud/migrate/cloud-to-self-hosted"
|
||||
- name: "Migrate from Heroku"
|
||||
url: "cloud/migrate/heroku"
|
||||
- name: "Billing"
|
||||
url: "cloud/how-billing-works"
|
||||
pages:
|
||||
- name: "How billing works"
|
||||
url: "cloud/how-billing-works"
|
||||
- name: "Accounts and billing"
|
||||
url: "cloud/accounts-and-billing"
|
||||
- name: "Troubleshooting"
|
||||
url: "troubleshooting-guide/"
|
||||
- name: "Developer guide"
|
||||
url: "developers-guide/start"
|
||||
- name: "Pro and Enterprise features"
|
||||
url: "/paid-features/"
|
||||
- name: "Accessibility"
|
||||
url: "installation-and-operation/accessibility"
|
||||
- name: "Supported browsers"
|
||||
url: "installation-and-operation/supported-browsers"
|
||||
- name: "Privacy"
|
||||
url: "installation-and-operation/privacy"
|
||||
- name: "About the anonymous usage data we collect"
|
||||
url: "installation-and-operation/information-collection"
|
||||
32
docs-build/package.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "@metabase/docs-build",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev:clean": "pkill -f 'docs-build/node_modules/.bin/astro' || true && rm -rf .astro node_modules/.astro dist",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"check": "node scripts/validate-nav.mjs && astro check",
|
||||
"test": "node --test 'src/plugins/**/*.test.mjs'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.9.9",
|
||||
"astro": "^6.3.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"mdast-util-from-markdown": "^2.0.2",
|
||||
"mdast-util-gfm": "^3.0.0",
|
||||
"micromark-extension-gfm": "^3.0.0",
|
||||
"rehype-autolink-headings": "^7.1.0",
|
||||
"rehype-external-links": "^3.0.0",
|
||||
"rehype-slug": "^6.0.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"typescript": "^6.0.3",
|
||||
"unist-util-visit": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^25.8.0"
|
||||
}
|
||||
}
|
||||
146
docs-build/public/css/inkeep.css
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Theme styles loaded by Inkeep's cxkit-js into its shadow DOM. The `.ikp-*`
|
||||
* selectors target debug class names emitted by the widget components — they
|
||||
* only resolve once cxkit imports this stylesheet via its `theme.styles` config.
|
||||
* Vendored from metabase.github.io/css/inkeep.css alongside the cxkit pin in
|
||||
* docs-build/public/js/inkeep.js.
|
||||
*/
|
||||
|
||||
inkeep-portal {
|
||||
position: relative;
|
||||
z-index: 100000;
|
||||
}
|
||||
|
||||
/* `--navigation-header-height` isn't set anywhere in the docs build, so the
|
||||
77px fallback is the effective value. The variable name is kept for parity
|
||||
with the marketing-site stylesheet this file is vendored from. */
|
||||
.ikp-modal__content {
|
||||
margin-top: var(--navigation-header-height, 77px);
|
||||
}
|
||||
|
||||
.ikp-modal__overlay {
|
||||
padding-top: var(--navigation-header-height, 77px);
|
||||
}
|
||||
|
||||
.ikp-search-bar-trigger__container {
|
||||
font-family: Lato, "Helvetica Neue", Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.ikp-modal-widget__content {
|
||||
font-family: Lato, sans-serif;
|
||||
}
|
||||
|
||||
.ikp-ai-chat__toolbar-header {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.ikp-search-bar-trigger__wrapper .ikp-btn {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #e2e4e9;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.ikp-search-bar-trigger__wrapper {
|
||||
background-color: #ffffff;
|
||||
border-radius: 6px;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
transition: background-color 0.3s ease, border-color 0.3s ease;
|
||||
}
|
||||
|
||||
.ikp-search-bar-trigger__wrapper:hover {
|
||||
background-color: #fafbfe;
|
||||
}
|
||||
|
||||
.ikp-search-bar-trigger__container--variant_subtle:is(:hover,[data-hover]) {
|
||||
background-color: #eef6fc !important;
|
||||
}
|
||||
|
||||
.ikp-ai-chat-page-wrapper {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.ikp-search-bar-trigger__text--size_compact {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ikp-switch-view-toggle {
|
||||
background-color: transparent;
|
||||
border: 1px solid #e2e4e9;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.ikp-switch-view-toggle button {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
margin-left: 6px;
|
||||
padding: 18px;
|
||||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
|
||||
.ikp-switch-view-toggle button:hover {
|
||||
background-color: #fafbfe;
|
||||
}
|
||||
|
||||
.ikp-switch-view-toggle button:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.ikp-switch-view-toggle__button--active {
|
||||
background-color: #eef6fc;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
color: #509ee3;
|
||||
}
|
||||
|
||||
.ikp-switch-view-toggle__button--active:hover {
|
||||
background-color: #eef6fc !important;
|
||||
border: none;
|
||||
color: #509ee3;
|
||||
}
|
||||
|
||||
.ikp-switch-view-toggle__button--active svg path {
|
||||
fill: #509ee3;
|
||||
}
|
||||
|
||||
.ikp-code {
|
||||
background-color: #f9fbfe;
|
||||
color: #5a6072;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.ikp-chat-bubble__user-message img {
|
||||
left: -2px;
|
||||
position: relative;
|
||||
zoom: 1.3;
|
||||
}
|
||||
|
||||
div.ikp-d_flex.ikp-flex_row.ikp-items_center.ikp-pt_3.ikp-pb_1:has(.ikp-search-bar-stand-alone__search-input) {
|
||||
margin-right: -2px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.ikp-ai-chat__message-box {
|
||||
background-color: #eef6fc;
|
||||
border: 1px solid #e4ecfb;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.ikp-ai-chat-footer__content-wrapper {
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 992px) {
|
||||
.ikp-search-bar-trigger__text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ikp-search-bar-trigger__icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
kbd {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
3
docs-build/public/images/chevron_blue_right.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="9" height="16" viewBox="0 0 9 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.5 4L6.5 8L2.5 12" stroke="#509EE3" stroke-width="1.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 169 B |
33
docs-build/public/images/favicon.svg
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="256px" height="256px" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Group</title>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Group">
|
||||
<rect id="Rectangle" x="0" y="0" width="256" height="256"></rect>
|
||||
<g id="favicon" transform="translate(22.000000, 0.000000)">
|
||||
<path d="M15.5514,76.0822 C24.1402,76.0822 31.1028,69.1196 31.1028,60.5308 C31.1028,51.942 24.1402,44.9794 15.5514,44.9794 C6.9626,44.9794 0,51.942 0,60.5308 C0,69.1196 6.9626,76.0822 15.5514,76.0822 Z" id="Path" fill="#509EE3"></path>
|
||||
<path d="M15.5514,121.062 C24.1402,121.062 31.1028,114.099 31.1028,105.51 C31.1028,96.9215 24.1402,89.9589 15.5514,89.9589 C6.9626,89.9589 0,96.9215 0,105.51 C0,114.099 6.9626,121.062 15.5514,121.062 Z" id="Path" fill="#509EE3"></path>
|
||||
<path d="M60.5309,121.062 C69.1197,121.062 76.0823,114.099 76.0823,105.51 C76.0823,96.9215 69.1197,89.9589 60.5309,89.9589 C51.9421,89.9589 44.9795,96.9215 44.9795,105.51 C44.9795,114.099 51.9421,121.062 60.5309,121.062 Z" id="Path" fill="#509EE3"></path>
|
||||
<path d="M195.469,76.0822 C204.058,76.0822 211.021,69.1196 211.021,60.5308 C211.021,51.942 204.058,44.9794 195.469,44.9794 C186.88,44.9794 179.918,51.942 179.918,60.5308 C179.918,69.1196 186.88,76.0822 195.469,76.0822 Z" id="Path" fill="#509EE3"></path>
|
||||
<path d="M150.49,121.062 C159.079,121.062 166.041,114.099 166.041,105.51 C166.041,96.9215 159.079,89.9589 150.49,89.9589 C141.901,89.9589 134.938,96.9215 134.938,105.51 C134.938,114.099 141.901,121.062 150.49,121.062 Z" id="Path" fill="#509EE3"></path>
|
||||
<path d="M105.51,166.041 C114.099,166.041 121.062,159.079 121.062,150.49 C121.062,141.901 114.099,134.938 105.51,134.938 C96.9215,134.938 89.9589,141.901 89.9589,150.49 C89.9589,159.079 96.9215,166.041 105.51,166.041 Z" id="Path" fill="#509EE3"></path>
|
||||
<path d="M195.469,121.062 C204.058,121.062 211.021,114.099 211.021,105.51 C211.021,96.9215 204.058,89.9589 195.469,89.9589 C186.88,89.9589 179.918,96.9215 179.918,105.51 C179.918,114.099 186.88,121.062 195.469,121.062 Z" id="Path" fill="#509EE3"></path>
|
||||
<path d="M15.5514,166.041 C24.1402,166.041 31.1028,159.079 31.1028,150.49 C31.1028,141.901 24.1402,134.938 15.5514,134.938 C6.9626,134.938 0,141.901 0,150.49 C0,159.079 6.9626,166.041 15.5514,166.041 Z" id="Path" fill="#509EE3"></path>
|
||||
<path d="M195.469,166.041 C204.058,166.041 211.021,159.079 211.021,150.49 C211.021,141.901 204.058,134.938 195.469,134.938 C186.88,134.938 179.918,141.901 179.918,150.49 C179.918,159.079 186.88,166.041 195.469,166.041 Z" id="Path" fill="#509EE3"></path>
|
||||
<path d="M15.5514,211.021 C24.1402,211.021 31.1028,204.058 31.1028,195.469 C31.1028,186.88 24.1402,179.918 15.5514,179.918 C6.9626,179.918 0,186.88 0,195.469 C0,204.058 6.9626,211.021 15.5514,211.021 Z" id="Path" fill="#509EE3"></path>
|
||||
<path d="M195.469,211.021 C204.058,211.021 211.021,204.058 211.021,195.469 C211.021,186.88 204.058,179.918 195.469,179.918 C186.88,179.918 179.918,186.88 179.918,195.469 C179.918,204.058 186.88,211.021 195.469,211.021 Z" id="Path" fill="#509EE3"></path>
|
||||
<path d="M60.5309,76.0822 C69.1197,76.0822 76.0823,69.1196 76.0823,60.5308 C76.0823,51.942 69.1197,44.9794 60.5309,44.9794 C51.9421,44.9794 44.9795,51.942 44.9795,60.5308 C44.9795,69.1196 51.9421,76.0822 60.5309,76.0822 Z" id="Path" fill="#C2DAF0"></path>
|
||||
<path d="M105.51,76.0822 C114.099,76.0822 121.062,69.1196 121.062,60.5308 C121.062,51.942 114.099,44.9794 105.51,44.9794 C96.9215,44.9794 89.9589,51.942 89.9589,60.5308 C89.9589,69.1196 96.9215,76.0822 105.51,76.0822 Z" id="Path" fill="#C2DAF0"></path>
|
||||
<path d="M105.51,31.1028 C114.099,31.1028 121.062,24.1402 121.062,15.5514 C121.062,6.9626 114.099,0 105.51,0 C96.9215,0 89.9589,6.9626 89.9589,15.5514 C89.9589,24.1402 96.9215,31.1028 105.51,31.1028 Z" id="Path" fill="#C2DAF0"></path>
|
||||
<path d="M105.51,121.062 C114.099,121.062 121.062,114.099 121.062,105.51 C121.062,96.9215 114.099,89.9589 105.51,89.9589 C96.9215,89.9589 89.9589,96.9215 89.9589,105.51 C89.9589,114.099 96.9215,121.062 105.51,121.062 Z" id="Path" fill="#C2DAF0"></path>
|
||||
<path d="M150.49,76.0822 C159.079,76.0822 166.041,69.1196 166.041,60.5308 C166.041,51.942 159.079,44.9794 150.49,44.9794 C141.901,44.9794 134.938,51.942 134.938,60.5308 C134.938,69.1196 141.901,76.0822 150.49,76.0822 Z" id="Path" fill="#C2DAF0"></path>
|
||||
<path d="M60.5309,166.041 C69.1197,166.041 76.0823,159.079 76.0823,150.49 C76.0823,141.901 69.1197,134.938 60.5309,134.938 C51.9421,134.938 44.9795,141.901 44.9795,150.49 C44.9795,159.079 51.9421,166.041 60.5309,166.041 Z" id="Path" fill="#C2DAF0"></path>
|
||||
<path d="M150.49,166.041 C159.079,166.041 166.041,159.079 166.041,150.49 C166.041,141.901 159.079,134.938 150.49,134.938 C141.901,134.938 134.938,141.901 134.938,150.49 C134.938,159.079 141.901,166.041 150.49,166.041 Z" id="Path" fill="#C2DAF0"></path>
|
||||
<path d="M60.5309,211.021 C69.1197,211.021 76.0823,204.058 76.0823,195.469 C76.0823,186.88 69.1197,179.918 60.5309,179.918 C51.9421,179.918 44.9795,186.88 44.9795,195.469 C44.9795,204.058 51.9421,211.021 60.5309,211.021 Z" id="Path" fill="#C2DAF0"></path>
|
||||
<path d="M105.51,211.021 C114.099,211.021 121.062,204.058 121.062,195.469 C121.062,186.88 114.099,179.918 105.51,179.918 C96.9215,179.918 89.9589,186.88 89.9589,195.469 C89.9589,204.058 96.9215,211.021 105.51,211.021 Z" id="Path" fill="#C2DAF0"></path>
|
||||
<path d="M105.51,256 C114.099,256 121.062,249.037 121.062,240.449 C121.062,231.86 114.099,224.897 105.51,224.897 C96.9215,224.897 89.9589,231.86 89.9589,240.449 C89.9589,249.037 96.9215,256 105.51,256 Z" id="Path" fill="#C2DAF0"></path>
|
||||
<path d="M150.49,211.021 C159.079,211.021 166.041,204.058 166.041,195.469 C166.041,186.88 159.079,179.918 150.49,179.918 C141.901,179.918 134.938,186.88 134.938,195.469 C134.938,204.058 141.901,211.021 150.49,211.021 Z" id="Path" fill="#C2DAF0"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.2 KiB |
4
docs-build/public/images/icons/Type=Learn.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 14.0929L20 9.42144L12 4.75L4 9.42144L12 14.0929ZM12 14.0929L17.4756 10.8957C18.196 12.8186 18.4005 14.4134 18.0667 16.45C15.8121 16.68 13.6864 17.6604 12 19.25C10.3138 17.6606 8.18852 16.6802 5.93422 16.45C5.60018 14.4134 5.80465 12.8185 6.52533 10.8957L12 14.0929Z" stroke="#509EE3" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12.5 9.5C12.5 9.7761 12.2761 10 12 10C11.7239 10 11.5 9.7761 11.5 9.5C11.5 9.2239 11.7239 9 12 9C12.2761 9 12.5 9.2239 12.5 9.5Z" stroke="#509EE3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 674 B |
1
docs-build/public/images/icons/stars.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"><path stroke="#509EE3" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M16.283 12.26 15.5 15l-.783-2.74a4.333 4.333 0 0 0-2.975-2.976L9 8.5l2.74-.783a4.333 4.333 0 0 0 2.976-2.975L15.5 2l.783 2.74a4.333 4.333 0 0 0 2.975 2.976L22 8.5l-2.74.783a4.334 4.334 0 0 0-2.976 2.975l-.001.001ZM6.5 22l.591-1.774a3.375 3.375 0 0 1 2.135-2.135L11 17.5l-1.774-.591a3.375 3.375 0 0 1-2.135-2.134L6.5 13l-.591 1.774a3.375 3.375 0 0 1-2.134 2.135L2 17.5l1.775.591a3.375 3.375 0 0 1 2.134 2.134L6.5 22Z"/></svg>
|
||||
|
After Width: | Height: | Size: 612 B |
32
docs-build/public/images/logo-with-wordmark.svg
Normal file
@@ -0,0 +1,32 @@
|
||||
<svg width="130" height="32" viewBox="0 0 130 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M53.85 7.98408V24.2246H50.8849V14.252C50.8849 14.043 50.8887 13.8192 50.8961 13.5804C50.911 13.3416 50.9334 13.0991 50.9633 12.8529L46.3199 21.6615C46.0662 22.1465 45.6746 22.389 45.145 22.389H44.6751C44.414 22.389 44.1828 22.3293 43.9814 22.2099C43.78 22.0831 43.6196 21.9002 43.5003 21.6615L38.8345 12.8193C38.8569 13.0805 38.8755 13.3342 38.8904 13.5804C38.9054 13.8192 38.9128 14.043 38.9128 14.252V24.2246H35.9478V7.98408H38.4988C38.6405 7.98408 38.7674 7.98781 38.8792 7.99527C38.9911 8.00274 39.0881 8.02512 39.1702 8.06243C39.2597 8.09228 39.338 8.14451 39.4051 8.21913C39.4797 8.28628 39.5506 8.38329 39.6177 8.51014L44.1604 17.1732C44.3021 17.4344 44.4327 17.703 44.552 17.9791C44.6788 18.2552 44.7982 18.5387 44.9101 18.8297C45.022 18.5313 45.1376 18.2403 45.2569 17.9567C45.3837 17.6732 45.518 17.4008 45.6597 17.1397L50.1688 8.51014C50.236 8.38329 50.3068 8.28628 50.3814 8.21913C50.456 8.14451 50.5343 8.09228 50.6164 8.06243C50.7059 8.02512 50.8029 8.00274 50.9073 7.99527C51.0192 7.98781 51.1497 7.98408 51.2989 7.98408H53.85Z" fill="#5A6072"/>
|
||||
<path d="M63.328 17.0949C63.328 16.774 63.2833 16.4681 63.1938 16.1771C63.1117 15.8861 62.9775 15.6286 62.791 15.4048C62.6045 15.1809 62.3695 15.0056 62.0861 14.8787C61.8026 14.7444 61.4632 14.6773 61.0679 14.6773C60.3294 14.6773 59.7513 14.8862 59.3336 15.3041C58.9159 15.7219 58.6436 16.3189 58.5168 17.0949H63.328ZM58.4721 18.9305C58.569 20.0124 58.8749 20.8034 59.3895 21.3033C59.9117 21.8032 60.5905 22.0532 61.4259 22.0532C61.8511 22.0532 62.2166 22.0047 62.5224 21.9077C62.8357 21.8032 63.108 21.6913 63.3392 21.5719C63.5779 21.4451 63.7905 21.3332 63.977 21.2362C64.1709 21.1317 64.3611 21.0795 64.5476 21.0795C64.7863 21.0795 64.9728 21.169 65.1071 21.3481L66.0022 22.4673C65.674 22.8479 65.3122 23.165 64.9169 23.4187C64.5215 23.665 64.1113 23.8627 63.6861 24.0119C63.2609 24.1537 62.832 24.2507 62.3994 24.3029C61.9667 24.3626 61.549 24.3925 61.1462 24.3925C60.3406 24.3925 59.5872 24.2619 58.886 24.0007C58.1923 23.7321 57.5844 23.3404 57.0623 22.8255C56.5476 22.3032 56.141 21.6577 55.8427 20.8892C55.5443 20.1206 55.3951 19.2289 55.3951 18.2141C55.3951 17.4307 55.5219 16.6919 55.7755 15.998C56.0366 15.3041 56.4096 14.6997 56.8944 14.1848C57.3793 13.6699 57.9686 13.2633 58.6623 12.9648C59.356 12.6589 60.1392 12.5059 61.0119 12.5059C61.7504 12.5059 62.4292 12.6253 63.0483 12.8641C63.6749 13.0954 64.212 13.4349 64.6595 13.8826C65.1145 14.3303 65.4651 14.8825 65.7113 15.5391C65.9649 16.1883 66.0917 16.9307 66.0917 17.7664C66.0917 17.9978 66.0805 18.188 66.0581 18.3373C66.0358 18.4865 65.9985 18.6059 65.9462 18.6954C65.894 18.785 65.8232 18.8484 65.7337 18.8857C65.6441 18.9155 65.5285 18.9305 65.3868 18.9305H58.4721Z" fill="#5A6072"/>
|
||||
<path d="M71.4125 24.4037C70.8904 24.4037 70.4279 24.3291 70.0251 24.1798C69.6223 24.0231 69.2829 23.803 69.0069 23.5194C68.7309 23.2359 68.5221 22.8927 68.3804 22.4897C68.2386 22.0868 68.1678 21.6316 68.1678 21.1242V14.9011H67.0824C66.9183 14.9011 66.7766 14.8489 66.6573 14.7444C66.5454 14.64 66.4894 14.4833 66.4894 14.2743V13.0655L68.3356 12.7298L68.9622 9.74132C69.0442 9.40555 69.2717 9.23766 69.6447 9.23766H71.2559V12.7521H74.2322V14.9011H71.2559V20.9228C71.2559 21.2436 71.3305 21.4973 71.4797 21.6839C71.6363 21.8704 71.8564 21.9637 72.1398 21.9637C72.289 21.9637 72.4158 21.9487 72.5202 21.9189C72.6247 21.8816 72.7142 21.8443 72.7888 21.807C72.8634 21.7622 72.9305 21.7249 72.9902 21.6951C73.0573 21.6577 73.1244 21.6391 73.1916 21.6391C73.2811 21.6391 73.3557 21.6615 73.4154 21.7062C73.475 21.7436 73.5347 21.8107 73.5944 21.9077L74.5231 23.3851C74.0979 23.7209 73.6168 23.9746 73.0797 24.1462C72.5426 24.3179 71.9869 24.4037 71.4125 24.4037Z" fill="#5A6072"/>
|
||||
<path d="M81.4698 19.4006C80.7164 19.4379 80.0898 19.505 79.5901 19.602C79.0903 19.6916 78.6912 19.811 78.3928 19.9602C78.1019 20.1094 77.8931 20.281 77.7663 20.4751C77.6469 20.6616 77.5872 20.8668 77.5872 21.0906C77.5872 21.5384 77.7103 21.8555 77.9565 22.042C78.2101 22.2286 78.5607 22.3218 79.0082 22.3218C79.5229 22.3218 79.9668 22.2323 80.3397 22.0532C80.7201 21.8667 81.0968 21.5794 81.4698 21.1914V19.4006ZM75.1816 14.2855C75.8604 13.6737 76.6101 13.2185 77.4306 12.92C78.2511 12.6141 79.1388 12.4611 80.0936 12.4611C80.7798 12.4611 81.3915 12.5731 81.9285 12.7969C82.4731 13.0208 82.9318 13.3342 83.3048 13.7371C83.6852 14.1326 83.9761 14.6064 84.1775 15.1586C84.3789 15.7107 84.4796 16.3151 84.4796 16.9718V24.2246H83.0698C82.7789 24.2246 82.5551 24.1835 82.3985 24.1015C82.2493 24.0194 82.1262 23.8478 82.0292 23.5866L81.7831 22.8479C81.4922 23.1016 81.2087 23.3254 80.9327 23.5194C80.6642 23.7135 80.3845 23.8776 80.0936 24.0119C79.8027 24.1388 79.4894 24.2358 79.1537 24.3029C78.8255 24.3701 78.4563 24.4037 78.046 24.4037C77.5388 24.4037 77.0763 24.3365 76.6586 24.2022C76.2409 24.0679 75.8791 23.8702 75.5732 23.609C75.2749 23.3404 75.0436 23.0083 74.8795 22.6128C74.7154 22.2174 74.6334 21.7622 74.6334 21.2473C74.6334 20.822 74.7415 20.3967 74.9579 19.9714C75.1742 19.5461 75.5434 19.1618 76.0656 18.8185C76.5952 18.4678 77.2963 18.1806 78.1691 17.9567C79.0493 17.7254 80.1495 17.5948 81.4698 17.565V16.9718C81.4698 16.248 81.3169 15.7182 81.0111 15.3824C80.7052 15.0392 80.2651 14.8675 79.6908 14.8675C79.2656 14.8675 78.9113 14.9198 78.6278 15.0242C78.3444 15.1212 78.0945 15.2294 77.8782 15.3488C77.6693 15.4608 77.4716 15.569 77.2851 15.6734C77.0987 15.7704 76.8823 15.8189 76.6362 15.8189C76.4199 15.8189 76.2371 15.7667 76.0879 15.6622C75.9462 15.5503 75.8269 15.4197 75.7299 15.2705L75.1816 14.2855Z" fill="#5A6072"/>
|
||||
<path d="M89.5307 21.1802C89.8216 21.516 90.1349 21.751 90.4706 21.8853C90.8137 22.0196 91.168 22.0868 91.5335 22.0868C91.9139 22.0868 92.2571 22.0196 92.5629 21.8853C92.8762 21.7436 93.1447 21.5234 93.3685 21.225C93.5923 20.919 93.7638 20.5236 93.8832 20.0385C94.0025 19.5535 94.0622 18.9678 94.0622 18.2813C94.0622 17.6694 94.0137 17.1508 93.9167 16.7255C93.8198 16.2927 93.6743 15.9383 93.4804 15.6622C93.2939 15.3861 93.0627 15.1847 92.7867 15.0578C92.5107 14.931 92.1899 14.8675 91.8244 14.8675C91.3396 14.8675 90.9181 14.9757 90.5601 15.1921C90.2095 15.4011 89.8664 15.7107 89.5307 16.1211V21.1802ZM89.5307 14.0169C89.9857 13.5617 90.4892 13.1961 91.0412 12.92C91.5932 12.6439 92.2123 12.5059 92.8986 12.5059C93.5624 12.5059 94.1592 12.6402 94.6888 12.9088C95.2258 13.17 95.6809 13.5468 96.0538 14.0393C96.4342 14.5318 96.7252 15.1287 96.9266 15.8301C97.1354 16.5241 97.2398 17.3038 97.2398 18.1694C97.2398 19.1021 97.1205 19.9527 96.8818 20.7213C96.6506 21.4824 96.3186 22.1353 95.886 22.68C95.4534 23.2247 94.9349 23.6463 94.3307 23.9448C93.734 24.2432 93.0664 24.3925 92.3279 24.3925C91.9699 24.3925 91.6491 24.3552 91.3657 24.2805C91.0822 24.2134 90.8212 24.1164 90.5825 23.9895C90.3438 23.8627 90.1237 23.7135 89.9223 23.5418C89.7209 23.3628 89.5307 23.165 89.3517 22.9486L89.2286 23.6426C89.1764 23.859 89.0906 24.0119 88.9713 24.1015C88.8519 24.1835 88.6915 24.2246 88.4901 24.2246H86.4426V7.53638H89.5307V14.0169Z" fill="#5A6072"/>
|
||||
<path d="M104.81 19.4006C104.057 19.4379 103.43 19.505 102.93 19.602C102.431 19.6916 102.031 19.811 101.733 19.9602C101.442 20.1094 101.233 20.281 101.106 20.4751C100.987 20.6616 100.927 20.8668 100.927 21.0906C100.927 21.5384 101.051 21.8555 101.297 22.042C101.55 22.2286 101.901 22.3218 102.348 22.3218C102.863 22.3218 103.307 22.2323 103.68 22.0532C104.06 21.8667 104.437 21.5794 104.81 21.1914V19.4006ZM98.5219 14.2855C99.2006 13.6737 99.9503 13.2185 100.771 12.92C101.591 12.6141 102.479 12.4611 103.434 12.4611C104.12 12.4611 104.732 12.5731 105.269 12.7969C105.813 13.0208 106.272 13.3342 106.645 13.7371C107.025 14.1326 107.316 14.6064 107.518 15.1586C107.719 15.7107 107.82 16.3151 107.82 16.9718V24.2246H106.41C106.119 24.2246 105.895 24.1835 105.739 24.1015C105.59 24.0194 105.466 23.8478 105.369 23.5866L105.123 22.8479C104.832 23.1016 104.549 23.3254 104.273 23.5194C104.004 23.7135 103.725 23.8776 103.434 24.0119C103.143 24.1388 102.83 24.2358 102.494 24.3029C102.166 24.3701 101.796 24.4037 101.386 24.4037C100.879 24.4037 100.417 24.3365 99.9988 24.2022C99.5811 24.0679 99.2193 23.8702 98.9135 23.609C98.6151 23.3404 98.3839 23.0083 98.2198 22.6128C98.0556 22.2174 97.9736 21.7622 97.9736 21.2473C97.9736 20.822 98.0818 20.3967 98.2981 19.9714C98.5144 19.5461 98.8836 19.1618 99.4058 18.8185C99.9354 18.4678 100.637 18.1806 101.509 17.9567C102.389 17.7254 103.49 17.5948 104.81 17.565V16.9718C104.81 16.248 104.657 15.7182 104.351 15.3824C104.045 15.0392 103.605 14.8675 103.031 14.8675C102.606 14.8675 102.251 14.9198 101.968 15.0242C101.685 15.1212 101.435 15.2294 101.218 15.3488C101.01 15.4608 100.812 15.569 100.625 15.6734C100.439 15.7704 100.223 15.8189 99.9764 15.8189C99.7601 15.8189 99.5773 15.7667 99.4282 15.6622C99.2864 15.5503 99.1671 15.4197 99.0701 15.2705L98.5219 14.2855Z" fill="#5A6072"/>
|
||||
<path d="M116.955 15.0578C116.873 15.1847 116.787 15.2779 116.698 15.3376C116.615 15.3899 116.5 15.416 116.351 15.416C116.201 15.416 116.049 15.3787 115.892 15.3041C115.735 15.2294 115.56 15.1511 115.366 15.069C115.172 14.9795 114.948 14.8974 114.695 14.8228C114.449 14.7482 114.161 14.7109 113.833 14.7109C113.333 14.7109 112.946 14.8153 112.67 15.0242C112.394 15.2257 112.256 15.4943 112.256 15.8301C112.256 16.0614 112.334 16.2554 112.49 16.4121C112.647 16.5688 112.852 16.7069 113.106 16.8263C113.367 16.9382 113.662 17.0464 113.99 17.1508C114.318 17.2478 114.654 17.356 114.997 17.4754C115.347 17.5948 115.687 17.7329 116.015 17.8896C116.343 18.0463 116.634 18.244 116.888 18.4828C117.149 18.7141 117.358 18.9976 117.514 19.3334C117.671 19.6617 117.749 20.0609 117.749 20.531C117.749 21.0906 117.645 21.6092 117.436 22.0868C117.235 22.5569 116.936 22.9635 116.541 23.3068C116.146 23.65 115.653 23.9187 115.064 24.1127C114.475 24.3067 113.796 24.4037 113.028 24.4037C112.632 24.4037 112.241 24.3664 111.853 24.2917C111.465 24.2246 111.096 24.1276 110.745 24.0007C110.394 23.8664 110.066 23.7135 109.76 23.5418C109.455 23.3702 109.19 23.1837 108.966 22.9822L109.682 21.8294C109.764 21.6951 109.865 21.5906 109.984 21.516C110.111 21.4414 110.271 21.404 110.465 21.404C110.652 21.404 110.823 21.4525 110.98 21.5495C111.137 21.6391 111.312 21.7398 111.506 21.8517C111.7 21.9562 111.927 22.0569 112.188 22.1539C112.457 22.2435 112.789 22.2883 113.184 22.2883C113.483 22.2883 113.74 22.2547 113.956 22.1875C114.173 22.1204 114.348 22.0308 114.482 21.9189C114.616 21.7995 114.713 21.6689 114.773 21.5272C114.84 21.3779 114.874 21.225 114.874 21.0683C114.874 20.8146 114.792 20.6094 114.628 20.4527C114.471 20.2885 114.262 20.1467 114.001 20.0273C113.747 19.908 113.453 19.7998 113.117 19.7028C112.781 19.6058 112.438 19.4976 112.088 19.3782C111.745 19.2588 111.405 19.117 111.07 18.9529C110.741 18.7887 110.447 18.5835 110.186 18.3373C109.932 18.0836 109.723 17.7739 109.559 17.4083C109.402 17.0426 109.324 16.5987 109.324 16.0764C109.324 15.5988 109.417 15.1474 109.604 14.722C109.79 14.2893 110.07 13.9087 110.443 13.5804C110.816 13.2521 111.278 12.9909 111.83 12.7969C112.39 12.6029 113.035 12.5059 113.766 12.5059C114.587 12.5059 115.332 12.6402 116.004 12.9088C116.675 13.1775 117.227 13.5282 117.66 13.9609L116.955 15.0578Z" fill="#5A6072"/>
|
||||
<path d="M126.552 17.0949C126.552 16.774 126.508 16.4681 126.418 16.1771C126.336 15.8861 126.202 15.6286 126.015 15.4048C125.829 15.1809 125.594 15.0056 125.31 14.8787C125.027 14.7444 124.688 14.6773 124.292 14.6773C123.554 14.6773 122.976 14.8862 122.558 15.3041C122.14 15.7219 121.868 16.3189 121.741 17.0949H126.552ZM121.696 18.9305C121.793 20.0124 122.099 20.8034 122.614 21.3033C123.136 21.8032 123.815 22.0532 124.65 22.0532C125.075 22.0532 125.441 22.0047 125.747 21.9077C126.06 21.8032 126.332 21.6913 126.564 21.5719C126.802 21.4451 127.015 21.3332 127.201 21.2362C127.395 21.1317 127.585 21.0795 127.772 21.0795C128.011 21.0795 128.197 21.169 128.331 21.3481L129.226 22.4673C128.898 22.8479 128.536 23.165 128.141 23.4187C127.746 23.665 127.336 23.8627 126.91 24.0119C126.485 24.1537 126.056 24.2507 125.624 24.3029C125.191 24.3626 124.773 24.3925 124.37 24.3925C123.565 24.3925 122.812 24.2619 122.11 24.0007C121.417 23.7321 120.809 23.3404 120.287 22.8255C119.772 22.3032 119.365 21.6577 119.067 20.8892C118.769 20.1206 118.619 19.2289 118.619 18.2141C118.619 17.4307 118.746 16.6919 119 15.998C119.261 15.3041 119.634 14.6997 120.119 14.1848C120.604 13.6699 121.193 13.2633 121.887 12.9648C122.58 12.6589 123.363 12.5059 124.236 12.5059C124.975 12.5059 125.653 12.6253 126.273 12.8641C126.899 13.0954 127.436 13.4349 127.884 13.8826C128.339 14.3303 128.689 14.8825 128.936 15.5391C129.189 16.1883 129.316 16.9307 129.316 17.7664C129.316 17.9978 129.305 18.188 129.282 18.3373C129.26 18.4865 129.223 18.6059 129.171 18.6954C129.118 18.785 129.047 18.8484 128.958 18.8857C128.868 18.9155 128.753 18.9305 128.611 18.9305H121.696Z" fill="#5A6072"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.94393 9.51017C3.01753 9.51017 3.88785 8.63984 3.88785 7.56624C3.88785 6.49264 3.01753 5.62231 1.94393 5.62231C0.870325 5.62231 0 6.49264 0 7.56624C0 8.63984 0.870325 9.51017 1.94393 9.51017Z" fill="#509EE3"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.94393 15.1327C3.01753 15.1327 3.88785 14.2624 3.88785 13.1888C3.88785 12.1152 3.01753 11.2449 1.94393 11.2449C0.870325 11.2449 0 12.1152 0 13.1888C0 14.2624 0.870325 15.1327 1.94393 15.1327Z" fill="#509EE3"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.56648 15.1327C8.64008 15.1327 9.51041 14.2624 9.51041 13.1888C9.51041 12.1152 8.64008 11.2449 7.56648 11.2449C6.49288 11.2449 5.62256 12.1152 5.62256 13.1888C5.62256 14.2624 6.49288 15.1327 7.56648 15.1327Z" fill="#509EE3"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.4337 9.51017C25.5073 9.51017 26.3776 8.63984 26.3776 7.56624C26.3776 6.49264 25.5073 5.62231 24.4337 5.62231C23.3601 5.62231 22.4897 6.49264 22.4897 7.56624C22.4897 8.63984 23.3601 9.51017 24.4337 9.51017Z" fill="#509EE3"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.8111 15.1327C19.8847 15.1327 20.755 14.2624 20.755 13.1888C20.755 12.1152 19.8847 11.2449 18.8111 11.2449C17.7375 11.2449 16.8672 12.1152 16.8672 13.1888C16.8672 14.2624 17.7375 15.1327 18.8111 15.1327Z" fill="#509EE3"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1886 20.755C14.2622 20.755 15.1325 19.8847 15.1325 18.8111C15.1325 17.7375 14.2622 16.8672 13.1886 16.8672C12.115 16.8672 11.2446 17.7375 11.2446 18.8111C11.2446 19.8847 12.115 20.755 13.1886 20.755Z" fill="#509EE3"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.4337 15.1327C25.5073 15.1327 26.3776 14.2624 26.3776 13.1888C26.3776 12.1152 25.5073 11.2449 24.4337 11.2449C23.3601 11.2449 22.4897 12.1152 22.4897 13.1888C22.4897 14.2624 23.3601 15.1327 24.4337 15.1327Z" fill="#509EE3"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.94393 20.755C3.01753 20.755 3.88785 19.8847 3.88785 18.8111C3.88785 17.7375 3.01753 16.8672 1.94393 16.8672C0.870325 16.8672 0 17.7375 0 18.8111C0 19.8847 0.870325 20.755 1.94393 20.755Z" fill="#509EE3"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.4337 20.755C25.5073 20.755 26.3776 19.8847 26.3776 18.8111C26.3776 17.7375 25.5073 16.8672 24.4337 16.8672C23.3601 16.8672 22.4897 17.7375 22.4897 18.8111C22.4897 19.8847 23.3601 20.755 24.4337 20.755Z" fill="#509EE3"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.94393 26.3776C3.01753 26.3776 3.88785 25.5073 3.88785 24.4337C3.88785 23.3601 3.01753 22.4897 1.94393 22.4897C0.870325 22.4897 0 23.3601 0 24.4337C0 25.5073 0.870325 26.3776 1.94393 26.3776Z" fill="#509EE3"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.4337 26.3776C25.5073 26.3776 26.3776 25.5073 26.3776 24.4337C26.3776 23.3601 25.5073 22.4897 24.4337 22.4897C23.3601 22.4897 22.4897 23.3601 22.4897 24.4337C22.4897 25.5073 23.3601 26.3776 24.4337 26.3776Z" fill="#509EE3"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.56648 9.51017C8.64008 9.51017 9.51041 8.63984 9.51041 7.56624C9.51041 6.49264 8.64008 5.62231 7.56648 5.62231C6.49288 5.62231 5.62256 6.49264 5.62256 7.56624C5.62256 8.63984 6.49288 9.51017 7.56648 9.51017Z" fill="#C2DAF0"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1886 9.51017C14.2622 9.51017 15.1325 8.63984 15.1325 7.56624C15.1325 6.49264 14.2622 5.62231 13.1886 5.62231C12.115 5.62231 11.2446 6.49264 11.2446 7.56624C11.2446 8.63984 12.115 9.51017 13.1886 9.51017Z" fill="#C2DAF0"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1886 3.88785C14.2622 3.88785 15.1325 3.01753 15.1325 1.94393C15.1325 0.870325 14.2622 0 13.1886 0C12.115 0 11.2446 0.870325 11.2446 1.94393C11.2446 3.01753 12.115 3.88785 13.1886 3.88785Z" fill="#C2DAF0"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1886 15.1327C14.2622 15.1327 15.1325 14.2624 15.1325 13.1888C15.1325 12.1152 14.2622 11.2449 13.1886 11.2449C12.115 11.2449 11.2446 12.1152 11.2446 13.1888C11.2446 14.2624 12.115 15.1327 13.1886 15.1327Z" fill="#C2DAF0"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.8111 9.51017C19.8847 9.51017 20.755 8.63984 20.755 7.56624C20.755 6.49264 19.8847 5.62231 18.8111 5.62231C17.7375 5.62231 16.8672 6.49264 16.8672 7.56624C16.8672 8.63984 17.7375 9.51017 18.8111 9.51017Z" fill="#C2DAF0"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.56648 20.755C8.64008 20.755 9.51041 19.8847 9.51041 18.8111C9.51041 17.7375 8.64008 16.8672 7.56648 16.8672C6.49288 16.8672 5.62256 17.7375 5.62256 18.8111C5.62256 19.8847 6.49288 20.755 7.56648 20.755Z" fill="#C2DAF0"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.8111 20.755C19.8847 20.755 20.755 19.8847 20.755 18.8111C20.755 17.7375 19.8847 16.8672 18.8111 16.8672C17.7375 16.8672 16.8672 17.7375 16.8672 18.8111C16.8672 19.8847 17.7375 20.755 18.8111 20.755Z" fill="#C2DAF0"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.56648 26.3776C8.64008 26.3776 9.51041 25.5073 9.51041 24.4337C9.51041 23.3601 8.64008 22.4897 7.56648 22.4897C6.49288 22.4897 5.62256 23.3601 5.62256 24.4337C5.62256 25.5073 6.49288 26.3776 7.56648 26.3776Z" fill="#C2DAF0"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1886 26.3776C14.2622 26.3776 15.1325 25.5073 15.1325 24.4337C15.1325 23.3601 14.2622 22.4897 13.1886 22.4897C12.115 22.4897 11.2446 23.3601 11.2446 24.4337C11.2446 25.5073 12.115 26.3776 13.1886 26.3776Z" fill="#C2DAF0"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.1886 31.9999C14.2622 31.9999 15.1325 31.1296 15.1325 30.056C15.1325 28.9824 14.2622 28.1121 13.1886 28.1121C12.115 28.1121 11.2446 28.9824 11.2446 30.056C11.2446 31.1296 12.115 31.9999 13.1886 31.9999Z" fill="#C2DAF0"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.8111 26.3776C19.8847 26.3776 20.755 25.5073 20.755 24.4337C20.755 23.3601 19.8847 22.4897 18.8111 22.4897C17.7375 22.4897 16.8672 23.3601 16.8672 24.4337C16.8672 25.5073 17.7375 26.3776 18.8111 26.3776Z" fill="#C2DAF0"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 18 KiB |
29
docs-build/public/images/logo.svg
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="50px" height="50px" viewBox="0 0 50 50" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
|
||||
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g fill="#509EE3">
|
||||
<ellipse id="Oval-7" sketch:type="MSShapeGroup" cx="10.5757576" cy="13.9911029" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-7" sketch:type="MSShapeGroup" cx="10.5757576" cy="21.3185048" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-7" opacity="0.2" sketch:type="MSShapeGroup" cx="17.7878788" cy="13.9911029" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-7" sketch:type="MSShapeGroup" cx="17.7878788" cy="21.3896853" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-8" opacity="0.2" sketch:type="MSShapeGroup" cx="25" cy="13.9911029" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-8" opacity="0.2" sketch:type="MSShapeGroup" cx="25" cy="5.61692926" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-8" opacity="0.2" sketch:type="MSShapeGroup" cx="25" cy="21.3896853" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-9" opacity="0.2" sketch:type="MSShapeGroup" cx="32.2121212" cy="13.9911029" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-9" sketch:type="MSShapeGroup" cx="39.4242424" cy="13.9911029" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-9" sketch:type="MSShapeGroup" cx="32.2121212" cy="21.3896853" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-9" sketch:type="MSShapeGroup" cx="39.4242424" cy="21.3896853" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-13" sketch:type="MSShapeGroup" cx="10.5757576" cy="28.6459067" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-12" opacity="0.2" sketch:type="MSShapeGroup" cx="17.7878788" cy="28.7170872" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-11" sketch:type="MSShapeGroup" cx="25" cy="28.7170872" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-10" opacity="0.2" sketch:type="MSShapeGroup" cx="32.2121212" cy="28.7170872" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-10" sketch:type="MSShapeGroup" cx="39.4242424" cy="28.7170872" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-17" sketch:type="MSShapeGroup" cx="10.5757576" cy="35.9733086" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-16" opacity="0.2" sketch:type="MSShapeGroup" cx="17.7878788" cy="36.0444891" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-15" opacity="0.2" sketch:type="MSShapeGroup" cx="25" cy="36.0444891" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-15" opacity="0.2" sketch:type="MSShapeGroup" cx="25" cy="43.371891" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-14" opacity="0.2" sketch:type="MSShapeGroup" cx="32.2121212" cy="36.0444891" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
<ellipse id="Oval-14" sketch:type="MSShapeGroup" cx="39.4242424" cy="36.0444891" rx="2.57575758" ry="2.61692926"></ellipse>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg width="64" height="48" viewBox="0 0 64 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_2621_882)">
|
||||
<rect x="0.75" y="0.75" width="62.5" height="46.5" rx="8" fill="#EEF6FC" stroke="#E4ECFB" stroke-width="1.5" stroke-linecap="round" stroke-dasharray="4 4"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2621_882">
|
||||
<rect width="64" height="48" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 404 B |
19
docs-build/public/images/navigation-header/embed.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<svg width="64" height="56" viewBox="0 0 64 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_2621_842)">
|
||||
<rect x="0.5" y="0.5" width="63" height="47" rx="7.5" fill="#EEF6FC"/>
|
||||
<rect x="0.5" y="0.5" width="63" height="47" rx="7.5" stroke="#8FC1ED"/>
|
||||
<path opacity="0.5" d="M7.01377 11.0626C7.01419 10.2859 7.42073 9.65603 7.9218 9.65576L24.0259 9.64712C24.527 9.64685 24.9329 10.2763 24.9324 11.0529L24.9317 12.4827C24.9313 13.2594 24.5247 13.8893 24.0236 13.8895L7.91953 13.8982C7.41845 13.8984 7.01259 13.269 7.01301 12.4924L7.01377 11.0626Z" fill="#8FC1ED"/>
|
||||
<path opacity="0.5" d="M7.00939 18.8221C7.00981 18.0454 7.41635 17.4156 7.91742 17.4153L16.0829 17.4109C16.584 17.4106 16.9898 18.04 16.9894 18.8167L16.9887 20.223C16.9882 20.9997 16.5817 21.6295 16.0806 21.6298L7.91516 21.6342C7.41409 21.6345 7.00822 21.0051 7.00864 20.2284L7.00939 18.8221Z" fill="#8FC1ED"/>
|
||||
<path opacity="0.5" d="M7.00547 26.5546C7.00589 25.7779 7.41243 25.1481 7.9135 25.1478L21.5226 25.1405C22.0237 25.1403 22.4296 25.7697 22.4291 26.5463L22.4284 27.9526C22.428 28.7293 22.0214 29.3592 21.5204 29.3594L7.91124 29.3667C7.41017 29.367 7.0043 28.7376 7.00472 27.9609L7.00547 26.5546Z" fill="#8FC1ED"/>
|
||||
<path opacity="0.5" d="M7.00151 34.2916C7.00192 33.5149 7.40846 32.8851 7.90953 32.8848L15.1677 32.8809C15.6688 32.8806 16.0747 33.51 16.0742 34.2867L16.0735 35.693C16.0731 36.4697 15.6665 37.0995 15.1655 37.0998L7.90727 37.1037C7.4062 37.104 7.00034 36.4746 7.00075 35.6979L7.00151 34.2916Z" fill="#8FC1ED"/>
|
||||
<path opacity="0.5" d="M57.6035 24.1784C57.6053 20.8371 56.5162 17.5898 54.5051 14.9401C52.494 12.2904 49.6734 10.3864 46.4807 9.52334C43.2879 8.66027 39.9015 8.88642 36.8466 10.1667C33.7917 11.447 31.239 13.7098 29.5846 16.6043C27.9301 19.4988 27.2662 22.8631 27.696 26.1756C28.1257 29.488 29.625 32.5634 31.9613 34.9247C34.2976 37.2861 37.3404 38.8014 40.6178 39.2358C43.8951 39.6702 47.2238 38.9992 50.0877 37.3271L45.5867 29.4427C44.4411 30.1115 43.1096 30.3799 41.7987 30.2062C40.4878 30.0324 39.2707 29.4263 38.3361 28.4817C37.4016 27.5372 36.8019 26.307 36.63 24.9821C36.4581 23.6571 36.7236 22.3114 37.3854 21.1536C38.0472 19.9958 39.0683 19.0906 40.2902 18.5785C41.5122 18.0664 42.8668 17.976 44.1439 18.3212C45.421 18.6664 46.5492 19.428 47.3536 20.4879C48.1581 21.5478 48.5937 22.8467 48.593 24.1832L57.6035 24.1784Z" fill="#8FC1ED"/>
|
||||
<path opacity="0.5" d="M51.994 36.0067C54.7574 33.759 56.6458 30.5965 57.3252 27.079L48.4817 25.3434C48.2099 26.7504 47.4545 28.0154 46.3492 28.9145L51.994 36.0067Z" fill="#8FC1ED"/>
|
||||
<path d="M20.2911 53.7822C20.3489 54.2162 19.7965 55.6534 18.8437 55.7656C17.8909 55.8778 16.8828 55.2402 16.8828 55.2402L15.9588 50.7049L11.5829 53.2376L15.7665 37.6243L23.4962 51.0458L19.5591 49.9909C19.6528 50.4139 20.2178 53.2308 20.2911 53.7822Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.0913 40.1135L13.1081 51.247L16.6313 49.2078L17.7028 54.467C17.7028 54.467 18.2753 54.835 18.6286 54.715C18.9819 54.5951 19.3881 54.2718 19.3401 53.9113C19.1138 52.2095 18.2686 48.6522 18.2686 48.6522L21.5089 49.5204L16.0913 40.1135Z" fill="#1C6BB0"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2621_842">
|
||||
<rect width="64" height="56" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
71
docs-build/public/js/anchor-links.js
Normal file
@@ -0,0 +1,71 @@
|
||||
// Adds a "copy link to section" icon next to each h2/h3 heading.
|
||||
// Headings already have ids from rehype-slug.
|
||||
(function () {
|
||||
function htmlToElement(html) {
|
||||
const t = document.createElement("template");
|
||||
t.innerHTML = html.trim();
|
||||
return t.content.firstChild;
|
||||
}
|
||||
|
||||
function hasParentLink(node) {
|
||||
let p = node.parentNode;
|
||||
while (p) {
|
||||
const n = p.nodeName.toLowerCase();
|
||||
if (n === "a") return true;
|
||||
if (n === "body") return false;
|
||||
p = p.parentNode;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function attachCopyHandler(el, link) {
|
||||
const tip = el.querySelector(".copy-to-clipboard-wrapper");
|
||||
const tipDone = el.querySelector(".copied-to-clipboard-wrapper");
|
||||
let tHide = null, tFade = null;
|
||||
|
||||
el.addEventListener("mouseenter", () => {
|
||||
tip.classList.remove("invisible");
|
||||
tip.style.opacity = "1";
|
||||
});
|
||||
el.addEventListener("mouseleave", () => {
|
||||
tip.classList.add("invisible");
|
||||
tip.style.opacity = "0";
|
||||
});
|
||||
|
||||
el.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
navigator.clipboard.writeText(link);
|
||||
if (tHide) {
|
||||
clearTimeout(tHide);
|
||||
clearTimeout(tFade);
|
||||
}
|
||||
tip.classList.add("invisible");
|
||||
tip.style.opacity = "0";
|
||||
tipDone.classList.remove("invisible");
|
||||
tipDone.style.opacity = "1";
|
||||
tHide = setTimeout(() => tipDone.classList.add("invisible"), 1200);
|
||||
tFade = setTimeout(() => (tipDone.style.opacity = "0"), 1000);
|
||||
});
|
||||
}
|
||||
|
||||
const ICON_HTML =
|
||||
'<a class="copy-clip" title="Copy link to this section">' +
|
||||
'<svg width="20" height="20" viewBox="0 0 32 32" fill="#C6C9D2" xmlns="http://www.w3.org/2000/svg">' +
|
||||
'<path fill-rule="evenodd" clip-rule="evenodd" d="M19.3 9.2a2.6 2.6 0 0 1 3.7 3.7l-3.7 3.7a2.6 2.6 0 0 1-3.7 0 .9.9 0 1 0-1.3 1.3 4.4 4.4 0 0 0 6.2 0l3.7-3.7a4.4 4.4 0 0 0-6.2-6.2L16 9.7a.9.9 0 1 0 1.3 1.3l2-1.8zM13 15.4a2.6 2.6 0 0 1 3.7 0 .9.9 0 1 0 1.3-1.3 4.4 4.4 0 0 0-6.2 0l-3.8 3.8a4.4 4.4 0 0 0 6.2 6.2L16 22.6a.9.9 0 1 0-1.3-1.3l-1.9 1.8a2.6 2.6 0 1 1-3.7-3.7l3.8-3.8z"/>' +
|
||||
"</svg>" +
|
||||
'<div class="copy-to-clipboard-wrapper invisible"><div class="copied-to-clipboard">Copy link</div></div>' +
|
||||
'<div class="copied-to-clipboard-wrapper invisible"><div class="copied-to-clipboard">Copied</div></div>' +
|
||||
"</a>";
|
||||
|
||||
document.querySelectorAll("article.docs-content h2[id], article.docs-content h3[id]").forEach((h) => {
|
||||
if (hasParentLink(h) || h.innerHTML.indexOf("<a ") > -1) return;
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.classList.add("copy-clip-container", h.tagName.toLowerCase());
|
||||
h.parentNode.replaceChild(wrapper, h);
|
||||
wrapper.appendChild(h);
|
||||
const icon = htmlToElement(ICON_HTML);
|
||||
wrapper.appendChild(icon);
|
||||
const url = location.href.split("#")[0] + "#" + h.id;
|
||||
attachCopyHandler(icon, url);
|
||||
});
|
||||
})();
|
||||
47
docs-build/public/js/code-snippet-copy.js
Normal file
@@ -0,0 +1,47 @@
|
||||
// Adds a copy button to every <pre><code> block.
|
||||
(function () {
|
||||
const BUTTON_HTML =
|
||||
'<div class="copy-button-copy-wrapper">' +
|
||||
'<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">' +
|
||||
'<path d="M6.75 15.5C5.78 15.5 5 14.7 5 13.75V7a2 2 0 0 1 2-2h6.75c1 0 1.75.8 1.75 1.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>' +
|
||||
'<path d="M17.5 9H11a2 2 0 0 0-2 2v6.5a2 2 0 0 0 2 2h6.5a2 2 0 0 0 2-2V11a2 2 0 0 0-2-2Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>' +
|
||||
"</svg>" +
|
||||
'<span class="copy-button-label">Copy</span>' +
|
||||
"</div>" +
|
||||
'<div class="copy-button-copied-wrapper">' +
|
||||
'<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">' +
|
||||
'<path fill-rule="evenodd" clip-rule="evenodd" d="M19.9 6 21 7.1 10.6 19 4 12l1.1-1.2 5.5 5.9L19.9 6Z" fill="currentColor"/>' +
|
||||
"</svg>" +
|
||||
'<span class="copy-button-label">Copied</span>' +
|
||||
"</div>";
|
||||
|
||||
document.querySelectorAll("pre > code").forEach((code) => {
|
||||
if (code.classList.contains("language-plaintext")) return;
|
||||
const pre = code.parentNode;
|
||||
if (pre.querySelector(".copy-code-button")) return;
|
||||
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.classList.add("code-snippet-wrapper");
|
||||
pre.parentNode.replaceChild(wrapper, pre);
|
||||
wrapper.appendChild(pre);
|
||||
|
||||
const btn = document.createElement("button");
|
||||
btn.classList.add("copy-code-button");
|
||||
btn.type = "button";
|
||||
btn.innerHTML = BUTTON_HTML;
|
||||
wrapper.appendChild(btn);
|
||||
|
||||
const copyEl = btn.querySelector(".copy-button-copy-wrapper");
|
||||
const copiedEl = btn.querySelector(".copy-button-copied-wrapper");
|
||||
|
||||
btn.addEventListener("click", () => {
|
||||
navigator.clipboard.writeText(code.innerText);
|
||||
copyEl.classList.add("d-none");
|
||||
copiedEl.classList.add("d-flex");
|
||||
setTimeout(() => {
|
||||
copyEl.classList.remove("d-none");
|
||||
copiedEl.classList.remove("d-flex");
|
||||
}, 2000);
|
||||
});
|
||||
});
|
||||
})();
|
||||
125
docs-build/public/js/feedback-widget.js
Normal file
@@ -0,0 +1,125 @@
|
||||
// "Was this helpful?" widget. Posts votes and comments to the CRM
|
||||
// feedback endpoint that the live metabase.com docs already use, so feedback
|
||||
// from this build lands in the same bucket. The markup is in
|
||||
// src/components/FeedbackWidget.astro.
|
||||
(function () {
|
||||
const widget = document.getElementById("was-this-helpful-widget");
|
||||
if (!widget) return;
|
||||
|
||||
const ENDPOINT = "https://store-api.metabase.com/api/v1/crm/learn-feedback";
|
||||
const UP_PROMPT = "What did you find especially helpful?";
|
||||
const DOWN_PROMPT = "How can we improve this article?";
|
||||
|
||||
const state = {
|
||||
article_url: window.location.href,
|
||||
article_title: document.title,
|
||||
article_section: getSection(window.location.href),
|
||||
};
|
||||
|
||||
// Map a pathname to the corresponding markdown source on GitHub.
|
||||
// Strips the `/docs/(latest|v0.NN)/` prefix and appends the right extension:
|
||||
// /docs/latest/ → README.md
|
||||
// /docs/latest/foo/ → foo/index.md (directory index page)
|
||||
// /docs/latest/foo/bar → foo/bar.md
|
||||
function toGithubUrl(pathname) {
|
||||
const stripped = pathname.replace(/^\/docs\/(?:latest|v\d+\.\d+)\/?/, "");
|
||||
let file;
|
||||
if (stripped === "") {
|
||||
file = "README.md";
|
||||
} else if (stripped.endsWith("/")) {
|
||||
file = stripped + "index.md";
|
||||
} else {
|
||||
file = stripped + ".md";
|
||||
}
|
||||
return "https://github.com/metabase/metabase/blob/master/docs/" + file;
|
||||
}
|
||||
|
||||
// Section is the first path segment after /docs/<version>/, e.g.
|
||||
// /docs/latest/databases/postgresql → "databases"
|
||||
// Empty (the landing page) falls back to "Docs".
|
||||
function getSection(url) {
|
||||
const m = url.match(/\/docs\/(?:latest|v\d+\.\d+)\/([^/]+)/);
|
||||
return m ? m[1] : "Docs";
|
||||
}
|
||||
|
||||
function renderFooter(isHelpful) {
|
||||
const gh = toGithubUrl(window.location.pathname);
|
||||
const proposeLink =
|
||||
'<a href="' + gh + '" target="_blank" rel="noopener">Propose a change.</a>';
|
||||
if (isHelpful === false) {
|
||||
return (
|
||||
'Need some help? <a href="https://www.metabase.com/help/">' +
|
||||
"We're right here.</a> " +
|
||||
"Want to improve these docs? " +
|
||||
proposeLink
|
||||
);
|
||||
}
|
||||
return "Want to improve these docs?<br>" + proposeLink;
|
||||
}
|
||||
|
||||
async function postFeedback(body) {
|
||||
const response = await fetch(ENDPOINT, {
|
||||
method: "POST",
|
||||
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
return response.json();
|
||||
}
|
||||
|
||||
const voteUp = document.getElementById("page-feedback-vote-up");
|
||||
const voteDown = document.getElementById("page-feedback-vote-down");
|
||||
const commentPanel = document.getElementById("page-comment");
|
||||
const commentQuestion = document.getElementById("page-feedback-question");
|
||||
const commentText = document.getElementById("feedback-comment-text");
|
||||
const commentSubmit = document.getElementById("page-comment-submit");
|
||||
const commentForm = document.getElementById("page-comment-form");
|
||||
const thanks = document.getElementById("page-feedback-message");
|
||||
const footer = document.getElementById("page-feedback-footer");
|
||||
|
||||
// Initial footer — visible before any interaction.
|
||||
footer.innerHTML = renderFooter(null);
|
||||
|
||||
function handleVote(isHelpful) {
|
||||
return function (ev) {
|
||||
ev.preventDefault();
|
||||
voteUp.classList.toggle("active", isHelpful);
|
||||
voteDown.classList.toggle("active", !isHelpful);
|
||||
|
||||
commentQuestion.innerText = isHelpful ? UP_PROMPT : DOWN_PROMPT;
|
||||
commentPanel.classList.remove("hide");
|
||||
commentText.focus();
|
||||
footer.innerHTML = renderFooter(isHelpful);
|
||||
|
||||
state.helpful = isHelpful;
|
||||
postFeedback(state)
|
||||
.then(({ feedback_id }) => {
|
||||
state.id = feedback_id;
|
||||
})
|
||||
.catch((err) => console.error("feedback vote failed", err));
|
||||
};
|
||||
}
|
||||
|
||||
function submitComment(ev) {
|
||||
ev.preventDefault();
|
||||
const comment = commentText.value.trim();
|
||||
if (!comment) {
|
||||
alert("Please add a comment before hitting the Send button.");
|
||||
return;
|
||||
}
|
||||
if (comment.length > 500) {
|
||||
alert("Comments cannot exceed 500 characters.");
|
||||
return;
|
||||
}
|
||||
commentPanel.classList.add("hide");
|
||||
thanks.classList.remove("hide");
|
||||
state.comments = comment;
|
||||
postFeedback(state).catch((err) =>
|
||||
console.error("feedback comment failed", err),
|
||||
);
|
||||
}
|
||||
|
||||
voteUp.addEventListener("click", handleVote(true));
|
||||
voteDown.addEventListener("click", handleVote(false));
|
||||
commentSubmit.addEventListener("click", submitComment);
|
||||
commentForm.addEventListener("submit", submitComment);
|
||||
})();
|
||||
29
docs-build/public/js/github-stars.js
Normal file
@@ -0,0 +1,29 @@
|
||||
// Vendored from the marketing site's github-stars.js. The marketing original
|
||||
// reports DOM and fetch failures via window.Sentry, which the docs build
|
||||
// does not load — those calls were stripped here.
|
||||
(function() {
|
||||
window.addEventListener("DOMContentLoaded", async () => {
|
||||
const MAX_RETRY = 3;
|
||||
|
||||
async function getGithubData(retryCount) {
|
||||
const $githubStars = document.querySelectorAll(".github-stars");
|
||||
if ($githubStars.length === 0) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
"https://api.github.com/repos/metabase/metabase",
|
||||
);
|
||||
const githubData = await response.json();
|
||||
const starsCount = Math.ceil(githubData.stargazers_count / 100) / 10;
|
||||
$githubStars.forEach(($node) => ($node.innerHTML = `${starsCount}k`));
|
||||
} catch (err) {
|
||||
if (retryCount + 1 < MAX_RETRY) {
|
||||
return await getGithubData(retryCount + 1);
|
||||
}
|
||||
console.warn("Failed to load GitHub data:", err);
|
||||
}
|
||||
}
|
||||
|
||||
await getGithubData(0);
|
||||
});
|
||||
})();
|
||||
29
docs-build/public/js/image-zoom.js
Normal file
@@ -0,0 +1,29 @@
|
||||
// Lightbox: click an inline image to open it full-screen.
|
||||
(function () {
|
||||
const overlay = document.createElement("div");
|
||||
overlay.classList.add("image-zoom-overlay");
|
||||
overlay.style.cssText = [
|
||||
"position:fixed", "inset:0", "background:rgba(255,255,255,0.96)",
|
||||
"z-index:2000", "display:none", "align-items:center", "justify-content:center",
|
||||
"padding:48px", "cursor:zoom-out", "transition:opacity 0.15s",
|
||||
].join(";");
|
||||
|
||||
const overlayImg = document.createElement("img");
|
||||
overlayImg.style.cssText = "max-width:100%;max-height:100%;box-shadow:0 4px 24px rgba(0,0,0,0.12);";
|
||||
overlay.appendChild(overlayImg);
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
overlay.addEventListener("click", () => {
|
||||
overlay.style.display = "none";
|
||||
});
|
||||
|
||||
document.querySelectorAll("article.docs-content img:not(.no-zoom)").forEach((img) => {
|
||||
img.style.cursor = "zoom-in";
|
||||
img.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
overlayImg.src = img.src;
|
||||
overlayImg.alt = img.alt;
|
||||
overlay.style.display = "flex";
|
||||
});
|
||||
});
|
||||
})();
|
||||
51
docs-build/public/js/inkeep.js
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Inkeep AI search bar — cxkit-js widget mounted into #inkeep in TopBar.astro.
|
||||
*
|
||||
* Paths that need to be resolved against DOCS_BASE_PATH are read from
|
||||
* window.__INKEEP_CONFIG__, which DocsLayout.astro sets via an inline script
|
||||
* before this module loads. Falls back to absolute marketing-site URLs so the
|
||||
* widget still works if the inline config is missing.
|
||||
*/
|
||||
import "https://cdn.jsdelivr.net/npm/@inkeep/cxkit-js@0.5.117/dist/embed.js";
|
||||
|
||||
const injected = window.__INKEEP_CONFIG__ ?? {};
|
||||
const themeUrl = injected.themeUrl ?? "/css/inkeep.css";
|
||||
const metabaseIconUrl =
|
||||
injected.metabaseIconUrl ?? "https://www.metabase.com/images/icons/metabase-icon.svg";
|
||||
const personIconUrl =
|
||||
injected.personIconUrl ?? "https://www.metabase.com/images/icons/person.svg";
|
||||
|
||||
const config = {
|
||||
baseSettings: {
|
||||
// Public read-only Inkeep search key — safe to embed in client code.
|
||||
apiKey: "6dd55673e83be3649d9ef8281b40795329b492a8fc320985",
|
||||
organizationDisplayName: "Metabase",
|
||||
primaryBrandColor: "#509EE3",
|
||||
theme: {
|
||||
styles: [{ type: "link", value: themeUrl }],
|
||||
},
|
||||
},
|
||||
aiChatSettings: {
|
||||
aiAssistantName: "Metabase",
|
||||
chatSubjectName: "Metabase",
|
||||
placeholder: "How long does it take to bake a pie chart?",
|
||||
introMessage:
|
||||
"Hi! You can ask me about Metabase. <em>(Please check the sources; I'm still learning.)</em>",
|
||||
prompts: ["Be succinct"],
|
||||
aiAssistantAvatar: { light: metabaseIconUrl, dark: metabaseIconUrl },
|
||||
userAvatar: personIconUrl,
|
||||
},
|
||||
searchSettings: {
|
||||
placeholder: "Search",
|
||||
},
|
||||
shouldShowAskAICard: false,
|
||||
askAILabel: "Ask",
|
||||
// Pin the widget to search mode; the AI chat tab stays hidden.
|
||||
forceDefaultView: "search",
|
||||
modalSettings: { shortcutKey: null },
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
if (!document.getElementById("inkeep")) return;
|
||||
window.Inkeep.SearchBar("#inkeep", config);
|
||||
});
|
||||
90
docs-build/public/js/main-nav.js
Normal file
@@ -0,0 +1,90 @@
|
||||
function initMainNav() {
|
||||
const buttons = [
|
||||
document.getElementById("product-nav-button-desktop"),
|
||||
document.getElementById("features-nav-button-desktop"),
|
||||
document.getElementById("resources-nav-button-desktop"),
|
||||
];
|
||||
|
||||
buttons.forEach((btn) => {
|
||||
btn.addEventListener("keydown", function(e) {
|
||||
if (e.key !== "Enter") return;
|
||||
btn.classList.toggle("open");
|
||||
buttons
|
||||
.filter((b) => b !== btn)
|
||||
.forEach((b) => b.classList.remove("open"));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function initToggleMobileMainNav() {
|
||||
const hamburger = document.getElementById("mobile-nav-hamburger-wrapper");
|
||||
const mobileNavMenu = document.getElementById("nav-menu-mobile");
|
||||
|
||||
hamburger.addEventListener("click", function() {
|
||||
hamburger.classList.toggle("open");
|
||||
mobileNavMenu.classList.toggle("d-none");
|
||||
});
|
||||
}
|
||||
|
||||
function initResizeMobileMainNav() {
|
||||
const navMenuMobile = document.getElementById("nav-menu-mobile");
|
||||
let navMenuMobileHeight = 0;
|
||||
|
||||
function setMenuMobileHeight() {
|
||||
if (window.innerHeight - 72 !== navMenuMobileHeight) {
|
||||
navMenuMobileHeight = window.innerHeight - 72;
|
||||
navMenuMobile.style.height = navMenuMobileHeight + "px";
|
||||
}
|
||||
}
|
||||
setMenuMobileHeight();
|
||||
|
||||
window.addEventListener("load", setMenuMobileHeight);
|
||||
window.addEventListener("resize", setMenuMobileHeight);
|
||||
window.addEventListener("scroll", setMenuMobileHeight);
|
||||
}
|
||||
|
||||
function initNavigationHeaderHoverHighlight() {
|
||||
const $navigationHeader = document.querySelector(".navigation-header");
|
||||
const $buttons = document.querySelectorAll(".button-desktop");
|
||||
|
||||
setTimeout(() => {
|
||||
document.getElementById("hover-highlight").classList.remove("hidden");
|
||||
}, 200);
|
||||
|
||||
$buttons.forEach(($button) => {
|
||||
$button.addEventListener("mouseenter", function() {
|
||||
mainNavHandleHoverHighlight($button);
|
||||
});
|
||||
|
||||
$navigationHeader.addEventListener("mouseleave", function() {});
|
||||
});
|
||||
}
|
||||
|
||||
function mainNavHandleHoverOnPageLoad() {
|
||||
const $button = document.querySelector(".button-desktop:hover");
|
||||
|
||||
if ($button) {
|
||||
mainNavHandleHoverHighlight($button);
|
||||
}
|
||||
}
|
||||
|
||||
function mainNavHandleHoverHighlight($button) {
|
||||
const $hoverHighlight = document.querySelector(
|
||||
".navigation-header #hover-highlight",
|
||||
);
|
||||
const buttonLeft = $button.offsetLeft;
|
||||
const buttonWidth = $button.offsetWidth;
|
||||
const buttonHeight = $button.offsetHeight;
|
||||
|
||||
$hoverHighlight.style.left = buttonLeft + "px";
|
||||
$hoverHighlight.style.width = buttonWidth + "px";
|
||||
$hoverHighlight.style.height = buttonHeight + "px";
|
||||
}
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
initMainNav();
|
||||
initToggleMobileMainNav();
|
||||
initResizeMobileMainNav();
|
||||
initNavigationHeaderHoverHighlight();
|
||||
mainNavHandleHoverOnPageLoad();
|
||||
});
|
||||
18
docs-build/public/js/more-resources.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// Click-to-expand toggle for the footer's "Choosing Metabase" / "More
|
||||
// Resources" lists. Vendored from
|
||||
// `metabase.github.io/_includes/footer-scripts.html`. Each `.more-resources`
|
||||
// anchor is paired with an adjacent `.more-resources-wrapper.hide` list — the
|
||||
// handler flips `.hide` on the wrapper and `.chevron-up` on the chevron.
|
||||
(function() {
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const $moreResourcesButtons = document.querySelectorAll(".more-resources");
|
||||
$moreResourcesButtons.forEach(($button) => {
|
||||
$button.addEventListener("click", () => {
|
||||
$button.nextElementSibling.classList.toggle("hide");
|
||||
$button
|
||||
.querySelector(".more-resources-chevron")
|
||||
.classList.toggle("chevron-up");
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
51
docs-build/public/js/promo-banner.js
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Minimal promo-banner controller for the docs site.
|
||||
*
|
||||
* The marketing site's promo-banner.js does a lot more (countdown timers,
|
||||
* Snowplow analytics, landing-page detection) but for the docs we only need
|
||||
* the dismiss button to work and to remember the dismissal across reloads.
|
||||
*
|
||||
* The chrome snapshot leaves the banner visible by default; this script just
|
||||
* checks localStorage on load (hide if dismissed within the last 7 days) and
|
||||
* wires the close button to add the slide-up class then remove the node.
|
||||
*/
|
||||
(function () {
|
||||
var STORAGE_KEY = "metabase-docs-promo-banner-dismissed-until";
|
||||
|
||||
function getBanner() {
|
||||
return document.querySelector(".promo-banner");
|
||||
}
|
||||
|
||||
function isDismissed() {
|
||||
try {
|
||||
var until = parseInt(localStorage.getItem(STORAGE_KEY) || "0", 10);
|
||||
return until > Date.now();
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function dismiss() {
|
||||
var banner = getBanner();
|
||||
if (!banner) return;
|
||||
var sevenDaysMs = 7 * 24 * 60 * 60 * 1000;
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, String(Date.now() + sevenDaysMs));
|
||||
} catch (_) {
|
||||
/* private mode: ignore */
|
||||
}
|
||||
banner.classList.add("no-height");
|
||||
setTimeout(function () { banner.remove(); }, 350);
|
||||
}
|
||||
|
||||
window.addEventListener("DOMContentLoaded", function () {
|
||||
var banner = getBanner();
|
||||
if (!banner) return;
|
||||
if (isDismissed()) {
|
||||
banner.remove();
|
||||
return;
|
||||
}
|
||||
var button = banner.querySelector("button");
|
||||
if (button) button.addEventListener("click", dismiss);
|
||||
});
|
||||
})();
|
||||
37
docs-build/public/js/right-sidebar.js
Normal file
@@ -0,0 +1,37 @@
|
||||
// Scroll-spy for the right-rail Table of Contents.
|
||||
// The list of links is server-rendered by TableOfContents.astro; this just
|
||||
// updates `.selected` based on which heading is currently in view.
|
||||
(function () {
|
||||
function init() {
|
||||
const headers = Array.from(
|
||||
document.querySelectorAll("article.docs-content h2[id]"),
|
||||
);
|
||||
const container = document.getElementById("sub-navigation-content");
|
||||
if (!container || headers.length === 0) return;
|
||||
|
||||
const linkFor = {};
|
||||
container.querySelectorAll("a[href^='#']").forEach((a) => {
|
||||
const id = a.getAttribute("href").slice(1);
|
||||
linkFor[id] = a;
|
||||
});
|
||||
|
||||
function update() {
|
||||
let activeId = headers[0]?.id;
|
||||
for (const h of headers) {
|
||||
if (h.getBoundingClientRect().top <= 80) activeId = h.id;
|
||||
}
|
||||
Object.entries(linkFor).forEach(([id, link]) => {
|
||||
link.classList.toggle("selected", id === activeId);
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("scroll", update, { passive: true });
|
||||
update();
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
window.addEventListener("DOMContentLoaded", init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
})();
|
||||
26
docs-build/public/js/sidebar.js
Normal file
@@ -0,0 +1,26 @@
|
||||
// Toggles expansion of sidebar groups when the chevron button is clicked.
|
||||
// Initial expanded state is rendered server-side from the active page's trail
|
||||
// (see Sidebar.astro / lib/nav.ts); this script only handles user interaction.
|
||||
//
|
||||
// Items without a URL (pure group labels) also toggle when the label itself
|
||||
// is clicked — there's nowhere to navigate to in that case, so the label
|
||||
// behaves like the chevron.
|
||||
(function () {
|
||||
const sidebar = document.querySelector(".docs-sidebar");
|
||||
if (!sidebar) return;
|
||||
|
||||
sidebar.addEventListener("click", function (e) {
|
||||
const target = e.target.closest(
|
||||
".docs-sidebar-toggle, .docs-sidebar-group-label",
|
||||
);
|
||||
if (!target) return;
|
||||
const li = target.closest("li.has-children");
|
||||
if (!li) return;
|
||||
e.preventDefault();
|
||||
const expanded = li.classList.toggle("expanded");
|
||||
const button = li.querySelector(":scope > .nav-row > .docs-sidebar-toggle");
|
||||
if (button) {
|
||||
button.setAttribute("aria-expanded", expanded ? "true" : "false");
|
||||
}
|
||||
});
|
||||
})();
|
||||
97
docs-build/public/js/snowplow.js
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Anonymous Snowplow page-view tracking for the docs site.
|
||||
*
|
||||
* Mirrors the marketing site's `anonymous-snowplow.js` so docs page views
|
||||
* land in the same `sp.metabase.com` stream the rest of metabase.com is
|
||||
* already reporting to. `withServerAnonymisation: true` + `stateStorageStrategy:
|
||||
* "none"` means no client-side cookies or localStorage and the IP is dropped
|
||||
* server-side, so this runs without a cookie-consent banner.
|
||||
*
|
||||
* The marketing site also conditionally loads the heavier `marketing-snowplow.js`
|
||||
* (cookies + identifiable session tracking) once the user accepts marketing
|
||||
* cookies. We don't carry the consent flow yet — if we want richer docs
|
||||
* analytics later, that's the layer to add.
|
||||
*
|
||||
* `redirectTo` is the URL normalizer the marketing site lives in
|
||||
* `_includes/globals.js`. We inline it here so the script has no external
|
||||
* dependency inside the docs build.
|
||||
*/
|
||||
(function () {
|
||||
// URL normalizer: strip /index.html, .html, /index suffixes so that
|
||||
// /docs/latest/foo.html and /docs/latest/foo report as the same page.
|
||||
window.redirectTo = window.redirectTo || function redirectTo(theLocation) {
|
||||
if (typeof theLocation === "string") {
|
||||
try { theLocation = new URL(theLocation); } catch (_) { return null; }
|
||||
}
|
||||
var suffixes = ["/index.html", ".html", "/index"];
|
||||
for (var i = 0; i < suffixes.length; i++) {
|
||||
var suffix = suffixes[i];
|
||||
if (
|
||||
theLocation.pathname &&
|
||||
theLocation.pathname.length > suffix.length &&
|
||||
theLocation.pathname.indexOf(suffix) === theLocation.pathname.length - suffix.length
|
||||
) {
|
||||
var pathname = theLocation.pathname.replace(suffix, "");
|
||||
if (pathname[pathname.length - 1] === "/") pathname = pathname.slice(0, -1);
|
||||
return pathname + theLocation.search + theLocation.hash;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Snowplow loader stub — same shape as the official sp.js loader.
|
||||
(function (p, l, o, w, i, n, g) {
|
||||
if (!p[i]) {
|
||||
p.GlobalSnowplowNamespace = p.GlobalSnowplowNamespace || [];
|
||||
p.GlobalSnowplowNamespace.push(i);
|
||||
p[i] = function () { (p[i].q = p[i].q || []).push(arguments); };
|
||||
p[i].q = p[i].q || [];
|
||||
n = l.createElement(o);
|
||||
g = l.getElementsByTagName(o)[0];
|
||||
n.async = 1;
|
||||
n.src = w;
|
||||
g.parentNode.insertBefore(n, g);
|
||||
}
|
||||
})(
|
||||
window,
|
||||
document,
|
||||
"script",
|
||||
"//cdn.jsdelivr.net/gh/snowplow/sp-js-assets@2.18.0/sp.min.js",
|
||||
"snowplowanon",
|
||||
);
|
||||
|
||||
window.snowplowanon("newTracker", "anon", "sp.metabase.com", {
|
||||
appId: "anon-www",
|
||||
platform: "web",
|
||||
anonymousTracking: { withServerAnonymisation: true },
|
||||
stateStorageStrategy: "none",
|
||||
eventMethod: "post",
|
||||
contexts: {
|
||||
webPage: true,
|
||||
performanceTiming: false,
|
||||
},
|
||||
});
|
||||
|
||||
// Use the normalized URL (without /index.html etc) for the page view.
|
||||
var formattedCustomUrl = window.redirectTo(window.location);
|
||||
if (formattedCustomUrl) {
|
||||
window.snowplowanon("setCustomUrl", formattedCustomUrl);
|
||||
}
|
||||
|
||||
// Preserve the referrer when the user navigated within metabase.com.
|
||||
if (
|
||||
document.referrer &&
|
||||
(document.referrer.indexOf("https://www.metabase.com") > -1 ||
|
||||
document.referrer.indexOf("https://metabase.com") > -1)
|
||||
) {
|
||||
var formattedReferrerUrl = window.redirectTo(document.referrer);
|
||||
if (formattedReferrerUrl) {
|
||||
window.snowplowanon(
|
||||
"setReferrerUrl",
|
||||
window.location.origin + formattedReferrerUrl,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
window.snowplowanon("trackPageView");
|
||||
})();
|
||||
48
docs-build/public/js/status.js
Normal file
@@ -0,0 +1,48 @@
|
||||
// Vendored from the marketing site's status.js. The marketing original
|
||||
// reports DOM and fetch failures via window.Sentry, which the docs build
|
||||
// does not load — those calls were stripped here. See
|
||||
// docs/developers-guide/docs.md (Marketing-site chrome) for context.
|
||||
(function() {
|
||||
document.addEventListener("DOMContentLoaded", async function() {
|
||||
const MAX_RETRY = 3;
|
||||
|
||||
async function getStatus(retryCount) {
|
||||
const $statusCircles = document.querySelectorAll(".status-circle");
|
||||
if ($statusCircles.length === 0) return;
|
||||
|
||||
try {
|
||||
const response = await fetch("https://status.metabase.com/");
|
||||
const pageStatusString = await response.text();
|
||||
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(pageStatusString, "text/html");
|
||||
const $pageStatus = doc.querySelector(".page-status");
|
||||
if (!$pageStatus) return;
|
||||
|
||||
const pageStatusClasses = $pageStatus.classList;
|
||||
let statusColor = "#84BB4C";
|
||||
if (
|
||||
pageStatusClasses.contains("status-minor") ||
|
||||
pageStatusClasses.contains("status-major")
|
||||
) {
|
||||
statusColor = "#F9CF48";
|
||||
} else if (pageStatusClasses.contains("status-critical")) {
|
||||
statusColor = "#ED6E6E";
|
||||
} else if (pageStatusClasses.contains("status-maintenance")) {
|
||||
statusColor = "#509EE3";
|
||||
}
|
||||
|
||||
$statusCircles.forEach(
|
||||
($node) => ($node.style.backgroundColor = statusColor),
|
||||
);
|
||||
} catch (err) {
|
||||
if (retryCount + 1 < MAX_RETRY) {
|
||||
return await getStatus(retryCount + 1);
|
||||
}
|
||||
console.warn("Failed to load Metabase status:", err);
|
||||
}
|
||||
}
|
||||
|
||||
await getStatus(0);
|
||||
});
|
||||
})();
|
||||
28
docs-build/public/js/table-overflow.js
Normal file
@@ -0,0 +1,28 @@
|
||||
// Wraps wide tables in an overflow-x scroll container with a fade indicator.
|
||||
(function () {
|
||||
const tables = document.querySelectorAll("article.docs-content table");
|
||||
|
||||
tables.forEach((table) => {
|
||||
const parent = table.parentNode;
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.classList.add("table-overflow-wrapper");
|
||||
const container = document.createElement("div");
|
||||
container.classList.add("table-overflow");
|
||||
parent.insertBefore(wrapper, table);
|
||||
wrapper.appendChild(container);
|
||||
container.appendChild(table);
|
||||
});
|
||||
|
||||
function check() {
|
||||
document.querySelectorAll(".table-overflow-wrapper").forEach((wrapper) => {
|
||||
const container = wrapper.firstElementChild;
|
||||
const table = container.firstElementChild;
|
||||
const overflowing =
|
||||
container.getBoundingClientRect().width <
|
||||
table.getBoundingClientRect().width;
|
||||
wrapper.classList.toggle("table-overflow-indicator", overflowing);
|
||||
});
|
||||
}
|
||||
window.addEventListener("resize", check);
|
||||
check();
|
||||
})();
|
||||
52
docs-build/public/marketing-redirects.json
Normal file
@@ -0,0 +1,52 @@
|
||||
[
|
||||
{
|
||||
"from": "/cloud/docs/custom-domain",
|
||||
"to": "/docs/latest/cloud/custom-domain",
|
||||
"source": "cloud/custom-domain.md"
|
||||
},
|
||||
{
|
||||
"from": "/pricing/how-billing-works",
|
||||
"to": "/docs/latest/cloud/how-billing-works",
|
||||
"source": "cloud/how-billing-works.md"
|
||||
},
|
||||
{
|
||||
"from": "/cloud/docs/ip-addresses-to-whitelist",
|
||||
"to": "/docs/latest/cloud/ip-addresses-to-whitelist",
|
||||
"source": "cloud/ip-addresses-to-whitelist.md"
|
||||
},
|
||||
{
|
||||
"from": "/cloud/docs/limitations",
|
||||
"to": "/docs/latest/cloud/limitations",
|
||||
"source": "cloud/limitations.md"
|
||||
},
|
||||
{
|
||||
"from": "/migrate/guide",
|
||||
"to": "/docs/latest/cloud/migrate/guide",
|
||||
"source": "cloud/migrate/guide.md"
|
||||
},
|
||||
{
|
||||
"from": "/migrate/post-signup-guide",
|
||||
"to": "/docs/latest/cloud/migrate/guide",
|
||||
"source": "cloud/migrate/guide.md"
|
||||
},
|
||||
{
|
||||
"from": "/blog/migrating-to-metabase-cloud",
|
||||
"to": "/docs/latest/cloud/migrate/guide",
|
||||
"source": "cloud/migrate/guide.md"
|
||||
},
|
||||
{
|
||||
"from": "/cloud/docs/migrate/guide",
|
||||
"to": "/docs/latest/cloud/migrate/guide",
|
||||
"source": "cloud/migrate/guide.md"
|
||||
},
|
||||
{
|
||||
"from": "/cloud/docs/migrate/heroku",
|
||||
"to": "/docs/latest/cloud/migrate/heroku",
|
||||
"source": "cloud/migrate/heroku.md"
|
||||
},
|
||||
{
|
||||
"from": "/cloud/docs",
|
||||
"to": "/docs/latest/cloud/start",
|
||||
"source": "cloud/start.md"
|
||||
}
|
||||
]
|
||||
124
docs-build/scripts/validate-nav.mjs
Normal file
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env node
|
||||
// Validates that every page URL referenced in `nav.yml` resolves to either
|
||||
// a markdown file under `docs/` or a known virtual route. Run by
|
||||
// `bun run docs:check` so typo'd slugs fail at check-time rather than
|
||||
// silently disappearing from the sidebar at runtime.
|
||||
//
|
||||
// Resolution rules, in order:
|
||||
// - http(s):// or // — external. Skipped.
|
||||
// - starts with "/" — site-absolute (e.g. /learn/..., /paid-features/).
|
||||
// Skipped (not under DOCS_BASE_PATH).
|
||||
// - contains "#" — strip the fragment; we don't validate heading IDs.
|
||||
// - trailing "/" — directory route, resolve as <slug>/index.md.
|
||||
// - VIRTUAL_ROUTES — routes served by an Astro page or generated HTML,
|
||||
// not by a `docs/*.md` file. See list below.
|
||||
// - otherwise — resolve as `docs/<slug>.md`, falling back to
|
||||
// `docs/<slug>/index.md`.
|
||||
//
|
||||
// Exit code 1 on any unresolved reference; prints the offending slug,
|
||||
// the originating nav category, and the breadcrumb trail.
|
||||
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import yaml from "js-yaml";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const DOCS_BUILD_DIR = path.resolve(__dirname, "..");
|
||||
const REPO_ROOT = path.resolve(DOCS_BUILD_DIR, "..");
|
||||
const DOCS_DIR = path.join(REPO_ROOT, "docs");
|
||||
const NAV_PATH = path.join(DOCS_BUILD_DIR, "nav.yml");
|
||||
|
||||
// Routes served by an Astro page or a generated HTML directory rather than
|
||||
// by a `docs/*.md` file. Keep this list short and explicit so additions are
|
||||
// deliberate.
|
||||
const VIRTUAL_ROUTES = new Set([
|
||||
// src/pages/api.astro — Scalar viewer over docs/api.json.
|
||||
"api",
|
||||
// docs-build/public/embedding/sdk/api/index.html — typedoc-generated.
|
||||
"embedding/sdk/api",
|
||||
]);
|
||||
|
||||
function isExternalOrAbsolute(url) {
|
||||
return /^https?:\/\//.test(url) || url.startsWith("//") || url.startsWith("/");
|
||||
}
|
||||
|
||||
function stripFragment(url) {
|
||||
const i = url.indexOf("#");
|
||||
return i === -1 ? url : url.slice(0, i);
|
||||
}
|
||||
|
||||
function resolveSlug(slug) {
|
||||
// Treat `foo/` (trailing slash) as `foo/index.md`.
|
||||
const hadTrailingSlash = slug.endsWith("/");
|
||||
const bare = hadTrailingSlash ? slug.slice(0, -1) : slug;
|
||||
|
||||
if (VIRTUAL_ROUTES.has(bare)) return { kind: "virtual", target: bare };
|
||||
|
||||
const candidates = hadTrailingSlash
|
||||
? [path.join(DOCS_DIR, bare, "index.md")]
|
||||
: [path.join(DOCS_DIR, `${bare}.md`), path.join(DOCS_DIR, bare, "index.md")];
|
||||
|
||||
for (const candidate of candidates) {
|
||||
if (fs.existsSync(candidate)) return { kind: "file", target: candidate };
|
||||
}
|
||||
|
||||
return { kind: "missing", candidates };
|
||||
}
|
||||
|
||||
function* walk(pages, trail) {
|
||||
for (const page of pages ?? []) {
|
||||
const here = [...trail, page.name];
|
||||
if (page.url) yield { url: page.url, trail: here };
|
||||
if (page.pages) yield* walk(page.pages, here);
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
const navYaml = fs.readFileSync(NAV_PATH, "utf8");
|
||||
const nav = yaml.load(navYaml);
|
||||
|
||||
const checked = [];
|
||||
const unresolved = [];
|
||||
|
||||
for (const category of nav.categories ?? []) {
|
||||
for (const ref of walk(category.pages, [category.name])) {
|
||||
const { url, trail } = ref;
|
||||
if (isExternalOrAbsolute(url)) continue;
|
||||
|
||||
const slug = stripFragment(url);
|
||||
// Pure fragments inside the current page aren't valid in a nav URL —
|
||||
// skip them defensively rather than crashing.
|
||||
if (!slug) continue;
|
||||
|
||||
const resolution = resolveSlug(slug);
|
||||
checked.push({ url, slug, trail, resolution });
|
||||
if (resolution.kind === "missing") unresolved.push({ url, slug, trail, resolution });
|
||||
}
|
||||
}
|
||||
|
||||
const total = checked.length;
|
||||
if (unresolved.length === 0) {
|
||||
console.log(`nav.yml: ${total} page references resolve OK.`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(`nav.yml: ${unresolved.length} of ${total} page references do not resolve:\n`);
|
||||
for (const { url, trail, resolution } of unresolved) {
|
||||
const breadcrumb = trail.join(" → ");
|
||||
console.error(` ✗ ${url}`);
|
||||
console.error(` in: ${breadcrumb}`);
|
||||
console.error(` looked for:`);
|
||||
for (const c of resolution.candidates) {
|
||||
console.error(` - ${path.relative(REPO_ROOT, c)}`);
|
||||
}
|
||||
console.error("");
|
||||
}
|
||||
console.error("Fix by either correcting the slug in docs-build/nav.yml,");
|
||||
console.error("creating the missing markdown file under docs/, or — if this is a");
|
||||
console.error("route served by src/pages/*.astro or generated HTML — adding it to");
|
||||
console.error("VIRTUAL_ROUTES in docs-build/scripts/validate-nav.mjs.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
main();
|
||||
39
docs-build/src/components/FeedbackWidget.astro
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
// The "Was this helpful?" widget that sits at the bottom of the right rail.
|
||||
// Server-renders static markup; the client hydrates it via /js/feedback-widget.js
|
||||
// (vote handling, comment form, POST to the CRM endpoint, and computing the
|
||||
// "Propose a change" GitHub link from window.location.pathname).
|
||||
import ThumbIcon from "./ThumbIcon.astro";
|
||||
---
|
||||
|
||||
<div id="was-this-helpful-widget" class="feedback-widget">
|
||||
<div id="page-feedback">
|
||||
<h6>Was this helpful?</h6>
|
||||
<button id="page-feedback-vote-up" class="vote-button" type="button">
|
||||
<ThumbIcon />
|
||||
Yes
|
||||
</button>
|
||||
<button id="page-feedback-vote-down" class="vote-button" type="button">
|
||||
<ThumbIcon flipped />
|
||||
No
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="page-comment" class="hide">
|
||||
<p id="page-feedback-question"></p>
|
||||
<form id="page-comment-form">
|
||||
<textarea
|
||||
id="feedback-comment-text"
|
||||
name="comment"
|
||||
placeholder="Type here…"
|
||||
maxlength="500"></textarea>
|
||||
<button id="page-comment-submit" type="submit">Send</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="page-feedback-message" class="hide">
|
||||
<h6>Thanks for your feedback!</h6>
|
||||
</div>
|
||||
|
||||
<span id="page-feedback-footer" class="feedback-footer"></span>
|
||||
</div>
|
||||
16
docs-build/src/components/Footer.astro
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
/**
|
||||
* Page footer: newsletter form + 5-column sitemap + social row. The HTML body
|
||||
* lives in `src/data/footer.html` and is imported as a raw string.
|
||||
*
|
||||
* The newsletter form POSTs directly to Mailchimp; HTML5 form validation
|
||||
* handles the email field (no JS dependency). The live status indicator and
|
||||
* GitHub star count come from `public/js/status.js` and
|
||||
* `public/js/github-stars.js`, loaded by DocsLayout.astro.
|
||||
*/
|
||||
import footerHtml from "../data/footer.html?raw";
|
||||
---
|
||||
|
||||
<div class="bootstrap footer-wrap">
|
||||
<footer class="body-footer" set:html={footerHtml} />
|
||||
</div>
|
||||
17
docs-build/src/components/Header.astro
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
/**
|
||||
* Top navigation header. The HTML body lives in `src/data/header.html` and is
|
||||
* imported as a raw string; per-page behavior (hover-highlight, mobile
|
||||
* hamburger, etc.) is in `public/js/main-nav.js`.
|
||||
*
|
||||
* Asset URLs in the markup are written with a `__DOCS_BASE__` placeholder so
|
||||
* they resolve against the docs base URL, which varies by version
|
||||
* (`/docs/latest`, `/docs/v0.NN`, etc.). Image assets live in `public/images/`.
|
||||
*/
|
||||
import headerHtml from "../data/header.html?raw";
|
||||
|
||||
const base = import.meta.env.BASE_URL.replace(/\/$/, "");
|
||||
const html = headerHtml.replaceAll("__DOCS_BASE__", base);
|
||||
---
|
||||
|
||||
<header class="bootstrap sticky" set:html={html} />
|
||||
69
docs-build/src/components/NavItem.astro
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
import { hrefForPage, urlMatchesSlug } from "../lib/nav";
|
||||
import type { NavPage } from "../lib/nav";
|
||||
|
||||
interface Props {
|
||||
page: NavPage;
|
||||
currentSlug: string;
|
||||
expandedNames: Set<string>;
|
||||
depth?: number;
|
||||
}
|
||||
|
||||
const { page, currentSlug, expandedNames, depth = 0 } = Astro.props;
|
||||
|
||||
const base = import.meta.env.BASE_URL.replace(/\/$/, "");
|
||||
|
||||
const label = page.navName ?? page.name;
|
||||
const href = hrefForPage(page, base);
|
||||
const isSelected = urlMatchesSlug(page.url, currentSlug);
|
||||
const isExpanded = expandedNames.has(page.name);
|
||||
const hasChildren = !!page.pages?.length;
|
||||
---
|
||||
|
||||
<li class:list={[{ "has-children": hasChildren, expanded: isExpanded }]}>
|
||||
{hasChildren ? (
|
||||
<div class="nav-row">
|
||||
{href ? (
|
||||
<a href={href} class:list={[{ selected: isSelected }]}>{label}</a>
|
||||
) : (
|
||||
<span class="docs-sidebar-group-label">{label}</span>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
class="docs-sidebar-toggle"
|
||||
aria-expanded={isExpanded ? "true" : "false"}
|
||||
aria-label={`Toggle ${label}`}
|
||||
>
|
||||
<svg
|
||||
width="10"
|
||||
height="10"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<polyline points="6 4 10 8 6 12"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
) : href ? (
|
||||
<a href={href} class:list={[{ selected: isSelected }]}>{label}</a>
|
||||
) : (
|
||||
<span class="docs-sidebar-group-label">{label}</span>
|
||||
)}
|
||||
{hasChildren && (
|
||||
<ul>
|
||||
{page.pages!.map((child) => (
|
||||
<Astro.self
|
||||
page={child}
|
||||
currentSlug={currentSlug}
|
||||
expandedNames={expandedNames}
|
||||
depth={depth + 1}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
31
docs-build/src/components/Sidebar.astro
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
import { getNav } from "../lib/nav";
|
||||
import type { NavPage } from "../lib/nav";
|
||||
import NavItem from "./NavItem.astro";
|
||||
|
||||
interface Props {
|
||||
currentSlug: string;
|
||||
trail: NavPage[];
|
||||
}
|
||||
|
||||
const { currentSlug, trail } = Astro.props;
|
||||
const nav = getNav();
|
||||
const expandedNames = new Set(trail.map((t) => t.name));
|
||||
---
|
||||
|
||||
<nav class="docs-sidebar" aria-label="Documentation">
|
||||
{nav.categories.map((category) => (
|
||||
<div class="docs-sidebar-category">
|
||||
<h5>{category.name}</h5>
|
||||
<ul>
|
||||
{category.pages.map((page) => (
|
||||
<NavItem
|
||||
page={page}
|
||||
currentSlug={currentSlug}
|
||||
expandedNames={expandedNames}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
24
docs-build/src/components/TableOfContents.astro
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
import type { MarkdownHeading } from "astro";
|
||||
|
||||
interface Props {
|
||||
headings: MarkdownHeading[];
|
||||
}
|
||||
|
||||
const { headings } = Astro.props;
|
||||
const h2s = headings.filter((h) => h.depth === 2);
|
||||
---
|
||||
|
||||
<div class="docs-toc">
|
||||
<div id="sub-navigation-content">
|
||||
{h2s.length > 0 && (
|
||||
<ul class="toc-list">
|
||||
{h2s.map((h) => (
|
||||
<li>
|
||||
<a href={`#${h.slug}`}>{h.text}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
24
docs-build/src/components/ThumbIcon.astro
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
// Thumbs-up icon used by the feedback widget for both Yes (default) and
|
||||
// No (rotated 180°). Single source of truth so the two buttons can't drift.
|
||||
interface Props {
|
||||
flipped?: boolean;
|
||||
}
|
||||
const { flipped = false } = Astro.props;
|
||||
---
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
style={flipped ? "transform: rotate(180deg)" : undefined}
|
||||
>
|
||||
<path
|
||||
d="M10.6667 7.33333H14.6367C14.9207 7.33334 15.2001 7.40595 15.4482 7.54427C15.6963 7.68259 15.9049 7.88202 16.0543 8.12364C16.2036 8.36525 16.2888 8.64104 16.3016 8.9248C16.3144 9.20857 16.2545 9.4909 16.1275 9.745L13.2108 15.5783C13.0724 15.8554 12.8595 16.0884 12.596 16.2512C12.3325 16.414 12.0289 16.5001 11.7192 16.5H8.37167C8.23583 16.5 8.1 16.4833 7.9675 16.45L4.83333 15.6667M10.6667 7.33333V3.16667C10.6667 2.72464 10.4911 2.30072 10.1785 1.98816C9.86595 1.67559 9.44203 1.5 9 1.5H8.92083C8.50417 1.5 8.16667 1.8375 8.16667 2.25417C8.16667 2.84917 7.99083 3.43083 7.66 3.92583L4.83333 8.16667V15.6667M10.6667 7.33333H9M4.83333 15.6667H3.16667C2.72464 15.6667 2.30072 15.4911 1.98816 15.1785C1.67559 14.866 1.5 14.442 1.5 14V9C1.5 8.55797 1.67559 8.13405 1.98816 7.82149C2.30072 7.50893 2.72464 7.33333 3.16667 7.33333H5.25"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"></path>
|
||||
</svg>
|
||||
40
docs-build/src/components/TopBar.astro
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
import { hrefForPage } from "../lib/nav";
|
||||
import type { NavPage } from "../lib/nav";
|
||||
|
||||
interface Props {
|
||||
trail: NavPage[];
|
||||
}
|
||||
|
||||
const { trail } = Astro.props;
|
||||
const base = import.meta.env.BASE_URL.replace(/\/$/, "");
|
||||
|
||||
// Build breadcrumb items. The last item is the current page (no link).
|
||||
// Intermediate items link only when the page has a url (group labels don't).
|
||||
const breadcrumbs = trail.map((p, i) => {
|
||||
const isLast = i === trail.length - 1;
|
||||
return {
|
||||
label: p.navName ?? p.name,
|
||||
href: isLast ? undefined : hrefForPage(p, base),
|
||||
};
|
||||
});
|
||||
---
|
||||
|
||||
<div class="docs-topbar">
|
||||
{breadcrumbs.length > 0 && (
|
||||
<nav class="docs-breadcrumbs" aria-label="Breadcrumb">
|
||||
<ol>
|
||||
{breadcrumbs.map((b) => (
|
||||
<li>{b.href ? <a href={b.href}>{b.label}</a> : <span>{b.label}</span>}</li>
|
||||
))}
|
||||
</ol>
|
||||
</nav>
|
||||
)}
|
||||
<div class="docs-topbar-right">
|
||||
<a class="docs-whats-new" href="https://www.metabase.com/releases">
|
||||
<img src={`${base}/images/icons/stars.svg`} alt="" aria-hidden="true" />
|
||||
<span>What's new</span>
|
||||
</a>
|
||||
<div id="inkeep"></div>
|
||||
</div>
|
||||
</div>
|
||||
31
docs-build/src/content.config.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { defineCollection, z } from "astro:content";
|
||||
import { glob } from "astro/loaders";
|
||||
|
||||
const docs = defineCollection({
|
||||
loader: glob({
|
||||
pattern: [
|
||||
"**/*.md",
|
||||
"!**/node_modules/**",
|
||||
// Generated typedoc fragments — transcluded into authored pages via
|
||||
// the include_file plugin; not standalone pages.
|
||||
"!embedding/sdk/api/**",
|
||||
"!embedding/eajs/snippets/**",
|
||||
],
|
||||
base: "../docs",
|
||||
}),
|
||||
schema: z
|
||||
.object({
|
||||
// title is optional: ~30% of /docs/ files (mostly auto-generated SDK
|
||||
// snippets) have no frontmatter at all. We derive a title from the
|
||||
// body's first heading or the filename when missing.
|
||||
title: z.string().optional(),
|
||||
summary: z.string().optional(),
|
||||
redirect_from: z.array(z.string()).optional(),
|
||||
})
|
||||
// .strict() makes any other key a build-time error. The Jekyll-era
|
||||
// schema (layout, version, has_magic_breadcrumbs, category, ...) was
|
||||
// silently passed through — strict surfaces typos and stale keys.
|
||||
.strict(),
|
||||
});
|
||||
|
||||
export const collections = { docs };
|
||||
227
docs-build/src/data/footer.html
Normal file
@@ -0,0 +1,227 @@
|
||||
<!-- Begin MailChimp Signup Form -->
|
||||
<form action="//metabase.us10.list-manage.com/subscribe/post?u=869fec0e4689e8fd1db91e795&id=b9664113a8" class="validate" id="mc-embedded-subscribe-form" method="post" name="mc-embedded-subscribe-form" novalidate="" data-form-id="1" target="_blank">
|
||||
<div>
|
||||
<h5>Subscribe to our newsletter</h5>
|
||||
<p>Stay in touch with updates and news from Metabase. No spam, ever.</p>
|
||||
</div>
|
||||
<div class="mc-field-group">
|
||||
<div>
|
||||
<input class="required email input input-small bordered mr-2" id="mce-EMAIL" name="EMAIL" placeholder="Email address" style="flex-grow: 1;" tabindex="-1" type="email" value="">
|
||||
|
||||
<input class="Button Button--primary Button--small" id="mc-embedded-subscribe" name="subscribe" type="submit" value="Sign up">
|
||||
</div>
|
||||
<div class="clear" id="mce-responses">
|
||||
<div class="response" id="mce-error-response" style="display:none"></div>
|
||||
<div class="response" id="mce-success-response" style="display:none"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups -->
|
||||
<div style="position: absolute; left: -5000px;">
|
||||
<input name="b_869fec0e4689e8fd1db91e795_b9664113a8" tabindex="-1" type="text" value="">
|
||||
</div>
|
||||
</form>
|
||||
<!-- End mc_embed_signup -->
|
||||
|
||||
|
||||
<div class="links">
|
||||
<div>
|
||||
<h6>Product</h6>
|
||||
<a href="/demo">Watch Demo</a>
|
||||
<a href="/data-sources/">Data Sources</a>
|
||||
<a href="/security">Security</a>
|
||||
<a href="/cloud/">Cloud</a>
|
||||
<a href="/roadmap">Roadmap</a>
|
||||
<a href="/releases">What's New</a>
|
||||
|
||||
<h6>Pricing</h6>
|
||||
<a href="/pricing/">Plans</a>
|
||||
<a href="/start/oss/">Open Source</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h6>Use Cases</h6>
|
||||
<a href="/product/business-intelligence">
|
||||
Self-Service Business Intelligence
|
||||
</a>
|
||||
<a href="/product/embedded-analytics">
|
||||
Embedded Analytics
|
||||
</a>
|
||||
|
||||
<h6>Metabase Plans</h6>
|
||||
<a href="/product/starter">Starter and Open Source</a>
|
||||
<a href="/product/pro">Pro</a>
|
||||
<a href="/product/enterprise">Enterprise</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h6>Features</h6>
|
||||
<a href="/features/metabase-ai">Metabase AI</a>
|
||||
<a href="/features/query-builder">Query Builder</a>
|
||||
<a href="/features/drill-through">Drill Through</a>
|
||||
<a href="/features/collections">Collections</a>
|
||||
<a href="/features/usage-analytics">Usage analytics</a>
|
||||
<a href="/features/analytics-dashboards">Dashboards and reporting</a>
|
||||
<a href="/features/sql-editor">SQL editor</a>
|
||||
<a href="/features/data-segregation">Data segregation</a>
|
||||
<a href="/product/data-studio">Data Studio</a>
|
||||
<a href="/features/permissions">Permissions</a>
|
||||
<a href="/product/csv-uploads">CSV upload</a>
|
||||
<a href="/product/embedded-analytics-sdk">Embedded analytics SDK</a>
|
||||
<a href="/features/white-label-analytics">White label analytics</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h6>Company</h6>
|
||||
<a href="/jobs">
|
||||
Jobs
|
||||
<div class="badge">
|
||||
We're hiring!
|
||||
</div>
|
||||
</a>
|
||||
<a href="/privacy-policies">Privacy Policies</a>
|
||||
<a href="/terms">Terms of Service</a>
|
||||
<a href="/license/">License</a>
|
||||
<a href="https://drive.google.com/drive/folders/1PbjChaDs_IROrrDz4n6eqmtcDITdVBf-?usp=sharing" target="_blank">Brand Guidelines</a>
|
||||
|
||||
<h6>Support</h6>
|
||||
<a href="/docs/latest/">Documentation</a>
|
||||
<a href="/help/">Help</a>
|
||||
<a href="/product/professional-services">
|
||||
Professional Services
|
||||
</a>
|
||||
<a href="/product/onboarding-professional-service">Onboarding
|
||||
</a>
|
||||
<a href="https://status.metabase.com/" target="_blank">
|
||||
<div class="status-circle mini-circle"></div>
|
||||
<span class="ml-2">Status</span>
|
||||
</a>
|
||||
<a class="pseudo-link light flex align-center" href="https://github.com/metabase/metabase" target="_blank">
|
||||
<svg id="" class="" width="12" height="12" viewBox="0 0 26 26" fill="currentcolor">
|
||||
<path d="M12.7888232,0 C5.72701843,0 0,5.72623326 0,12.7903935 C0,18.4412506 3.664381,23.2347038 8.74677616,24.9259565 C9.38668842,25.0437318 9.61988344,24.6487921 9.61988344,24.3095993 C9.61988344,24.0065243 9.60889108,23.2017267 9.60260973,22.1346828 C6.04501164,22.9072885 5.29439063,20.419875 5.29439063,20.419875 C4.71258083,18.942188 3.87402097,18.5488187 3.87402097,18.5488187 C2.71275688,17.7557986 3.96195983,17.7715019 3.96195983,17.7715019 C5.24571019,17.8617963 5.92095503,19.0897997 5.92095503,19.0897997 C7.06180474,21.0440839 8.9148022,20.4795478 9.64343849,20.1521326 C9.75964341,19.3261354 10.0901993,18.7623845 10.4553026,18.4428209 C7.61534847,18.1201167 4.62935298,17.0224513 4.62935298,12.12143 C4.62935298,10.7254006 5.12793493,9.58298052 5.94608042,8.68945886 C5.81417212,8.36596947 5.37526298,7.06494541 6.07170736,5.30459782 C6.07170736,5.30459782 7.14503259,4.96069405 9.5884767,6.61582907 C10.6084105,6.33159811 11.7029352,6.19026779 12.7903935,6.18477161 C13.8770666,6.19026779 14.9708062,6.33159811 15.9923103,6.61582907 C18.4341841,4.96069405 19.505939,5.30459782 19.505939,5.30459782 C20.2039537,7.06494541 19.7650445,8.36596947 19.6339214,8.68945886 C20.4536372,9.58298052 20.9482933,10.7254006 20.9482933,12.12143 C20.9482933,17.035014 17.9575868,18.1161909 15.1089958,18.4326137 C15.5675342,18.8275535 15.9766069,19.6080109 15.9766069,20.8014669 C15.9766069,22.5107785 15.9609036,23.8903194 15.9609036,24.3095993 C15.9609036,24.6519328 16.1917431,25.0500132 16.8402922,24.9251714 C21.9187615,23.2299928 25.5800018,18.4396803 25.5800018,12.7903935 C25.5800018,5.72623326 19.8529834,0 12.7888232,0">
|
||||
</path>
|
||||
</svg>
|
||||
|
||||
<span class="ml-2" style="padding-bottom: 1px;">GitHub</span>
|
||||
<div class="mini-box bg-light-yellow text-dark-yellow ml-2 flex align-center">
|
||||
<span class="github-stars mr-1"></span>
|
||||
<svg fill="none" height="11" viewBox="0 0 12 11" width="12">
|
||||
<path d="M6 1.07149L7.39167 3.70919L7.50639 3.92663L7.74864 3.96855L10.6873 4.47701L8.60874 6.61565L8.43739 6.79196L8.47239 7.03531L8.89691 9.98725L6.22062 8.67131L6 8.56283L5.77938 8.67131L3.10309 9.98725L3.52761 7.03531L3.56261 6.79196L3.39126 6.61565L1.31271 4.47701L4.25136 3.96855L4.49361 3.92663L4.60833 3.70919L6 1.07149Z" fill="#F7C217" stroke="#F7C217"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h6>Resources</h6>
|
||||
<a href="/case-studies">Case Studies</a>
|
||||
<a href="/blog">Blog</a>
|
||||
<a href="/learn/">Learn</a>
|
||||
<a href="/community">Community Stories</a>
|
||||
<a href="/glossary/">
|
||||
Data and Business Intelligence Glossary
|
||||
</a>
|
||||
<a href="/partners/">Partners</a>
|
||||
<a class="more-resources">
|
||||
<span class="mr-1">Choosing Metabase</span>
|
||||
<svg class="more-resources-chevron" fill="none" height="8" viewBox="0 0 32 32" width="8">
|
||||
<rect fill="none" height="32" width="32"></rect>
|
||||
<path d="M3 8.96338L16 21.9634L29 8.96338" opacity="0.9" stroke-linecap="round" stroke-linejoin="round" stroke-width="5" stroke="currentcolor"></path>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="more-resources-wrapper hide flex flex-column">
|
||||
<a href="/lp/business-intelligence-demo">Business Intelligence</a>
|
||||
<a href="/lp/bi-for-startups-demo">Business Intelligence for Startups</a>
|
||||
<a href="/lp/embedded-analytics-demo">Embedded Analytics</a>
|
||||
<a href="/lp/metabase-vs-tableau">Metabase vs. Tableau</a>
|
||||
<a href="/lp/metabase-vs-looker">Metabase vs. Looker</a>
|
||||
<a href="/lp/metabase-vs-looker-studio">Metabase vs. Looker Studio</a>
|
||||
<a href="/lp/metabase-vs-power-bi">Metabase vs. PowerBI</a>
|
||||
<a href="/lp/metabase-vs-superset">Metabase vs. Superset</a>
|
||||
</div>
|
||||
|
||||
<a class="more-resources">
|
||||
<span class="mr-1">More Resources</span>
|
||||
<svg class="more-resources-chevron" fill="none" height="8" viewBox="0 0 32 32" width="8">
|
||||
<rect fill="none" height="32" width="32"></rect>
|
||||
<path d="M3 8.96338L16 21.9634L29 8.96338" opacity="0.9" stroke-linecap="round" stroke-linejoin="round" stroke-width="5" stroke="currentcolor"></path>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="more-resources-wrapper hide flex flex-column">
|
||||
<a href="/ai-data-generator">Open source AI data generator</a>
|
||||
<a href="/dashboards/">Example Dashboards</a>
|
||||
<a href="/learn/sql/working-with-sql/sql-best-practices">
|
||||
SQL Best Practices
|
||||
</a>
|
||||
<a href="/learn/cheat-sheets/which-chart-to-use">
|
||||
Which chart to use? A cheat sheet
|
||||
</a>
|
||||
<a href="/blog/what-is-embedded-analytics">
|
||||
What is embedded analytics?
|
||||
</a>
|
||||
<a href="/learn/sql/">
|
||||
Learn SQL
|
||||
</a>
|
||||
<a href="/learn/sql/working-with-sql/sql-join-types">
|
||||
Types of SQL Joins
|
||||
</a>
|
||||
<a href="/blog/6-most-common-type-of-data-bias-in-data-analysis">
|
||||
Data Bias Examples
|
||||
</a>
|
||||
<a href="/dashboards/marketing-teams">
|
||||
Dashboard for Marketing Teams
|
||||
</a>
|
||||
<a href="/dashboards/sales-teams">
|
||||
Dashboards for Sales Teams
|
||||
</a>
|
||||
<a href="/dashboards/engineering-teams">
|
||||
Dashboards for Engineering Teams
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer-logo-container">
|
||||
<div>
|
||||
<a href="/">
|
||||
<div class="MB-Logo-small"></div>
|
||||
</a>
|
||||
<span>
|
||||
© Metabase 2026
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://www.youtube.com/channel/UCvg53nhM-xPUiFGqDJDhjaw" target="_blank">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="18" fill="none"><g clip-path="url(#a)">
|
||||
<path fill="#8D93A5" fill-rule="evenodd" d="M21.377 1.078a3.01 3.01 0 0 1 2.122 2.128C24 5.083 24 9 24 9s0 3.917-.502 5.794a3.01 3.01 0 0 1-2.121 2.129c-1.872.503-9.377.503-9.377.503s-7.505 0-9.377-.503a3.01 3.01 0 0 1-2.121-2.129C0 12.917 0 9 0 9s0-3.917.502-5.794a3.01 3.01 0 0 1 2.121-2.128C4.495.575 12 .575 12 .575s7.505 0 9.377.503ZM15.818 9l-6.273 3.556V5.444L15.818 9Z" clip-rule="evenodd"></path></g><defs><clipPath id="a"><path fill="#fff" d="M0 .575h24v16.85H0z"></path></clipPath></defs>
|
||||
</svg>
|
||||
|
||||
</a>
|
||||
<a href="https://www.github.com/metabase" target="_blank">
|
||||
<svg id="" class="" width="24" height="24" viewBox="0 0 26 26" fill="#8D93A5">
|
||||
<path d="M12.7888232,0 C5.72701843,0 0,5.72623326 0,12.7903935 C0,18.4412506 3.664381,23.2347038 8.74677616,24.9259565 C9.38668842,25.0437318 9.61988344,24.6487921 9.61988344,24.3095993 C9.61988344,24.0065243 9.60889108,23.2017267 9.60260973,22.1346828 C6.04501164,22.9072885 5.29439063,20.419875 5.29439063,20.419875 C4.71258083,18.942188 3.87402097,18.5488187 3.87402097,18.5488187 C2.71275688,17.7557986 3.96195983,17.7715019 3.96195983,17.7715019 C5.24571019,17.8617963 5.92095503,19.0897997 5.92095503,19.0897997 C7.06180474,21.0440839 8.9148022,20.4795478 9.64343849,20.1521326 C9.75964341,19.3261354 10.0901993,18.7623845 10.4553026,18.4428209 C7.61534847,18.1201167 4.62935298,17.0224513 4.62935298,12.12143 C4.62935298,10.7254006 5.12793493,9.58298052 5.94608042,8.68945886 C5.81417212,8.36596947 5.37526298,7.06494541 6.07170736,5.30459782 C6.07170736,5.30459782 7.14503259,4.96069405 9.5884767,6.61582907 C10.6084105,6.33159811 11.7029352,6.19026779 12.7903935,6.18477161 C13.8770666,6.19026779 14.9708062,6.33159811 15.9923103,6.61582907 C18.4341841,4.96069405 19.505939,5.30459782 19.505939,5.30459782 C20.2039537,7.06494541 19.7650445,8.36596947 19.6339214,8.68945886 C20.4536372,9.58298052 20.9482933,10.7254006 20.9482933,12.12143 C20.9482933,17.035014 17.9575868,18.1161909 15.1089958,18.4326137 C15.5675342,18.8275535 15.9766069,19.6080109 15.9766069,20.8014669 C15.9766069,22.5107785 15.9609036,23.8903194 15.9609036,24.3095993 C15.9609036,24.6519328 16.1917431,25.0500132 16.8402922,24.9251714 C21.9187615,23.2299928 25.5800018,18.4396803 25.5800018,12.7903935 C25.5800018,5.72623326 19.8529834,0 12.7888232,0">
|
||||
</path>
|
||||
</svg>
|
||||
|
||||
</a>
|
||||
<a href="https://www.twitter.com/metabase" target="_blank">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_2878_2487)">
|
||||
<path d="M13.9761 10.1625L22.7186 0.00012207H20.6469L13.0558 8.82396L6.99289 0.00012207H0L9.16837 13.3433L0 24.0001H2.07179L10.0881 14.6819L16.491 24.0001H23.4839L13.9756 10.1625H13.9761ZM11.1385 13.4609L10.2096 12.1322L2.81829 1.55974H6.00044L11.9653 10.0921L12.8942 11.4207L20.6479 22.5114H17.4657L11.1385 13.4614V13.4609Z" fill="#8D93A5"></path>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2878_2487">
|
||||
<rect width="24" height="24" fill="white"></rect>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
</a>
|
||||
<a href="https://www.linkedin.com/company/metabase" target="_blank">
|
||||
<svg id="" class="" fill="#8D93A5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30" width="28" height="28">
|
||||
<path d="M24,4H6C4.895,4,4,4.895,4,6v18c0,1.105,0.895,2,2,2h18c1.105,0,2-0.895,2-2V6C26,4.895,25.105,4,24,4z M10.954,22h-2.95 v-9.492h2.95V22z M9.449,11.151c-0.951,0-1.72-0.771-1.72-1.72c0-0.949,0.77-1.719,1.72-1.719c0.948,0,1.719,0.771,1.719,1.719 C11.168,10.38,10.397,11.151,9.449,11.151z M22.004,22h-2.948v-4.616c0-1.101-0.02-2.517-1.533-2.517 c-1.535,0-1.771,1.199-1.771,2.437V22h-2.948v-9.492h2.83v1.297h0.04c0.394-0.746,1.356-1.533,2.791-1.533 c2.987,0,3.539,1.966,3.539,4.522V22z"></path>
|
||||
</svg>
|
||||
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
1422
docs-build/src/data/header.html
Normal file
95
docs-build/src/layouts/DocsLayout.astro
Normal file
@@ -0,0 +1,95 @@
|
||||
---
|
||||
import type { MarkdownHeading } from "astro";
|
||||
import Sidebar from "../components/Sidebar.astro";
|
||||
import TopBar from "../components/TopBar.astro";
|
||||
import TableOfContents from "../components/TableOfContents.astro";
|
||||
import FeedbackWidget from "../components/FeedbackWidget.astro";
|
||||
import Header from "../components/Header.astro";
|
||||
import Footer from "../components/Footer.astro";
|
||||
import { findTrail } from "../lib/nav";
|
||||
import "../styles/chrome.css";
|
||||
import "../styles/docs.css";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
summary?: string;
|
||||
headings: MarkdownHeading[];
|
||||
slug: string;
|
||||
}
|
||||
|
||||
const { title, summary, headings, slug } = Astro.props;
|
||||
const base = import.meta.env.BASE_URL.replace(/\/$/, "");
|
||||
// Walk the nav once per page render and share the trail with both Sidebar
|
||||
// (drives initial expansion state) and TopBar (drives the breadcrumbs).
|
||||
const trail = findTrail(slug) ?? [];
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{title} | Metabase Documentation</title>
|
||||
{summary && <meta name="description" content={summary} />}
|
||||
<link rel="icon" type="image/svg+xml" href={`${base}/images/favicon.svg`} />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;0,900;1,400;1,700&family=Roboto+Mono&display=swap"
|
||||
/>
|
||||
{/* Image URLs that chrome.css needs are passed in as CSS variables so the
|
||||
background-image rules stay correct under any DOCS_BASE_PATH. */}
|
||||
<style is:inline set:html={`:root {
|
||||
--mb-logo-url: url("${base}/images/logo.svg");
|
||||
--mb-logo-wordmark-url: url("${base}/images/logo-with-wordmark.svg");
|
||||
--mb-embed-background-url: url("${base}/images/navigation-header/embed-background.svg");
|
||||
}`} />
|
||||
{/* Inkeep search widget reads these paths from window.__INKEEP_CONFIG__
|
||||
at init time. The inline script runs synchronously during head parsing
|
||||
so the global is set before js/inkeep.js (a deferred module) executes. */}
|
||||
<script is:inline define:vars={{ base }}>
|
||||
window.__INKEEP_CONFIG__ = {
|
||||
themeUrl: `${base}/css/inkeep.css`,
|
||||
metabaseIconUrl: "https://www.metabase.com/images/icons/metabase-icon.svg",
|
||||
personIconUrl: "https://www.metabase.com/images/icons/person.svg",
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<Header />
|
||||
<div class="docs-shell">
|
||||
<Sidebar currentSlug={slug} trail={trail} />
|
||||
<TopBar trail={trail} />
|
||||
<article class="docs-content">
|
||||
<slot />
|
||||
</article>
|
||||
<aside class="docs-rail" aria-label="On this page">
|
||||
<TableOfContents headings={headings} />
|
||||
<FeedbackWidget />
|
||||
</aside>
|
||||
</div>
|
||||
<Footer />
|
||||
|
||||
<script src={`${base}/js/anchor-links.js`} defer></script>
|
||||
<script src={`${base}/js/code-snippet-copy.js`} defer></script>
|
||||
<script src={`${base}/js/table-overflow.js`} defer></script>
|
||||
<script src={`${base}/js/right-sidebar.js`} defer></script>
|
||||
<script src={`${base}/js/image-zoom.js`} defer></script>
|
||||
<script src={`${base}/js/feedback-widget.js`} defer></script>
|
||||
<script src={`${base}/js/sidebar.js`} defer></script>
|
||||
<script src={`${base}/js/main-nav.js`} defer></script>
|
||||
<script src={`${base}/js/promo-banner.js`} defer></script>
|
||||
<script src={`${base}/js/status.js`} defer></script>
|
||||
<script src={`${base}/js/github-stars.js`} defer></script>
|
||||
<script src={`${base}/js/more-resources.js`} defer></script>
|
||||
<script src={`${base}/js/inkeep.js`} type="module" defer></script>
|
||||
{/* Snowplow page-view tracking is skipped under `astro dev` so local
|
||||
browsing doesn't pollute the production analytics stream. The tag is
|
||||
still emitted by `astro build` (and therefore by `astro preview`); set
|
||||
DOCS_DISABLE_ANALYTICS=1 in the build env to opt out of that too. */}
|
||||
{!import.meta.env.DEV && !process.env.DOCS_DISABLE_ANALYTICS && (
|
||||
<script src={`${base}/js/snowplow.js`} async></script>
|
||||
)}
|
||||
</body>
|
||||
</html>
|
||||
41
docs-build/src/lib/api-spec.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
// Locates and reads the OpenAPI spec at <repo>/docs/api.json.
|
||||
//
|
||||
// docs/api.json is generated by `clojure -M:ee:doc api-documentation` and is
|
||||
// gitignored — it only exists after `./bin/mage docs-generate --api` (or a full
|
||||
// `bun run docs:build`). So we read it at build time instead of `import`ing it:
|
||||
// a static `import ... from "../../docs/api.json"` would make Vite fail the
|
||||
// whole dev server / build on a fresh checkout where the file isn't present.
|
||||
//
|
||||
// We resolve the path relative to `process.cwd()` (which is always `docs-build/`
|
||||
// when running `astro dev` / `astro build`, same as the content-collection glob
|
||||
// in src/content.config.ts) rather than `import.meta.url`, which doesn't survive
|
||||
// Vite bundling — see the note in src/lib/nav.ts.
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
export const API_JSON_PATH = path.resolve(process.cwd(), "../docs/api.json");
|
||||
|
||||
// Minimal valid OpenAPI document — keeps api.json.ts serving something parseable
|
||||
// when the real spec is absent (api.astro renders a "not generated" notice).
|
||||
export const STUB_API_SPEC = {
|
||||
openapi: "3.1.0",
|
||||
info: { title: "Metabase API — not generated", version: "0" },
|
||||
paths: {},
|
||||
} as const;
|
||||
|
||||
export function hasApiSpec(): boolean {
|
||||
return fs.existsSync(API_JSON_PATH);
|
||||
}
|
||||
|
||||
export function readApiSpec(): unknown {
|
||||
// Skip the read entirely (and any warning) when the spec just isn't
|
||||
// generated yet — that's the expected dev-mode state. Only warn when
|
||||
// the file exists but won't parse, which is a real authoring problem.
|
||||
if (!hasApiSpec()) return STUB_API_SPEC;
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(API_JSON_PATH, "utf8"));
|
||||
} catch (err) {
|
||||
console.warn(`Could not parse ${API_JSON_PATH}: ${err}`);
|
||||
return STUB_API_SPEC;
|
||||
}
|
||||
}
|
||||
275
docs-build/src/lib/llms.ts
Normal file
@@ -0,0 +1,275 @@
|
||||
// Shared helpers for the llms.txt endpoint family (src/pages/llms*.txt.ts).
|
||||
// Reads the same `docs` content collection as the rest of the site, applies
|
||||
// the prefix-based include/exclude filter the legacy generate-llms-files.mjs
|
||||
// script used, and assembles index + per-section bundles for AI-tool RAG.
|
||||
//
|
||||
// One version per build: DOCS_BASE_PATH (set by `./bin/mage docs-build`,
|
||||
// e.g. /docs/latest or /docs/v0.58) determines which version's files are
|
||||
// emitted. Multi-version coverage comes from running docs-build per release
|
||||
// branch, not a single multi-version pass.
|
||||
|
||||
import fs from "node:fs";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
const REPO = "metabase/metabase";
|
||||
|
||||
export const LLMS_FULL_SECTIONS = ["embedding", "agent-api"] as const;
|
||||
export type LlmsSection = (typeof LLMS_FULL_SECTIONS)[number];
|
||||
|
||||
// Prefix matching: trailing "/" → directory; otherwise → exact file path.
|
||||
const INCLUDED_PATHS = [
|
||||
// All embedding docs (SDK, modular embedding, integration guides)
|
||||
"embedding/",
|
||||
|
||||
// Auth/SSO configuration for embedding
|
||||
"people-and-groups/api-keys.md",
|
||||
"people-and-groups/authenticating-with-jwt.md",
|
||||
"people-and-groups/authenticating-with-saml.md",
|
||||
"people-and-groups/saml-auth0.md",
|
||||
"people-and-groups/saml-azure.md",
|
||||
"people-and-groups/saml-google.md",
|
||||
"people-and-groups/saml-keycloak.md",
|
||||
"people-and-groups/saml-okta.md",
|
||||
"people-and-groups/google-sign-in.md",
|
||||
"people-and-groups/ldap.md",
|
||||
|
||||
// Configuration reference
|
||||
"configuring-metabase/environment-variables.md",
|
||||
"configuring-metabase/config-file.md",
|
||||
|
||||
// Agent API reference
|
||||
"agent-api/",
|
||||
];
|
||||
|
||||
const EXCLUDED_PATHS = ["embedding/sdk/api/snippets"];
|
||||
|
||||
const FRONTMATTER_RE = /^---\s*\n([\s\S]*?)\n---\s*\n/;
|
||||
// `{% include_file ... %}` transclusion tags — the included example code isn't
|
||||
// carried into the llms.txt bundle. We deliberately do NOT strip `{{ ... }}`
|
||||
// (those are literal content, e.g. SQL parameter examples like `{{category}}`).
|
||||
const INCLUDE_FILE_TAG_RE = /\{%[\s\S]*?%\}/g;
|
||||
const H1_RE = /^#\s+(.+)$/m;
|
||||
|
||||
const docsBasePath = process.env.DOCS_BASE_PATH ?? "/docs/latest";
|
||||
const baseMatch = docsBasePath.match(/^\/docs\/(.+)$/);
|
||||
export const version = baseMatch ? baseMatch[1] : "latest";
|
||||
|
||||
const latestBranch = process.env.GITHUB_REF_NAME || "master";
|
||||
|
||||
export function formatVersionForDisplay(v: string): string {
|
||||
if (v === "master") return "development (unreleased)";
|
||||
if (v === "latest") {
|
||||
const m = latestBranch.match(/^release-x\.(\d+)\.x$/);
|
||||
if (m) return `${m[1]} (latest)`;
|
||||
return "latest";
|
||||
}
|
||||
const m = v.match(/^v0\.(\d+)$/);
|
||||
return m ? m[1] : v;
|
||||
}
|
||||
|
||||
export function versionToBranch(v: string): string {
|
||||
if (v === "master") return "master";
|
||||
if (v === "latest") return latestBranch;
|
||||
const m = v.match(/^v0\.(\d+)$/);
|
||||
return m ? `release-x.${m[1]}.x` : "master";
|
||||
}
|
||||
|
||||
export function aboveVersion(sourceVersion: string, target: number): boolean {
|
||||
if (sourceVersion === "master" || sourceVersion === "latest") return true;
|
||||
const m = sourceVersion.match(/^v0\.(\d+)$/);
|
||||
if (!m) return false;
|
||||
return parseInt(m[1], 10) >= target;
|
||||
}
|
||||
|
||||
export type DocEntry = CollectionEntry<"docs">;
|
||||
|
||||
// Reconstructs the on-disk-relative path (e.g. "embedding/sdk/intro.md") from
|
||||
// either entry.filePath (most reliable) or entry.id (fallback). The filter
|
||||
// patterns above are written against on-disk paths, not Astro's slug-like ids.
|
||||
export function entryRelativePath(entry: DocEntry): string {
|
||||
if (entry.filePath) {
|
||||
const m = entry.filePath.match(/\/docs\/(.+)$/);
|
||||
if (m) return m[1];
|
||||
}
|
||||
return `${entry.id}.md`;
|
||||
}
|
||||
|
||||
export function pathMatchesAllowlist(rel: string): boolean {
|
||||
return INCLUDED_PATHS.some((pattern) =>
|
||||
pattern.endsWith("/") ? rel.startsWith(pattern) : rel === pattern,
|
||||
);
|
||||
}
|
||||
|
||||
export function pathMatchesExcludelist(rel: string): boolean {
|
||||
return EXCLUDED_PATHS.some((pattern) => rel.startsWith(pattern));
|
||||
}
|
||||
|
||||
export function entryAllowed(entry: DocEntry): boolean {
|
||||
const rel = entryRelativePath(entry);
|
||||
return pathMatchesAllowlist(rel) && !pathMatchesExcludelist(rel);
|
||||
}
|
||||
|
||||
function readSource(entry: DocEntry): string {
|
||||
if (!entry.filePath) return "";
|
||||
return fs.readFileSync(entry.filePath, "utf8");
|
||||
}
|
||||
|
||||
function stripFrontmatter(src: string): string {
|
||||
return src.replace(FRONTMATTER_RE, "");
|
||||
}
|
||||
|
||||
export function entryTitle(entry: DocEntry): string {
|
||||
if (entry.data.title) return entry.data.title.trim();
|
||||
const body = stripFrontmatter(readSource(entry));
|
||||
const h1 = body.match(H1_RE);
|
||||
if (h1) return h1[1].trim();
|
||||
const rel = entryRelativePath(entry);
|
||||
const base = rel.split("/").pop()?.replace(/\.md$/, "") ?? rel;
|
||||
return base
|
||||
.split(/[-_]/)
|
||||
.map((p) => (p.length ? p[0].toUpperCase() + p.slice(1) : p))
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
export function bodyForBundle(entry: DocEntry): string {
|
||||
return stripFrontmatter(readSource(entry))
|
||||
.replace(INCLUDE_FILE_TAG_RE, "")
|
||||
.trim();
|
||||
}
|
||||
|
||||
export function rawGithubUrl(relativePath: string): string {
|
||||
const branch = versionToBranch(version);
|
||||
return `https://raw.githubusercontent.com/${REPO}/refs/heads/${branch}/docs/${relativePath}`;
|
||||
}
|
||||
|
||||
// ----- Static prose (verbatim from the legacy script) -----
|
||||
|
||||
export const VERSION_DETECTION_INSTRUCTIONS = `## IMPORTANT: Verify SDK and Metabase Version Compatibility
|
||||
|
||||
The SDK version MUST match the Metabase instance version. Mismatched versions can cause errors. When looking up documentation, ALWAYS check the Metabase version.
|
||||
|
||||
**Step 1: Ask the user for their Metabase instance URL**
|
||||
|
||||
Before proceeding, ask the user where their Metabase instance is located. Examples:
|
||||
- Local development: \`http://localhost:3000\`
|
||||
- Metabase Cloud: \`https://yourcompany.metabaseapp.com\`
|
||||
- Self-hosted: \`https://metabase.yourcompany.com\`
|
||||
|
||||
**Step 2: Check if SDK is already installed (React SDK / Modular Embedding only)**
|
||||
|
||||
Skip this step if not using the React SDK (\`@metabase/embedding-sdk-react\`).
|
||||
|
||||
\`\`\`bash
|
||||
npm list @metabase/embedding-sdk-react
|
||||
\`\`\`
|
||||
|
||||
If installed, note the version (e.g., \`0.58.0\` means this is for Metabase 58).
|
||||
|
||||
**Step 3: Query the Metabase instance version**
|
||||
|
||||
Using the URL from Step 1:
|
||||
|
||||
\`\`\`bash
|
||||
curl <METABASE_INSTANCE_URL>/api/session/properties | jq .version
|
||||
\`\`\`
|
||||
|
||||
This returns (no authentication required):
|
||||
\`\`\`json
|
||||
{ "date": "2025-01-10", "tag": "v1.58.0", "hash": "8e44dd8" }
|
||||
\`\`\`
|
||||
|
||||
If \`jq\` is not installed, you can grep the version. Extract the major version: \`58\` from \`v1.58.x\` or \`v0.58.x\`.
|
||||
|
||||
**Step 4: Ensure versions match**
|
||||
|
||||
- If the versions mismatch, you MUST fetch the version-specific llms.txt documentation that matches the Metabase instance version: \`https://metabase.com/docs/v0.{VERSION}/llms.txt\` (e.g., \`/docs/v0.58/llms.txt\` for Metabase 58)
|
||||
- For React SDK, ask the user to install or update their SDK packages if they are mismatched: \`npm install @metabase/embedding-sdk-react@{VERSION}-stable\` (e.g., \`@58-stable\` for Metabase 58)
|
||||
|
||||
**Do NOT guess versions or use versions from your training data. Always verify first.**`;
|
||||
|
||||
export const MODULAR_EMBEDDING_GOTCHA_NOTES = `## Modular Embedding Deprecations and Gotchas
|
||||
|
||||
Watch out for these deprecated props and gotchas for Metabase 57 onwards, for modular embedding.
|
||||
|
||||
1. \`config\` prop on MetabaseProvider no longer exist as it is replaced by \`authConfig\`.
|
||||
2. \`authProviderUri\` field no longer exist.
|
||||
3. \`jwtProviderUri\` is an optional field that only exists in v58+. This is used to make JWT auth faster by skipping the \`GET /auth/sso\` discovery request. This field is not required for the initial implementation.
|
||||
4. Numeric IDs must be integers not strings, e.g. \`dashboardId={1}\`. When the ID is retrieved from the router as a string AND it is numeric, \`parseInt\` it before passing it to the SDK.
|
||||
5. IDs can also be strings for entity IDs, so you should NOT parse all IDs as numbers if entity IDs are also to be expected.
|
||||
6. \`fetchRequestToken\` is not needed by default in most implementations. This is only used to customize how the SDK fetches the request token. For example, if the \`/sso/metabase\` endpoint in the user's backend requires passing custom auth tokens or headers.
|
||||
7. When using \`fetchRequestToken\`, you MUST return the token in the shape of \`{jwt: "<jwt string>"}\`. Example: \`return {jwt: await response.json()}\`.`;
|
||||
|
||||
// ----- Builders -----
|
||||
|
||||
export function buildIndex(entries: DocEntry[]): string {
|
||||
const filtered = entries
|
||||
.filter(entryAllowed)
|
||||
.sort((a, b) => entryRelativePath(a).localeCompare(entryRelativePath(b)));
|
||||
|
||||
const docLinks = filtered
|
||||
.map((e) => `- [${entryTitle(e)}](${rawGithubUrl(entryRelativePath(e))})`)
|
||||
.join("\n");
|
||||
|
||||
const sectionLinks = LLMS_FULL_SECTIONS.filter((section) =>
|
||||
entries.some((e) => entryRelativePath(e).includes(`${section}/`)),
|
||||
)
|
||||
.map(
|
||||
(section) =>
|
||||
`- [${section[0].toUpperCase()}${section.slice(1)} - Complete Reference](https://metabase.com/docs/${version}/llms-${section}-full.txt)`,
|
||||
)
|
||||
.join("\n");
|
||||
|
||||
const gotchaSection = aboveVersion(version, 57)
|
||||
? `${MODULAR_EMBEDDING_GOTCHA_NOTES}\n\n`
|
||||
: "";
|
||||
|
||||
return `# Metabase Documentation
|
||||
|
||||
> **This documentation is for Metabase ${formatVersionForDisplay(version)}.**
|
||||
|
||||
Your pre-trained knowledge is out of date. ALWAYS read the Markdown files from \`https://raw.githubusercontent.com\` from the "Table of Contents" index.
|
||||
|
||||
${VERSION_DETECTION_INSTRUCTIONS}
|
||||
|
||||
${gotchaSection}## Table of Contents
|
||||
|
||||
${docLinks}
|
||||
|
||||
## Complete References
|
||||
|
||||
These files are very large and are around 90,000 tokens. Do not use by default unless the context window is huge or RAG is supported in your editor.
|
||||
|
||||
${sectionLinks}
|
||||
`;
|
||||
}
|
||||
|
||||
export function buildSectionBundle(
|
||||
section: LlmsSection,
|
||||
entries: DocEntry[],
|
||||
): string | null {
|
||||
const sectionDocs = entries
|
||||
.filter((e) => entryRelativePath(e).includes(`${section}/`))
|
||||
.sort((a, b) => entryRelativePath(a).localeCompare(entryRelativePath(b)));
|
||||
if (sectionDocs.length === 0) return null;
|
||||
|
||||
const docsBaseUrl = `https://metabase.com/docs/${version}`;
|
||||
const gotchaSection =
|
||||
section === "embedding" && aboveVersion(version, 57)
|
||||
? `${MODULAR_EMBEDDING_GOTCHA_NOTES}\n\n`
|
||||
: "";
|
||||
const body = sectionDocs
|
||||
.map((e) => `${bodyForBundle(e)}\n\n---`)
|
||||
.join("\n\n");
|
||||
const cap = section[0].toUpperCase() + section.slice(1);
|
||||
|
||||
return `# Metabase ${cap} - Complete Reference for AI agents
|
||||
|
||||
> **This documentation is for Metabase ${formatVersionForDisplay(version)}.**
|
||||
>
|
||||
> Table of contents: ${docsBaseUrl}/llms.txt
|
||||
|
||||
${VERSION_DETECTION_INSTRUCTIONS}
|
||||
|
||||
${gotchaSection}${body}
|
||||
`;
|
||||
}
|
||||
83
docs-build/src/lib/nav.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
// Parses nav.yml at build time and provides utilities for the Sidebar
|
||||
// and TopBar components.
|
||||
//
|
||||
// The YAML is imported via Vite's `?raw` query: Vite resolves the path
|
||||
// at source compile time and inlines the file contents as a string. This
|
||||
// avoids the import.meta.url / fs.readFileSync resolution trap that breaks
|
||||
// after Astro bundles the module.
|
||||
|
||||
import yaml from "js-yaml";
|
||||
import navYamlSource from "../../nav.yml?raw";
|
||||
|
||||
export interface NavPage {
|
||||
name: string;
|
||||
url?: string;
|
||||
navName?: string;
|
||||
pages?: NavPage[];
|
||||
}
|
||||
|
||||
export interface NavCategory {
|
||||
name: string;
|
||||
pages: NavPage[];
|
||||
}
|
||||
|
||||
export interface NavData {
|
||||
categories: NavCategory[];
|
||||
}
|
||||
|
||||
let cached: NavData | null = null;
|
||||
|
||||
export function getNav(): NavData {
|
||||
if (cached) return cached;
|
||||
cached = yaml.load(navYamlSource) as NavData;
|
||||
return cached;
|
||||
}
|
||||
|
||||
export function findTrail(
|
||||
slug: string,
|
||||
nav: NavData = getNav(),
|
||||
): NavPage[] | null {
|
||||
for (const cat of nav.categories) {
|
||||
const trail = walk(cat.pages, slug, [{ name: cat.name }]);
|
||||
if (trail) return trail;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Resolve a nav page's `url` field to a usable href:
|
||||
// - external (http/https) and /learn/* links pass through unchanged
|
||||
// - everything else is treated as a docs-relative path and prefixed with `base`
|
||||
// Returns undefined when the page has no url (group label).
|
||||
export function hrefForPage(p: NavPage, base: string): string | undefined {
|
||||
if (!p.url) return undefined;
|
||||
if (/^https?:\/\//.test(p.url) || p.url.startsWith("/learn/")) return p.url;
|
||||
return base + "/" + p.url.replace(/^\/+/, "");
|
||||
}
|
||||
|
||||
// True when a nav page's `url` corresponds to the current page's `slug`.
|
||||
// External and /learn/* links are never "current" — those leave the docs.
|
||||
export function urlMatchesSlug(
|
||||
url: string | undefined,
|
||||
slug: string,
|
||||
): boolean {
|
||||
if (!url) return false;
|
||||
if (/^https?:/.test(url) || url.startsWith("/learn/")) return false;
|
||||
const norm = (s: string) => s.replace(/^\/+|\/+$/g, "");
|
||||
return norm(url) === norm(slug);
|
||||
}
|
||||
|
||||
function walk(
|
||||
pages: NavPage[],
|
||||
slug: string,
|
||||
trail: NavPage[],
|
||||
): NavPage[] | null {
|
||||
for (const page of pages) {
|
||||
const here = [...trail, page];
|
||||
if (urlMatchesSlug(page.url, slug)) return here;
|
||||
if (page.pages) {
|
||||
const found = walk(page.pages, slug, here);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
39
docs-build/src/pages/[...slug].astro
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
import { getCollection, render } from "astro:content";
|
||||
import DocsLayout from "../layouts/DocsLayout.astro";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
// Astro's glob loader lowercases entry ids. Strip trailing /index for
|
||||
// dir/index.md → /dir routing.
|
||||
const normalize = (id: string): string => id.replace(/\/index$/, "");
|
||||
const entries = await getCollection("docs");
|
||||
return entries.map((entry) => {
|
||||
const id = normalize(entry.id);
|
||||
// The repo's README is the docs landing page → render at the base root.
|
||||
const slug = id === "readme" ? undefined : id;
|
||||
return { params: { slug }, props: { entry } };
|
||||
});
|
||||
}
|
||||
|
||||
const { entry } = Astro.props;
|
||||
const { Content, headings } = await render(entry);
|
||||
const id = entry.id.replace(/\/index$/, "");
|
||||
const slug = id === "readme" ? "" : id;
|
||||
|
||||
function prettifyFilename(s: string): string {
|
||||
const base = s.split("/").pop() ?? s;
|
||||
return base.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
||||
}
|
||||
|
||||
const title =
|
||||
entry.data.title ?? headings[0]?.text ?? prettifyFilename(entry.id);
|
||||
---
|
||||
|
||||
<DocsLayout
|
||||
title={title}
|
||||
summary={entry.data.summary}
|
||||
headings={headings}
|
||||
slug={slug}
|
||||
>
|
||||
<Content />
|
||||
</DocsLayout>
|
||||
63
docs-build/src/pages/api.astro
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
// Renders the Scalar viewer for the OpenAPI spec at /api.json (same origin,
|
||||
// emitted by src/pages/api.json.ts). docs/api.json is generated by
|
||||
// `clojure -M:ee:doc api-documentation` and is gitignored — when it's absent
|
||||
// (e.g. `bun run docs:dev` without running the generator first) we render a
|
||||
// short notice instead of an empty viewer.
|
||||
import { hasApiSpec } from "../lib/api-spec";
|
||||
const base = import.meta.env.BASE_URL.replace(/\/$/, "");
|
||||
const scalarVersion = "1.39.1";
|
||||
const specReady = hasApiSpec();
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>API Reference | Metabase Documentation</title>
|
||||
<meta name="description" content="The official API documentation for Metabase." />
|
||||
<link rel="icon" type="image/svg+xml" href={`${base}/images/favicon.svg`} />
|
||||
<style>
|
||||
:root {
|
||||
--scalar-font: 'Lato', sans-serif;
|
||||
}
|
||||
html, body { margin: 0; }
|
||||
.api-not-generated {
|
||||
font-family: 'Lato', sans-serif;
|
||||
max-width: 42rem;
|
||||
margin: 4rem auto;
|
||||
padding: 0 1.5rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.api-not-generated code {
|
||||
background: #f0f0f0;
|
||||
padding: 0.1em 0.35em;
|
||||
border-radius: 3px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{specReady ? (
|
||||
<Fragment>
|
||||
<script id="api-reference" data-url={`${base}/api.json`}></script>
|
||||
<script
|
||||
src={`https://cdn.jsdelivr.net/npm/@scalar/api-reference@${scalarVersion}/dist/browser/standalone.min.js`}
|
||||
></script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href={`https://cdn.jsdelivr.net/npm/@scalar/api-reference@${scalarVersion}/dist/style.min.css`}
|
||||
/>
|
||||
</Fragment>
|
||||
) : (
|
||||
<div class="api-not-generated">
|
||||
<h1>API reference not generated</h1>
|
||||
<p>
|
||||
The OpenAPI spec (<code>docs/api.json</code>) isn't present in this build.
|
||||
Generate it with <code>./bin/generate-docs.sh --api</code> — or run a full
|
||||
<code>bun run docs:build</code> — and reload.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</body>
|
||||
</html>
|
||||
13
docs-build/src/pages/api.json.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// Re-serves the OpenAPI spec (docs/api.json) under the docs base path so the
|
||||
// Scalar viewer in api.astro can fetch /docs/<version>/api.json from the same
|
||||
// origin. The spec is read at build time rather than `import`ed — see
|
||||
// src/lib/api-spec.ts for why (it's gitignored / may be absent in dev).
|
||||
import type { APIRoute } from "astro";
|
||||
import { readApiSpec } from "../lib/api-spec";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export const GET: APIRoute = () =>
|
||||
new Response(JSON.stringify(readApiSpec()), {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
35
docs-build/src/pages/llms-[section]-full.txt.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
// Dynamic endpoint for llms-{section}-full.txt: full concatenated section
|
||||
// reference for AI RAG / large-context tooling (~90k tokens per file).
|
||||
// Emitted only for sections that actually have content — see src/lib/llms.ts
|
||||
// for the section list and filter rules.
|
||||
import type { APIRoute, GetStaticPaths } from "astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import {
|
||||
LLMS_FULL_SECTIONS,
|
||||
type LlmsSection,
|
||||
buildSectionBundle,
|
||||
entryRelativePath,
|
||||
} from "../lib/llms";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
const entries = await getCollection("docs");
|
||||
return LLMS_FULL_SECTIONS
|
||||
.filter((section) =>
|
||||
entries.some((e) => entryRelativePath(e).includes(`${section}/`)),
|
||||
)
|
||||
.map((section) => ({ params: { section } }));
|
||||
};
|
||||
|
||||
export const GET: APIRoute = async ({ params }) => {
|
||||
const section = params.section as LlmsSection;
|
||||
const entries = await getCollection("docs");
|
||||
const body = buildSectionBundle(section, entries);
|
||||
// buildSectionBundle returning null is unreachable here — getStaticPaths
|
||||
// already filters out empty sections — but TS narrowing wants the guard.
|
||||
if (!body) return new Response("", { status: 404 });
|
||||
return new Response(body, {
|
||||
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
||||
});
|
||||
};
|
||||
15
docs-build/src/pages/llms.txt.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
// llms.txt — the index file consumed by AI tooling (Cursor, etc.) to
|
||||
// discover docs pages by raw.githubusercontent.com URL. See src/lib/llms.ts
|
||||
// for filter rules and version handling.
|
||||
import type { APIRoute } from "astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import { buildIndex } from "../lib/llms";
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export const GET: APIRoute = async () => {
|
||||
const entries = await getCollection("docs");
|
||||
return new Response(buildIndex(entries), {
|
||||
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
||||
});
|
||||
};
|
||||
61
docs-build/src/plugins/rehype-blockquote-classes.mjs
Normal file
@@ -0,0 +1,61 @@
|
||||
// Adds semantic CSS classes to <blockquote> elements based on the leading
|
||||
// inline marker, mimicking GitHub's note/tip/warning callout style.
|
||||
//
|
||||
// Recognizes:
|
||||
// > **Note:** ... → blockquote.note
|
||||
// > **Tip:** ... → blockquote.tip
|
||||
// > **Warning:** ... → blockquote.warning
|
||||
// > **Caution:** ... → blockquote.warning
|
||||
// > **Danger:** ... → blockquote.danger
|
||||
// > **Plans:** ... → blockquote.plans-callout (pricing / availability)
|
||||
//
|
||||
// Also leaves alone blockquotes already classed (e.g. an author-written
|
||||
// .plans-callout).
|
||||
|
||||
import { visit } from "unist-util-visit";
|
||||
|
||||
const MARKERS = {
|
||||
note: "note",
|
||||
tip: "tip",
|
||||
hint: "tip",
|
||||
warning: "warning",
|
||||
caution: "warning",
|
||||
danger: "danger",
|
||||
plans: "plans-callout",
|
||||
};
|
||||
|
||||
function existingClasses(node) {
|
||||
const cls = node.properties?.className;
|
||||
if (Array.isArray(cls)) return cls;
|
||||
if (typeof cls === "string") return cls.split(/\s+/);
|
||||
return [];
|
||||
}
|
||||
|
||||
export function rehypeBlockquoteClasses() {
|
||||
return (tree) => {
|
||||
visit(tree, "element", (node) => {
|
||||
if (node.tagName !== "blockquote") return;
|
||||
const classes = existingClasses(node);
|
||||
// If author already classed it (e.g., .plans-callout), don't override.
|
||||
if (classes.some((c) => c.endsWith("-callout"))) return;
|
||||
|
||||
// Find first text content inside the blockquote.
|
||||
const firstP = node.children?.find(
|
||||
(c) => c.type === "element" && c.tagName === "p",
|
||||
);
|
||||
if (!firstP) return;
|
||||
const strong = firstP.children?.find(
|
||||
(c) => c.type === "element" && c.tagName === "strong",
|
||||
);
|
||||
const text = strong?.children?.[0]?.value ?? "";
|
||||
const marker = text.replace(/:\s*$/, "").trim().toLowerCase();
|
||||
const kind = MARKERS[marker];
|
||||
if (!kind) return;
|
||||
|
||||
node.properties = node.properties ?? {};
|
||||
const next = new Set(classes);
|
||||
next.add(kind);
|
||||
node.properties.className = Array.from(next);
|
||||
});
|
||||
};
|
||||
}
|
||||
128
docs-build/src/plugins/rehype-blockquote-classes.test.mjs
Normal file
@@ -0,0 +1,128 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { rehypeBlockquoteClasses } from "./rehype-blockquote-classes.mjs";
|
||||
|
||||
// Minimal hast builders. The plugin only inspects the first <p>'s first
|
||||
// <strong>, so we can build very small trees.
|
||||
function el(tagName, properties, ...children) {
|
||||
return { type: "element", tagName, properties: properties || {}, children };
|
||||
}
|
||||
function root(...children) {
|
||||
return { type: "root", children };
|
||||
}
|
||||
function text(value) {
|
||||
return { type: "text", value };
|
||||
}
|
||||
function strong(value) {
|
||||
return el("strong", {}, text(value));
|
||||
}
|
||||
function calloutBQ(markerText, body = " and the rest of the note.") {
|
||||
return el("blockquote", {}, el("p", {}, strong(markerText), text(body)));
|
||||
}
|
||||
|
||||
function run(tree) {
|
||||
rehypeBlockquoteClasses()(tree);
|
||||
return tree;
|
||||
}
|
||||
|
||||
function classOf(blockquoteNode) {
|
||||
return blockquoteNode.properties?.className;
|
||||
}
|
||||
|
||||
// Recognized markers ----------------------------------------------------------
|
||||
|
||||
test("**Tip:** → blockquote.tip", () => {
|
||||
const tree = root(calloutBQ("Tip:"));
|
||||
run(tree);
|
||||
assert.deepEqual(classOf(tree.children[0]), ["tip"]);
|
||||
});
|
||||
|
||||
test("**Note:** → blockquote.note", () => {
|
||||
const tree = root(calloutBQ("Note:"));
|
||||
run(tree);
|
||||
assert.deepEqual(classOf(tree.children[0]), ["note"]);
|
||||
});
|
||||
|
||||
test("**Warning:** → blockquote.warning", () => {
|
||||
const tree = root(calloutBQ("Warning:"));
|
||||
run(tree);
|
||||
assert.deepEqual(classOf(tree.children[0]), ["warning"]);
|
||||
});
|
||||
|
||||
test("**Caution:** → blockquote.warning (alias)", () => {
|
||||
const tree = root(calloutBQ("Caution:"));
|
||||
run(tree);
|
||||
assert.deepEqual(classOf(tree.children[0]), ["warning"]);
|
||||
});
|
||||
|
||||
test("**Hint:** → blockquote.tip (alias)", () => {
|
||||
const tree = root(calloutBQ("Hint:"));
|
||||
run(tree);
|
||||
assert.deepEqual(classOf(tree.children[0]), ["tip"]);
|
||||
});
|
||||
|
||||
test("**Danger:** → blockquote.danger", () => {
|
||||
const tree = root(calloutBQ("Danger:"));
|
||||
run(tree);
|
||||
assert.deepEqual(classOf(tree.children[0]), ["danger"]);
|
||||
});
|
||||
|
||||
test("**Plans:** → blockquote.plans-callout", () => {
|
||||
const tree = root(calloutBQ("Plans:"));
|
||||
run(tree);
|
||||
assert.deepEqual(classOf(tree.children[0]), ["plans-callout"]);
|
||||
});
|
||||
|
||||
// Colon optional, case-insensitive --------------------------------------------
|
||||
|
||||
test("colon is optional — **Tip** alone still matches", () => {
|
||||
const tree = root(calloutBQ("Tip"));
|
||||
run(tree);
|
||||
assert.deepEqual(classOf(tree.children[0]), ["tip"]);
|
||||
});
|
||||
|
||||
test("marker matching is case-insensitive", () => {
|
||||
const tree = root(calloutBQ("TIP:"));
|
||||
run(tree);
|
||||
assert.deepEqual(classOf(tree.children[0]), ["tip"]);
|
||||
});
|
||||
|
||||
// Author intent preserved -----------------------------------------------------
|
||||
|
||||
test("blockquote already carrying a *-callout class is left untouched", () => {
|
||||
// Authors who hand-write `<blockquote class="plans-callout">` keep control.
|
||||
const bq = el(
|
||||
"blockquote",
|
||||
{ className: ["plans-callout"] },
|
||||
el("p", {}, strong("Tip:"), text(" overridden by author")),
|
||||
);
|
||||
const tree = root(bq);
|
||||
run(tree);
|
||||
assert.deepEqual(classOf(tree.children[0]), ["plans-callout"]);
|
||||
});
|
||||
|
||||
// Non-matching content --------------------------------------------------------
|
||||
|
||||
test("unrecognized marker → no class added", () => {
|
||||
const tree = root(calloutBQ("Random:"));
|
||||
run(tree);
|
||||
assert.equal(classOf(tree.children[0]), undefined);
|
||||
});
|
||||
|
||||
test("blockquote with no leading <strong> → no class", () => {
|
||||
const tree = root(el("blockquote", {}, el("p", {}, text("just prose, no marker"))));
|
||||
run(tree);
|
||||
assert.equal(classOf(tree.children[0]), undefined);
|
||||
});
|
||||
|
||||
test("blockquote with no <p> → no class", () => {
|
||||
const tree = root(el("blockquote", {}, text("text directly under bq")));
|
||||
run(tree);
|
||||
assert.equal(classOf(tree.children[0]), undefined);
|
||||
});
|
||||
|
||||
test("non-blockquote elements are ignored", () => {
|
||||
const tree = root(el("div", {}, el("p", {}, strong("Tip:"), text(" outside a blockquote"))));
|
||||
run(tree);
|
||||
assert.equal(classOf(tree.children[0]), undefined);
|
||||
});
|
||||
43
docs-build/src/plugins/rehype-internal-links.mjs
Normal file
@@ -0,0 +1,43 @@
|
||||
// Rewrites in-content anchor hrefs so they work after Astro's build:
|
||||
//
|
||||
// - strips ".md" / ".md#anchor" suffixes — Astro routes have no .md
|
||||
// - strips "/index" trailing segment
|
||||
// - prefixes the configured base path when the link references a docs path
|
||||
// - leaves external (http/https/mailto), in-page (#…), and /learn/ links alone
|
||||
//
|
||||
// `base` must match `astro.config.mjs`'s `base` (e.g. "/docs/latest").
|
||||
|
||||
import { visit } from "unist-util-visit";
|
||||
|
||||
const EXTERNAL = /^(https?:|mailto:|tel:|#|\/\/)/;
|
||||
|
||||
export function rehypeInternalLinks({ base = "/" } = {}) {
|
||||
const baseClean = base.replace(/\/$/, "");
|
||||
|
||||
return (tree) => {
|
||||
visit(tree, "element", (node) => {
|
||||
if (node.tagName !== "a") return;
|
||||
const href = node.properties?.href;
|
||||
if (typeof href !== "string" || href === "") return;
|
||||
if (EXTERNAL.test(href)) return;
|
||||
|
||||
// /learn/... and other cross-site absolute paths (not under /docs/)
|
||||
// pass through unchanged.
|
||||
if (href.startsWith("/learn/")) return;
|
||||
|
||||
// Strip .md (possibly with an anchor).
|
||||
let next = href.replace(/\.md(#.*)?$/, (_, hash) => hash ?? "");
|
||||
// Strip /index endings (e.g., "foo/index.md" → "foo/").
|
||||
next = next.replace(/\/index(#.*)?$/, "/$1");
|
||||
|
||||
// If the link is an absolute /docs/... path with the old hardcoded
|
||||
// /docs/latest prefix, swap that prefix for the build's base. Other
|
||||
// absolute paths and relative paths pass through with just the .md
|
||||
// strip — Astro resolves the latter against the current page URL.
|
||||
if (next.startsWith("/docs/")) {
|
||||
next = next.replace(/^\/docs\/[^/]+/, baseClean);
|
||||
}
|
||||
node.properties.href = next;
|
||||
});
|
||||
};
|
||||
}
|
||||
104
docs-build/src/plugins/rehype-internal-links.test.mjs
Normal file
@@ -0,0 +1,104 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { rehypeInternalLinks } from "./rehype-internal-links.mjs";
|
||||
|
||||
// Minimal hast builders — keep them inline so the test file is self-contained
|
||||
// (the production plugin operates on whatever Astro hands it; we mimic the
|
||||
// shape it cares about).
|
||||
function el(tagName, properties, ...children) {
|
||||
return { type: "element", tagName, properties: properties || {}, children };
|
||||
}
|
||||
function root(...children) {
|
||||
return { type: "root", children };
|
||||
}
|
||||
function link(href, text = "x") {
|
||||
return el("a", { href }, { type: "text", value: text });
|
||||
}
|
||||
|
||||
function run(tree, base = "/docs/latest") {
|
||||
rehypeInternalLinks({ base })(tree);
|
||||
return tree;
|
||||
}
|
||||
|
||||
// .md stripping ---------------------------------------------------------------
|
||||
|
||||
test("strips .md suffix from /docs/<version>/... paths", () => {
|
||||
const tree = root(link("/docs/latest/foo/bar.md"));
|
||||
run(tree);
|
||||
assert.equal(tree.children[0].properties.href, "/docs/latest/foo/bar");
|
||||
});
|
||||
|
||||
test("preserves anchor when stripping .md", () => {
|
||||
const tree = root(link("/docs/latest/foo/bar.md#section"));
|
||||
run(tree);
|
||||
assert.equal(tree.children[0].properties.href, "/docs/latest/foo/bar#section");
|
||||
});
|
||||
|
||||
test("drops /index trailing segment", () => {
|
||||
const tree = root(link("/docs/latest/foo/index.md"));
|
||||
run(tree);
|
||||
assert.equal(tree.children[0].properties.href, "/docs/latest/foo/");
|
||||
});
|
||||
|
||||
test("relative .md path: strips .md, leaves the rest unchanged", () => {
|
||||
// Astro resolves the resulting relative URL against the current page.
|
||||
const tree = root(link("./foo.md"));
|
||||
run(tree);
|
||||
assert.equal(tree.children[0].properties.href, "./foo");
|
||||
});
|
||||
|
||||
// Base-path rewriting ---------------------------------------------------------
|
||||
|
||||
test("swaps the /docs/<version> prefix for the configured base", () => {
|
||||
const tree = root(link("/docs/latest/foo/bar.md"));
|
||||
run(tree, "/docs/v0.58");
|
||||
assert.equal(tree.children[0].properties.href, "/docs/v0.58/foo/bar");
|
||||
});
|
||||
|
||||
test("base path trailing slash is normalized", () => {
|
||||
const tree = root(link("/docs/latest/foo.md"));
|
||||
run(tree, "/docs/v0.58/");
|
||||
assert.equal(tree.children[0].properties.href, "/docs/v0.58/foo");
|
||||
});
|
||||
|
||||
// Pass-through cases ----------------------------------------------------------
|
||||
|
||||
test("leaves http/https/mailto/tel/protocol-relative URLs alone", () => {
|
||||
for (const href of [
|
||||
"https://example.com/path",
|
||||
"http://x.test",
|
||||
"mailto:a@b.test",
|
||||
"tel:+15551234",
|
||||
"//cdn.example.com/x",
|
||||
]) {
|
||||
const tree = root(link(href));
|
||||
run(tree);
|
||||
assert.equal(tree.children[0].properties.href, href, `expected ${href} unchanged`);
|
||||
}
|
||||
});
|
||||
|
||||
test("leaves fragment-only links alone", () => {
|
||||
const tree = root(link("#in-page"));
|
||||
run(tree);
|
||||
assert.equal(tree.children[0].properties.href, "#in-page");
|
||||
});
|
||||
|
||||
test("leaves /learn/ cross-site links alone", () => {
|
||||
const tree = root(link("/learn/sql-basics"));
|
||||
run(tree);
|
||||
assert.equal(tree.children[0].properties.href, "/learn/sql-basics");
|
||||
});
|
||||
|
||||
test("leaves empty href untouched", () => {
|
||||
const tree = root(link(""));
|
||||
run(tree);
|
||||
assert.equal(tree.children[0].properties.href, "");
|
||||
});
|
||||
|
||||
// Non-anchor elements ---------------------------------------------------------
|
||||
|
||||
test("non-anchor elements are not rewritten", () => {
|
||||
const tree = root(el("img", { src: "/docs/latest/foo/bar.md" }));
|
||||
run(tree);
|
||||
assert.equal(tree.children[0].properties.src, "/docs/latest/foo/bar.md");
|
||||
});
|
||||
34
docs-build/src/plugins/remark-docs-version.mjs
Normal file
@@ -0,0 +1,34 @@
|
||||
// Replaces the `{SAMPLE_APP_BRANCH}` token in markdown with the branch of the
|
||||
// metabase-nodejs-react-sdk-embedding-sample repo that matches this docs build:
|
||||
// "<NN>-stable" for a released version, "master" for an unreleased / latest
|
||||
// build without release info.
|
||||
//
|
||||
// The version is derived from DOCS_BASE_PATH (set by `./bin/mage docs-build`:
|
||||
// /docs/latest, /docs/v0.NN, or /docs/master), falling back to GITHUB_REF_NAME
|
||||
// for "latest" builds running off a release branch.
|
||||
|
||||
import { visit } from "unist-util-visit";
|
||||
|
||||
const TOKEN = /\{SAMPLE_APP_BRANCH\}/g;
|
||||
|
||||
export function resolveSampleAppBranch(env = process.env) {
|
||||
const base = env.DOCS_BASE_PATH ?? "/docs/latest";
|
||||
const m = base.match(/^\/docs\/(.+)$/);
|
||||
const version = m ? m[1] : "latest";
|
||||
if (version === "master") return "master";
|
||||
let vm = version.match(/^v0\.(\d+)$/);
|
||||
if (vm) return `${vm[1]}-stable`;
|
||||
// "latest" — use the release branch CI is building from, if any.
|
||||
vm = (env.GITHUB_REF_NAME ?? "").match(/^release-x\.(\d+)\.x$/);
|
||||
return vm ? `${vm[1]}-stable` : "master";
|
||||
}
|
||||
|
||||
export function remarkDocsVersion() {
|
||||
const branch = resolveSampleAppBranch();
|
||||
return (tree) => {
|
||||
visit(tree, (node) => {
|
||||
if (typeof node.value === "string") node.value = node.value.replace(TOKEN, branch);
|
||||
if (typeof node.url === "string") node.url = node.url.replace(TOKEN, branch);
|
||||
});
|
||||
};
|
||||
}
|
||||
43
docs-build/src/plugins/remark-docs-version.test.mjs
Normal file
@@ -0,0 +1,43 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { fromMarkdown } from "mdast-util-from-markdown";
|
||||
import { remarkDocsVersion, resolveSampleAppBranch } from "./remark-docs-version.mjs";
|
||||
|
||||
test("resolveSampleAppBranch: /docs/v0.NN → NN-stable", () => {
|
||||
assert.equal(resolveSampleAppBranch({ DOCS_BASE_PATH: "/docs/v0.58" }), "58-stable");
|
||||
});
|
||||
|
||||
test("resolveSampleAppBranch: /docs/master → master", () => {
|
||||
assert.equal(resolveSampleAppBranch({ DOCS_BASE_PATH: "/docs/master" }), "master");
|
||||
});
|
||||
|
||||
test("resolveSampleAppBranch: /docs/latest + release ref → NN-stable", () => {
|
||||
assert.equal(
|
||||
resolveSampleAppBranch({ DOCS_BASE_PATH: "/docs/latest", GITHUB_REF_NAME: "release-x.60.x" }),
|
||||
"60-stable",
|
||||
);
|
||||
});
|
||||
|
||||
test("resolveSampleAppBranch: /docs/latest with no usable ref → master", () => {
|
||||
assert.equal(resolveSampleAppBranch({ DOCS_BASE_PATH: "/docs/latest" }), "master");
|
||||
assert.equal(resolveSampleAppBranch({}), "master");
|
||||
assert.equal(resolveSampleAppBranch({ DOCS_BASE_PATH: "/docs/latest", GITHUB_REF_NAME: "feature/x" }), "master");
|
||||
});
|
||||
|
||||
test("remarkDocsVersion replaces {SAMPLE_APP_BRANCH} in text, links, and code", () => {
|
||||
const prev = process.env.DOCS_BASE_PATH;
|
||||
process.env.DOCS_BASE_PATH = "/docs/v0.59";
|
||||
try {
|
||||
const tree = fromMarkdown(
|
||||
"See [sample](https://x/tree/{SAMPLE_APP_BRANCH}).\n\n```bash\ngit checkout {SAMPLE_APP_BRANCH}\n```",
|
||||
);
|
||||
remarkDocsVersion()(tree);
|
||||
const link = tree.children[0].children.find((n) => n.type === "link");
|
||||
assert.equal(link.url, "https://x/tree/59-stable");
|
||||
const code = tree.children.find((n) => n.type === "code");
|
||||
assert.equal(code.value, "git checkout 59-stable");
|
||||
} finally {
|
||||
if (prev === undefined) delete process.env.DOCS_BASE_PATH;
|
||||
else process.env.DOCS_BASE_PATH = prev;
|
||||
}
|
||||
});
|
||||
253
docs-build/src/plugins/remark-include-file.mjs
Normal file
@@ -0,0 +1,253 @@
|
||||
// Expands `{% include_file "path" [snippet="NAME"] %}` transclusion tags.
|
||||
//
|
||||
// The SDK docs pull example code straight out of committed, type-checked
|
||||
// source files (docs/embedding/sdk/snippets/**.tsx) and prop tables out of
|
||||
// generated typedoc output (docs/embedding/sdk/api/snippets/*.md,
|
||||
// docs/embedding/eajs/snippets/*.md), so the docs can't drift from the
|
||||
// real types/examples. `include_file` is the only build-time tag the
|
||||
// markdown layer understands.
|
||||
//
|
||||
// Two shapes are handled (an audit of docs/ shows every `include_file` is one
|
||||
// of these — nothing inline in prose / lists / tables):
|
||||
//
|
||||
// 1. A paragraph whose entire text is one tag:
|
||||
// {% include_file "{{ dirname }}/snippets/config/config-base.tsx" %}
|
||||
// → for a .md target: the file content (or named snippet region) is parsed
|
||||
// as markdown and spliced into the tree, so headings/tables/links flow
|
||||
// through the rest of the pipeline.
|
||||
// → for a source-code target: a single fenced `code` node (Shiki then
|
||||
// highlights it), language inferred from the extension, dedented.
|
||||
//
|
||||
// 2. A fenced code block containing one or more tags. Each tag is replaced
|
||||
// with the snippet's raw text; the author's chosen fence language is kept.
|
||||
//
|
||||
// `{{ dirname }}` in a path resolves to the directory of the consuming
|
||||
// markdown file.
|
||||
//
|
||||
// Snippet markers in source files:
|
||||
// .md : <!-- [<snippet NAME>] --> … <!-- [<endsnippet NAME>] -->
|
||||
// .ts/.tsx/... : // [<snippet NAME>] … // [<endsnippet NAME>] (leading
|
||||
// whitespace allowed; output is dedented)
|
||||
//
|
||||
// On any failure (missing file, missing snippet) we emit a placeholder
|
||||
// blockquote and a build warning via vfile rather than failing the build — a
|
||||
// fresh `bun run docs:dev` checkout has the typedoc output ungenerated, and one
|
||||
// bad include shouldn't take the whole site down.
|
||||
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fromMarkdown } from "mdast-util-from-markdown";
|
||||
import { gfmFromMarkdown } from "mdast-util-gfm";
|
||||
import { gfm } from "micromark-extension-gfm";
|
||||
|
||||
const INCLUDE_FILE_RE = /\{%\s*include_file\s+([^%]+?)\s*%\}/g;
|
||||
// A paragraph is "purely one include_file" if its trimmed text is exactly one tag.
|
||||
const SOLO_INCLUDE_FILE_RE = /^\{%\s*include_file\s+(.+?)\s*%\}$/;
|
||||
|
||||
function parseArgs(s) {
|
||||
const args = {};
|
||||
// Accept straight and curly quote characters (some docs use curly quotes).
|
||||
const re = /([\w-]+)\s*=\s*["“”‘’]([^"“”‘’]*)["“”‘’]/g;
|
||||
for (const m of s.matchAll(re)) {
|
||||
args[m[1]] = m[2];
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
function escapeHtml(s) {
|
||||
return String(s)
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """);
|
||||
}
|
||||
|
||||
function escapeRegex(s) {
|
||||
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
// Last-resort fallback when expansion fails (missing file or missing snippet).
|
||||
function placeholderNode(rawArgs) {
|
||||
const args = parseArgs(rawArgs);
|
||||
const pathMatch = rawArgs.match(/["“”]([^"“”]+)["“”]/);
|
||||
const refPath = pathMatch ? pathMatch[1] : "external snippet";
|
||||
const snippet = args.snippet ? ` (${args.snippet})` : "";
|
||||
return {
|
||||
type: "html",
|
||||
value:
|
||||
'<blockquote class="note">' +
|
||||
`<p>See the <code>${escapeHtml(refPath)}</code>${escapeHtml(snippet)} reference snippet.</p>` +
|
||||
"</blockquote>",
|
||||
};
|
||||
}
|
||||
|
||||
// Source path of the current markdown file. remark exposes it via vfile.history[0].
|
||||
function dirnameOf(file) {
|
||||
const src = file && Array.isArray(file.history) && file.history[0];
|
||||
return src ? path.dirname(src) : process.cwd();
|
||||
}
|
||||
|
||||
function resolveIncludePath(rawPath, dirname) {
|
||||
const substituted = rawPath.replace(/\{\{\s*dirname\s*\}\}/g, dirname);
|
||||
return path.isAbsolute(substituted)
|
||||
? substituted
|
||||
: path.resolve(dirname, substituted);
|
||||
}
|
||||
|
||||
function extractSnippet(content, snippetName, ext) {
|
||||
if (!snippetName) return content;
|
||||
const nameEsc = escapeRegex(snippetName);
|
||||
let startRe;
|
||||
let endRe;
|
||||
if (ext === ".md") {
|
||||
startRe = new RegExp(`<!--\\s*\\[<snippet\\s+${nameEsc}>\\]\\s*-->`);
|
||||
endRe = new RegExp(`<!--\\s*\\[<endsnippet\\s+${nameEsc}>\\]\\s*-->`);
|
||||
} else {
|
||||
startRe = new RegExp(`^[ \\t]*//\\s*\\[<snippet\\s+${nameEsc}>\\][ \\t]*$`, "m");
|
||||
endRe = new RegExp(`^[ \\t]*//\\s*\\[<endsnippet\\s+${nameEsc}>\\][ \\t]*$`, "m");
|
||||
}
|
||||
const startMatch = startRe.exec(content);
|
||||
if (!startMatch) return null;
|
||||
const afterStart = content.slice(startMatch.index + startMatch[0].length);
|
||||
const endMatch = endRe.exec(afterStart);
|
||||
if (!endMatch) return null;
|
||||
return afterStart.slice(0, endMatch.index);
|
||||
}
|
||||
|
||||
function dedent(text) {
|
||||
const lines = text.split(/\r?\n/);
|
||||
while (lines.length && lines[0].trim() === "") lines.shift();
|
||||
while (lines.length && lines[lines.length - 1].trim() === "") lines.pop();
|
||||
let minIndent = Infinity;
|
||||
for (const line of lines) {
|
||||
if (line.trim() === "") continue;
|
||||
const m = line.match(/^[ \t]*/);
|
||||
const indent = m ? m[0].length : 0;
|
||||
if (indent < minIndent) minIndent = indent;
|
||||
}
|
||||
if (minIndent === Infinity || minIndent === 0) return lines.join("\n");
|
||||
return lines.map((l) => l.slice(Math.min(minIndent, l.length))).join("\n");
|
||||
}
|
||||
|
||||
function extToLang(ext) {
|
||||
switch (ext) {
|
||||
case ".ts": return "ts";
|
||||
case ".tsx": return "tsx";
|
||||
case ".js":
|
||||
case ".mjs":
|
||||
case ".cjs": return "js";
|
||||
case ".jsx": return "jsx";
|
||||
case ".json": return "json";
|
||||
case ".html": return "html";
|
||||
case ".css": return "css";
|
||||
case ".scss": return "scss";
|
||||
case ".sh":
|
||||
case ".bash": return "bash";
|
||||
case ".rb": return "ruby";
|
||||
case ".py": return "python";
|
||||
case ".yml":
|
||||
case ".yaml": return "yaml";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
function parseMarkdownChildren(value) {
|
||||
const tree = fromMarkdown(value, {
|
||||
extensions: [gfm()],
|
||||
mdastExtensions: [gfmFromMarkdown()],
|
||||
});
|
||||
return tree.children;
|
||||
}
|
||||
|
||||
// Returns mdast nodes to splice in, or null on failure (caller emits a warning
|
||||
// and falls back to the placeholder blockquote).
|
||||
function expandIncludeFile(rawArgs, file) {
|
||||
const dirname = dirnameOf(file);
|
||||
const pathMatch = rawArgs.match(/["“”]([^"“”]+)["“”]/);
|
||||
if (!pathMatch) return null;
|
||||
const includePath = resolveIncludePath(pathMatch[1], dirname);
|
||||
|
||||
const args = parseArgs(rawArgs);
|
||||
const snippetName = args.snippet ?? null;
|
||||
|
||||
let raw;
|
||||
try {
|
||||
raw = fs.readFileSync(includePath, "utf8");
|
||||
} catch (err) {
|
||||
file?.message?.(`include_file: cannot read ${includePath}: ${err.message}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const ext = path.extname(includePath).toLowerCase();
|
||||
const region = extractSnippet(raw, snippetName, ext);
|
||||
if (region == null) {
|
||||
file?.message?.(`include_file: snippet "${snippetName}" not found in ${includePath}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ext === ".md") {
|
||||
return parseMarkdownChildren(region.trim());
|
||||
}
|
||||
const lang = extToLang(ext);
|
||||
return [{ type: "code", lang: lang || null, value: dedent(region) }];
|
||||
}
|
||||
|
||||
// Walk top-down so includes in spliced subtrees are also processed. We build a
|
||||
// new children array rather than mutating during traversal because splicing one
|
||||
// paragraph may produce many nodes.
|
||||
function transformTree(node, file) {
|
||||
if (!node || !Array.isArray(node.children)) return;
|
||||
const newChildren = [];
|
||||
for (const child of node.children) {
|
||||
if (child.type === "paragraph") {
|
||||
const trimmed = flattenText(child).trim();
|
||||
const soloMatch = trimmed.match(SOLO_INCLUDE_FILE_RE);
|
||||
if (soloMatch) {
|
||||
const expanded = expandIncludeFile(soloMatch[1], file);
|
||||
if (expanded && expanded.length > 0) {
|
||||
for (const n of expanded) {
|
||||
transformTree(n, file);
|
||||
newChildren.push(n);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
newChildren.push(placeholderNode(soloMatch[1]));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Fenced code blocks may wrap one or more include_file tags. Replace each
|
||||
// with its snippet's raw text, keeping the author's chosen fence language.
|
||||
if (child.type === "code" && typeof child.value === "string" && INCLUDE_FILE_RE.test(child.value)) {
|
||||
INCLUDE_FILE_RE.lastIndex = 0;
|
||||
child.value = child.value.replace(INCLUDE_FILE_RE, (_match, rawArgs) => {
|
||||
const expanded = expandIncludeFile(rawArgs, file);
|
||||
if (expanded && expanded.length === 1 && expanded[0].type === "code") {
|
||||
return expanded[0].value;
|
||||
}
|
||||
if (expanded && expanded.length > 0) {
|
||||
// Markdown inside a code fence — emit the source text. Very rare.
|
||||
return expanded.map((n) => (typeof n.value === "string" ? n.value : "")).join("\n");
|
||||
}
|
||||
file?.message?.(`include_file inside code fence could not expand: ${rawArgs}`);
|
||||
return _match; // leave the tag visible so the warning is actionable
|
||||
});
|
||||
}
|
||||
|
||||
transformTree(child, file);
|
||||
newChildren.push(child);
|
||||
}
|
||||
node.children = newChildren;
|
||||
}
|
||||
|
||||
function flattenText(paragraph) {
|
||||
return (paragraph.children ?? [])
|
||||
.map((c) => (typeof c.value === "string" ? c.value : ""))
|
||||
.join("");
|
||||
}
|
||||
|
||||
export function remarkIncludeFile() {
|
||||
return (tree, file) => {
|
||||
transformTree(tree, file);
|
||||
};
|
||||
}
|
||||
108
docs-build/src/plugins/remark-include-file.test.mjs
Normal file
@@ -0,0 +1,108 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { fromMarkdown } from "mdast-util-from-markdown";
|
||||
import { gfmFromMarkdown } from "mdast-util-gfm";
|
||||
import { gfm } from "micromark-extension-gfm";
|
||||
import { remarkIncludeFile } from "./remark-include-file.mjs";
|
||||
|
||||
function parse(md) {
|
||||
return fromMarkdown(md, {
|
||||
extensions: [gfm()],
|
||||
mdastExtensions: [gfmFromMarkdown()],
|
||||
});
|
||||
}
|
||||
|
||||
// `consumerPath` need not exist on disk — the plugin only uses its dirname to
|
||||
// resolve `{{ dirname }}`.
|
||||
function run(md, consumerPath) {
|
||||
const tree = parse(md);
|
||||
const messages = [];
|
||||
const file = { history: [consumerPath], message: (m) => messages.push(String(m)) };
|
||||
remarkIncludeFile()(tree, file);
|
||||
return { tree, messages };
|
||||
}
|
||||
|
||||
function makeFixtures(files) {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "incfile-"));
|
||||
for (const [name, content] of Object.entries(files)) {
|
||||
const p = path.join(dir, name);
|
||||
fs.mkdirSync(path.dirname(p), { recursive: true });
|
||||
fs.writeFileSync(p, content);
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
test("splices a .md snippet as mdast so headings/tables flow through", () => {
|
||||
const dir = makeFixtures({
|
||||
"snip.md": [
|
||||
"before",
|
||||
"<!-- [<snippet props>] -->",
|
||||
"### Properties",
|
||||
"",
|
||||
"| name | type |",
|
||||
"| ---- | ------ |",
|
||||
"| id | number |",
|
||||
"<!-- [<endsnippet props>] -->",
|
||||
"after",
|
||||
].join("\n"),
|
||||
});
|
||||
const md = `## Props\n\n{% include_file "{{ dirname }}/snip.md" snippet="props" %}\n\n## Next`;
|
||||
const { tree, messages } = run(md, path.join(dir, "consumer.md"));
|
||||
assert.equal(messages.length, 0);
|
||||
assert.deepEqual(
|
||||
tree.children.map((n) => n.type),
|
||||
["heading", "heading", "table", "heading"],
|
||||
);
|
||||
assert.equal(tree.children[1].depth, 3);
|
||||
});
|
||||
|
||||
test("source-code snippet becomes a fenced code node, dedented, lang from ext", () => {
|
||||
const dir = makeFixtures({
|
||||
"ex.tsx": [
|
||||
'import x from "y";',
|
||||
"// [<snippet example>]",
|
||||
" const a = 1;",
|
||||
" const b = 2;",
|
||||
"// [<endsnippet example>]",
|
||||
"export default a;",
|
||||
].join("\n"),
|
||||
});
|
||||
const md = `text\n\n{% include_file "{{ dirname }}/ex.tsx" snippet="example" %}\n\nmore`;
|
||||
const { tree, messages } = run(md, path.join(dir, "consumer.md"));
|
||||
assert.equal(messages.length, 0);
|
||||
const code = tree.children.find((n) => n.type === "code");
|
||||
assert.ok(code, "expected a code node");
|
||||
assert.equal(code.lang, "tsx");
|
||||
assert.equal(code.value, "const a = 1;\nconst b = 2;");
|
||||
});
|
||||
|
||||
test("whole-file include (no snippet) inside a code fence keeps the fence language", () => {
|
||||
const dir = makeFixtures({ "f.ts": "export const z = 42;\n" });
|
||||
const md = "```ts\n{% include_file \"{{ dirname }}/f.ts\" %}\n```";
|
||||
const { tree, messages } = run(md, path.join(dir, "consumer.md"));
|
||||
assert.equal(messages.length, 0);
|
||||
const code = tree.children.find((n) => n.type === "code");
|
||||
assert.equal(code.lang, "ts");
|
||||
assert.match(code.value, /export const z = 42;/);
|
||||
});
|
||||
|
||||
test("missing file → placeholder blockquote + warning, build not failed", () => {
|
||||
const dir = makeFixtures({});
|
||||
const md = `x\n\n{% include_file "{{ dirname }}/nope.tsx" %}\n\ny`;
|
||||
const { tree, messages } = run(md, path.join(dir, "consumer.md"));
|
||||
assert.equal(messages.length, 1);
|
||||
assert.match(messages[0], /cannot read/);
|
||||
const html = tree.children.find((n) => n.type === "html");
|
||||
assert.ok(html && /reference snippet/.test(html.value));
|
||||
});
|
||||
|
||||
test("missing snippet name → placeholder + warning", () => {
|
||||
const dir = makeFixtures({ "s.md": "no markers here" });
|
||||
const md = `x\n\n{% include_file "{{ dirname }}/s.md" snippet="ghost" %}\n\ny`;
|
||||
const { messages } = run(md, path.join(dir, "consumer.md"));
|
||||
assert.equal(messages.length, 1);
|
||||
assert.match(messages[0], /snippet "ghost" not found/);
|
||||
});
|
||||
132
docs-build/src/redirects.mjs
Normal file
@@ -0,0 +1,132 @@
|
||||
// Loads the `redirect_from:` frontmatter array from every doc and builds
|
||||
// the `{fromPath: toPath}` map that Astro's `redirects` config consumes.
|
||||
//
|
||||
// Convention: docs use Jekyll-style absolute paths like
|
||||
// `/docs/latest/foo/bar`. We strip the leading `/docs/<version>/` segment
|
||||
// from the source URL so the redirect KEY is base-relative — Astro emits
|
||||
// the stub file under the build's `base` automatically. The destination
|
||||
// URL, however, is NOT base-prepended by Astro, so we splice the build's
|
||||
// `base` into the front of every target slug here. That way the same
|
||||
// `redirect_from` entries work for /docs/latest, /docs/v0.NN, and
|
||||
// /docs/master builds without per-version edits.
|
||||
//
|
||||
// Out-of-scope: entries whose path is NOT under `/docs/` (e.g. `/cloud/foo`,
|
||||
// `/migrate/bar`, `/pricing/baz`) are marketing-site URLs. The docs build
|
||||
// cannot emit redirects for routes outside its `base`, so we write them
|
||||
// to a sidecar `public/marketing-redirects.json` for the delivery hop to
|
||||
// pick up. Astro copies `public/` into `dist/`, so the file lands at
|
||||
// `<base>/marketing-redirects.json` next to `llms.txt`.
|
||||
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import yaml from "js-yaml";
|
||||
|
||||
const FRONTMATTER_RE = /^---\s*\n([\s\S]*?)\n---/;
|
||||
const DOCS_PREFIX_RE = /^\/docs\/[^/]+\//;
|
||||
|
||||
function walkMarkdown(dir, rel = "") {
|
||||
const out = [];
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const abs = path.join(dir, entry.name);
|
||||
const r = rel ? `${rel}/${entry.name}` : entry.name;
|
||||
if (entry.isDirectory()) {
|
||||
out.push(...walkMarkdown(abs, r));
|
||||
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
||||
out.push({ abs, rel: r });
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Mirror the routing logic in src/pages/[...slug].astro: drop the .md
|
||||
// extension, normalize a trailing /index, lowercase to match Astro's
|
||||
// glob loader (which lowercases entry ids), and route the repo's README
|
||||
// at the base root.
|
||||
function slugFromRel(rel) {
|
||||
let s = rel.replace(/\.md$/i, "").toLowerCase().replace(/\/index$/, "");
|
||||
if (s === "readme") s = "";
|
||||
return s ? `/${s}` : "/";
|
||||
}
|
||||
|
||||
export function loadRedirects({ docsDir, sidecarPath, base = "" }) {
|
||||
const redirects = {};
|
||||
const marketing = [];
|
||||
const collisions = [];
|
||||
// Normalize base: ensure a single leading "/", strip any trailing "/".
|
||||
const basePrefix = base.replace(/\/+$/, "").replace(/^\/?/, "/") === "/"
|
||||
? ""
|
||||
: base.replace(/\/+$/, "").replace(/^\/?/, "/");
|
||||
const withBase = (slug) => {
|
||||
if (!basePrefix) return slug;
|
||||
return slug === "/" ? basePrefix : `${basePrefix}${slug}`;
|
||||
};
|
||||
|
||||
for (const { abs, rel } of walkMarkdown(docsDir)) {
|
||||
let txt;
|
||||
try {
|
||||
txt = fs.readFileSync(abs, "utf8");
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
const m = FRONTMATTER_RE.exec(txt);
|
||||
if (!m) continue;
|
||||
let data;
|
||||
try {
|
||||
data = yaml.load(m[1]) ?? {};
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
const list = data.redirect_from;
|
||||
if (!Array.isArray(list)) continue;
|
||||
|
||||
const targetSlug = slugFromRel(rel);
|
||||
const target = withBase(targetSlug);
|
||||
for (const raw of list) {
|
||||
if (typeof raw !== "string") continue;
|
||||
const from = raw.trim();
|
||||
if (!from.startsWith("/")) continue;
|
||||
|
||||
if (DOCS_PREFIX_RE.test(from)) {
|
||||
const key = from.replace(DOCS_PREFIX_RE, "/");
|
||||
// Redirect-to-self is a no-op; check against the unprefixed slug
|
||||
// since the key is also base-relative.
|
||||
if (key === targetSlug) continue;
|
||||
if (redirects[key] && redirects[key] !== target) {
|
||||
collisions.push({
|
||||
key,
|
||||
existing: redirects[key],
|
||||
replacing: target,
|
||||
source: rel,
|
||||
});
|
||||
}
|
||||
redirects[key] = target;
|
||||
} else {
|
||||
// Marketing sidecar records canonical (base-prefixed) targets so
|
||||
// the delivery hop can emit absolute redirects without re-deriving
|
||||
// the build version.
|
||||
marketing.push({ from, to: target, source: rel });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sidecarPath) {
|
||||
fs.mkdirSync(path.dirname(sidecarPath), { recursive: true });
|
||||
if (marketing.length > 0) {
|
||||
fs.writeFileSync(sidecarPath, JSON.stringify(marketing, null, 2) + "\n");
|
||||
} else if (fs.existsSync(sidecarPath)) {
|
||||
fs.unlinkSync(sidecarPath);
|
||||
}
|
||||
}
|
||||
|
||||
for (const c of collisions) {
|
||||
console.warn(
|
||||
`[redirects] collision: ${c.key} -> ${c.replacing} (was ${c.existing}); source: ${c.source}`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
redirects,
|
||||
marketingCount: marketing.length,
|
||||
inDocsCount: Object.keys(redirects).length,
|
||||
};
|
||||
}
|
||||
3039
docs-build/src/styles/chrome.css
Normal file
831
docs-build/src/styles/docs.css
Normal file
@@ -0,0 +1,831 @@
|
||||
/* Metabase docs styles.
|
||||
*
|
||||
* Plain CSS — no preprocessor. The colour palette below uses the standard
|
||||
* Metabase design tokens.
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* Blues */
|
||||
--blue-25: #12436e;
|
||||
--blue-40: #1c6bb0;
|
||||
--blue-60: #509ee3;
|
||||
--blue-85: #c2daf0;
|
||||
--blue-90: #e4ecfb;
|
||||
--blue-95: #eef6fc;
|
||||
--blue-99: #f9fbfe;
|
||||
|
||||
/* Neutrals */
|
||||
--neutral-15: #22242b;
|
||||
--neutral-20: #4c5773;
|
||||
--neutral-40: #5a6072;
|
||||
--neutral-60: #8d93a5;
|
||||
--neutral-80: #c6c9d2;
|
||||
--neutral-90: #e2e4e9;
|
||||
--neutral-95: #f1f2f4;
|
||||
--neutral-97: #fcfdff;
|
||||
--neutral-98: #fafbfe;
|
||||
--neutral-99: #fcfcfd;
|
||||
--white: #fff;
|
||||
|
||||
/* Accents (callouts) */
|
||||
--purple-50: #5d60a2;
|
||||
--purple-95: #efeff6;
|
||||
--green-40: #669438;
|
||||
--green-95: #f7f9f1;
|
||||
--yellow-50: #f7be08;
|
||||
--yellow-95: #fef9e6;
|
||||
--red-40: #b51717;
|
||||
--red-95: #fce8e8;
|
||||
|
||||
--link: var(--blue-60);
|
||||
--link-hover: var(--blue-40);
|
||||
|
||||
--font-body: Lato, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
--font-mono: "Roboto Mono", "SFMono-Regular", Menlo, Consolas, monospace;
|
||||
|
||||
--shell-max: 1280px;
|
||||
--topbar-height: 57px;
|
||||
--shell-padding-x: 24px;
|
||||
--sidebar-width: 240px;
|
||||
|
||||
/* Chrome tokens. chrome.css carries some magic numbers from the Bootstrap
|
||||
* subset (top: 106px, etc.); docs.css neutralizes them with overrides
|
||||
* further down. These tokens give the overrides a single home. */
|
||||
--mb-z-header: 1001;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Responsive breakpoints used in @media queries throughout this file:
|
||||
* 991px — tablet max (rail hides, sidebar hides at deeper level)
|
||||
* 1199px — desktop narrow max (rail hides, breadcrumb spans wider)
|
||||
* 1200px — desktop mid max ("What's new" pill hides)
|
||||
* Kept as literals because CSS media-query feature values cannot resolve
|
||||
* custom properties (spec: media queries evaluate before style computation).
|
||||
* If you change a breakpoint, search the file for that pixel value.
|
||||
* --------------------------------------------------------------------------*/
|
||||
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
/* Paint the canvas (viewport surface outside body's border box) white so the
|
||||
* body's --neutral-98 tint doesn't propagate there. Without this, the 50px
|
||||
* bottom margin on .footer-logo-container escapes through .body-footer and
|
||||
* .footer-wrap and shows up as a tinted strip below the footer. */
|
||||
html { scroll-padding-top: 5rem; background: var(--white); }
|
||||
|
||||
body {
|
||||
font-family: var(--font-body);
|
||||
color: var(--neutral-40);
|
||||
background: var(--neutral-98);
|
||||
line-height: 1.6;
|
||||
font-size: 16px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: calc(max(0px, (100vw - var(--shell-max)) / 2) + var(--shell-padding-x) + var(--sidebar-width));
|
||||
background: var(--white);
|
||||
/* z-index: auto (default). With html having an explicit background, body's
|
||||
* --neutral-98 no longer propagates to the canvas, so body paints its own
|
||||
* box. We need to land *above* body's bg (else it gets hidden) but below
|
||||
* the sticky sidebar/header (which use higher z-index). Document order does
|
||||
* this for us: this pseudo-element comes before all body children, so the
|
||||
* sidebar/header still paint on top where they overlap. */
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* The fixed body::before sidebar overlay extends from the top to the bottom
|
||||
* of the viewport. When the user scrolls to the footer, the overlay would
|
||||
* otherwise sit *above* the in-flow footer (positioned descendants paint
|
||||
* after in-flow ones), clipping the left columns of the footer's link grid.
|
||||
* Lift the footer into a positive-z-index layer so it always paints above
|
||||
* the overlay where they overlap. */
|
||||
.footer-wrap {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
body::before { display: none; }
|
||||
}
|
||||
|
||||
/* ---------- Page layout ---------- */
|
||||
.docs-shell {
|
||||
display: grid;
|
||||
grid-template-columns: var(--sidebar-width) minmax(0, 1fr) 220px;
|
||||
grid-template-rows: auto 1fr;
|
||||
column-gap: 48px;
|
||||
row-gap: 20px;
|
||||
max-width: var(--shell-max);
|
||||
margin: 0 auto;
|
||||
padding: 0 var(--shell-padding-x) 64px;
|
||||
}
|
||||
.docs-shell > .docs-sidebar { grid-row: 1 / span 2; }
|
||||
.docs-shell > .docs-topbar { grid-column: 2 / 4; grid-row: 1; }
|
||||
.docs-shell > article.docs-content { grid-column: 2; grid-row: 2; }
|
||||
.docs-shell > .docs-rail { grid-column: 3; grid-row: 2; }
|
||||
|
||||
@media (max-width: 1199px) {
|
||||
.docs-shell {
|
||||
grid-template-columns: 226px minmax(0, 1fr);
|
||||
column-gap: 40px;
|
||||
}
|
||||
.docs-shell > .docs-topbar { grid-column: 2 / 3; }
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
.docs-shell { grid-template-columns: minmax(0, 1fr); }
|
||||
.docs-sidebar { display: none; }
|
||||
.docs-shell > .docs-topbar { grid-column: 1 / 2; }
|
||||
}
|
||||
|
||||
/* ---------- Left sidebar ---------- */
|
||||
.docs-sidebar {
|
||||
position: sticky;
|
||||
top: 16px;
|
||||
align-self: start;
|
||||
max-height: calc(100vh - 32px);
|
||||
overflow-y: auto;
|
||||
padding: 8px 12px 32px 0;
|
||||
border-right: 1px solid var(--neutral-95);
|
||||
background: var(--white);
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.docs-sidebar h5 {
|
||||
color: var(--neutral-60);
|
||||
font-size: 11px;
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.2em;
|
||||
margin: 24px 0 4px;
|
||||
padding: 4px 8px 5px;
|
||||
}
|
||||
.docs-sidebar-category:first-child h5 { margin-top: 4px; }
|
||||
|
||||
.docs-sidebar ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.docs-sidebar li { margin: 0; padding: 0 0 2px; }
|
||||
|
||||
.docs-sidebar a,
|
||||
.docs-sidebar .docs-sidebar-group-label {
|
||||
display: block;
|
||||
color: var(--neutral-40);
|
||||
text-decoration: none;
|
||||
padding: 4px 8px 5px;
|
||||
border-radius: 8px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.docs-sidebar a { transition: background-color 0.2s ease, color 0.2s ease; }
|
||||
.docs-sidebar a:hover { background: var(--blue-95); color: var(--blue-60); }
|
||||
.docs-sidebar a.selected { background: var(--blue-95); color: var(--blue-60); font-weight: 700; }
|
||||
|
||||
/* Nested levels get an indent rule, matching the live docs. */
|
||||
.docs-sidebar ul ul { padding-left: 0; }
|
||||
.docs-sidebar ul ul li { border-left: 1px solid var(--blue-90); padding-left: 10px; }
|
||||
.docs-sidebar ul ul ul li { margin-left: 10px; }
|
||||
|
||||
/* Collapsible groups */
|
||||
.docs-sidebar li.has-children > ul { display: none; }
|
||||
.docs-sidebar li.has-children.expanded > ul { display: block; }
|
||||
|
||||
.docs-sidebar .nav-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
.docs-sidebar .nav-row > a,
|
||||
.docs-sidebar .nav-row > .docs-sidebar-group-label {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.docs-sidebar-toggle {
|
||||
flex: 0 0 auto;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
padding: 6px;
|
||||
margin-right: 2px;
|
||||
cursor: pointer;
|
||||
color: var(--neutral-60);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
transition: color 0.2s ease, background-color 0.2s ease;
|
||||
}
|
||||
.docs-sidebar-toggle:hover { color: var(--blue-60); background: var(--blue-95); }
|
||||
.docs-sidebar-toggle svg { transition: transform 0.2s ease; display: block; }
|
||||
.docs-sidebar li.has-children.expanded > .nav-row > .docs-sidebar-toggle svg {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
/* ---------- Top bar (flex row: breadcrumbs left, What's new + search right) ---------- */
|
||||
/* Search/right group is anchored with justify-content: flex-end so it stays
|
||||
* on the right regardless of whether breadcrumbs are present (the docs home
|
||||
* has no breadcrumbs, so without flex-end the single right child would
|
||||
* collapse to flex-start). When breadcrumbs do render, margin-right: auto
|
||||
* on the breadcrumb nav pushes it back to the left edge. */
|
||||
.docs-topbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
height: 57px;
|
||||
padding: 8px 0;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px solid var(--blue-90);
|
||||
}
|
||||
.docs-topbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.docs-whats-new {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
height: 36px;
|
||||
padding: 0 16px 0 13px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 6px;
|
||||
background: var(--blue-95);
|
||||
border: 1px solid var(--blue-95);
|
||||
color: var(--blue-60);
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
.docs-whats-new:hover { background: #d8e9fa; color: var(--blue-40); }
|
||||
.docs-whats-new img { width: 17px; height: 17px; }
|
||||
@media (max-width: 1200px) {
|
||||
.docs-whats-new { display: none; }
|
||||
}
|
||||
.docs-topbar-right #inkeep {
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
.docs-breadcrumbs {
|
||||
font-size: 14px;
|
||||
color: var(--neutral-60);
|
||||
margin-right: auto;
|
||||
}
|
||||
.docs-breadcrumbs ol {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
.docs-breadcrumbs li + li::before { content: "›"; margin-right: 6px; color: var(--neutral-80); }
|
||||
.docs-breadcrumbs a { color: var(--neutral-60); text-decoration: none; }
|
||||
.docs-breadcrumbs a:hover { color: var(--blue-60); }
|
||||
|
||||
/* ---------- Main article ---------- */
|
||||
article.docs-content {
|
||||
min-width: 0;
|
||||
max-width: 760px;
|
||||
font-size: 18px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
article.docs-content h1,
|
||||
article.docs-content h2,
|
||||
article.docs-content h3,
|
||||
article.docs-content h4,
|
||||
article.docs-content h5,
|
||||
article.docs-content h6 {
|
||||
color: var(--neutral-15);
|
||||
}
|
||||
|
||||
article.docs-content h1 {
|
||||
font-size: 36px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
margin: 0 0 24px;
|
||||
}
|
||||
article.docs-content h2 {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
line-height: 1.34;
|
||||
margin: 56px 0 24px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--neutral-80);
|
||||
scroll-margin-top: 24px;
|
||||
}
|
||||
article.docs-content h3 {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
margin: 40px 0 12px;
|
||||
scroll-margin-top: 24px;
|
||||
}
|
||||
article.docs-content h4 {
|
||||
font-size: 16px;
|
||||
font-weight: 900;
|
||||
margin: 32px 0 8px;
|
||||
}
|
||||
article.docs-content h5,
|
||||
article.docs-content h6 {
|
||||
font-size: 14px;
|
||||
font-weight: 900;
|
||||
margin: 24px 0 8px;
|
||||
}
|
||||
|
||||
/* Heading auto-links (rehype-autolink-headings, `wrap`): render as plain text. */
|
||||
article.docs-content :is(h1, h2, h3, h4, h5, h6) a {
|
||||
color: inherit;
|
||||
font-weight: inherit;
|
||||
text-decoration: none;
|
||||
display: inline;
|
||||
}
|
||||
article.docs-content :is(h1, h2, h3, h4, h5, h6) a:hover { color: inherit; }
|
||||
|
||||
article.docs-content p { margin: 0 0 1.3em; line-height: 30px; }
|
||||
|
||||
/* Lede paragraph (first paragraph after the H1). */
|
||||
article.docs-content > p:first-of-type,
|
||||
article.docs-content h1 + p {
|
||||
color: var(--neutral-40);
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
article.docs-content ul,
|
||||
article.docs-content ol { margin: 0 0 1.3em; padding-left: 24px; }
|
||||
article.docs-content ol { padding-left: 28px; }
|
||||
article.docs-content li { margin: 0 0 0.5em; line-height: 30px; }
|
||||
article.docs-content li > ul,
|
||||
article.docs-content li > ol { margin-top: 0.5em; }
|
||||
|
||||
article.docs-content a {
|
||||
color: var(--link);
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
article.docs-content a:hover { color: var(--link-hover); }
|
||||
|
||||
article.docs-content hr {
|
||||
border: 0;
|
||||
border-top: 1px solid var(--neutral-90);
|
||||
margin: 32px 0;
|
||||
}
|
||||
|
||||
article.docs-content strong, article.docs-content b { font-weight: 700; }
|
||||
|
||||
article.docs-content img,
|
||||
article.docs-content iframe:not(.no-frame) {
|
||||
background: var(--white);
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 3px 24px rgba(0, 0, 0, 0.08);
|
||||
margin: 1.2em 0 1.728em;
|
||||
}
|
||||
/* Image-zoom.js sets `cursor: zoom-in` inline; this is the no-JS fallback. */
|
||||
article.docs-content img:not(.no-zoom) { cursor: zoom-in; }
|
||||
|
||||
/* ---------- Inline code & code blocks ---------- */
|
||||
article.docs-content :not(pre) > code {
|
||||
background: var(--blue-90);
|
||||
color: var(--neutral-40);
|
||||
padding: 1px 4px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9em;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
article.docs-content pre {
|
||||
background: var(--white);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 3px 24px rgba(0, 0, 0, 0.08);
|
||||
padding: 1.5rem;
|
||||
margin: 1.2em 0;
|
||||
overflow-x: auto;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
article.docs-content pre code {
|
||||
background: transparent;
|
||||
color: var(--neutral-15);
|
||||
padding: 0;
|
||||
font-size: inherit;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
/* ---------- Blockquotes / callouts ---------- */
|
||||
article.docs-content blockquote {
|
||||
background: var(--blue-95);
|
||||
border: 1px solid var(--blue-60);
|
||||
border-radius: 0.625rem;
|
||||
color: var(--neutral-40);
|
||||
margin: 2rem 0;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
article.docs-content blockquote p { font-size: 16px; line-height: 1.5rem; margin: 0 0 0.75rem; }
|
||||
article.docs-content blockquote p:last-child { margin-bottom: 0; }
|
||||
article.docs-content blockquote a { display: inline; }
|
||||
|
||||
article.docs-content blockquote.note { border-color: var(--blue-60); background: var(--blue-95); }
|
||||
article.docs-content blockquote.tip { border-color: var(--green-40); background: var(--green-95); }
|
||||
article.docs-content blockquote.warning { border-color: var(--yellow-50); background: var(--yellow-95); }
|
||||
article.docs-content blockquote.danger { border-color: var(--red-40); background: var(--red-95); }
|
||||
article.docs-content blockquote.plans-callout,
|
||||
article.docs-content blockquote.plans-blockquote {
|
||||
border-color: var(--purple-50);
|
||||
background: var(--purple-95);
|
||||
}
|
||||
article.docs-content blockquote.plans-callout a,
|
||||
article.docs-content blockquote.plans-blockquote a { color: var(--purple-50); }
|
||||
|
||||
/* ---------- Embeds ---------- */
|
||||
article.docs-content .youtube-embed {
|
||||
position: relative;
|
||||
margin: 1.2em 0;
|
||||
padding-top: 56.25%; /* 16:9 */
|
||||
}
|
||||
article.docs-content .youtube-embed iframe {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* ---------- Tables ---------- */
|
||||
article.docs-content table {
|
||||
background: var(--white);
|
||||
border: 1px solid var(--neutral-95);
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
margin: 1.2em 0 2rem;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
article.docs-content th,
|
||||
article.docs-content td {
|
||||
border-bottom: 1px solid var(--neutral-95);
|
||||
padding: 10px 14px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
color: var(--neutral-20);
|
||||
}
|
||||
article.docs-content th {
|
||||
background: var(--neutral-99);
|
||||
font-size: 11px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
article.docs-content tr:last-child td { border-bottom: 0; }
|
||||
article.docs-content td code { background: var(--neutral-95); }
|
||||
|
||||
.table-overflow-wrapper { position: relative; }
|
||||
.table-overflow { overflow-x: auto; }
|
||||
.table-overflow-indicator::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0; right: 0; bottom: 0;
|
||||
width: 24px;
|
||||
background: linear-gradient(270deg, var(--white) 0%, rgba(255, 255, 255, 0) 100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ---------- Right sidebar (on this page) ---------- */
|
||||
/* The rail itself isn't sticky — it stretches to the article column's height
|
||||
and lets its two children stick independently (TOC to viewport top, feedback
|
||||
widget to viewport bottom). That way the widget stays anchored to the same
|
||||
screen position as the user scrolls, instead of moving with the rail. */
|
||||
.docs-rail {
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.docs-toc {
|
||||
position: sticky;
|
||||
top: 16px;
|
||||
max-height: calc(100vh - 32px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.docs-toc::before {
|
||||
content: "On this page";
|
||||
display: block;
|
||||
font-weight: 900;
|
||||
color: var(--neutral-60);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
font-size: 11px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.docs-toc .toc-list { list-style: none; padding: 0; margin: 0; }
|
||||
.docs-toc li { margin: 8px 0; }
|
||||
.docs-toc a {
|
||||
display: block;
|
||||
color: var(--neutral-60);
|
||||
text-decoration: none;
|
||||
border-left: 2px solid var(--neutral-95);
|
||||
padding-left: 12px;
|
||||
line-height: 1.4;
|
||||
transition: color 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
.docs-toc a:hover { color: var(--blue-60); }
|
||||
.docs-toc a.selected { color: var(--blue-60); border-left-color: var(--blue-60); font-weight: 700; }
|
||||
|
||||
/* ---------- Feedback widget ---------- */
|
||||
.feedback-widget {
|
||||
position: sticky;
|
||||
bottom: 16px;
|
||||
margin-top: auto;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--neutral-90);
|
||||
background: var(--neutral-98);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
.feedback-widget h6 {
|
||||
color: var(--neutral-15);
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
}
|
||||
.feedback-widget #page-feedback {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.feedback-widget #page-feedback h6 {
|
||||
flex: 0 0 100%;
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
.feedback-widget .vote-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border: 1px solid var(--blue-90);
|
||||
border-radius: 8px;
|
||||
background: var(--white);
|
||||
color: var(--neutral-60);
|
||||
font-family: var(--font-body);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
padding: 6px 10px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s ease, background-color 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
.feedback-widget .vote-button:hover {
|
||||
border-color: var(--blue-60);
|
||||
color: var(--blue-60);
|
||||
}
|
||||
.feedback-widget .vote-button.active {
|
||||
background: var(--blue-60);
|
||||
border-color: var(--blue-60);
|
||||
color: var(--white);
|
||||
}
|
||||
.feedback-widget .vote-button.active:hover { color: var(--white); }
|
||||
|
||||
.feedback-widget #page-comment p {
|
||||
color: var(--neutral-40);
|
||||
font-size: 13px;
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
.feedback-widget #page-comment-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
background: var(--white);
|
||||
border: 1px solid var(--neutral-95);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
}
|
||||
.feedback-widget textarea {
|
||||
border: none;
|
||||
outline: none;
|
||||
resize: vertical;
|
||||
min-height: 60px;
|
||||
font-family: var(--font-body);
|
||||
font-size: 13px;
|
||||
color: var(--neutral-40);
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
}
|
||||
.feedback-widget #page-comment-submit {
|
||||
align-self: flex-end;
|
||||
border: none;
|
||||
background: var(--blue-60);
|
||||
color: var(--white);
|
||||
border-radius: 6px;
|
||||
font-family: var(--font-body);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
padding: 8px 14px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
.feedback-widget #page-comment-submit:hover { background: var(--blue-40); }
|
||||
|
||||
.feedback-widget .feedback-footer {
|
||||
color: var(--neutral-60);
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.feedback-widget .feedback-footer a,
|
||||
.feedback-widget #page-feedback-footer a {
|
||||
color: var(--blue-60);
|
||||
text-decoration: none;
|
||||
border-bottom: 0;
|
||||
}
|
||||
.feedback-widget .feedback-footer a:hover { color: var(--blue-40); }
|
||||
|
||||
.feedback-widget .hide { display: none; }
|
||||
|
||||
/* Hide the right rail (TOC + feedback widget) at tablet/mobile widths.
|
||||
* Placed AFTER the .docs-rail / .docs-toc / .feedback-widget blocks above
|
||||
* because media queries don't add specificity — source order decides which
|
||||
* rule wins when both match. Keeping this last for the rail ensures the
|
||||
* hide always beats the visible-state styles. See breakpoint table at top
|
||||
* of file. */
|
||||
@media (max-width: 1199px) {
|
||||
.docs-rail { display: none; }
|
||||
}
|
||||
|
||||
/* ---------- Section-link copy icon (anchor-links.js fallback) ---------- */
|
||||
.copy-clip-container { position: relative; display: flex; align-items: baseline; gap: 8px; }
|
||||
.copy-clip {
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: var(--neutral-80);
|
||||
}
|
||||
.copy-clip:hover { color: var(--blue-60); }
|
||||
.copy-clip-container:hover .copy-clip { opacity: 1; }
|
||||
.copy-to-clipboard-wrapper,
|
||||
.copied-to-clipboard-wrapper {
|
||||
background: var(--neutral-15);
|
||||
color: var(--white);
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
margin-left: 4px;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
.invisible { opacity: 0; pointer-events: none; }
|
||||
|
||||
/* ---------- Code-copy button (code-snippet-copy.js) ---------- */
|
||||
.code-snippet-wrapper { position: relative; }
|
||||
.code-snippet-wrapper .copy-code-button {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
background: var(--blue-95);
|
||||
color: var(--blue-60);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 6px 8px;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s, background 0.15s;
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
.code-snippet-wrapper:hover .copy-code-button { opacity: 1; background: var(--blue-90); }
|
||||
.copy-code-button .copy-button-label { font-size: 13px; font-weight: 900; }
|
||||
.copy-code-button .copy-button-copy-wrapper,
|
||||
.copy-code-button .copy-button-copied-wrapper { display: inline-flex; align-items: center; gap: 4px; }
|
||||
.copy-code-button .copy-button-copied-wrapper { display: none; }
|
||||
.copy-code-button .d-none { display: none !important; }
|
||||
.copy-code-button .d-flex { display: inline-flex !important; }
|
||||
|
||||
/* ---------- Navigation header overrides ----------
|
||||
*
|
||||
* The Bootstrap-derived rules in chrome.css position dropdowns with
|
||||
* position:fixed at viewport center, which only lines up with the buttons
|
||||
* if the button row is also viewport-centered. The docs layout uses
|
||||
* col-lg-2 (logo) + col-lg-3 (actions), so the middle column sits left
|
||||
* of center and the dropdowns float away from their triggers, leaving a
|
||||
* dead zone the cursor can't cross without losing :hover. Re-anchor each
|
||||
* dropdown directly below its button, center the button row on the
|
||||
* viewport, and suppress the sliding hover-highlight pill (positioned by
|
||||
* main-nav.js via the hovered button's offsetLeft — stranded off-button
|
||||
* in our layout). */
|
||||
|
||||
/* chrome.css sets z-index:1001 on .navigation-header but never gives it a
|
||||
* position, so the z-index is inert — leaving the nav (and its dropdowns)
|
||||
* painting in document order against the docs shell, which then bleeds
|
||||
* the sidebar and TOC through any open mega menu. Sticky-position the
|
||||
* outer <header> so its 1001 takes effect (and so the nav stays pinned
|
||||
* as you scroll, which the `sticky` class on the element already
|
||||
* implies). */
|
||||
header.bootstrap.sticky {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: var(--mb-z-header);
|
||||
}
|
||||
|
||||
.bootstrap .navigation-header > .container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* With the button row lifted out of flow below, the only remaining
|
||||
* flex children are the logo and the action column. Default flex-start
|
||||
* packs them next to each other on the left — push the actions back
|
||||
* to the right edge so the centered button row sits between them. */
|
||||
.bootstrap .navigation-header .row.flex-grow-1 {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.bootstrap .navigation-header .submenus-container {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.bootstrap .navigation-header .button-desktop {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.bootstrap .navigation-header [id$=nav-menu-wrapper] {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
}
|
||||
|
||||
.bootstrap .navigation-header #hover-highlight {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* chrome.css line 2720 — `.bootstrap .Button { background-color: transparent }`
|
||||
* comes AFTER line 280 — `.bootstrap .btn-primary { background-color: #509ee3 }`
|
||||
* in source order. Both selectors have equal specificity, so the later rule
|
||||
* wins and the Get started pill renders transparent until :hover reapplies a
|
||||
* background. Anchor the primary blue back on by ID so neither cascade order
|
||||
* nor specificity comes into play. */
|
||||
.bootstrap .navigation-header #get-started-btn {
|
||||
background-color: #509ee3;
|
||||
color: #fff;
|
||||
}
|
||||
.bootstrap .navigation-header #get-started-btn:hover {
|
||||
background-color: #1c6bb0;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* The marketing site sets `a { text-decoration: none }` globally on body
|
||||
* (main.css:211-220) which the docs site does not carry — so anchors inside
|
||||
* the chrome header (mobile menu links, mega-menu cards, "Watch 5-minute
|
||||
* demo", section labels wrapped as <a>) render with the browser default
|
||||
* underline. We already scoped this for the footer via `.bootstrap.footer-wrap
|
||||
* a`; do the same for the nav header. */
|
||||
.bootstrap .navigation-header a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* The mobile menu section labels are <span class="h4">. In standard Bootstrap
|
||||
* `.h1`-`.h6` are utility classes that mimic the corresponding tag's typography,
|
||||
* but the docs-build chrome subset only styles the actual <h1>-<h6> tags. Add
|
||||
* the `.h4` class so "Product", "Features", "Docs", "Resources", "Pricing"
|
||||
* (and "Log in" at the menu bottom) render bold instead of as plain inline
|
||||
* text. Mirrors the cascade for <h4> in chrome.css. */
|
||||
.bootstrap .h4 {
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
font-weight: 700;
|
||||
line-height: 1.33;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
color: #22242b;
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
.bootstrap .h4 { font-size: 1.5rem; }
|
||||
}
|
||||
|
||||
/* Newsletter email input in the footer is hard-coded to 360px in the chrome
|
||||
* styles, which overflows on viewports narrower than the footer's
|
||||
* column-content width. Let it shrink to fit; the 360px stays as a max so
|
||||
* the desktop layout doesn't widen. */
|
||||
.bootstrap .body-footer > form input#mce-EMAIL {
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
}
|
||||
8
docs-build/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"compilerOptions": {
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": [".astro/types.d.ts", "**/*"],
|
||||
"exclude": ["dist"]
|
||||
}
|
||||
@@ -20,11 +20,11 @@ In the action editor, you can write your own code to create an action, like writ
|
||||
|
||||
## Field types for action variables
|
||||
|
||||
For each {% raw %}{{ variable }}{% endraw %} that you set in an action, you'll need to set the field type.
|
||||
For each {{ variable }} that you set in an action, you'll need to set the field type.
|
||||
|
||||
Each of these variable field types present different options. Click on the gear icon to change options.
|
||||
|
||||
If you don't require a variable, you can optionally specify a default value for Metabase to use in cases where people don't fill out the field. In the SQL code, remember to enclose any optional variables and commas in brackets, like `{% raw %}[[, column = {{ optional_variable }}]] {% endraw %}`.
|
||||
If you don't require a variable, you can optionally specify a default value for Metabase to use in cases where people don't fill out the field. In the SQL code, remember to enclose any optional variables and commas in brackets, like `[[, column = {{ optional_variable }}]] `.
|
||||
|
||||
You can include placeholder text for all fields in the action form.
|
||||
|
||||
@@ -79,13 +79,13 @@ If something goes wrong, Metabase will display the error message it received fro
|
||||
You could write an action that would update the `plan` column for a record in the `invoices` table in the Sample Database:
|
||||
|
||||
```sql
|
||||
{% raw %}
|
||||
|
||||
UPDATE invoices
|
||||
SET plan = {{ plan }}
|
||||
[[, payment = {{ payment }}]]
|
||||
WHERE
|
||||
id = {{ id }}
|
||||
{% endraw %}
|
||||
|
||||
```
|
||||
|
||||
The above code will create a form that prompts people to input updated values for the (required) `plan` field and optionally the `payment` field for a given record specified by `ID`.
|
||||
@@ -99,7 +99,7 @@ The code in brackets `[[ ]]` makes the statement optional: the bracket-enclosed
|
||||
Insert statements are pretty straightforward:
|
||||
|
||||
```sql
|
||||
{% raw %}
|
||||
|
||||
INSERT INTO invoices (
|
||||
account_id
|
||||
,payment
|
||||
@@ -114,7 +114,7 @@ VALUES (
|
||||
,{{plan}}
|
||||
,({{date_received}}
|
||||
);
|
||||
{% endraw %}
|
||||
|
||||
```
|
||||
|
||||
### Casting field values in actions
|
||||
@@ -122,11 +122,11 @@ VALUES (
|
||||
If you get a type error when you submit a form, you may need to `CAST` the data type in the query so it matches the data type of the target field in the database. Here we're casting a value to a `boolean`:
|
||||
|
||||
```sql
|
||||
{% raw %}
|
||||
|
||||
UPDATE invoices
|
||||
SET expected_invoice = CAST({{expected_invoice}} AS boolean)
|
||||
WHERE id = {{id}};
|
||||
{% endraw %}
|
||||
|
||||
```
|
||||
|
||||
### Referencing saved questions in actions
|
||||
@@ -134,7 +134,7 @@ WHERE id = {{id}};
|
||||
You can also reference saved questions in actions. Here we're taking the results of a `SELECT` statement on a saved question ("Potential customers") and inserting the results into a `people_to_write` table.
|
||||
|
||||
```sql
|
||||
{% raw %}
|
||||
|
||||
WITH prospects AS {{#6-potential-customers}}
|
||||
|
||||
INSERT INTO
|
||||
@@ -148,7 +148,7 @@ SELECT
|
||||
,last_name
|
||||
,email
|
||||
FROM prospects;
|
||||
{% endraw %}
|
||||
|
||||
```
|
||||
|
||||
## Further reading
|
||||
|
||||
@@ -85,7 +85,7 @@ X-Metabase-Session: SESSION_TOKEN
|
||||
|
||||
### JWT
|
||||
|
||||
{% include plans-blockquote.html feature="JWT-based authentication" %}
|
||||
> **Plans:** JWT-based authentication is only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (both self-hosted and on Metabase Cloud).
|
||||
|
||||
If you've configured [JWT-based authentication](../people-and-groups/authenticating-with-jwt.md) (**Admin** > **Settings** > **Authentication** > **JWT**), you can pass a signed JWT directly:
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ summary: "Change how Metabot appears in Metabase for a branded look."
|
||||
|
||||
# AI customization
|
||||
|
||||
{% include plans-blockquote.html feature="AI customization" %}
|
||||
> **Plans:** AI customization is only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (both self-hosted and on Metabase Cloud).
|
||||
|
||||
_Admin > AI > Customization_
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ summary: "Use a coding agent and the Metabase CLI to create Metabase content, th
|
||||
|
||||
# Agent-driven development
|
||||
|
||||
{% include plans-blockquote.html feature="Agent-driven development" %}
|
||||
> **Plans:** File-based development is only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (both self-hosted and on Metabase Cloud).
|
||||
|
||||
Now that we have actual genies, you can just ask the genies to create the content for you (call it "lamp-rubbing development").
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ summary: Write custom instructions for Metabot to match your organization's tone
|
||||
|
||||
# AI system prompts
|
||||
|
||||
{% include plans-blockquote.html feature="AI system prompts" is_plural=true%}
|
||||
> **Plans:** AI system prompts are only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (both self-hosted and on Metabase Cloud).
|
||||
|
||||
_Admin > AI > System prompts_
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ summary: "See token and message counts, chat logs, and more."
|
||||
|
||||
# AI usage auditing
|
||||
|
||||
{% include plans-blockquote.html feature="AI usage auditing" %}
|
||||
> **Plans:** AI usage auditing is only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (both self-hosted and on Metabase Cloud).
|
||||
|
||||
_Admin > AI > Usage auditing_
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ summary: Control who can use Metabot, and cap token or message usage by instance
|
||||
|
||||
# AI usage controls
|
||||
|
||||
{% include plans-blockquote.html feature="AI controls" is_plural=true%}
|
||||
> **Plans:** AI controls are only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (both self-hosted and on Metabase Cloud).
|
||||
|
||||
_Admin > AI > Usage controls_
|
||||
|
||||
|
||||
51579
docs/api.json
@@ -1,11 +1,5 @@
|
||||
---
|
||||
title: "Accounts and billing"
|
||||
version: latest
|
||||
has_magic_breadcrumbs: true
|
||||
show_category_breadcrumb: true
|
||||
show_title_breadcrumb: true
|
||||
category: "Cloud"
|
||||
layout: new-docs
|
||||
---
|
||||
|
||||
# Accounts and billing
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
---
|
||||
title: "Changing which region your Metabase is hosted in"
|
||||
version: latest
|
||||
has_magic_breadcrumbs: true
|
||||
show_category_breadcrumb: true
|
||||
show_title_breadcrumb: true
|
||||
category: "Cloud"
|
||||
layout: new-docs
|
||||
---
|
||||
|
||||
# Changing which region your Metabase is hosted in
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
---
|
||||
title: "Metabase Cloud versus self-hosting"
|
||||
version: latest
|
||||
has_magic_breadcrumbs: true
|
||||
show_category_breadcrumb: true
|
||||
show_title_breadcrumb: true
|
||||
category: "Cloud"
|
||||
layout: new-docs
|
||||
---
|
||||
|
||||
# Metabase Cloud versus self-hosting
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
---
|
||||
title: "Changing your domain name"
|
||||
version: latest
|
||||
has_magic_breadcrumbs: true
|
||||
show_category_breadcrumb: true
|
||||
show_title_breadcrumb: true
|
||||
category: "Cloud"
|
||||
layout: new-docs
|
||||
redirect_from:
|
||||
- /cloud/docs/custom-domain
|
||||
---
|
||||
@@ -16,39 +10,40 @@ By default, Metabase will automatically provision a domain name for your Metabas
|
||||
|
||||
[Pro and Enterprise](https://www.metabase.com/pricing/) plans allow you to add a custom domain. Setting a custom domain will update your instance's [Site URL](../configuring-metabase/settings.md#site-url). The Site URL is used for things like creating links in emails, auth redirects, and in some embedding scenarios.
|
||||
|
||||
## Add a custom domain to your Metabase
|
||||
## How to add a custom domain to your Metabase
|
||||
|
||||
To add a custom domain to your Metabase:
|
||||
1. Log in to your [Metabase Store account](https://store.metabase.com/).
|
||||
|
||||
1. Log in to your Metabase [Store account](https://store.metabase.com).
|
||||
2. Navigate to **Instances**.
|
||||
3. In the instance you want to add the custom domain to, click **Settings**.
|
||||
4. Find the **Custom domain** section.
|
||||
5. Enter your custom domain (e.g., `your.custom.domain`).
|
||||
6. Click **Update**.
|
||||
7. Ask the manager of your Domain Name System (DNS) records to add a CNAME pointing to:
|
||||
2. Find the Metabase instance that you want to add the custom domain to, and select **Manage** on the bottom right of the instance's card. Then click on the **Domains** tab.
|
||||
|
||||
```
|
||||
You'll see something like this:
|
||||
|
||||

|
||||
|
||||
3. In the custom domain field (the input prefixed with `https://` in the Metabase Store), enter your custom domain (e.g., your.custom.domain).
|
||||
4. Talk to whoever manages your Domain Name System (DNS) records, and have them add a CNAME to wherever you manage your DNS records. Point that CNAME to this URL:
|
||||
|
||||
```text
|
||||
us-1.cd.metabaseapp.com
|
||||
```
|
||||
|
||||
So your CNAME record would look something like:
|
||||
|
||||
```
|
||||
```text
|
||||
CNAME your.custom.domain us-1.cd.metabaseapp.com
|
||||
```
|
||||
|
||||
> Enter this record where you manage your DNS records, not in the Metabase Store
|
||||
Just to be clear: you don't enter this record in the Metabase Store; you enter it in wherever you manage your DNS records.
|
||||
|
||||
8. If you've already set up SSO (like SAML or LDAP) for your Metabase, update the return URL in your identity provider configuration to include the new custom domain.
|
||||
5. If you've already set up SSO (like SAML or LDAP) for your Metabase, you'll need to update the return URL in your identity provider configuration to include this new custom domain.
|
||||
|
||||
You may need to wait for the CNAME to update (this delay is specific to how DNS works, not something Metabase has control over). Metabase takes care of the rest, making sure that:
|
||||
That's it. You may need to wait a bit for the CNAME to update (this delay is specific to how DNS works, and not something Metabase has control over). Metabase will take care of the rest: we'll make sure that:
|
||||
|
||||
- The URL directs users to your Metabase.
|
||||
- Your domain has SSL certificates set up to serve your instance over HTTPS to the new domain.
|
||||
- Your [metabase.com/cloud/login](https://www.metabase.com/cloud/login) works as expected.
|
||||
- the URL directs people to your Metabase,
|
||||
- your domain has SSL certificates set up to serve your instance over HTTPS to the new domain,
|
||||
- and that your [metabase.com/cloud/login](https://www.metabase.com/cloud/login) works as expected.
|
||||
|
||||
You'll still be able to access your original, automatically provisioned domain (e.g., `yourcompany.metabaseapp.com`), so existing embedded links should continue to work.
|
||||
You'll still be able to access your original, automatically provisioned domain (e.g., yourcompany.metabaseapp.com), so existing embedded links should continue to work.
|
||||
|
||||
## Why you'd want to use a custom domain
|
||||
|
||||
@@ -57,13 +52,7 @@ You'll still be able to access your original, automatically provisioned domain (
|
||||
|
||||
## Change the name of your Metabase (your DNS alias)
|
||||
|
||||
To change your DNS alias:
|
||||
|
||||
1. Log in to your Metabase [Store account](https://store.metabase.com).
|
||||
2. Navigate to **Instances**.
|
||||
3. Find the **DNS Alias** section.
|
||||
4. Enter your new alias.
|
||||
5. Click **Update**.
|
||||
Log in to your Metabase Store account and go to **Instances** > **DNS Alias**, where you can update your alias.
|
||||
|
||||
## Custom SMTP server
|
||||
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
---
|
||||
title: Sync Google Sheets with Metabase
|
||||
version: latest
|
||||
has_magic_breadcrumbs: true
|
||||
show_category_breadcrumb: true
|
||||
show_title_breadcrumb: true
|
||||
category: "Cloud"
|
||||
layout: new-docs
|
||||
---
|
||||
|
||||
# Sync Google Sheets with Metabase
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
---
|
||||
title: "How Metabase billing works"
|
||||
version: latest
|
||||
has_magic_breadcrumbs: true
|
||||
show_category_breadcrumb: true
|
||||
show_title_breadcrumb: true
|
||||
category: "Cloud"
|
||||
layout: new-docs
|
||||
redirect_from:
|
||||
- /pricing/how-billing-works
|
||||
---
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
---
|
||||
title: "IP addresses to whitelist"
|
||||
version: latest
|
||||
has_magic_breadcrumbs: true
|
||||
show_category_breadcrumb: true
|
||||
show_title_breadcrumb: true
|
||||
category: "Cloud"
|
||||
layout: new-docs
|
||||
redirect_from:
|
||||
- /cloud/docs/ip-addresses-to-whitelist
|
||||
---
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
---
|
||||
title: "Limitations of Metabase Cloud"
|
||||
version: latest
|
||||
has_magic_breadcrumbs: true
|
||||
show_category_breadcrumb: true
|
||||
show_title_breadcrumb: true
|
||||
category: "Cloud"
|
||||
layout: new-docs
|
||||
redirect_from:
|
||||
- /cloud/docs/limitations
|
||||
---
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
---
|
||||
title: "Migrate from Metabase Cloud to a self-hosted Metabase"
|
||||
version: latest
|
||||
has_magic_breadcrumbs: true
|
||||
show_category_breadcrumb: true
|
||||
show_title_breadcrumb: true
|
||||
category: "Cloud"
|
||||
layout: new-docs
|
||||
section: Migrate
|
||||
---
|
||||
|
||||
# Migrate from Metabase Cloud to a self-hosted Metabase
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
---
|
||||
title: "Migrate to Metabase Cloud - Metabase 49 or lower"
|
||||
version: latest
|
||||
has_magic_breadcrumbs: true
|
||||
show_category_breadcrumb: true
|
||||
show_title_breadcrumb: true
|
||||
category: "Cloud"
|
||||
layout: new-docs
|
||||
section: Migrate
|
||||
---
|
||||
|
||||
# Migrate to Metabase Cloud - Metabase 49 or lower
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
---
|
||||
title: "Migrate to Metabase Cloud"
|
||||
version: latest
|
||||
has_magic_breadcrumbs: true
|
||||
show_category_breadcrumb: true
|
||||
show_title_breadcrumb: true
|
||||
category: "Cloud"
|
||||
layout: new-docs
|
||||
section: Migrate
|
||||
redirect_from:
|
||||
- /migrate/guide
|
||||
- /migrate/post-signup-guide
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
---
|
||||
title: "Migrating from Heroku to Metabase Cloud"
|
||||
version: latest
|
||||
has_magic_breadcrumbs: true
|
||||
show_category_breadcrumb: true
|
||||
show_title_breadcrumb: true
|
||||
category: "Cloud"
|
||||
layout: new-docs
|
||||
redirect_from:
|
||||
- /cloud/docs/migrate/heroku
|
||||
---
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
---
|
||||
title: Metabase Cloud
|
||||
version: latest
|
||||
has_magic_breadcrumbs: true
|
||||
show_category_breadcrumb: true
|
||||
show_title_breadcrumb: false
|
||||
category: "Cloud"
|
||||
redirect_from:
|
||||
- /cloud/docs
|
||||
---
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
---
|
||||
title: Metabase Cloud Storage
|
||||
version: latest
|
||||
has_magic_breadcrumbs: true
|
||||
show_category_breadcrumb: true
|
||||
show_title_breadcrumb: true
|
||||
category: "Cloud"
|
||||
layout: new-docs
|
||||
---
|
||||
|
||||
# Metabase Cloud Storage
|
||||
|
||||
@@ -5,14 +5,13 @@ redirect_from:
|
||||
- /docs/latest/enterprise-guide/whitelabeling
|
||||
- /docs/latest/enterprise-guide/customize-embeds
|
||||
- /docs/latest/embedding/whitelabeling
|
||||
- /docs/latest/embedding/fonts
|
||||
- /docs/latest/embedding/customize-embeds
|
||||
summary: Configure colors, fonts, and illustrations in your Metabase.
|
||||
---
|
||||
|
||||
# Appearance
|
||||
|
||||
{% include plans-blockquote.html feature="Custom appearance" %}
|
||||
> **Plans:** Custom appearance is only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (both self-hosted and on Metabase Cloud).
|
||||
|
||||
_Admin > Settings > Appearance_
|
||||
|
||||
|
||||
@@ -36,13 +36,13 @@ These policies determine how long cached results will remain valid.
|
||||
|
||||
### Duration caching policy
|
||||
|
||||
{% include plans-blockquote.html feature="Duration caching policy" %}
|
||||
> **Plans:** Duration caching policy is only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (both self-hosted and on Metabase Cloud).
|
||||
|
||||
Invalidate and clear the cache after a certain number of hours. When someone runs a query, Metabase will first check whether it has cached the results, and whether those results are still valid. If not, Metabase runs the query against the database and caches the results. These cached results will remain valid for the duration you've set.
|
||||
|
||||
### Schedule caching policy
|
||||
|
||||
{% include plans-blockquote.html feature="Schedule caching policy" %}
|
||||
> **Plans:** Schedule caching policy is only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (both self-hosted and on Metabase Cloud).
|
||||
|
||||
Pick when to regularly invalidate the cache. Metabase will only store results when people run a query, and it will clear the cached results according to the schedule you set here.
|
||||
|
||||
@@ -72,7 +72,7 @@ If you select "Don't cache results" for a question, dashboard, or database, Meta
|
||||
|
||||
## Refresh cache automatically
|
||||
|
||||
{% include plans-blockquote.html feature="Refresh cache automatically" %}
|
||||
> **Plans:** Refresh cache automatically is only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (both self-hosted and on Metabase Cloud).
|
||||
|
||||
If you turn on "Refresh cache automatically" for a question or dashboard, Metabase will rerun the query/queries as soon as the cache is invalidated by whichever caching policy you've set.
|
||||
|
||||
@@ -118,7 +118,7 @@ Click on the button next to **Default policy**, and select a [cache invalidation
|
||||
|
||||
### Database caching policy
|
||||
|
||||
{% include plans-blockquote.html feature="Database caching" %}
|
||||
> **Plans:** Database caching is only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (both self-hosted and on Metabase Cloud).
|
||||
|
||||

|
||||
|
||||
@@ -128,7 +128,7 @@ If you have databases connected to Metabase that are set to **Use default** poli
|
||||
|
||||
### Dashboard caching policy
|
||||
|
||||
{% include plans-blockquote.html feature="Dashboard caching" %}
|
||||
> **Plans:** Dashboard caching is only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (both self-hosted and on Metabase Cloud).
|
||||
|
||||
To set a caching policy for a dashboard, you must have [curate access](../permissions/collections.md#curate-access) to the dashboard's collection.
|
||||
|
||||
@@ -142,7 +142,7 @@ To set a caching policy for a dashboard, you must have [curate access](../permis
|
||||
|
||||
### Question caching policy
|
||||
|
||||
{% include plans-blockquote.html feature="Question caching" %}
|
||||
> **Plans:** Question caching is only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (both self-hosted and on Metabase Cloud).
|
||||
|
||||
To set a caching policy for a question, you must have [curate access](../permissions/collections.md#curate-access) to the question's collection.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ title: "Configuration file"
|
||||
|
||||
# Configuration file
|
||||
|
||||
{% include plans-blockquote.html feature="Loading from a configuration file" self-hosted-only="true" %}
|
||||
> **Plans:** Loading from a configuration file is only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (self-hosted only).
|
||||
|
||||
On self-hosted [Pro](https://www.metabase.com/product/pro) and [Enterprise](https://www.metabase.com/product/enterprise) plans, Metabase supports initialization on launch from a config file named `config.yml`. The config file should be located at:
|
||||
|
||||
@@ -76,7 +76,7 @@ If the Metabase has already been set up, then `first@example.com` will be loaded
|
||||
On a new Metabase, the example below sets up an admin user account and one database connection.
|
||||
|
||||
```yml
|
||||
{% raw %}
|
||||
|
||||
version: 1
|
||||
config:
|
||||
users:
|
||||
@@ -93,7 +93,7 @@ config:
|
||||
user: dbuser
|
||||
password: "{{ env POSTGRES_TEST_DATA_PASSWORD }}"
|
||||
dbname: test-data
|
||||
{% endraw %}
|
||||
|
||||
```
|
||||
|
||||
To determine which keys you can specify for a database, check out the fields available in Metabase itself for the database that you want to add.
|
||||
@@ -109,7 +109,7 @@ You can also configure [uploads](../databases/uploads.md) in the config file wit
|
||||
Here's an example:
|
||||
|
||||
```yml
|
||||
{% raw %}
|
||||
|
||||
version: 1
|
||||
config:
|
||||
users:
|
||||
@@ -129,7 +129,7 @@ config:
|
||||
uploads_enabled: true
|
||||
uploads_schema_name: uploads
|
||||
uploads_table_prefix: uploads_
|
||||
{% endraw %}
|
||||
|
||||
```
|
||||
|
||||
See [Uploads](../databases/uploads.md).
|
||||
@@ -141,7 +141,7 @@ You can use the config file to create API keys, which is useful for automated de
|
||||
You can add API keys like so:
|
||||
|
||||
```yaml
|
||||
{% raw %}
|
||||
|
||||
version: 1
|
||||
config:
|
||||
users:
|
||||
@@ -158,19 +158,19 @@ config:
|
||||
group: all-users
|
||||
creator: cam@example.com
|
||||
key: mb_secondtestapikey
|
||||
{% endraw %}
|
||||
|
||||
```
|
||||
|
||||
You can also use an environment variable to supply an API key, like so:
|
||||
|
||||
```
|
||||
{% raw %}
|
||||
|
||||
api-keys:
|
||||
- name: "ENV API Key"
|
||||
key: "{{env API_KEY_FROM_ENV}}"
|
||||
creator: "admin@example.com"
|
||||
group: "admin"
|
||||
{% endraw %}
|
||||
|
||||
```
|
||||
|
||||
See below for more on [environment variables in the config file](#referring-to-environment-variables-in-the-configyml).
|
||||
@@ -204,26 +204,26 @@ Some other things to note about API keys in the config file:
|
||||
As shown in the examples above, environment variables can be specified with template tags like so:
|
||||
|
||||
```
|
||||
{% raw %}
|
||||
|
||||
setting: "{{ env POSTGRES_TEST_DATA_PASSWORD }}"
|
||||
{% endraw %}
|
||||
|
||||
```
|
||||
|
||||
Note the quote marks wrapping the template `{% raw %}"{{ env API_KEY_FROM_ENV }}"{% endraw %}`; if you don't include the quotes, the YAML parser won't know it's a string template for Metabase to expand, and Metabase won't know to swap in the env var's value.
|
||||
Note the quote marks wrapping the template `"{{ env API_KEY_FROM_ENV }}"`; if you don't include the quotes, the YAML parser won't know it's a string template for Metabase to expand, and Metabase won't know to swap in the env var's value.
|
||||
|
||||
Metabase doesn't support recursive expansion, so if one of your environment variables references _another_ environment variable, you're going to have a bad time.
|
||||
|
||||
## Values with special characters in the `config.yml`
|
||||
|
||||
If a value contains double braces (`{%raw %}}}{% endraw %}` or `{%raw %}{{{% endraw %}`), you must use triple braces to tell the config parser to use the literal value. For example, if your password was `{% raw %}MetaPa$$123{{>{% endraw %}`, you'd need to wrap the value in triple braces, like so:
|
||||
If a value contains double braces (`}}` or `{{`), you must use triple braces to tell the config parser to use the literal value. For example, if your password was `MetaPa$$123{{>`, you'd need to wrap the value in triple braces, like so:
|
||||
|
||||
```
|
||||
{% raw %}
|
||||
|
||||
password: "{{{ MetaPa$$123{{> }}}"
|
||||
{% endraw %}
|
||||
|
||||
```
|
||||
|
||||
Note the quote marks in `{% raw %}"{{{ MetaPa$$123{{> }}}"{% endraw %}`.
|
||||
Note the quote marks in `"{{{ MetaPa$$123{{> }}}"`.
|
||||
|
||||
## Disable initial database sync
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ When Metabase manages the SMTP server, you can't change the from address. If you
|
||||
|
||||
### Custom SMTP Server on Metabase Cloud
|
||||
|
||||
{% include plans-blockquote.html feature="Custom SMTP server on Metabase Cloud" %}
|
||||
> **Plans:** Custom SMTP server on Metabase Cloud is only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (both self-hosted and on Metabase Cloud).
|
||||
|
||||
_Admin > Settings > Email > Set up a custom SMTP server_
|
||||
|
||||
@@ -114,7 +114,7 @@ To configure CC/BCC:
|
||||
|
||||
### Approved domains for notifications
|
||||
|
||||
{% include plans-blockquote.html feature="Approved domains for notifications" %}
|
||||
> **Plans:** Approved domains for notifications is only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (both self-hosted and on Metabase Cloud).
|
||||
|
||||
You can configure allowed email address domain(s) for new [dashboard subscriptions](../dashboards/subscriptions.md) and [alerts](../questions/alerts.md).
|
||||
|
||||
@@ -134,7 +134,7 @@ You can also set this property using the environment variable [`MB_SUBSCRIPTION_
|
||||
|
||||
### Suggest recipients on dashboard subscriptions and alerts
|
||||
|
||||
{% include plans-blockquote.html feature="Configuring suggested recipients" %}
|
||||
> **Plans:** Configuring suggested recipients is only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (both self-hosted and on Metabase Cloud).
|
||||
|
||||
You can control which recipients people can see when they create a new [dashboard subscription](../dashboards/subscriptions.md) or [alert](../questions/alerts.md).
|
||||
|
||||
|
||||
@@ -82,18 +82,12 @@ Whether AI features are enabled.
|
||||
|
||||
### `MB_AI_USAGE_MAX_RETENTION_DAYS`
|
||||
|
||||
- Type: integer
|
||||
- Type: string
|
||||
- Default: `null`
|
||||
- [Exported as](../installation-and-operation/serialization.md): `ai-usage-max-retention-days`.
|
||||
- [Configuration file name](./config-file.md): `ai-usage-max-retention-days`
|
||||
|
||||
Number of days to retain rows in the ai_usage_log, metabot_conversation, and metabot_message tables. Minimum value is 30; set to 0 to retain data indefinitely.
|
||||
Number of days to retain rows in the ai_usage_log table. Minimum value is 30; set to 0 to retain data indefinitely.
|
||||
|
||||
Sets the maximum number of days Metabase preserves rows for the following application database tables:
|
||||
|
||||
- `ai_usage_log`
|
||||
- `metabot_conversation`
|
||||
- `metabot_message`
|
||||
Sets the maximum number of days Metabase preserves rows in the `ai_usage_log` table.
|
||||
|
||||
Once a day, Metabase deletes rows older than this threshold. The minimum value is 30 days (Metabase will treat entered values of 1 to 29 the same as 30).
|
||||
If set to 0, Metabase will keep all rows.
|
||||
@@ -354,24 +348,6 @@ Identify when new versions of Metabase are available.
|
||||
Whether to (asynchronously) sync newly created Databases during config-from-file initialization. By default, true,
|
||||
but you can disable this behavior if you want to sync it manually or use SerDes to populate its data model.
|
||||
|
||||
### `MB_CSP_IMG_ALLOWED_HOSTS`
|
||||
|
||||
- Type: string
|
||||
- Default: ``
|
||||
- [Exported as](../installation-and-operation/serialization.md): `csp-img-allowed-hosts`.
|
||||
- [Configuration file name](./config-file.md): `csp-img-allowed-hosts`
|
||||
|
||||
Comma-separated list of hosts that images may load from (e.g. in dashboard text, entity descriptions, and custom visualizations) when `csp-img-enabled` is on. Empty by default, which restricts images to this Metabase instance.
|
||||
|
||||
### `MB_CSP_IMG_ENABLED`
|
||||
|
||||
- Type: boolean
|
||||
- Default: `false`
|
||||
- [Exported as](../installation-and-operation/serialization.md): `csp-img-enabled`.
|
||||
- [Configuration file name](./config-file.md): `csp-img-enabled`
|
||||
|
||||
Restrict the browser Content Security Policy so images can only load from this Metabase instance or the hosts listed in `csp-img-allowed-hosts`. Must be on to enable Custom Visualizations.
|
||||
|
||||
### `MB_CSV_FIELD_SEPARATOR`
|
||||
|
||||
- Type: string
|
||||
@@ -467,7 +443,8 @@ By default, this is 20 minutes.
|
||||
Timeout in minutes for the database's query execution, both for the Metabase application database and any data connections.
|
||||
If you have long-running queries, you might consider increasing this value. Adjusting the timeout does not impact Metabase’s frontend.
|
||||
|
||||
This setting does not apply to queries executed within transforms; those are governed by MB_TRANSFORM_TIMEOUT instead.
|
||||
This setting also applies to individual queries executed within transforms, so make sure the duration is long enough
|
||||
that it doesn't timeout any long-running queries in your transforms.
|
||||
|
||||
Please be aware that other services (like Nginx) may still drop long-running queries.
|
||||
|
||||
@@ -480,13 +457,6 @@ Timeout in minutes for the database's query execution, both for the Metabase app
|
||||
|
||||
Whether or not the default GeoJSON maps are enabled.
|
||||
|
||||
### `MB_DISABLE_AUTO_SYNC`
|
||||
|
||||
- Type: boolean
|
||||
- Default: `false`
|
||||
|
||||
When true, suppresses automatically-triggered syncs: the scheduled sync-and-analyze and update-field-values jobs do not run (and new triggers are not registered), and adding a new database does not kick off an initial sync. Syncs originating from an explicit request — the Sync-now REST endpoints, or a transform finalizing its output table — are unaffected. For deployments that load database metadata from disk at startup and should not have Metabase re-discover it.
|
||||
|
||||
### `MB_DISABLE_CORS_ON_LOCALHOST`
|
||||
|
||||
- Type: boolean
|
||||
@@ -530,17 +500,6 @@ The email address you want to use for the sender of emails from your custom SMTP
|
||||
|
||||
The name you want to use for the sender of emails.
|
||||
|
||||
### `MB_EMAIL_MAX_RECIPIENTS_PER_MESSAGE`
|
||||
|
||||
- Type: integer
|
||||
- Default: `50`
|
||||
- [Exported as](../installation-and-operation/serialization.md): `email-max-recipients-per-message`.
|
||||
- [Configuration file name](./config-file.md): `email-max-recipients-per-message`
|
||||
|
||||
The maximum number of recipients allowed on a single email. Notifications with more recipients than
|
||||
this are split into multiple messages. This guards against SMTP providers (e.g. Amazon SES) that reject
|
||||
any message exceeding their per-message recipient cap. Defaults to 50; set to 0 to disable batching.
|
||||
|
||||
### `MB_EMAIL_MAX_RECIPIENTS_PER_SECOND`
|
||||
|
||||
- Type: integer
|
||||
@@ -784,16 +743,6 @@ Enable admins to create publicly viewable links (and embeddable iframes) for Que
|
||||
|
||||
Allow users to explore data using X-rays.
|
||||
|
||||
### `MB_FINGERPRINT_MAX_FIELDS_PER_TABLE`
|
||||
|
||||
- Type: integer
|
||||
- Default: `10000`
|
||||
- [Exported as](../installation-and-operation/serialization.md): `fingerprint-max-fields-per-table`.
|
||||
|
||||
Maximum number of fields per table to fingerprint. If a table has more eligible fields than this, the rest are
|
||||
skipped during fingerprinting analysis -- loading that many fields into memory at once can exhaust the heap (this has
|
||||
OOM'd instances syncing document databases like MongoDB with very large or dynamic schemas).
|
||||
|
||||
### `MB_FOLLOW_UP_EMAIL_SENT`
|
||||
|
||||
- Type: boolean
|
||||
@@ -1069,7 +1018,7 @@ When set to `true`, users who log in via JWT will automatically get a Metabase a
|
||||
- [Exported as](../installation-and-operation/serialization.md): `landing-page`.
|
||||
- [Configuration file name](./config-file.md): `landing-page`
|
||||
|
||||
Enter a relative URL like /dashboard/1 or /collection/2.
|
||||
Enter a URL of the landing page to show the user. This overrides the custom homepage setting above.
|
||||
|
||||
### `MB_LANDING_PAGE_ILLUSTRATION`
|
||||
|
||||
@@ -1347,13 +1296,6 @@ Popular MCP clients enabled for CORS, stored as CSV client keys (e.g. claude, vs
|
||||
|
||||
Whether Metabot is enabled for regular usage.
|
||||
|
||||
### `MB_METABOT_RECENT_VIEWS_ENABLED`
|
||||
|
||||
- Type: boolean
|
||||
- Default: `true`
|
||||
|
||||
Whether the user's recently viewed items are included in the Metabot system prompt.
|
||||
|
||||
### `MB_METABOT_SLACK_SIGNING_SECRET`
|
||||
|
||||
- Type: string
|
||||
@@ -1589,14 +1531,6 @@ The remote branch to sync with, e.g. `main`.
|
||||
|
||||
Time-to-live in seconds for the remote changes check cache. Default is 60 seconds.
|
||||
|
||||
### `MB_REMOTE_SYNC_GIT_TIMEOUT_SECONDS`
|
||||
|
||||
- Type: integer
|
||||
- Default: `60`
|
||||
- [Configuration file name](./config-file.md): `remote-sync-git-timeout-seconds`
|
||||
|
||||
Network timeout (in seconds) for remote git operations such as fetch, push, clone, and ls-remote. A stalled connection would otherwise hang a sync indefinitely.
|
||||
|
||||
### `MB_REMOTE_SYNC_TASK_TIME_LIMIT_MS`
|
||||
|
||||
- Type: integer
|
||||
@@ -1879,16 +1813,6 @@ Determines what happens when a user logs in via SAML and doesn't have a Metabase
|
||||
|
||||
When set to `true`, users who log in via SAML will automatically get a Metabase account if they don't have one, or get their existing account reactivated. When set to `false`, only users with active Metabase accounts can log in via SAML.
|
||||
|
||||
### `MB_SCAN_MAX_FIELDS_PER_TABLE`
|
||||
|
||||
- Type: integer
|
||||
- Default: `10000`
|
||||
- [Exported as](../installation-and-operation/serialization.md): `scan-max-fields-per-table`.
|
||||
|
||||
Maximum number of fields per table to scan for field values. If a table has more active fields than this, the rest
|
||||
are skipped when scanning field values -- scanning that many fields would load them all into memory and, on non-SQL
|
||||
drivers like MongoDB, issue a warehouse request per field.
|
||||
|
||||
### `MB_SCIM_ENABLED`
|
||||
|
||||
- Type: boolean
|
||||
@@ -2236,16 +2160,6 @@ Enable or disable surveys.
|
||||
|
||||
Maximum number of leaf fields synced per collection of document database. Currently relevant for Mongo. Not to be confused with total number of synced fields. For every chosen leaf field, all intermediate fields from root to leaf are synced as well.
|
||||
|
||||
### `MB_SYNC_MAX_FIELDS_PER_TABLE`
|
||||
|
||||
- Type: integer
|
||||
- Default: `10000`
|
||||
- [Exported as](../installation-and-operation/serialization.md): `sync-max-fields-per-table`.
|
||||
|
||||
Maximum number of fields per table to sync as :model/Field rows. If a table's warehouse schema has more fields than
|
||||
this, only the first (by name) are synced and the rest are skipped -- keeps document databases with very large or
|
||||
dynamic schemas (e.g. MongoDB) from creating an unbounded number of Fields.
|
||||
|
||||
### `MB_SYNCHRONOUS_BATCH_UPDATES`
|
||||
|
||||
- Type: boolean
|
||||
@@ -2265,18 +2179,6 @@ By default, this is 0 and the thread interrupt escalation does not run.
|
||||
Timeout in milliseconds to wait after query cancellation before escalating to thread interruption.
|
||||
This is used to free up threads that are stuck waiting for a DB response after a query has been cancelled.
|
||||
|
||||
### `MB_TRANSFORM_RUN_JOB_SQL_CONCURRENCY`
|
||||
|
||||
> Only available on Metabase [Pro](https://www.metabase.com/product/pro) and [Enterprise](https://www.metabase.com/product/enterprise) plans.
|
||||
|
||||
- Type: integer
|
||||
- Default: `3`
|
||||
|
||||
Maximum number of SQL-backed transforms a single transform-job run may execute in parallel.
|
||||
|
||||
This setting is only configurable on instances with the transforms add-on; OSS
|
||||
deployments without the add-on always use the default.
|
||||
|
||||
### `MB_TRANSFORM_TIMEOUT`
|
||||
|
||||
> Only available on Metabase [Pro](https://www.metabase.com/product/pro) and [Enterprise](https://www.metabase.com/product/enterprise) plans.
|
||||
@@ -2286,11 +2188,8 @@ This setting is only configurable on instances with the transforms add-on; OSS
|
||||
|
||||
The timeout for a transform job, in minutes.
|
||||
|
||||
Controls the timeout for transform runs, including the queries they execute. This takes precedence
|
||||
over MB_DB_QUERY_TIMEOUT_MINUTES for queries executed inside a transform, so transforms can run longer than regular
|
||||
Metabase queries. Enforced per-statement via `Statement.setQueryTimeout`; transforms also use a separate JDBC
|
||||
connection pool whose c3p0 leak-detector tolerates this longer runtime, so non-transform connections continue to
|
||||
use the shorter `MB_DB_QUERY_TIMEOUT_MINUTES` leak-detector.
|
||||
Each query executed by a transform is also subject to the MB_DB_QUERY_TIMEOUT_MINUTES timeout,
|
||||
so make sure that value isn't lower, or it will timeout your transform.
|
||||
|
||||
### `MB_TRANSFORMS_ENABLED`
|
||||
|
||||
@@ -2355,34 +2254,6 @@ Upload settings.
|
||||
|
||||
Prefix for upload table names.
|
||||
|
||||
### `MB_USAGE_METADATA_ENABLED`
|
||||
|
||||
- Type: boolean
|
||||
- Default: `false`
|
||||
|
||||
Whether usage-driven metadata batch processing is enabled.
|
||||
|
||||
### `MB_USAGE_METADATA_LAST_COMPLETED_DAY`
|
||||
|
||||
- Type: string
|
||||
- Default: `null`
|
||||
|
||||
Internal watermark for the last completed usage metadata day.
|
||||
|
||||
### `MB_USAGE_METADATA_RETENTION_DAYS`
|
||||
|
||||
- Type: integer
|
||||
- Default: `90`
|
||||
|
||||
How many days of usage metadata rollups to retain.
|
||||
|
||||
### `MB_USAGE_METADATA_SCHEDULE`
|
||||
|
||||
- Type: string
|
||||
- Default: `0 0 2 * * ? *`
|
||||
|
||||
Cron schedule (in UTC) for usage metadata batch processing.
|
||||
|
||||
### `MB_USER_VISIBILITY`
|
||||
|
||||
> Only available on Metabase [Pro](https://www.metabase.com/product/pro) and [Enterprise](https://www.metabase.com/product/enterprise) plans.
|
||||
@@ -2410,26 +2281,6 @@ Use [MB_SESSION_COOKIES](#mb_session_cookies) to also expire sessions, when brow
|
||||
|
||||
Also see the [Changing session expiration](../people-and-groups/changing-session-expiration.md) documentation page.
|
||||
|
||||
### `MB_APPLICATION_DB_IDLE_CONNECTION_TEST_PERIOD_SECONDS`
|
||||
|
||||
Type: integer<br>
|
||||
Default: `60`<br>
|
||||
Since: v62.0
|
||||
|
||||
How often, in seconds, to test idle connections in the application database pool. Set to `0` to disable idle connection testing.
|
||||
|
||||
### `MB_APPLICATION_DB_MAX_CONNECTION_AGE_SECONDS`
|
||||
|
||||
Type: integer<br>
|
||||
Default: `3600`<br>
|
||||
Since: v62.0
|
||||
|
||||
Maximum lifetime, in seconds, of a connection in the application database pool. After this duration the connection is closed and a new one is opened. Set to `0` to keep connections until the database closes them.
|
||||
|
||||
The default of one hour is set primarily to limit memory growth (especially on PostgreSQL).
|
||||
|
||||
If you are connecting with a short-lived credential (e.g. an AWS RDS IAM auth token) baked into the URL or password at startup, setting this to `0` is a partial workaround — eventual reconnects from the database or the network will still fail. Prefer [MB_DB_AWS_IAM](#mb_db_aws_iam) (AWS RDS / Aurora) or [MB_DB_AZURE_MANAGED_IDENTITY_CLIENT_ID](#mb_db_azure_managed_identity_client_id) (Azure).
|
||||
|
||||
### `MB_APPLICATION_DB_MAX_CONNECTION_POOL_SIZE`
|
||||
|
||||
Type: integer<br>
|
||||
@@ -2444,30 +2295,6 @@ To see how many connections are being used, check the Metabase logs and look for
|
||||
|
||||
See [MB_JDBC_DATA_WAREHOUSE_MAX_CONNECTION_POOL_SIZE](#mb_jdbc_data_warehouse_max_connection_pool_size) for setting maximum connections to the databases connected to Metabase.
|
||||
|
||||
### `MB_APPLICATION_DB_MAX_IDLE_TIME_EXCESS_CONNECTIONS_SECONDS`
|
||||
|
||||
Type: integer<br>
|
||||
Default: `600`<br>
|
||||
Since: v62.0
|
||||
|
||||
How long, in seconds, an idle connection beyond the minimum pool size may stay open before being culled. Set to `0` to never cull excess idle connections.
|
||||
|
||||
### `MB_APPLICATION_DB_TEST_CONNECTION_ON_CHECKOUT`
|
||||
|
||||
Type: boolean<br>
|
||||
Default: `false`<br>
|
||||
Since: v62.0
|
||||
|
||||
When `true`, each connection is validated when checked out of the pool. Adds latency to every checkout. Useful when credentials may invalidate connections behind the pool's back.
|
||||
|
||||
### `MB_APPLICATION_DB_UNRETURNED_CONNECTION_TIMEOUT_SECONDS`
|
||||
|
||||
Type: integer<br>
|
||||
Default: `3600`<br>
|
||||
Since: v62.0
|
||||
|
||||
How long, in seconds, before a checked-out but unreturned connection is forcibly reclaimed. The legacy name `MB_APPLICATION_DB_UNRETURNED_CONNECTION_TIMEOUT` continues to work; the suffixed form is preferred.
|
||||
|
||||
### `MB_ASYNC_QUERY_THREAD_POOL_SIZE`
|
||||
|
||||
Type: integer<br>
|
||||
@@ -2497,28 +2324,6 @@ Default: `true`
|
||||
|
||||
When set to `false`, Metabase will print migrations needed to be done in the application database and exit. Those migrations need to be applied manually. When `true`, Metabase will automatically make changes to the application database. This is not related to migrating away from H2.
|
||||
|
||||
### `MB_DB_AWS_IAM`
|
||||
|
||||
Type: boolean<br>
|
||||
Default: `false`<br>
|
||||
Since: v0.58.0
|
||||
|
||||
When `true`, authenticate to the application database (PostgreSQL or MySQL/MariaDB on AWS RDS or Aurora) using AWS IAM instead of a password. Omit [MB_DB_PASS](#mb_db_pass). Auth tokens are refreshed automatically.
|
||||
|
||||
Requires that AWS credentials are available via the standard credential chain (e.g. EKS IRSA, EC2 instance profile, ECS task role, or `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`), that the credentials hold the `rds-db:connect` permission for your database user, and that the DB user is set up for IAM authentication.
|
||||
|
||||
For MySQL/MariaDB, also set [MB_DB_SSL_CERT](#mb_db_ssl_cert), or pass the SSL parameters in [MB_DB_CONNECTION_URI](#mb_db_connection_uri).
|
||||
|
||||
### `MB_DB_AZURE_MANAGED_IDENTITY_CLIENT_ID`
|
||||
|
||||
Type: string<br>
|
||||
Default: `null`<br>
|
||||
Since: v0.51.0
|
||||
|
||||
Authenticate to a PostgreSQL or MySQL application database using an Azure Managed Identity instead of a password. Set this to the client ID of a user-assigned Managed Identity attached to your compute resource. Omit [MB_DB_PASS](#mb_db_pass). Access tokens are refreshed automatically.
|
||||
|
||||
Requires the Database authentication providers Pro/Enterprise feature.
|
||||
|
||||
### `MB_DB_CONNECTION_URI`
|
||||
|
||||
Type: string<br>
|
||||
@@ -2582,17 +2387,6 @@ Default: `null`
|
||||
|
||||
The port for [MB_DB_HOST](#mb_db_host).
|
||||
|
||||
### `MB_DB_SSL_CERT`
|
||||
|
||||
Type: string<br>
|
||||
Default: `null`<br>
|
||||
Since: v0.58.0
|
||||
|
||||
SSL configuration for the application database. Used with [MB_DB_AWS_IAM](#mb_db_aws_iam) on MySQL/MariaDB, where SSL is required.
|
||||
|
||||
- `"trust"` — trust the server certificate without validation.
|
||||
- A filesystem path to a PEM file — validate against the supplied CA certificate.
|
||||
|
||||
### `MB_DB_TYPE`
|
||||
|
||||
Type: string (`"h2"`, `"postgres"`, `"mysql"`)<br>
|
||||
@@ -2847,7 +2641,7 @@ Comma-separated namespaces to trace. **WARNING:** Could log sensitive informatio
|
||||
|
||||
### `MB_PASSWORD_COMPLEXITY`
|
||||
|
||||
Type: string (`"weak"`, `"normal"`, `"strong"`, `"strong-enough"`)<br>
|
||||
Type: string (`"weak"`, `"normal"`, `"strong"`)<br>
|
||||
Default: `"normal"`
|
||||
|
||||
Enforce a password complexity rule to increase security for regular logins. This only applies to new users or users that are changing their password. Related [MB_PASSWORD_LENGTH](#mb_password_length)
|
||||
@@ -2855,7 +2649,6 @@ Enforce a password complexity rule to increase security for regular logins. This
|
||||
- `weak` no character constraints
|
||||
- `normal` at least 1 digit
|
||||
- `strong` minimum 8 characters w/ 2 lowercase, 2 uppercase, 1 digit, and 1 special character
|
||||
- `strong-enough` minimum 15 characters
|
||||
|
||||
### `MB_PASSWORD_LENGTH`
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ redirect_from:
|
||||
|
||||
# Fonts
|
||||
|
||||
{% include plans-blockquote.html feature="Customizable fonts" is_plural=true%}
|
||||
> **Plans:** Customizable fonts are only available on [**Pro**](/product/pro) and [**Enterprise**](/product/enterprise) plans (both self-hosted and on Metabase Cloud).
|
||||
|
||||
On Pro and Enterprise plans, you can customize the font Metabase uses (the default is [Lato](https://fonts.google.com/specimen/Lato)). You can choose from a curated set of Google Fonts that accommodate the regular, bold, and heavy font weights that Metabase relies on for its various UI elements.
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ And the data as seen in the table view, which Metabase will send as a `raw_data`
|
||||
|
||||
Here's an example payload for an alert (we truncated the PNG encoding because it's boring):
|
||||
|
||||
```JSON
|
||||
```json
|
||||
{
|
||||
"type": "alert",
|
||||
"alert_id": null,
|
||||
|
||||