mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2026-01-31 13:25:10 +01:00
new: docs website (#1033)
* new: docs website Fixes #1032 * opt. * opt. * opt. * fix
This commit is contained in:
21
docs/.gitignore
vendored
Normal file
21
docs/.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# build output
|
||||
dist/
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
4
docs/.vscode/extensions.json
vendored
Normal file
4
docs/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"recommendations": ["astro-build.astro-vscode"],
|
||||
"unwantedRecommendations": []
|
||||
}
|
||||
11
docs/.vscode/launch.json
vendored
Normal file
11
docs/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "./node_modules/.bin/astro dev",
|
||||
"name": "Development server",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
49
docs/README.md
Normal file
49
docs/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Starlight Starter Kit: Basics
|
||||
|
||||
[](https://starlight.astro.build)
|
||||
|
||||
```
|
||||
npm create astro@latest -- --template starlight
|
||||
```
|
||||
|
||||
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
||||
|
||||
## 🚀 Project Structure
|
||||
|
||||
Inside of your Astro + Starlight project, you'll see the following folders and files:
|
||||
|
||||
```
|
||||
.
|
||||
├── public/
|
||||
├── src/
|
||||
│ ├── assets/
|
||||
│ ├── content/
|
||||
│ │ └── docs/
|
||||
│ └── content.config.ts
|
||||
├── astro.config.mjs
|
||||
├── package.json
|
||||
└── tsconfig.json
|
||||
```
|
||||
|
||||
Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name.
|
||||
|
||||
Images can be added to `src/assets/` and embedded in Markdown with a relative link.
|
||||
|
||||
Static assets, like favicons, can be placed in the `public/` directory.
|
||||
|
||||
## 🧞 Commands
|
||||
|
||||
All commands are run from the root of the project, from a terminal:
|
||||
|
||||
| Command | Action |
|
||||
| :------------------------ | :----------------------------------------------- |
|
||||
| `npm install` | Installs dependencies |
|
||||
| `npm run dev` | Starts local dev server at `localhost:4321` |
|
||||
| `npm run build` | Build your production site to `./dist/` |
|
||||
| `npm run preview` | Preview your build locally, before deploying |
|
||||
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
||||
| `npm run astro -- --help` | Get help using the Astro CLI |
|
||||
|
||||
## 👀 Want to learn more?
|
||||
|
||||
Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat).
|
||||
93
docs/astro.config.mjs
Normal file
93
docs/astro.config.mjs
Normal file
@@ -0,0 +1,93 @@
|
||||
// @ts-check
|
||||
import { defineConfig } from 'astro/config';
|
||||
import starlight from '@astrojs/starlight';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [
|
||||
starlight({
|
||||
title: 'Flutter Server Box',
|
||||
description: 'A comprehensive cross-platform server management application built with Flutter',
|
||||
logo: {
|
||||
src: './src/assets/logo.svg',
|
||||
},
|
||||
social: [
|
||||
{ icon: 'github', label: 'GitHub', href: 'https://github.com/lollipopkit/flutter_server_box' },
|
||||
],
|
||||
sidebar: [
|
||||
{
|
||||
label: 'Getting Started',
|
||||
items: [
|
||||
{ label: 'Introduction', slug: 'introduction' },
|
||||
{ label: 'Installation', slug: 'installation' },
|
||||
{ label: 'Quick Start', slug: 'quick-start' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Features',
|
||||
items: [
|
||||
{ label: 'Server Monitoring', slug: 'features/monitoring' },
|
||||
{ label: 'Docker Management', slug: 'features/docker' },
|
||||
{ label: 'Process & Services', slug: 'features/process' },
|
||||
{ label: 'Command Snippets', slug: 'features/snippets' },
|
||||
{ label: 'Network Tools', slug: 'features/network' },
|
||||
{ label: 'PVE (Proxmox)', slug: 'features/pve' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Configuration',
|
||||
items: [
|
||||
{ label: 'Server Setup', slug: 'configuration/server' },
|
||||
{ label: 'Terminal & SSH', slug: 'configuration/terminal' },
|
||||
{ label: 'SFTP File Browser', slug: 'configuration/sftp' },
|
||||
{ label: 'Jump Server', slug: 'configuration/jump-server' },
|
||||
{ label: 'Backup & Restore', slug: 'configuration/backup' },
|
||||
{ label: 'Appearance', slug: 'configuration/appearance' },
|
||||
{ label: 'Localizations', slug: 'configuration/localizations' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Platform Features',
|
||||
items: [
|
||||
{ label: 'Mobile', slug: 'platforms/mobile' },
|
||||
{ label: 'Desktop', slug: 'platforms/desktop' },
|
||||
{ label: 'watchOS', slug: 'platforms/watchos' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Advanced',
|
||||
items: [
|
||||
{ label: 'Bulk Import Servers', slug: 'advanced/bulk-import' },
|
||||
{ label: 'Widget Setup', slug: 'advanced/widgets' },
|
||||
{ label: 'Custom Commands', slug: 'advanced/custom-commands' },
|
||||
{ label: 'Custom Logo', slug: 'advanced/custom-logo' },
|
||||
{ label: 'JSON Settings', slug: 'advanced/json-settings' },
|
||||
{ label: 'Common Issues', slug: 'advanced/troubleshooting' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'How It Works',
|
||||
items: [
|
||||
{ label: 'Architecture', slug: 'principles/architecture' },
|
||||
{ label: 'SSH Connection', slug: 'principles/ssh' },
|
||||
{ label: 'Terminal', slug: 'principles/terminal' },
|
||||
{ label: 'SFTP', slug: 'principles/sftp' },
|
||||
{ label: 'State Management', slug: 'principles/state' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Development',
|
||||
items: [
|
||||
{ label: 'Project Structure', slug: 'development/structure' },
|
||||
{ label: 'Architecture', slug: 'development/architecture' },
|
||||
{ label: 'State Management', slug: 'development/state' },
|
||||
{ label: 'Code Generation', slug: 'development/codegen' },
|
||||
{ label: 'Building', slug: 'development/building' },
|
||||
{ label: 'Testing', slug: 'development/testing' },
|
||||
],
|
||||
},
|
||||
],
|
||||
customCss: ['./src/styles/custom.css'],
|
||||
}),
|
||||
],
|
||||
});
|
||||
900
docs/bun.lock
Normal file
900
docs/bun.lock
Normal file
@@ -0,0 +1,900 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "docs",
|
||||
"dependencies": {
|
||||
"@astrojs/starlight": "^0.37.4",
|
||||
"astro": "^5.6.1",
|
||||
"sharp": "^0.34.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@astrojs/compiler": ["@astrojs/compiler@2.13.0", "", {}, "sha512-mqVORhUJViA28fwHYaWmsXSzLO9osbdZ5ImUfxBarqsYdMlPbqAqGJCxsNzvppp1BEzc1mJNjOVvQqeDN8Vspw=="],
|
||||
|
||||
"@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.7.5", "", {}, "sha512-vreGnYSSKhAjFJCWAwe/CNhONvoc5lokxtRoZims+0wa3KbHBdPHSSthJsKxPd8d/aic6lWKpRTYGY/hsgK6EA=="],
|
||||
|
||||
"@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.10", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.5", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.2.0", "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", "shiki": "^3.19.0", "smol-toml": "^1.5.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-kk4HeYR6AcnzC4QV8iSlOfh+N8TZ3MEStxPyenyCtemqn8IpEATBFMTJcfrNW32dgpt6MY3oCkMM/Tv3/I4G3A=="],
|
||||
|
||||
"@astrojs/mdx": ["@astrojs/mdx@4.3.13", "", { "dependencies": { "@astrojs/markdown-remark": "6.3.10", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.15.0", "es-module-lexer": "^1.7.0", "estree-util-visit": "^2.0.0", "hast-util-to-html": "^9.0.5", "piccolore": "^0.1.3", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-smartypants": "^3.0.2", "source-map": "^0.7.6", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-IHDHVKz0JfKBy3//52JSiyWv089b7GVSChIXLrlUOoTLWowG3wr2/8hkaEgEyd/vysvNQvGk+QhysXpJW5ve6Q=="],
|
||||
|
||||
"@astrojs/prism": ["@astrojs/prism@3.3.0", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ=="],
|
||||
|
||||
"@astrojs/sitemap": ["@astrojs/sitemap@3.7.0", "", { "dependencies": { "sitemap": "^8.0.2", "stream-replace-string": "^2.0.0", "zod": "^3.25.76" } }, "sha512-+qxjUrz6Jcgh+D5VE1gKUJTA3pSthuPHe6Ao5JCxok794Lewx8hBFaWHtOnN0ntb2lfOf7gvOi9TefUswQ/ZVA=="],
|
||||
|
||||
"@astrojs/starlight": ["@astrojs/starlight@0.37.4", "", { "dependencies": { "@astrojs/markdown-remark": "^6.3.1", "@astrojs/mdx": "^4.2.3", "@astrojs/sitemap": "^3.3.0", "@pagefind/default-ui": "^1.3.0", "@types/hast": "^3.0.4", "@types/js-yaml": "^4.0.9", "@types/mdast": "^4.0.4", "astro-expressive-code": "^0.41.1", "bcp-47": "^2.1.0", "hast-util-from-html": "^2.0.1", "hast-util-select": "^6.0.2", "hast-util-to-string": "^3.0.0", "hastscript": "^9.0.0", "i18next": "^23.11.5", "js-yaml": "^4.1.0", "klona": "^2.0.6", "magic-string": "^0.30.17", "mdast-util-directive": "^3.0.0", "mdast-util-to-markdown": "^2.1.0", "mdast-util-to-string": "^4.0.0", "pagefind": "^1.3.0", "rehype": "^13.0.1", "rehype-format": "^5.0.0", "remark-directive": "^3.0.0", "ultrahtml": "^1.6.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0", "vfile": "^6.0.2" }, "peerDependencies": { "astro": "^5.5.0" } }, "sha512-ygPGDgRd9nCcNgaYMNN7UeAMAkDOR1ibv3ps3xEz+cuvKG3CRLd19UwdB+Gyz1tbkyfjPWPkFKNhLwNybro8Tw=="],
|
||||
|
||||
"@astrojs/telemetry": ["@astrojs/telemetry@3.3.0", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ=="],
|
||||
|
||||
"@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.28.6", "", { "dependencies": { "@babel/types": "^7.28.6" }, "bin": "./bin/babel-parser.js" }, "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ=="],
|
||||
|
||||
"@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="],
|
||||
|
||||
"@babel/types": ["@babel/types@7.28.6", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg=="],
|
||||
|
||||
"@capsizecss/unpack": ["@capsizecss/unpack@4.0.0", "", { "dependencies": { "fontkitten": "^1.0.0" } }, "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA=="],
|
||||
|
||||
"@ctrl/tinycolor": ["@ctrl/tinycolor@4.2.0", "", {}, "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A=="],
|
||||
|
||||
"@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
|
||||
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="],
|
||||
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="],
|
||||
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="],
|
||||
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="],
|
||||
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="],
|
||||
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="],
|
||||
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="],
|
||||
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="],
|
||||
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="],
|
||||
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="],
|
||||
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="],
|
||||
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="],
|
||||
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="],
|
||||
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="],
|
||||
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="],
|
||||
|
||||
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="],
|
||||
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="],
|
||||
|
||||
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="],
|
||||
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="],
|
||||
|
||||
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="],
|
||||
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="],
|
||||
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="],
|
||||
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="],
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="],
|
||||
|
||||
"@expressive-code/core": ["@expressive-code/core@0.41.6", "", { "dependencies": { "@ctrl/tinycolor": "^4.0.4", "hast-util-select": "^6.0.2", "hast-util-to-html": "^9.0.1", "hast-util-to-text": "^4.0.1", "hastscript": "^9.0.0", "postcss": "^8.4.38", "postcss-nested": "^6.0.1", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1" } }, "sha512-FvJQP+hG0jWi/FLBSmvHInDqWR7jNANp9PUDjdMqSshHb0y7sxx3vHuoOr6SgXjWw+MGLqorZyPQ0aAlHEok6g=="],
|
||||
|
||||
"@expressive-code/plugin-frames": ["@expressive-code/plugin-frames@0.41.6", "", { "dependencies": { "@expressive-code/core": "^0.41.6" } }, "sha512-d+hkSYXIQot6fmYnOmWAM+7TNWRv/dhfjMsNq+mIZz8Tb4mPHOcgcfZeEM5dV9TDL0ioQNvtcqQNuzA1sRPjxg=="],
|
||||
|
||||
"@expressive-code/plugin-shiki": ["@expressive-code/plugin-shiki@0.41.6", "", { "dependencies": { "@expressive-code/core": "^0.41.6", "shiki": "^3.2.2" } }, "sha512-Y6zmKBmsIUtWTzdefqlzm/h9Zz0Rc4gNdt2GTIH7fhHH2I9+lDYCa27BDwuBhjqcos6uK81Aca9dLUC4wzN+ng=="],
|
||||
|
||||
"@expressive-code/plugin-text-markers": ["@expressive-code/plugin-text-markers@0.41.6", "", { "dependencies": { "@expressive-code/core": "^0.41.6" } }, "sha512-PBFa1wGyYzRExMDzBmAWC6/kdfG1oLn4pLpBeTfIRrALPjcGA/59HP3e7q9J0Smk4pC7U+lWkA2LHR8FYV8U7Q=="],
|
||||
|
||||
"@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="],
|
||||
|
||||
"@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=="],
|
||||
|
||||
"@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="],
|
||||
|
||||
"@oslojs/encoding": ["@oslojs/encoding@1.1.0", "", {}, "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ=="],
|
||||
|
||||
"@pagefind/darwin-arm64": ["@pagefind/darwin-arm64@1.4.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ=="],
|
||||
|
||||
"@pagefind/darwin-x64": ["@pagefind/darwin-x64@1.4.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-e7JPIS6L9/cJfow+/IAqknsGqEPjJnVXGjpGm25bnq+NPdoD3c/7fAwr1OXkG4Ocjx6ZGSCijXEV4ryMcH2E3A=="],
|
||||
|
||||
"@pagefind/default-ui": ["@pagefind/default-ui@1.4.0", "", {}, "sha512-wie82VWn3cnGEdIjh4YwNESyS1G6vRHwL6cNjy9CFgNnWW/PGRjsLq300xjVH5sfPFK3iK36UxvIBymtQIEiSQ=="],
|
||||
|
||||
"@pagefind/freebsd-x64": ["@pagefind/freebsd-x64@1.4.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-WcJVypXSZ+9HpiqZjFXMUobfFfZZ6NzIYtkhQ9eOhZrQpeY5uQFqNWLCk7w9RkMUwBv1HAMDW3YJQl/8OqsV0Q=="],
|
||||
|
||||
"@pagefind/linux-arm64": ["@pagefind/linux-arm64@1.4.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-PIt8dkqt4W06KGmQjONw7EZbhDF+uXI7i0XtRLN1vjCUxM9vGPdtJc2mUyVPevjomrGz5M86M8bqTr6cgDp1Uw=="],
|
||||
|
||||
"@pagefind/linux-x64": ["@pagefind/linux-x64@1.4.0", "", { "os": "linux", "cpu": "x64" }, "sha512-z4oddcWwQ0UHrTHR8psLnVlz6USGJ/eOlDPTDYZ4cI8TK8PgwRUPQZp9D2iJPNIPcS6Qx/E4TebjuGJOyK8Mmg=="],
|
||||
|
||||
"@pagefind/windows-x64": ["@pagefind/windows-x64@1.4.0", "", { "os": "win32", "cpu": "x64" }, "sha512-NkT+YAdgS2FPCn8mIA9bQhiBs+xmniMGq1LFPDhcFn0+2yIUEiIG06t7bsZlhdjknEQRTSdT7YitP6fC5qwP0g=="],
|
||||
|
||||
"@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.57.0", "", { "os": "android", "cpu": "arm" }, "sha512-tPgXB6cDTndIe1ah7u6amCI1T0SsnlOuKgg10Xh3uizJk4e5M1JGaUMk7J4ciuAUcFpbOiNhm2XIjP9ON0dUqA=="],
|
||||
|
||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.57.0", "", { "os": "android", "cpu": "arm64" }, "sha512-sa4LyseLLXr1onr97StkU1Nb7fWcg6niokTwEVNOO7awaKaoRObQ54+V/hrF/BP1noMEaaAW6Fg2d/CfLiq3Mg=="],
|
||||
|
||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.57.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-/NNIj9A7yLjKdmkx5dC2XQ9DmjIECpGpwHoGmA5E1AhU0fuICSqSWScPhN1yLCkEdkCwJIDu2xIeLPs60MNIVg=="],
|
||||
|
||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.57.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-xoh8abqgPrPYPr7pTYipqnUi1V3em56JzE/HgDgitTqZBZ3yKCWI+7KUkceM6tNweyUKYru1UMi7FC060RyKwA=="],
|
||||
|
||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.57.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-PCkMh7fNahWSbA0OTUQ2OpYHpjZZr0hPr8lId8twD7a7SeWrvT3xJVyza+dQwXSSq4yEQTMoXgNOfMCsn8584g=="],
|
||||
|
||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.57.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-1j3stGx+qbhXql4OCDZhnK7b01s6rBKNybfsX+TNrEe9JNq4DLi1yGiR1xW+nL+FNVvI4D02PUnl6gJ/2y6WJA=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.57.0", "", { "os": "linux", "cpu": "arm" }, "sha512-eyrr5W08Ms9uM0mLcKfM/Uzx7hjhz2bcjv8P2uynfj0yU8GGPdz8iYrBPhiLOZqahoAMB8ZiolRZPbbU2MAi6Q=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.57.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Xds90ITXJCNyX9pDhqf85MKWUI4lqjiPAipJ8OLp8xqI2Ehk+TCVhF9rvOoN8xTbcafow3QOThkNnrM33uCFQA=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.57.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Xws2KA4CLvZmXjy46SQaXSejuKPhwVdaNinldoYfqruZBaJHqVo6hnRa8SDo9z7PBW5x84SH64+izmldCgbezw=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.57.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-hrKXKbX5FdaRJj7lTMusmvKbhMJSGWJ+w++4KmjiDhpTgNlhYobMvKfDoIWecy4O60K6yA4SnztGuNTQF+Lplw=="],
|
||||
|
||||
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.57.0", "", { "os": "linux", "cpu": "none" }, "sha512-6A+nccfSDGKsPm00d3xKcrsBcbqzCTAukjwWK6rbuAnB2bHaL3r9720HBVZ/no7+FhZLz/U3GwwZZEh6tOSI8Q=="],
|
||||
|
||||
"@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.57.0", "", { "os": "linux", "cpu": "none" }, "sha512-4P1VyYUe6XAJtQH1Hh99THxr0GKMMwIXsRNOceLrJnaHTDgk1FTcTimDgneRJPvB3LqDQxUmroBclQ1S0cIJwQ=="],
|
||||
|
||||
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.57.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-8Vv6pLuIZCMcgXre6c3nOPhE0gjz1+nZP6T+hwWjr7sVH8k0jRkH+XnfjjOTglyMBdSKBPPz54/y1gToSKwrSQ=="],
|
||||
|
||||
"@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.57.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-r1te1M0Sm2TBVD/RxBPC6RZVwNqUTwJTA7w+C/IW5v9Ssu6xmxWEi+iJQlpBhtUiT1raJ5b48pI8tBvEjEFnFA=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.57.0", "", { "os": "linux", "cpu": "none" }, "sha512-say0uMU/RaPm3CDQLxUUTF2oNWL8ysvHkAjcCzV2znxBr23kFfaxocS9qJm+NdkRhF8wtdEEAJuYcLPhSPbjuQ=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.57.0", "", { "os": "linux", "cpu": "none" }, "sha512-/MU7/HizQGsnBREtRpcSbSV1zfkoxSTR7wLsRmBPQ8FwUj5sykrP1MyJTvsxP5KBq9SyE6kH8UQQQwa0ASeoQQ=="],
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.57.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-Q9eh+gUGILIHEaJf66aF6a414jQbDnn29zeu0eX3dHMuysnhTvsUvZTCAyZ6tJhUjnvzBKE4FtuaYxutxRZpOg=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.57.0", "", { "os": "linux", "cpu": "x64" }, "sha512-OR5p5yG5OKSxHReWmwvM0P+VTPMwoBS45PXTMYaskKQqybkS3Kmugq1W+YbNWArF8/s7jQScgzXUhArzEQ7x0A=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.57.0", "", { "os": "linux", "cpu": "x64" }, "sha512-XeatKzo4lHDsVEbm1XDHZlhYZZSQYym6dg2X/Ko0kSFgio+KXLsxwJQprnR48GvdIKDOpqWqssC3iBCjoMcMpw=="],
|
||||
|
||||
"@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.57.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-Lu71y78F5qOfYmubYLHPcJm74GZLU6UJ4THkf/a1K7Tz2ycwC2VUbsqbJAXaR6Bx70SRdlVrt2+n5l7F0agTUw=="],
|
||||
|
||||
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.57.0", "", { "os": "none", "cpu": "arm64" }, "sha512-v5xwKDWcu7qhAEcsUubiav7r+48Uk/ENWdr82MBZZRIm7zThSxCIVDfb3ZeRRq9yqk+oIzMdDo6fCcA5DHfMyA=="],
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.57.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-XnaaaSMGSI6Wk8F4KK3QP7GfuuhjGchElsVerCplUuxRIzdvZ7hRBpLR0omCmw+kI2RFJB80nenhOoGXlJ5TfQ=="],
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.57.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-3K1lP+3BXY4t4VihLw5MEg6IZD3ojSYzqzBG571W3kNQe4G4CcFpSUQVgurYgib5d+YaCjeFow8QivWp8vuSvA=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.57.0", "", { "os": "win32", "cpu": "x64" }, "sha512-MDk610P/vJGc5L5ImE4k5s+GZT3en0KoK1MKPXCRgzmksAMk79j4h3k1IerxTNqwDLxsGxStEZVBqG0gIqZqoA=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.57.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Zv7v6q6aV+VslnpwzqKAmrk5JdVkLUzok2208ZXGipjb+msxBr/fJPZyeEXiFgH7k62Ak0SLIfxQRZQvTuf7rQ=="],
|
||||
|
||||
"@shikijs/core": ["@shikijs/core@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-AXSQu/2n1UIQekY8euBJlvFYZIw0PHY63jUzGbrOma4wPxzznJXTXkri+QcHeBNaFxiiOljKxxJkVSoB3PjbyA=="],
|
||||
|
||||
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-ATwv86xlbmfD9n9gKRiwuPpWgPENAWCLwYCGz9ugTJlsO2kOzhOkvoyV/UD+tJ0uT7YRyD530x6ugNSffmvIiQ=="],
|
||||
|
||||
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-OYknTCct6qiwpQDqDdf3iedRdzj6hFlOPv5hMvI+hkWfCKs5mlJ4TXziBG9nyabLwGulrUjHiCq3xCspSzErYQ=="],
|
||||
|
||||
"@shikijs/langs": ["@shikijs/langs@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0" } }, "sha512-g6mn5m+Y6GBJ4wxmBYqalK9Sp0CFkUqfNzUy2pJglUginz6ZpWbaWjDB4fbQ/8SHzFjYbtU6Ddlp1pc+PPNDVA=="],
|
||||
|
||||
"@shikijs/themes": ["@shikijs/themes@3.21.0", "", { "dependencies": { "@shikijs/types": "3.21.0" } }, "sha512-BAE4cr9EDiZyYzwIHEk7JTBJ9CzlPuM4PchfcA5ao1dWXb25nv6hYsoDiBq2aZK9E3dlt3WB78uI96UESD+8Mw=="],
|
||||
|
||||
"@shikijs/types": ["@shikijs/types@3.21.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-zGrWOxZ0/+0ovPY7PvBU2gIS9tmhSUUt30jAcNV0Bq0gb2S98gwfjIs1vxlmH5zM7/4YxLamT6ChlqqAJmPPjA=="],
|
||||
|
||||
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
|
||||
|
||||
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||
|
||||
"@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="],
|
||||
|
||||
"@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/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="],
|
||||
|
||||
"@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@17.0.45", "", {}, "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="],
|
||||
|
||||
"@types/sax": ["@types/sax@1.2.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A=="],
|
||||
|
||||
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
||||
|
||||
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
|
||||
|
||||
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||
|
||||
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||
|
||||
"ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="],
|
||||
|
||||
"ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||
|
||||
"ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||
|
||||
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
|
||||
|
||||
"arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="],
|
||||
|
||||
"astro": ["astro@5.16.16", "", { "dependencies": { "@astrojs/compiler": "^2.13.0", "@astrojs/internal-helpers": "0.7.5", "@astrojs/markdown-remark": "6.3.10", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^4.0.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "acorn": "^8.15.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.3.1", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.1.1", "cssesc": "^3.0.0", "debug": "^4.4.3", "deterministic-object-hash": "^2.0.2", "devalue": "^5.6.2", "diff": "^8.0.3", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.7.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.4.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.1", "magic-string": "^0.30.21", "magicast": "^0.5.1", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.1", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.3", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.3", "shiki": "^3.21.0", "smol-toml": "^1.6.0", "svgo": "^4.0.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", "unifont": "~0.7.3", "unist-util-visit": "^5.0.0", "unstorage": "^1.17.4", "vfile": "^6.0.3", "vite": "^6.4.1", "vitefu": "^1.1.1", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.3", "zod": "^3.25.76", "zod-to-json-schema": "^3.25.1", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "astro.js" } }, "sha512-MFlFvQ84ixaHyqB3uGwMhNHdBLZ3vHawyq3PqzQS2TNWiNfQrxp5ag6S3lX+Cvnh0MUcXX+UnJBPMBHjP1/1ZQ=="],
|
||||
|
||||
"astro-expressive-code": ["astro-expressive-code@0.41.6", "", { "dependencies": { "rehype-expressive-code": "^0.41.6" }, "peerDependencies": { "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta" } }, "sha512-l47tb1uhmVIebHUkw+HEPtU/av0G4O8Q34g2cbkPvC7/e9ZhANcjUUciKt9Hp6gSVDdIuXBBLwJQn2LkeGMOAw=="],
|
||||
|
||||
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
||||
|
||||
"bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="],
|
||||
|
||||
"base-64": ["base-64@1.0.0", "", {}, "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg=="],
|
||||
|
||||
"bcp-47": ["bcp-47@2.1.0", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w=="],
|
||||
|
||||
"bcp-47-match": ["bcp-47-match@2.0.3", "", {}, "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ=="],
|
||||
|
||||
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
|
||||
|
||||
"boxen": ["boxen@8.0.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^8.0.0", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "string-width": "^7.2.0", "type-fest": "^4.21.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0" } }, "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw=="],
|
||||
|
||||
"camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="],
|
||||
|
||||
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
|
||||
|
||||
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="],
|
||||
|
||||
"chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
||||
|
||||
"ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="],
|
||||
|
||||
"cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="],
|
||||
|
||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||
|
||||
"collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="],
|
||||
|
||||
"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@1.0.1", "", {}, "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="],
|
||||
|
||||
"cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
|
||||
|
||||
"cookie-es": ["cookie-es@1.2.2", "", {}, "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="],
|
||||
|
||||
"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-selector-parser": ["css-selector-parser@3.3.0", "", {}, "sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g=="],
|
||||
|
||||
"css-tree": ["css-tree@3.1.0", "", { "dependencies": { "mdn-data": "2.12.2", "source-map-js": "^1.0.1" } }, "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w=="],
|
||||
|
||||
"css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
|
||||
|
||||
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
|
||||
|
||||
"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.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"deterministic-object-hash": ["deterministic-object-hash@2.0.2", "", { "dependencies": { "base-64": "^1.0.0" } }, "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ=="],
|
||||
|
||||
"devalue": ["devalue@5.6.2", "", {}, "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg=="],
|
||||
|
||||
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
|
||||
|
||||
"diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="],
|
||||
|
||||
"direction": ["direction@2.0.1", "", { "bin": { "direction": "cli.js" } }, "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA=="],
|
||||
|
||||
"dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
|
||||
|
||||
"entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
|
||||
|
||||
"es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
|
||||
|
||||
"esast-util-from-estree": ["esast-util-from-estree@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "unist-util-position-from-estree": "^2.0.0" } }, "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ=="],
|
||||
|
||||
"esast-util-from-js": ["esast-util-from-js@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "acorn": "^8.0.0", "esast-util-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw=="],
|
||||
|
||||
"esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
|
||||
|
||||
"escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
|
||||
|
||||
"estree-util-attach-comments": ["estree-util-attach-comments@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw=="],
|
||||
|
||||
"estree-util-build-jsx": ["estree-util-build-jsx@3.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-walker": "^3.0.0" } }, "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ=="],
|
||||
|
||||
"estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="],
|
||||
|
||||
"estree-util-scope": ["estree-util-scope@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0" } }, "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ=="],
|
||||
|
||||
"estree-util-to-js": ["estree-util-to-js@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "astring": "^1.8.0", "source-map": "^0.7.0" } }, "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg=="],
|
||||
|
||||
"estree-util-visit": ["estree-util-visit@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/unist": "^3.0.0" } }, "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww=="],
|
||||
|
||||
"estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
|
||||
|
||||
"eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="],
|
||||
|
||||
"expressive-code": ["expressive-code@0.41.6", "", { "dependencies": { "@expressive-code/core": "^0.41.6", "@expressive-code/plugin-frames": "^0.41.6", "@expressive-code/plugin-shiki": "^0.41.6", "@expressive-code/plugin-text-markers": "^0.41.6" } }, "sha512-W/5+IQbrpCIM5KGLjO35wlp1NCwDOOVQb+PAvzEoGkW1xjGM807ZGfBKptNWH6UECvt6qgmLyWolCMYKh7eQmA=="],
|
||||
|
||||
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
|
||||
|
||||
"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.0", "", { "dependencies": { "fontkitten": "^1.0.0" } }, "sha512-moThBCItUe2bjZip5PF/iZClpKHGLwMvR79Kp8XpGRBrvoRSnySN4VcILdv3/MJzbhvUA5WeiUXF5o538m5fvg=="],
|
||||
|
||||
"fontkitten": ["fontkitten@1.0.2", "", { "dependencies": { "tiny-inflate": "^1.0.3" } }, "sha512-piJxbLnkD9Xcyi7dWJRnqszEURixe7CrF/efBfbffe2DPyabmuIuqraruY8cXTs19QoM8VJzx47BDRVNXETM7Q=="],
|
||||
|
||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||
|
||||
"get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
||||
|
||||
"github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
|
||||
|
||||
"h3": ["h3@1.15.5", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": "^0.3.5", "defu": "^6.1.4", "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-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg=="],
|
||||
|
||||
"hast-util-embedded": ["hast-util-embedded@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-is-element": "^3.0.0" } }, "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA=="],
|
||||
|
||||
"hast-util-format": ["hast-util-format@1.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-embedded": "^3.0.0", "hast-util-minify-whitespace": "^1.0.0", "hast-util-phrasing": "^3.0.0", "hast-util-whitespace": "^3.0.0", "html-whitespace-sensitive-tag-names": "^3.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA=="],
|
||||
|
||||
"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-has-property": ["hast-util-has-property@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA=="],
|
||||
|
||||
"hast-util-is-body-ok-link": ["hast-util-is-body-ok-link@3.0.1", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ=="],
|
||||
|
||||
"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-minify-whitespace": ["hast-util-minify-whitespace@1.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-embedded": "^3.0.0", "hast-util-is-element": "^3.0.0", "hast-util-whitespace": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw=="],
|
||||
|
||||
"hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="],
|
||||
|
||||
"hast-util-phrasing": ["hast-util-phrasing@3.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-embedded": "^3.0.0", "hast-util-has-property": "^3.0.0", "hast-util-is-body-ok-link": "^3.0.0", "hast-util-is-element": "^3.0.0" } }, "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ=="],
|
||||
|
||||
"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-select": ["hast-util-select@6.0.4", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "bcp-47-match": "^2.0.0", "comma-separated-tokens": "^2.0.0", "css-selector-parser": "^3.0.0", "devlop": "^1.0.0", "direction": "^2.0.0", "hast-util-has-property": "^3.0.0", "hast-util-to-string": "^3.0.0", "hast-util-whitespace": "^3.0.0", "nth-check": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw=="],
|
||||
|
||||
"hast-util-to-estree": ["hast-util-to-estree@3.1.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-attach-comments": "^3.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w=="],
|
||||
|
||||
"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-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"html-whitespace-sensitive-tag-names": ["html-whitespace-sensitive-tag-names@3.0.1", "", {}, "sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA=="],
|
||||
|
||||
"http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="],
|
||||
|
||||
"i18next": ["i18next@23.16.8", "", { "dependencies": { "@babel/runtime": "^7.23.2" } }, "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg=="],
|
||||
|
||||
"import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="],
|
||||
|
||||
"inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="],
|
||||
|
||||
"iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="],
|
||||
|
||||
"is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="],
|
||||
|
||||
"is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="],
|
||||
|
||||
"is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="],
|
||||
|
||||
"is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
|
||||
|
||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||
|
||||
"is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="],
|
||||
|
||||
"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.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
|
||||
|
||||
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
||||
|
||||
"kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
|
||||
|
||||
"klona": ["klona@2.0.6", "", {}, "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="],
|
||||
|
||||
"longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
|
||||
|
||||
"lru-cache": ["lru-cache@11.2.5", "", {}, "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw=="],
|
||||
|
||||
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||
|
||||
"magicast": ["magicast@0.5.1", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "source-map-js": "^1.2.1" } }, "sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw=="],
|
||||
|
||||
"markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="],
|
||||
|
||||
"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-directive": ["mdast-util-directive@3.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q=="],
|
||||
|
||||
"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.2", "", { "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-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="],
|
||||
|
||||
"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-mdx": ["mdast-util-mdx@3.0.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w=="],
|
||||
|
||||
"mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="],
|
||||
|
||||
"mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="],
|
||||
|
||||
"mdast-util-mdxjs-esm": ["mdast-util-mdxjs-esm@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg=="],
|
||||
|
||||
"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.12.2", "", {}, "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA=="],
|
||||
|
||||
"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-directive": ["micromark-extension-directive@3.0.2", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "parse-entities": "^4.0.0" } }, "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA=="],
|
||||
|
||||
"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-extension-mdx-expression": ["micromark-extension-mdx-expression@3.0.1", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q=="],
|
||||
|
||||
"micromark-extension-mdx-jsx": ["micromark-extension-mdx-jsx@3.0.2", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ=="],
|
||||
|
||||
"micromark-extension-mdx-md": ["micromark-extension-mdx-md@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ=="],
|
||||
|
||||
"micromark-extension-mdxjs": ["micromark-extension-mdxjs@3.0.0", "", { "dependencies": { "acorn": "^8.0.0", "acorn-jsx": "^5.0.0", "micromark-extension-mdx-expression": "^3.0.0", "micromark-extension-mdx-jsx": "^3.0.0", "micromark-extension-mdx-md": "^2.0.0", "micromark-extension-mdxjs-esm": "^3.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ=="],
|
||||
|
||||
"micromark-extension-mdxjs-esm": ["micromark-extension-mdxjs-esm@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A=="],
|
||||
|
||||
"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-mdx-expression": ["micromark-factory-mdx-expression@2.0.3", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ=="],
|
||||
|
||||
"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-events-to-acorn": ["micromark-util-events-to-acorn@2.0.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||
|
||||
"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-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=="],
|
||||
|
||||
"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.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="],
|
||||
|
||||
"oniguruma-to-es": ["oniguruma-to-es@4.3.4", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA=="],
|
||||
|
||||
"p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="],
|
||||
|
||||
"p-queue": ["p-queue@8.1.1", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="],
|
||||
|
||||
"p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="],
|
||||
|
||||
"package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="],
|
||||
|
||||
"pagefind": ["pagefind@1.4.0", "", { "optionalDependencies": { "@pagefind/darwin-arm64": "1.4.0", "@pagefind/darwin-x64": "1.4.0", "@pagefind/freebsd-x64": "1.4.0", "@pagefind/linux-arm64": "1.4.0", "@pagefind/linux-x64": "1.4.0", "@pagefind/windows-x64": "1.4.0" }, "bin": { "pagefind": "lib/runner/bin.cjs" } }, "sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g=="],
|
||||
|
||||
"parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"piccolore": ["piccolore@0.1.3", "", {}, "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
||||
|
||||
"postcss-nested": ["postcss-nested@6.2.0", "", { "dependencies": { "postcss-selector-parser": "^6.1.1" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="],
|
||||
|
||||
"postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
|
||||
|
||||
"prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="],
|
||||
|
||||
"prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="],
|
||||
|
||||
"property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
|
||||
|
||||
"radix3": ["radix3@1.1.2", "", {}, "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="],
|
||||
|
||||
"readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
||||
|
||||
"recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-build-jsx": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="],
|
||||
|
||||
"recma-jsx": ["recma-jsx@1.0.1", "", { "dependencies": { "acorn-jsx": "^5.0.0", "estree-util-to-js": "^2.0.0", "recma-parse": "^1.0.0", "recma-stringify": "^1.0.0", "unified": "^11.0.0" }, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w=="],
|
||||
|
||||
"recma-parse": ["recma-parse@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "esast-util-from-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ=="],
|
||||
|
||||
"recma-stringify": ["recma-stringify@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-to-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g=="],
|
||||
|
||||
"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-expressive-code": ["rehype-expressive-code@0.41.6", "", { "dependencies": { "expressive-code": "^0.41.6" } }, "sha512-aBMX8kxPtjmDSFUdZlAWJkMvsQ4ZMASfee90JWIAV8tweltXLzkWC3q++43ToTelI8ac5iC0B3/S/Cl4Ql1y2g=="],
|
||||
|
||||
"rehype-format": ["rehype-format@5.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-format": "^1.0.0" } }, "sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ=="],
|
||||
|
||||
"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-recma": ["rehype-recma@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "hast-util-to-estree": "^3.0.0" } }, "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw=="],
|
||||
|
||||
"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-directive": ["remark-directive@3.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-directive": "^3.0.0", "micromark-extension-directive": "^3.0.0", "unified": "^11.0.0" } }, "sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A=="],
|
||||
|
||||
"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-mdx": ["remark-mdx@3.1.1", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"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.57.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.0", "@rollup/rollup-android-arm64": "4.57.0", "@rollup/rollup-darwin-arm64": "4.57.0", "@rollup/rollup-darwin-x64": "4.57.0", "@rollup/rollup-freebsd-arm64": "4.57.0", "@rollup/rollup-freebsd-x64": "4.57.0", "@rollup/rollup-linux-arm-gnueabihf": "4.57.0", "@rollup/rollup-linux-arm-musleabihf": "4.57.0", "@rollup/rollup-linux-arm64-gnu": "4.57.0", "@rollup/rollup-linux-arm64-musl": "4.57.0", "@rollup/rollup-linux-loong64-gnu": "4.57.0", "@rollup/rollup-linux-loong64-musl": "4.57.0", "@rollup/rollup-linux-ppc64-gnu": "4.57.0", "@rollup/rollup-linux-ppc64-musl": "4.57.0", "@rollup/rollup-linux-riscv64-gnu": "4.57.0", "@rollup/rollup-linux-riscv64-musl": "4.57.0", "@rollup/rollup-linux-s390x-gnu": "4.57.0", "@rollup/rollup-linux-x64-gnu": "4.57.0", "@rollup/rollup-linux-x64-musl": "4.57.0", "@rollup/rollup-openbsd-x64": "4.57.0", "@rollup/rollup-openharmony-arm64": "4.57.0", "@rollup/rollup-win32-arm64-msvc": "4.57.0", "@rollup/rollup-win32-ia32-msvc": "4.57.0", "@rollup/rollup-win32-x64-gnu": "4.57.0", "@rollup/rollup-win32-x64-msvc": "4.57.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA=="],
|
||||
|
||||
"sax": ["sax@1.4.4", "", {}, "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw=="],
|
||||
|
||||
"semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||
|
||||
"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@3.21.0", "", { "dependencies": { "@shikijs/core": "3.21.0", "@shikijs/engine-javascript": "3.21.0", "@shikijs/engine-oniguruma": "3.21.0", "@shikijs/langs": "3.21.0", "@shikijs/themes": "3.21.0", "@shikijs/types": "3.21.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-N65B/3bqL/TI2crrXr+4UivctrAGEjmsib5rPMMPpFp1xAx/w03v8WZ9RDDFYteXoEgY7qZ4HGgl5KBIu1153w=="],
|
||||
|
||||
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
|
||||
|
||||
"sitemap": ["sitemap@8.0.2", "", { "dependencies": { "@types/node": "^17.0.5", "@types/sax": "^1.2.1", "arg": "^5.0.0", "sax": "^1.4.1" }, "bin": { "sitemap": "dist/cli.js" } }, "sha512-LwktpJcyZDoa0IL6KT++lQ53pbSrx2c9ge41/SeLTyqy2XUNA6uR4+P9u5IVo5lPeL2arAcOKn1aZAxoYbCKlQ=="],
|
||||
|
||||
"smol-toml": ["smol-toml@1.6.0", "", {}, "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw=="],
|
||||
|
||||
"source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"stream-replace-string": ["stream-replace-string@2.0.0", "", {}, "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w=="],
|
||||
|
||||
"string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
||||
|
||||
"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@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
||||
|
||||
"style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="],
|
||||
|
||||
"style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="],
|
||||
|
||||
"svgo": ["svgo@4.0.0", "", { "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.4.1" }, "bin": "./bin/svgo.js" }, "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw=="],
|
||||
|
||||
"tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="],
|
||||
|
||||
"tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
|
||||
|
||||
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||
|
||||
"trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
|
||||
|
||||
"trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="],
|
||||
|
||||
"tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="],
|
||||
|
||||
"ultrahtml": ["ultrahtml@1.6.0", "", {}, "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw=="],
|
||||
|
||||
"uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="],
|
||||
|
||||
"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.3", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-b0GtQzKCyuSHGsfj5vyN8st7muZ6VCI4XD4vFlr7Uy1rlWVYxC3npnfk8MyreHxJYrz1ooLDqDzFe9XqQTlAhA=="],
|
||||
|
||||
"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-position-from-estree": ["unist-util-position-from-estree@2.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ=="],
|
||||
|
||||
"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.4", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", "h3": "^1.15.5", "lru-cache": "^11.2.0", "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-fHK0yNg38tBiJKp/Vgsq4j0JEsCmgqH58HAn707S7zGkArbZsVr/CwINoi+nh3h98BRCwKvx1K3Xg9u3VV83sw=="],
|
||||
|
||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||
|
||||
"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@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "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-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
|
||||
|
||||
"vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="],
|
||||
|
||||
"web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
|
||||
|
||||
"which-pm-runs": ["which-pm-runs@1.1.0", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="],
|
||||
|
||||
"widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="],
|
||||
|
||||
"wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
|
||||
|
||||
"xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="],
|
||||
|
||||
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
|
||||
|
||||
"yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="],
|
||||
|
||||
"yocto-spinner": ["yocto-spinner@0.2.3", "", { "dependencies": { "yoctocolors": "^2.1.1" } }, "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ=="],
|
||||
|
||||
"yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="],
|
||||
|
||||
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||
|
||||
"zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
|
||||
|
||||
"zod-to-ts": ["zod-to-ts@1.2.0", "", { "peerDependencies": { "typescript": "^4.9.4 || ^5.0.2", "zod": "^3" } }, "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA=="],
|
||||
|
||||
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
||||
|
||||
"@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||
|
||||
"ansi-align/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=="],
|
||||
|
||||
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
|
||||
|
||||
"ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="],
|
||||
|
||||
"ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
}
|
||||
}
|
||||
17
docs/package.json
Normal file
17
docs/package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "docs",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/starlight": "^0.37.4",
|
||||
"astro": "^5.6.1",
|
||||
"sharp": "^0.34.2"
|
||||
}
|
||||
}
|
||||
1
docs/public/favicon.svg
Normal file
1
docs/public/favicon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128"><path fill-rule="evenodd" d="M81 36 64 0 47 36l-1 2-9-10a6 6 0 0 0-9 9l10 10h-2L0 64l36 17h2L28 91a6 6 0 1 0 9 9l9-10 1 2 17 36 17-36v-2l9 10a6 6 0 1 0 9-9l-9-9 2-1 36-17-36-17-2-1 9-9a6 6 0 1 0-9-9l-9 10v-2Zm-17 2-2 5c-4 8-11 15-19 19l-5 2 5 2c8 4 15 11 19 19l2 5 2-5c4-8 11-15 19-19l5-2-5-2c-8-4-15-11-19-19l-2-5Z" clip-rule="evenodd"/><path d="M118 19a6 6 0 0 0-9-9l-3 3a6 6 0 1 0 9 9l3-3Zm-96 4c-2 2-6 2-9 0l-3-3a6 6 0 1 1 9-9l3 3c3 2 3 6 0 9Zm0 82c-2-2-6-2-9 0l-3 3a6 6 0 1 0 9 9l3-3c3-2 3-6 0-9Zm96 4a6 6 0 0 1-9 9l-3-3a6 6 0 1 1 9-9l3 3Z"/><style>path{fill:#000}@media (prefers-color-scheme:dark){path{fill:#fff}}</style></svg>
|
||||
|
After Width: | Height: | Size: 696 B |
BIN
docs/src/assets/houston.webp
Normal file
BIN
docs/src/assets/houston.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
30
docs/src/assets/logo.svg
Normal file
30
docs/src/assets/logo.svg
Normal file
@@ -0,0 +1,30 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none">
|
||||
<!-- Background Circle -->
|
||||
<circle cx="32" cy="32" r="30" fill="#02569B"/>
|
||||
|
||||
<!-- Terminal Window -->
|
||||
<rect x="14" y="16" width="36" height="32" rx="3" fill="#fff" opacity="0.9"/>
|
||||
|
||||
<!-- Terminal Header -->
|
||||
<rect x="14" y="16" width="36" height="8" rx="3" fill="#02569B" opacity="0.3"/>
|
||||
<circle cx="19" cy="20" r="1.5" fill="#ff5f57"/>
|
||||
<circle cx="24" cy="20" r="1.5" fill="#ffbd2e"/>
|
||||
<circle cx="29" cy="20" r="1.5" fill="#28c940"/>
|
||||
|
||||
<!-- Terminal Prompt -->
|
||||
<text x="18" y="34" font-family="monospace" font-size="6" fill="#333">></text>
|
||||
|
||||
<!-- Terminal Cursor -->
|
||||
<rect x="23" y="30" width="6" height="6" fill="#02569B" opacity="0.5">
|
||||
<animate attributeName="opacity" values="0.5;1;0.5" dur="1s" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
|
||||
<!-- Server Icon -->
|
||||
<g transform="translate(38, 24)">
|
||||
<rect x="0" y="0" width="8" height="12" rx="1" fill="#02569B"/>
|
||||
<circle cx="2" cy="3" r="0.8" fill="#28c940"/>
|
||||
<circle cx="4" cy="3" r="0.8" fill="#28c940"/>
|
||||
<circle cx="6" cy="3" r="0.8" fill="#28c940"/>
|
||||
<line x1="1" y1="8" x2="7" y2="8" stroke="#fff" stroke-width="0.5"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
7
docs/src/content.config.ts
Normal file
7
docs/src/content.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { defineCollection } from 'astro:content';
|
||||
import { docsLoader } from '@astrojs/starlight/loaders';
|
||||
import { docsSchema } from '@astrojs/starlight/schema';
|
||||
|
||||
export const collections = {
|
||||
docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
|
||||
};
|
||||
83
docs/src/content/docs/advanced/bulk-import.md
Normal file
83
docs/src/content/docs/advanced/bulk-import.md
Normal file
@@ -0,0 +1,83 @@
|
||||
---
|
||||
title: Bulk Import Servers
|
||||
description: Import multiple servers from JSON file
|
||||
---
|
||||
|
||||
Import multiple server configurations at once using a JSON file.
|
||||
|
||||
## JSON Format
|
||||
|
||||
:::danger[Security Warning]
|
||||
**Never store plaintext passwords in files!** This JSON example shows a password field for demonstration only, but you should:
|
||||
|
||||
- **Prefer SSH keys** (`keyId`) instead of `pwd` - they're more secure
|
||||
- **Use secret managers** or environment variables if you must use passwords
|
||||
- **Delete the file immediately** after import - don't leave credentials lying around
|
||||
- **Add to .gitignore** - never commit credential files to version control
|
||||
:::
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "My Server",
|
||||
"ip": "example.com",
|
||||
"port": 22,
|
||||
"user": "root",
|
||||
"pwd": "password",
|
||||
"keyId": "",
|
||||
"tags": ["production"],
|
||||
"autoConnect": false
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Fields
|
||||
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `name` | Yes | Display name |
|
||||
| `ip` | Yes | Domain or IP address |
|
||||
| `port` | Yes | SSH port (usually 22) |
|
||||
| `user` | Yes | SSH username |
|
||||
| `pwd` | No | Password (avoid - use SSH keys instead) |
|
||||
| `keyId` | No | SSH key name (from Private Keys - recommended) |
|
||||
| `tags` | No | Organization tags |
|
||||
| `autoConnect` | No | Auto-connect on startup |
|
||||
|
||||
## Import Steps
|
||||
|
||||
1. Create JSON file with server configurations
|
||||
2. Settings → Backup → Bulk Import Servers
|
||||
3. Select your JSON file
|
||||
4. Confirm import
|
||||
|
||||
## Example
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "Production",
|
||||
"ip": "prod.example.com",
|
||||
"port": 22,
|
||||
"user": "admin",
|
||||
"keyId": "my-key",
|
||||
"tags": ["production", "web"]
|
||||
},
|
||||
{
|
||||
"name": "Development",
|
||||
"ip": "dev.example.com",
|
||||
"port": 2222,
|
||||
"user": "dev",
|
||||
"keyId": "dev-key",
|
||||
"tags": ["development"]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
- **Use SSH keys** instead of passwords when possible
|
||||
- **Test connection** after import
|
||||
- **Organize with tags** for easier management
|
||||
- **Delete JSON file** after import
|
||||
- **Never commit** JSON files with credentials to version control
|
||||
72
docs/src/content/docs/advanced/custom-commands.md
Normal file
72
docs/src/content/docs/advanced/custom-commands.md
Normal file
@@ -0,0 +1,72 @@
|
||||
---
|
||||
title: Custom Commands
|
||||
description: Display custom command output on server page
|
||||
---
|
||||
|
||||
Add custom shell commands to show their output on the server detail page.
|
||||
|
||||
## Setup
|
||||
|
||||
1. Server settings → Custom Commands
|
||||
2. Enter commands in JSON format
|
||||
|
||||
## Basic Format
|
||||
|
||||
```json
|
||||
{
|
||||
"Display Name": "shell command"
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"Memory": "free -h",
|
||||
"Disk": "df -h",
|
||||
"Uptime": "uptime"
|
||||
}
|
||||
```
|
||||
|
||||
## Viewing Results
|
||||
|
||||
After setup, custom commands appear on server detail page and refresh automatically.
|
||||
|
||||
## Special Command Names
|
||||
|
||||
### server_card_top_right
|
||||
|
||||
Display on home page server card (top-right corner):
|
||||
|
||||
```json
|
||||
{
|
||||
"server_card_top_right": "your-command-here"
|
||||
}
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
**Use absolute paths:**
|
||||
```json
|
||||
{"My Script": "/usr/local/bin/my-script.sh"}
|
||||
```
|
||||
|
||||
**Pipe commands:**
|
||||
```json
|
||||
{"Top Process": "ps aux | sort -rk 3 | head -5"}
|
||||
```
|
||||
|
||||
**Format output:**
|
||||
```json
|
||||
{"CPU Load": "uptime | awk -F'load average:' '{print $2}'"}
|
||||
```
|
||||
|
||||
**Keep commands fast:** Under 5 seconds for best experience
|
||||
|
||||
**Limit output:**
|
||||
```json
|
||||
{"Logs": "tail -20 /var/log/syslog"}
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
Commands run with SSH user permissions. Avoid commands that modify system state.
|
||||
54
docs/src/content/docs/advanced/custom-logo.md
Normal file
54
docs/src/content/docs/advanced/custom-logo.md
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: Custom Server Logo
|
||||
description: Use custom images for server cards
|
||||
---
|
||||
|
||||
Display custom logos on server cards using image URLs.
|
||||
|
||||
## Setup
|
||||
|
||||
1. Server settings → Custom Logo
|
||||
2. Enter image URL
|
||||
|
||||
## URL Placeholders
|
||||
|
||||
### {DIST} - Linux Distribution
|
||||
|
||||
Auto-replaced with detected distribution:
|
||||
|
||||
```
|
||||
https://example.com/{DIST}.png
|
||||
```
|
||||
|
||||
Becomes: `debian.png`, `ubuntu.png`, `arch.png`, etc.
|
||||
|
||||
### {BRIGHT} - Theme
|
||||
|
||||
Auto-replaced with current theme:
|
||||
|
||||
```
|
||||
https://example.com/{BRIGHT}.png
|
||||
```
|
||||
|
||||
Becomes: `light.png` or `dark.png`
|
||||
|
||||
### Combine Both
|
||||
|
||||
```
|
||||
https://example.com/{DIST}-{BRIGHT}.png
|
||||
```
|
||||
|
||||
Becomes: `debian-light.png`, `ubuntu-dark.png`, etc.
|
||||
|
||||
## Tips
|
||||
|
||||
- Use PNG or SVG formats
|
||||
- Recommended size: 64x64 to 128x128 pixels
|
||||
- Use HTTPS URLs
|
||||
- Keep file sizes small
|
||||
|
||||
## Supported Distributions
|
||||
|
||||
debian, ubuntu, centos, fedora, opensuse, kali, alpine, arch, rocky, deepin, armbian, wrt
|
||||
|
||||
Full list: [`dist.dart`](https://github.com/lollipopkit/flutter_server_box/blob/main/lib/data/model/server/dist.dart)
|
||||
75
docs/src/content/docs/advanced/json-settings.md
Normal file
75
docs/src/content/docs/advanced/json-settings.md
Normal file
@@ -0,0 +1,75 @@
|
||||
---
|
||||
title: Hidden Settings (JSON)
|
||||
description: Access advanced settings via JSON editor
|
||||
---
|
||||
|
||||
Some settings are hidden from the UI but accessible via JSON editor.
|
||||
|
||||
## Access
|
||||
|
||||
Long-press **Settings** in drawer to open JSON editor.
|
||||
|
||||
## Common Hidden Settings
|
||||
|
||||
### serverTabUseOldUI
|
||||
|
||||
Use old server tab UI.
|
||||
|
||||
```json
|
||||
{"serverTabUseOldUI": true}
|
||||
```
|
||||
|
||||
**Type:** boolean | **Default:** false
|
||||
|
||||
### timeout
|
||||
|
||||
Connection timeout in seconds.
|
||||
|
||||
```json
|
||||
{"timeout": 10}
|
||||
```
|
||||
|
||||
**Type:** integer | **Default:** 5 | **Range:** 1-60
|
||||
|
||||
### recordHistory
|
||||
|
||||
Save history (SFTP paths, etc.).
|
||||
|
||||
```json
|
||||
{"recordHistory": true}
|
||||
```
|
||||
|
||||
**Type:** boolean | **Default:** true
|
||||
|
||||
### textFactor
|
||||
|
||||
Text scaling factor.
|
||||
|
||||
```json
|
||||
{"textFactor": 1.2}
|
||||
```
|
||||
|
||||
**Type:** double | **Default:** 1.0 | **Range:** 0.8-1.5
|
||||
|
||||
## Finding More Settings
|
||||
|
||||
All settings defined in [`setting.dart`](https://github.com/lollipopkit/flutter_server_box/blob/main/lib/data/store/setting.dart).
|
||||
|
||||
Look for:
|
||||
```dart
|
||||
late final settingName = StoreProperty(box, 'settingKey', defaultValue);
|
||||
```
|
||||
|
||||
## ⚠️ Important
|
||||
|
||||
**Before editing:**
|
||||
- **Create backup** - Wrong settings can cause app to not open
|
||||
- **Edit carefully** - JSON must be valid
|
||||
- **Change one at a time** - Test each setting
|
||||
|
||||
## Recovery
|
||||
|
||||
If app won't open after editing:
|
||||
1. Clear app data (last resort)
|
||||
2. Reinstall app
|
||||
3. Restore from backup
|
||||
118
docs/src/content/docs/advanced/troubleshooting.md
Normal file
118
docs/src/content/docs/advanced/troubleshooting.md
Normal file
@@ -0,0 +1,118 @@
|
||||
---
|
||||
title: Common Issues
|
||||
description: Solutions to common problems
|
||||
---
|
||||
|
||||
## Connection Issues
|
||||
|
||||
### SSH Won't Connect
|
||||
|
||||
**Symptoms:** Timeout, connection refused, auth failed
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Verify server type:** Only Unix-like systems supported (Linux, macOS, Android/Termux)
|
||||
2. **Test manually:** `ssh user@server -p port`
|
||||
3. **Check firewall:** Port 22 must be open
|
||||
4. **Verify credentials:** Username and password/key correct
|
||||
|
||||
### Frequent Disconnections
|
||||
|
||||
**Symptoms:** Terminal disconnects after inactivity
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Server keep-alive:**
|
||||
```bash
|
||||
# /etc/ssh/sshd_config
|
||||
ClientAliveInterval 60
|
||||
ClientAliveCountMax 3
|
||||
```
|
||||
|
||||
2. **Disable battery optimization:**
|
||||
- MIUI: Battery → "No limits"
|
||||
- Android: Settings → Apps → Disable optimization
|
||||
- iOS: Enable background refresh
|
||||
|
||||
## Input Issues
|
||||
|
||||
### Can't Type Certain Characters
|
||||
|
||||
**Solution:** Settings → Keyboard Type → Switch to `visiblePassword`
|
||||
|
||||
Note: CJK input may not work after this change.
|
||||
|
||||
## App Issues
|
||||
|
||||
### App Crashes on Startup
|
||||
|
||||
**Symptoms:** App won't open, black screen
|
||||
|
||||
**Causes:** Corrupted settings, especially from JSON editor
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Clear app data:**
|
||||
- Android: Settings → Apps → ServerBox → Clear Data
|
||||
- iOS: Delete and reinstall
|
||||
|
||||
2. **Restore backup:** Import backup created before changing settings
|
||||
|
||||
### Backup/Restore Issues
|
||||
|
||||
**Backup not working:**
|
||||
- Check storage space
|
||||
- Verify app has storage permissions
|
||||
- Try different location
|
||||
|
||||
**Restore fails:**
|
||||
- Verify backup file integrity
|
||||
- Check app version compatibility
|
||||
|
||||
## Widget Issues
|
||||
|
||||
### Widget Not Updating
|
||||
|
||||
**iOS:**
|
||||
- Wait up to 30 minutes for automatic refresh
|
||||
- Remove and re-add widget
|
||||
- Check URL ends with `/status`
|
||||
|
||||
**Android:**
|
||||
- Tap widget to force refresh
|
||||
- Verify widget ID matches configuration in app settings
|
||||
|
||||
**watchOS:**
|
||||
- Restart watch app
|
||||
- Wait a few minutes after config change
|
||||
- Verify URL format
|
||||
|
||||
### Widget Shows Error
|
||||
|
||||
- Verify ServerBox Monitor is running on server
|
||||
- Test URL in browser
|
||||
- Check authentication credentials
|
||||
|
||||
## Performance Issues
|
||||
|
||||
### App is Slow
|
||||
|
||||
**Solutions:**
|
||||
- Reduce refresh rate in settings
|
||||
- Check network speed
|
||||
- Disable unused servers
|
||||
|
||||
### High Battery Usage
|
||||
|
||||
**Solutions:**
|
||||
- Increase refresh intervals
|
||||
- Disable background refresh
|
||||
- Close unused SSH sessions
|
||||
|
||||
## Getting Help
|
||||
|
||||
If issues persist:
|
||||
|
||||
1. **Search GitHub Issues:** https://github.com/lollipopkit/flutter_server_box/issues
|
||||
2. **Create New Issue:** Include app version, platform, and steps to reproduce
|
||||
3. **Check Wiki:** This documentation and GitHub Wiki
|
||||
91
docs/src/content/docs/advanced/widgets.md
Normal file
91
docs/src/content/docs/advanced/widgets.md
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: Home Screen Widgets
|
||||
description: Add server status widgets to your home screen
|
||||
---
|
||||
|
||||
Requires [ServerBox Monitor](https://github.com/lollipopkit/server_box_monitor) installed on your servers.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Install ServerBox Monitor on your server first. See [ServerBox Monitor Wiki](https://github.com/lollipopkit/server_box_monitor/wiki/Home) for setup instructions.
|
||||
|
||||
After installation, your server should have:
|
||||
- HTTP/HTTPS endpoint
|
||||
- `/status` API endpoint
|
||||
- Optional authentication
|
||||
|
||||
## URL Format
|
||||
|
||||
```
|
||||
https://your-server.com/status
|
||||
```
|
||||
|
||||
Must end with `/status`.
|
||||
|
||||
## iOS Widget
|
||||
|
||||
### Setup
|
||||
|
||||
1. Long press home screen → Tap **+**
|
||||
2. Search "ServerBox"
|
||||
3. Choose widget size
|
||||
4. Long press widget → **Edit Widget**
|
||||
5. Enter URL ending with `/status`
|
||||
|
||||
### Notes
|
||||
|
||||
- Must use HTTPS (except local IPs)
|
||||
- Max refresh rate: 30 minutes (iOS limit)
|
||||
- Add multiple widgets for multiple servers
|
||||
|
||||
## Android Widget
|
||||
|
||||
### Setup
|
||||
|
||||
1. Long press home screen → **Widgets**
|
||||
2. Find "ServerBox" → Add to home screen
|
||||
3. Note the widget ID number displayed
|
||||
4. Open ServerBox app → Settings
|
||||
5. Tap **Config home widget link**
|
||||
6. Add entry: `Widget ID` = `Status URL`
|
||||
|
||||
Example:
|
||||
- Key: `17`
|
||||
- Value: `https://my-server.com/status`
|
||||
|
||||
7. Tap widget on home screen to refresh
|
||||
|
||||
## watchOS Widget
|
||||
|
||||
### Setup
|
||||
|
||||
1. Open iPhone app → Settings
|
||||
2. **iOS Settings** → **Watch app**
|
||||
3. Tap **Add URL**
|
||||
4. Enter URL ending with `/status`
|
||||
5. Wait for watch app to sync
|
||||
|
||||
### Notes
|
||||
|
||||
- Try restarting watch app if not updating
|
||||
- Verify phone and watch are connected
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Widget Not Updating
|
||||
|
||||
**iOS:** Wait up to 30 minutes, then remove and re-add
|
||||
**Android:** Tap widget to force refresh, verify ID in settings
|
||||
**watchOS:** Restart watch app, wait a few minutes
|
||||
|
||||
### Widget Shows Error
|
||||
|
||||
- Verify ServerBox Monitor is running
|
||||
- Test URL in browser
|
||||
- Check URL ends with `/status`
|
||||
|
||||
## Security
|
||||
|
||||
- **Always use HTTPS** when possible
|
||||
- **Local IPs only** on trusted networks
|
||||
124
docs/src/content/docs/configuration/appearance.md
Normal file
124
docs/src/content/docs/configuration/appearance.md
Normal file
@@ -0,0 +1,124 @@
|
||||
---
|
||||
title: Appearance
|
||||
description: Customize the look and feel
|
||||
---
|
||||
|
||||
Flutter Server Box offers extensive appearance customization.
|
||||
|
||||
## Themes
|
||||
|
||||
### Light Theme
|
||||
|
||||
Clean, bright interface for daytime use.
|
||||
|
||||
### Dark Theme
|
||||
|
||||
Easy on the eyes for low-light environments.
|
||||
|
||||
### AMOLED Dark
|
||||
|
||||
Pure black background for OLED screens, saves battery.
|
||||
|
||||
### System Theme
|
||||
|
||||
Automatically switches between light and dark based on system settings.
|
||||
|
||||
Set in: **Settings > Appearance > Theme**
|
||||
|
||||
## Terminal Appearance
|
||||
|
||||
### Customization Options
|
||||
|
||||
- **Font Size**: Adjust text size in terminal
|
||||
- **Font Family**: Choose from available fonts
|
||||
- **Text Color**: Customize text color
|
||||
- **Background Color**: Set terminal background
|
||||
- **Background Opacity**: Adjust transparency
|
||||
- **Blur Effect**: Enable background blur
|
||||
|
||||
### Terminal Themes
|
||||
|
||||
Create terminal color themes:
|
||||
|
||||
1. Go to Settings > Terminal > Themes
|
||||
2. Create new theme or edit existing
|
||||
3. Customize colors:
|
||||
- Text color
|
||||
- Background color
|
||||
- Cursor color
|
||||
- Selection color
|
||||
|
||||
## Server Cards
|
||||
|
||||
### Card Style
|
||||
|
||||
- **Compact**: Minimal information per card
|
||||
- **Detailed**: Extended information per card
|
||||
- **Custom**: Choose which metrics to show
|
||||
|
||||
### Card Order
|
||||
|
||||
1. Go to Settings > Server Card Order
|
||||
2. Drag cards to reorder
|
||||
3. Changes apply immediately
|
||||
|
||||
### Card Metrics
|
||||
|
||||
Enable/disable metrics:
|
||||
- CPU
|
||||
- Memory
|
||||
- Disk
|
||||
- Network
|
||||
- GPU
|
||||
- Temperature
|
||||
|
||||
## Charts and Graphs
|
||||
|
||||
### Chart Style
|
||||
|
||||
- **Line**: Continuous line chart
|
||||
- **Area**: Filled area chart
|
||||
- **Bar**: Bar chart visualization
|
||||
|
||||
### Chart Colors
|
||||
|
||||
Customize chart colors in:
|
||||
**Settings > Charts > Colors**
|
||||
|
||||
### Refresh Rate
|
||||
|
||||
Adjust how often charts update:
|
||||
**Settings > Charts > Refresh Rate**
|
||||
|
||||
- **Power Saving**: 5 seconds
|
||||
- **Normal**: 2 seconds
|
||||
- **High Performance**: 1 second
|
||||
|
||||
## Layout
|
||||
|
||||
### Navigation Style
|
||||
|
||||
- **Bottom Navigation**: Mobile-style bottom tabs
|
||||
- **Side Navigation**: Desktop-style sidebar
|
||||
- **Tabs**: Classic tab interface
|
||||
|
||||
### View Mode
|
||||
|
||||
- **List**: Vertical list view
|
||||
- **Grid**: Grid layout for servers
|
||||
- **Cards**: Card-based layout
|
||||
|
||||
## Icons and Symbols
|
||||
|
||||
Choose icon style:
|
||||
- **Filled**: Solid icons
|
||||
- **Outlined**: Line icons
|
||||
- **Rounded**: Soft, rounded icons
|
||||
|
||||
## Animations
|
||||
|
||||
Control animation speed:
|
||||
- **Off**: No animations
|
||||
- **Reduced**: Minimal animations
|
||||
- **Normal**: Standard animations
|
||||
- **Enhanced**: Extra animations
|
||||
80
docs/src/content/docs/configuration/backup.md
Normal file
80
docs/src/content/docs/configuration/backup.md
Normal file
@@ -0,0 +1,80 @@
|
||||
---
|
||||
title: Backup & Restore
|
||||
description: Backup and restore your app data
|
||||
---
|
||||
|
||||
Protect your server configurations and settings with built-in backup functionality.
|
||||
|
||||
## What Gets Backed Up
|
||||
|
||||
- **Server Configurations**: All saved servers
|
||||
- **SSH Keys**: Imported private keys (encrypted)
|
||||
- **Snippets**: Saved command snippets
|
||||
- **Settings**: App preferences
|
||||
|
||||
**Not included:** Passwords (for security)
|
||||
|
||||
## Creating Backups
|
||||
|
||||
### Manual Backup
|
||||
|
||||
1. Settings → Backup
|
||||
2. Tap **Create Backup**
|
||||
3. Choose location
|
||||
4. Backup saved with timestamp
|
||||
|
||||
### Auto Backup
|
||||
|
||||
Settings → Backup → Auto Backup:
|
||||
- Daily / Weekly / Monthly / Off
|
||||
|
||||
### Cloud Sync
|
||||
|
||||
- **iOS/macOS**: iCloud automatic backup
|
||||
- **Android**: Google Drive integration
|
||||
|
||||
## Restoring
|
||||
|
||||
### From Local Backup
|
||||
|
||||
1. Settings → Backup → Restore Backup
|
||||
2. Select backup file
|
||||
3. Authenticate (biometric/password)
|
||||
4. Confirm restore
|
||||
|
||||
### From Cloud
|
||||
|
||||
1. Sign in to same cloud account
|
||||
2. Settings → Backup → Restore from Cloud
|
||||
3. Select backup from list
|
||||
4. Authenticate and confirm
|
||||
|
||||
## Important Notes
|
||||
|
||||
### Passwords Not Backed Up
|
||||
|
||||
After restore, you'll need to re-enter passwords for each server.
|
||||
|
||||
**Tip:** Use SSH keys instead - they ARE backed up.
|
||||
|
||||
### Cross-Platform
|
||||
|
||||
Backups work across all platforms (iOS ↔ Android ↔ Desktop).
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Enable auto backup** for peace of mind
|
||||
2. **Test restore** periodically to verify backups work
|
||||
3. **Backup before** updating app or switching devices
|
||||
4. **Use SSH keys** to avoid re-entering passwords
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Restore failed:**
|
||||
- Check backup file integrity
|
||||
- Ensure sufficient storage
|
||||
- Verify app version compatibility
|
||||
|
||||
**Missing data after restore:**
|
||||
- Passwords are not backed up (re-enter them)
|
||||
- Check selective restore settings
|
||||
88
docs/src/content/docs/configuration/jump-server.md
Normal file
88
docs/src/content/docs/configuration/jump-server.md
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: Jump Server
|
||||
description: Route connections through intermediate servers
|
||||
---
|
||||
|
||||
Connect to servers behind firewalls or in private networks by routing through an intermediate jump server.
|
||||
|
||||
## What is Jump Server?
|
||||
|
||||
A jump server acts as a gateway to access other servers that:
|
||||
- Are behind firewalls
|
||||
- Don't have direct SSH access
|
||||
- Are in private networks
|
||||
- Require multi-hop connections
|
||||
|
||||
## Setup
|
||||
|
||||
### Step 1: Configure Jump Server
|
||||
|
||||
Add the jump server as a normal server first:
|
||||
1. Add server with SSH credentials
|
||||
2. Test connection to ensure it works
|
||||
3. This server will be your jump host
|
||||
|
||||
### Step 2: Configure Target Servers
|
||||
|
||||
For each server you want to access via jump:
|
||||
1. Add target server (credentials for target, not jump)
|
||||
2. Server settings → Jump Server
|
||||
3. Select your jump server from list
|
||||
4. Save
|
||||
|
||||
### Step 3: Connect
|
||||
|
||||
Connect to target server normally. The app automatically:
|
||||
1. Connects to jump server
|
||||
2. Tunnels through to target server
|
||||
3. Maintains connection
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Private Network Access
|
||||
|
||||
```
|
||||
Your Device → Jump Server (public IP) → Private Server (10.0.0.x)
|
||||
```
|
||||
|
||||
### Behind Firewall
|
||||
|
||||
```
|
||||
Your Device → Bastion Host → Internal Server
|
||||
```
|
||||
|
||||
### Multi-Hop
|
||||
|
||||
You can chain multiple jump servers for complex networks.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Jump server must be accessible from your device
|
||||
- Jump server must be able to reach target servers
|
||||
- SSH keys recommended for jump server (faster authentication)
|
||||
|
||||
## Tips
|
||||
|
||||
- **Use SSH keys** on jump server for faster connections
|
||||
- **Test direct access** to jump server first
|
||||
- **Check firewall rules** on both ends
|
||||
- **Monitor connection** - issues could be on jump or target
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connection Times Out
|
||||
|
||||
- Verify jump server is accessible
|
||||
- Check jump server can reach target
|
||||
- Test manually: `ssh -J jump@jump-server user@target-server`
|
||||
|
||||
### Authentication Fails
|
||||
|
||||
- Verify credentials for target server (not jump)
|
||||
- Check SSH keys if using key authentication
|
||||
|
||||
### Slow Connection
|
||||
|
||||
- Normal for jump connections (extra hop)
|
||||
- Consider using SSH keys for faster auth
|
||||
- Check network latency to jump server
|
||||
93
docs/src/content/docs/configuration/localizations.md
Normal file
93
docs/src/content/docs/configuration/localizations.md
Normal file
@@ -0,0 +1,93 @@
|
||||
---
|
||||
title: Localizations
|
||||
description: Language and region settings
|
||||
---
|
||||
|
||||
Flutter Server Box supports 12+ languages with full localization.
|
||||
|
||||
## Supported Languages
|
||||
|
||||
| Language | Status |
|
||||
|----------|--------|
|
||||
| English (en) | ✅ Native |
|
||||
| 简体中文 (zh) | ✅ Native |
|
||||
| 繁體中文 (zh-Hant) | ✅ Native |
|
||||
| Deutsch (de) | ✅ Native |
|
||||
| Français (fr) | ✅ Native |
|
||||
| Español (es) | ✅ Native |
|
||||
| Português (pt) | ✅ Native |
|
||||
| Русский (ru) | ✅ Native |
|
||||
| Türkçe (tr) | ✅ Native |
|
||||
| Українська (uk) | ✅ Native |
|
||||
| Bahasa Indonesia (id) | ✅ Native |
|
||||
| Nederlands (nl) | ✅ Native |
|
||||
| 日本語 (ja) | ✅ AI-translated |
|
||||
|
||||
## Changing Language
|
||||
|
||||
1. Go to **Settings > Language**
|
||||
2. Select preferred language
|
||||
3. App restarts to apply changes
|
||||
|
||||
## Number Formatting
|
||||
|
||||
Numbers are formatted according to locale:
|
||||
|
||||
- **Thousands separator**: Comma vs period
|
||||
- **Decimal separator**: Period vs comma
|
||||
- **Date format**: Locale-specific
|
||||
|
||||
## Time Format
|
||||
|
||||
Choose between:
|
||||
|
||||
- **24-hour**: 13:00, 14:30
|
||||
- **12-hour**: 1:00 PM, 2:30 PM
|
||||
|
||||
Set in: **Settings > Time Format**
|
||||
|
||||
## Contributing Translations
|
||||
|
||||
We welcome community translations!
|
||||
|
||||
### Translation Files
|
||||
|
||||
Located in `lib/l10n/`:
|
||||
|
||||
- `app_en.arb` - English (reference)
|
||||
- `app_zh.arb` - Simplified Chinese
|
||||
- etc.
|
||||
|
||||
### How to Contribute
|
||||
|
||||
1. Fork the repository
|
||||
2. Copy `app_en.arb` to `app_YOUR_LOCALE.arb`
|
||||
3. Translate values (keep keys the same)
|
||||
4. Test your translations
|
||||
5. Submit pull request
|
||||
|
||||
### Translation Guidelines
|
||||
|
||||
- Keep technical terms consistent
|
||||
- Use formal address for professional tone
|
||||
- Maintain placeholder format: `{variable}`
|
||||
- Test UI with translated strings
|
||||
|
||||
## Adding New Language
|
||||
|
||||
1. Create new ARB file: `app_xx.arb`
|
||||
2. Copy all keys from `app_en.arb`
|
||||
3. Translate all values
|
||||
4. Add to `l10n.yaml` configuration
|
||||
5. Run `flutter gen-l10n`
|
||||
6. Test with new locale
|
||||
|
||||
## RTL Languages
|
||||
|
||||
Right-to-left languages (Arabic, Hebrew) are partially supported. Full RTL layout support is planned for future releases.
|
||||
|
||||
## Quality Notes
|
||||
|
||||
- Some languages are AI-translated and may contain errors
|
||||
- Native speaker reviews are appreciated
|
||||
- Report translation issues via GitHub
|
||||
90
docs/src/content/docs/configuration/server.md
Normal file
90
docs/src/content/docs/configuration/server.md
Normal file
@@ -0,0 +1,90 @@
|
||||
---
|
||||
title: Server Setup
|
||||
description: Configure and manage server connections
|
||||
---
|
||||
|
||||
## Adding a Server
|
||||
|
||||
1. Tap the **+** button on the main screen
|
||||
2. Fill in connection details:
|
||||
- **Name**: Friendly name for identification
|
||||
- **Host**: IP address or domain name
|
||||
- **Port**: SSH port (default: 22)
|
||||
- **Username**: SSH login user
|
||||
- **Authentication**: Password or SSH key
|
||||
|
||||
3. Configure optional settings:
|
||||
- **Initial Directory**: Starting directory for terminal/SFTP
|
||||
- **Environment**: Custom environment variables
|
||||
- **Keep-alive Interval**: Connection keep-alive setting
|
||||
|
||||
4. Tap **Save**
|
||||
|
||||
## Connection Types
|
||||
|
||||
### Password Authentication
|
||||
|
||||
Simple username/password authentication:
|
||||
|
||||
- Enter password in the password field
|
||||
- Password is stored securely (encrypted)
|
||||
- Requires re-entry on app restart (unless saved)
|
||||
|
||||
### SSH Key Authentication
|
||||
|
||||
More secure, passwordless authentication:
|
||||
|
||||
1. Generate or import SSH key
|
||||
2. Add key to server's `~/.ssh/authorized_keys`
|
||||
3. Select key in app when adding server
|
||||
|
||||
See [SSH Keys](/configuration/ssh-keys/) for detailed setup.
|
||||
|
||||
## Server Groups
|
||||
|
||||
Organize servers into groups for easier management:
|
||||
|
||||
1. Go to Settings > Server Groups
|
||||
2. Create a new group
|
||||
3. Assign servers to groups
|
||||
4. Groups appear as sections in main view
|
||||
|
||||
## Server Cards
|
||||
|
||||
Customize what information appears on server cards:
|
||||
|
||||
1. Go to Settings > Server Card Settings
|
||||
2. Enable/disable metrics:
|
||||
- CPU
|
||||
- Memory
|
||||
- Disk
|
||||
- Network
|
||||
3. Reorder cards by dragging
|
||||
|
||||
## Connection Profiles
|
||||
|
||||
Save connection profiles for different use cases:
|
||||
|
||||
- **Default Profile**: Standard settings
|
||||
- **Low Bandwidth**: Reduced refresh rate
|
||||
- **High Performance**: Maximum refresh rate
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connection Refused
|
||||
|
||||
- Check server is running
|
||||
- Verify SSH port
|
||||
- Check firewall rules
|
||||
|
||||
### Authentication Failed
|
||||
|
||||
- Verify username/password
|
||||
- Check SSH key permissions
|
||||
- Ensure SSH service is running
|
||||
|
||||
### Timeout
|
||||
|
||||
- Check network connectivity
|
||||
- Increase timeout in settings
|
||||
- Try different network
|
||||
118
docs/src/content/docs/configuration/sftp.md
Normal file
118
docs/src/content/docs/configuration/sftp.md
Normal file
@@ -0,0 +1,118 @@
|
||||
---
|
||||
title: SFTP File Browser
|
||||
description: File transfer and management via SFTP
|
||||
---
|
||||
|
||||
Browse, edit, and transfer files on your servers with a built-in SFTP client.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Opening SFTP
|
||||
|
||||
1. Connect to server
|
||||
2. Tap **Files** button on server page
|
||||
3. Or from terminal: Tap **SFTP** button
|
||||
|
||||
### Navigation
|
||||
|
||||
- **Tap folder**: Enter directory
|
||||
- **Tap file**: View/Edit/Download options
|
||||
- **Back button**: Previous directory
|
||||
- **Home button**: User's home directory
|
||||
- **Goto button**: Jump to path with autocomplete
|
||||
|
||||
## File Operations
|
||||
|
||||
### Common Actions
|
||||
|
||||
| Action | How |
|
||||
|--------|-----|
|
||||
| **Download** | Long-press file → Download |
|
||||
| **Upload** | Folder icon → Select file |
|
||||
| **Rename** | Long-press → Rename |
|
||||
| **Delete** | Long-press → Delete |
|
||||
| **Copy/Move** | Long-press → Select → Choose destination |
|
||||
| **Permissions** | Tap file info → Edit permissions |
|
||||
|
||||
### Permission Editor
|
||||
|
||||
Unix permissions editor:
|
||||
|
||||
- **3x3 Grid**: User/Group/Other × Read/Write/Execute
|
||||
- **Numeric**: Direct input (755, 644, etc.)
|
||||
- **Symbolic**: rwxr-xr-x format
|
||||
|
||||
### Edit Files
|
||||
|
||||
1. Tap file → Edit
|
||||
2. Edit in built-in editor
|
||||
3. Save → Upload back to server
|
||||
|
||||
**Size limit:** Files up to 1 MB. For larger files, use the terminal with vim/nano instead.
|
||||
|
||||
**Editor settings:** Settings → SFTP Editor
|
||||
- Preferred editor (vim, nano, etc.)
|
||||
- Close after save
|
||||
- Soft wrap
|
||||
- Syntax highlighting
|
||||
|
||||
## Display Settings
|
||||
|
||||
### Sort Order
|
||||
|
||||
Settings → Sort By:
|
||||
- Name (alphabetical)
|
||||
- Size (largest first)
|
||||
- Time (newest first)
|
||||
|
||||
### Folders First
|
||||
|
||||
Show directories before files:
|
||||
Settings → Folders First
|
||||
|
||||
### Hidden Files
|
||||
|
||||
Show dotfiles (`.git`, `.bashrc`, etc.):
|
||||
Settings → Show Hidden Files
|
||||
|
||||
## Archive Support
|
||||
|
||||
Extract common archive formats directly on your server.
|
||||
|
||||
| Format | Variants | Command Required |
|
||||
|--------|----------|------------------|
|
||||
| .tar.gz | .tgz, .tar.Z | tar |
|
||||
| .tar.bz2 | .tbz2, .tar.bz2 | tar |
|
||||
| .tar.xz | .txz | tar |
|
||||
| .zip | .zipx | unzip |
|
||||
| .7z | - | 7z |
|
||||
| .rar | - | unrar |
|
||||
|
||||
**Note:** The corresponding command (`tar`, `unzip`, `7z`, `unrar`) must be installed on your server. These tools handle many sub-formats not listed above.
|
||||
|
||||
## Quick Access
|
||||
|
||||
### From Terminal
|
||||
|
||||
Tap **SFTP** button to open current terminal directory in file browser.
|
||||
|
||||
### Remember Last Path
|
||||
|
||||
Automatically return to last visited directory:
|
||||
Settings → SFTP Open Last Path
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Permission Denied
|
||||
|
||||
- Check user has read access to directory
|
||||
- Verify directory permissions: `ls -la`
|
||||
- Ensure SFTP is enabled in sshd_config
|
||||
|
||||
### Slow Listing
|
||||
|
||||
Large directories (1000+ items) use pagination for performance.
|
||||
|
||||
### Can't Edit File
|
||||
|
||||
File larger than 1 MB? Use terminal with vim/nano instead.
|
||||
127
docs/src/content/docs/configuration/terminal.md
Normal file
127
docs/src/content/docs/configuration/terminal.md
Normal file
@@ -0,0 +1,127 @@
|
||||
---
|
||||
title: Terminal & SSH
|
||||
description: SSH terminal setup and configuration
|
||||
---
|
||||
|
||||
Complete SSH terminal access with full keyboard support and customizable interface.
|
||||
|
||||
## Basic Setup
|
||||
|
||||
### First Connection
|
||||
|
||||
1. Add server with SSH credentials
|
||||
2. Tap server card to connect
|
||||
3. Accept host key fingerprint (first time only)
|
||||
4. Terminal opens automatically
|
||||
|
||||
### Virtual Keyboard (Mobile)
|
||||
|
||||
Customizable virtual keyboard for terminal access:
|
||||
|
||||
| Button | Function |
|
||||
|--------|----------|
|
||||
| **Ctrl, Alt, Shift** | Modifier keys (tap before other key) |
|
||||
| **Esc, Tab** | Special characters |
|
||||
| **Arrows** | Navigation |
|
||||
| **F1-F12** | Function keys |
|
||||
| **SFTP** | Open current directory in file browser |
|
||||
| **Clipboard** | Copy selection / Paste clipboard |
|
||||
| **Snippets** | Quick command execution |
|
||||
|
||||
**Customize keyboard:** Settings → SSH Virtual Keys
|
||||
- Enable/disable keys
|
||||
- Reorder layout
|
||||
- Add/remove buttons
|
||||
|
||||
## Terminal Settings
|
||||
|
||||
### Appearance
|
||||
|
||||
**Font Size:** Settings → Terminal Font Size
|
||||
- Affects all new sessions
|
||||
- Typical range: 8-24 pixels
|
||||
|
||||
**Colors:** Settings → Terminal Color
|
||||
- Text color
|
||||
- Background color & opacity
|
||||
- Blur effect (iOS/macOS)
|
||||
- Cursor color
|
||||
|
||||
### Keyboard Type
|
||||
|
||||
If you can't input certain characters:
|
||||
|
||||
1. Settings → Keyboard Type
|
||||
2. Switch to `visiblePassword`
|
||||
3. Note: CJK input may not work after this change
|
||||
|
||||
## Connection Management
|
||||
|
||||
### Multi-Tab
|
||||
|
||||
- **Desktop**: Ctrl+T (new), Ctrl+W (close)
|
||||
- **Mobile**: Tap + button
|
||||
- Sessions persist between app launches
|
||||
|
||||
### Auto-Connect
|
||||
|
||||
Set server to auto-connect on app open:
|
||||
1. Server settings → Auto-Connect
|
||||
2. Enable toggle
|
||||
|
||||
### Jump Server
|
||||
|
||||
Route through intermediate server:
|
||||
|
||||
1. Add and configure jump server first
|
||||
2. Target server settings → Select jump server
|
||||
3. Connection routes through jump server automatically
|
||||
|
||||
## SSH Keys (Recommended)
|
||||
|
||||
More secure than passwords:
|
||||
|
||||
1. Generate key: Settings → Private Keys → Add
|
||||
2. Upload public key to server: `ssh-copy-id -i pubkey user@server`
|
||||
3. Server settings → Use key instead of password
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Can't Connect
|
||||
|
||||
**Timeout/Refused:**
|
||||
- Verify server is Unix-like (Linux, macOS, Android/Termux)
|
||||
- Check firewall allows SSH port (default 22)
|
||||
- Test manually: `ssh user@server -p port`
|
||||
|
||||
**Auth Failed:**
|
||||
- Verify username and password
|
||||
- Check SSH key is uploaded correctly
|
||||
- Ensure account is not locked
|
||||
|
||||
### Terminal Disconnects
|
||||
|
||||
**Frequent disconnections:**
|
||||
|
||||
1. Check server keep-alive settings:
|
||||
```bash
|
||||
# /etc/ssh/sshd_config
|
||||
ClientAliveInterval 60
|
||||
ClientAliveCountMax 3
|
||||
```
|
||||
|
||||
2. Disable battery optimization:
|
||||
- **MIUI**: Battery → "No limits"
|
||||
- **Android**: Settings → Apps → Disable optimization
|
||||
- **iOS**: Enable background refresh
|
||||
|
||||
### Can't Input Characters
|
||||
|
||||
Change keyboard type to `visiblePassword` in settings.
|
||||
|
||||
## Tips
|
||||
|
||||
- **Test connection** first with regular SSH client
|
||||
- **Use SSH keys** instead of passwords for security
|
||||
- **Save snippets** for frequently used commands
|
||||
- **Pinch to zoom** for temporary font size change (mobile)
|
||||
86
docs/src/content/docs/development/architecture.md
Normal file
86
docs/src/content/docs/development/architecture.md
Normal file
@@ -0,0 +1,86 @@
|
||||
---
|
||||
title: Architecture
|
||||
description: Architecture patterns and design decisions
|
||||
---
|
||||
|
||||
Flutter Server Box follows clean architecture principles with clear separation between data, domain, and presentation layers.
|
||||
|
||||
## Layered Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Presentation Layer │
|
||||
│ (lib/view/page/) │
|
||||
│ - Pages, Widgets, Controllers │
|
||||
└─────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────┐
|
||||
│ Business Logic Layer │
|
||||
│ (lib/data/provider/) │
|
||||
│ - Riverpod Providers │
|
||||
│ - State Management │
|
||||
└─────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────┐
|
||||
│ Data Layer │
|
||||
│ (lib/data/model/, store/) │
|
||||
│ - Models, Storage, Services │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Key Patterns
|
||||
|
||||
### State Management: Riverpod
|
||||
|
||||
- **Code Generation**: Uses `riverpod_generator` for type-safe providers
|
||||
- **State Notifiers**: For mutable state with business logic
|
||||
- **Async Notifiers**: For loading and error states
|
||||
- **Stream Providers**: For real-time data
|
||||
|
||||
### Immutable Models: Freezed
|
||||
|
||||
- All data models use Freezed for immutability
|
||||
- Union types for state representation
|
||||
- Built-in JSON serialization
|
||||
- CopyWith extensions for updates
|
||||
|
||||
### Local Storage: Hive
|
||||
|
||||
- **hive_ce**: Community edition of Hive
|
||||
- No manual `@HiveField` or `@HiveType` needed
|
||||
- Type adapters auto-generated
|
||||
- Persistent key-value storage
|
||||
|
||||
## Dependency Injection
|
||||
|
||||
Services and stores are injected via:
|
||||
|
||||
1. **Providers**: Expose dependencies to UI
|
||||
2. **GetIt**: Service location (where applicable)
|
||||
3. **Constructor Injection**: Explicit dependencies
|
||||
|
||||
## Data Flow
|
||||
|
||||
```
|
||||
User Action → Widget → Provider → Service/Store → Model Update → UI Rebuild
|
||||
```
|
||||
|
||||
1. User interacts with widget
|
||||
2. Widget calls provider method
|
||||
3. Provider updates state via service/store
|
||||
3. State change triggers UI rebuild
|
||||
4. New state reflected in widget
|
||||
|
||||
## Custom Dependencies
|
||||
|
||||
The project uses several custom forks to extend functionality:
|
||||
|
||||
- **dartssh2**: Enhanced SSH features
|
||||
- **xterm**: Terminal emulator with mobile support
|
||||
- **fl_lib**: Shared UI components and utilities
|
||||
|
||||
## Threading
|
||||
|
||||
- **Isolates**: Heavy computation off main thread
|
||||
- **computer package**: Multi-threading utilities
|
||||
- **Async/Await**: Non-blocking I/O operations
|
||||
116
docs/src/content/docs/development/building.md
Normal file
116
docs/src/content/docs/development/building.md
Normal file
@@ -0,0 +1,116 @@
|
||||
---
|
||||
title: Building
|
||||
description: Build instructions for different platforms
|
||||
---
|
||||
|
||||
Flutter Server Box uses a custom build system (`fl_build`) for cross-platform builds.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Flutter SDK (stable channel)
|
||||
- Platform-specific tools (Xcode for iOS, Android Studio for Android)
|
||||
- Rust toolchain (for some native dependencies)
|
||||
|
||||
## Development Build
|
||||
|
||||
```bash
|
||||
# Run in development mode
|
||||
flutter run
|
||||
|
||||
# Run on specific device
|
||||
flutter run -d <device-id>
|
||||
```
|
||||
|
||||
## Production Build
|
||||
|
||||
The project uses `fl_build` for building:
|
||||
|
||||
```bash
|
||||
# Build for specific platform
|
||||
dart run fl_build -p <platform>
|
||||
|
||||
# Available platforms:
|
||||
# - ios
|
||||
# - android
|
||||
# - macos
|
||||
# - linux
|
||||
# - windows
|
||||
```
|
||||
|
||||
## Platform-Specific Builds
|
||||
|
||||
### iOS
|
||||
|
||||
```bash
|
||||
dart run fl_build -p ios
|
||||
```
|
||||
|
||||
Requires:
|
||||
- macOS with Xcode
|
||||
- CocoaPods
|
||||
- Apple Developer account for signing
|
||||
|
||||
### Android
|
||||
|
||||
```bash
|
||||
dart run fl_build -p android
|
||||
```
|
||||
|
||||
Requires:
|
||||
- Android SDK
|
||||
- Java Development Kit
|
||||
- Keystore for signing
|
||||
|
||||
### macOS
|
||||
|
||||
```bash
|
||||
dart run fl_build -p macos
|
||||
```
|
||||
|
||||
### Linux
|
||||
|
||||
```bash
|
||||
dart run fl_build -p linux
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
```bash
|
||||
dart run fl_build -p windows
|
||||
```
|
||||
|
||||
Requires Windows with Visual Studio.
|
||||
|
||||
## Pre/Post Build
|
||||
|
||||
The `make.dart` script handles:
|
||||
|
||||
- Metadata generation
|
||||
- Version string updates
|
||||
- Platform-specific configurations
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Clean Build
|
||||
|
||||
```bash
|
||||
flutter clean
|
||||
dart run build_runner build --delete-conflicting-outputs
|
||||
flutter pub get
|
||||
```
|
||||
|
||||
### Version Mismatch
|
||||
|
||||
Ensure all dependencies are compatible:
|
||||
```bash
|
||||
flutter pub upgrade
|
||||
```
|
||||
|
||||
## Release Checklist
|
||||
|
||||
1. Update version in `pubspec.yaml`
|
||||
2. Run code generation
|
||||
3. Run tests
|
||||
4. Build for all target platforms
|
||||
5. Test on physical devices
|
||||
6. Create GitHub release
|
||||
98
docs/src/content/docs/development/codegen.md
Normal file
98
docs/src/content/docs/development/codegen.md
Normal file
@@ -0,0 +1,98 @@
|
||||
---
|
||||
title: Code Generation
|
||||
description: Using build_runner for code generation
|
||||
---
|
||||
|
||||
Flutter Server Box heavily uses code generation for models, state management, and serialization.
|
||||
|
||||
## When to Run Code Generation
|
||||
|
||||
Run after modifying:
|
||||
|
||||
- Models with `@freezed` annotation
|
||||
- Classes with `@JsonSerializable`
|
||||
- Hive models
|
||||
- Providers with `@riverpod`
|
||||
- Localizations (ARB files)
|
||||
|
||||
## Running Code Generation
|
||||
|
||||
```bash
|
||||
# Generate all code
|
||||
dart run build_runner build --delete-conflicting-outputs
|
||||
|
||||
# Clean and regenerate
|
||||
dart run build_runner build --delete-conflicting-outputs --clean
|
||||
```
|
||||
|
||||
## Generated Files
|
||||
|
||||
### Freezed (`*.freezed.dart`)
|
||||
|
||||
Immutable data models with union types:
|
||||
|
||||
```dart
|
||||
@freezed
|
||||
class ServerState with _$ServerState {
|
||||
const factory ServerState.connected() = Connected;
|
||||
const factory ServerState.disconnected() = Disconnected;
|
||||
const factory ServerState.error(String message) = Error;
|
||||
}
|
||||
```
|
||||
|
||||
### JSON Serialization (`*.g.dart`)
|
||||
|
||||
Generated from `json_serializable`:
|
||||
|
||||
```dart
|
||||
@JsonSerializable()
|
||||
class Server {
|
||||
final String id;
|
||||
final String name;
|
||||
final String host;
|
||||
|
||||
Server({required this.id, required this.name, required this.host});
|
||||
|
||||
factory Server.fromJson(Map<String, dynamic> json) =>
|
||||
_$ServerFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$ServerToJson(this);
|
||||
}
|
||||
```
|
||||
|
||||
### Riverpod Providers (`*.g.dart`)
|
||||
|
||||
Generated from `@riverpod` annotation:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class MyNotifier extends _$MyNotifier {
|
||||
@override
|
||||
int build() => 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Hive Adapters (`*.g.dart`)
|
||||
|
||||
Auto-generated for Hive models (hive_ce):
|
||||
|
||||
```dart
|
||||
@HiveType(typeId: 0)
|
||||
class ServerModel {
|
||||
@HiveField(0)
|
||||
final String id;
|
||||
}
|
||||
```
|
||||
|
||||
## Localization Generation
|
||||
|
||||
```bash
|
||||
flutter gen-l10n
|
||||
```
|
||||
|
||||
Generates `lib/generated/l10n/` from `lib/l10n/*.arb` files.
|
||||
|
||||
## Tips
|
||||
|
||||
- Use `--delete-conflicting-outputs` to avoid conflicts
|
||||
- Add generated files to `.gitignore`
|
||||
- Never manually edit generated files
|
||||
115
docs/src/content/docs/development/state.md
Normal file
115
docs/src/content/docs/development/state.md
Normal file
@@ -0,0 +1,115 @@
|
||||
---
|
||||
title: State Management
|
||||
description: Riverpod-based state management patterns
|
||||
---
|
||||
|
||||
Flutter Server Box uses Riverpod with code generation for state management.
|
||||
|
||||
## Provider Types
|
||||
|
||||
### StateProvider
|
||||
|
||||
Simple state that can be read and written:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class Settings extends _$Settings {
|
||||
@override
|
||||
SettingsModel build() {
|
||||
return SettingsModel.defaults();
|
||||
}
|
||||
|
||||
void update(SettingsModel newSettings) {
|
||||
state = newSettings;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### AsyncNotifierProvider
|
||||
|
||||
State that loads asynchronously with loading/error states:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class ServerStatus extends _$ServerStatus {
|
||||
@override
|
||||
Future<StatusModel> build(Server server) async {
|
||||
return fetchStatus(server);
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
state = const AsyncValue.loading();
|
||||
state = await AsyncValue.guard(() => fetchStatus(server));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### StreamProvider
|
||||
|
||||
Real-time data from streams:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
Stream<CpuUsage> cpuUsage(CpuUsageRef ref, Server server) {
|
||||
return cpuService.monitor(server);
|
||||
}
|
||||
```
|
||||
|
||||
## State Patterns
|
||||
|
||||
### Loading States
|
||||
|
||||
```dart
|
||||
state.when(
|
||||
data: (data) => DataWidget(data),
|
||||
loading: () => LoadingWidget(),
|
||||
error: (error, stack) => ErrorWidget(error),
|
||||
)
|
||||
```
|
||||
|
||||
### Family Providers
|
||||
|
||||
Parameterized providers:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
List<Container> containers(ContainersRef ref, Server server) {
|
||||
return containerService.list(server);
|
||||
}
|
||||
```
|
||||
|
||||
### Auto-Dispose
|
||||
|
||||
Providers that dispose when no longer referenced:
|
||||
|
||||
```dart
|
||||
@Riverpod(keepAlive: false)
|
||||
class TempState extends _$TempState {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use code generation**: Always use `@riverpod` annotation
|
||||
2. **Co-locate providers**: Place near consuming widgets
|
||||
3. **Avoid singletons**: Use providers instead
|
||||
4. **Layer correctly**: Keep UI logic separate from business logic
|
||||
|
||||
## Reading State in Widgets
|
||||
|
||||
```dart
|
||||
class ServerWidget extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final status = ref.watch(serverStatusProvider(server));
|
||||
return status.when(...);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Modifying State
|
||||
|
||||
```dart
|
||||
ref.read(settingsProvider.notifier).update(newSettings);
|
||||
```
|
||||
96
docs/src/content/docs/development/structure.md
Normal file
96
docs/src/content/docs/development/structure.md
Normal file
@@ -0,0 +1,96 @@
|
||||
---
|
||||
title: Project Structure
|
||||
description: Understanding the Flutter Server Box codebase
|
||||
---
|
||||
|
||||
The Flutter Server Box project follows a modular architecture with clear separation of concerns.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
lib/
|
||||
├── core/ # Core utilities and extensions
|
||||
├── data/ # Data layer
|
||||
│ ├── model/ # Data models by feature
|
||||
│ ├── provider/ # Riverpod providers
|
||||
│ └── store/ # Local storage (Hive)
|
||||
├── view/ # UI layer
|
||||
│ ├── page/ # Main pages
|
||||
│ └── widget/ # Reusable widgets
|
||||
├── generated/ # Generated localization
|
||||
├── l10n/ # Localization ARB files
|
||||
└── hive/ # Hive adapters
|
||||
```
|
||||
|
||||
## Core Layer (`lib/core/`)
|
||||
|
||||
Contains utilities, extensions, and routing configuration:
|
||||
|
||||
- **Extensions**: Dart extensions for common types
|
||||
- **Routes**: App routing configuration
|
||||
- **Utils**: Shared utility functions
|
||||
|
||||
## Data Layer (`lib/data/`)
|
||||
|
||||
### Models (`lib/data/model/`)
|
||||
|
||||
Organized by feature:
|
||||
|
||||
- `server/` - Server connection and status models
|
||||
- `container/` - Docker container models
|
||||
- `ssh/` - SSH session models
|
||||
- `sftp/` - SFTP file models
|
||||
- `app/` - App-specific models
|
||||
|
||||
### Providers (`lib/data/provider/`)
|
||||
|
||||
Riverpod providers for dependency injection and state management:
|
||||
|
||||
- Server providers
|
||||
- UI state providers
|
||||
- Service providers
|
||||
|
||||
### Stores (`lib/data/store/`)
|
||||
|
||||
Hive-based local storage:
|
||||
|
||||
- Server storage
|
||||
- Settings storage
|
||||
- Cache storage
|
||||
|
||||
## View Layer (`lib/view/`)
|
||||
|
||||
### Pages (`lib/view/page/`)
|
||||
|
||||
Main application screens:
|
||||
|
||||
- `server/` - Server management pages
|
||||
- `ssh/` - SSH terminal pages
|
||||
- `container/` - Container pages
|
||||
- `setting/` - Settings pages
|
||||
- `storage/` - SFTP pages
|
||||
- `snippet/` - Snippet pages
|
||||
|
||||
### Widgets (`lib/view/widget/`)
|
||||
|
||||
Reusable UI components:
|
||||
|
||||
- Server cards
|
||||
- Status charts
|
||||
- Input components
|
||||
- Dialogs
|
||||
|
||||
## Generated Files
|
||||
|
||||
- `lib/generated/l10n/` - Auto-generated localization
|
||||
- `*.g.dart` - Generated code (json_serializable, freezed, hive, riverpod)
|
||||
- `*.freezed.dart` - Freezed immutable classes
|
||||
|
||||
## Packages Directory (`/packages/`)
|
||||
|
||||
Contains custom forks of dependencies:
|
||||
|
||||
- `dartssh2/` - SSH library
|
||||
- `xterm/` - Terminal emulator
|
||||
- `fl_lib/` - Shared utilities
|
||||
- `fl_build/` - Build system
|
||||
113
docs/src/content/docs/development/testing.md
Normal file
113
docs/src/content/docs/development/testing.md
Normal file
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: Testing
|
||||
description: Testing strategies and running tests
|
||||
---
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
flutter test
|
||||
|
||||
# Run specific test file
|
||||
flutter test test/battery_test.dart
|
||||
|
||||
# Run with coverage
|
||||
flutter test --coverage
|
||||
```
|
||||
|
||||
## Test Structure
|
||||
|
||||
Tests are located in the `test/` directory mirroring the lib structure:
|
||||
|
||||
```
|
||||
test/
|
||||
├── data/
|
||||
│ ├── model/
|
||||
│ └── provider/
|
||||
├── view/
|
||||
│ └── widget/
|
||||
└── test_helpers.dart
|
||||
```
|
||||
|
||||
## Unit Tests
|
||||
|
||||
Test business logic and data models:
|
||||
|
||||
```dart
|
||||
test('should calculate CPU percentage', () {
|
||||
final cpu = CpuModel(usage: 75.0);
|
||||
expect(cpu.usagePercentage, '75%');
|
||||
});
|
||||
```
|
||||
|
||||
## Widget Tests
|
||||
|
||||
Test UI components:
|
||||
|
||||
```dart
|
||||
testWidgets('ServerCard displays server name', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
child: MaterialApp(
|
||||
home: ServerCard(server: testServer),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.text('Test Server'), findsOneWidget);
|
||||
});
|
||||
```
|
||||
|
||||
## Provider Tests
|
||||
|
||||
Test Riverpod providers:
|
||||
|
||||
```dart
|
||||
test('serverStatusProvider returns status', () async {
|
||||
final container = ProviderContainer();
|
||||
final status = await container.read(serverStatusProvider(testServer).future);
|
||||
expect(status, isA<StatusModel>());
|
||||
});
|
||||
```
|
||||
|
||||
## Mocking
|
||||
|
||||
Use mocks for external dependencies:
|
||||
|
||||
```dart
|
||||
class MockSshService extends Mock implements SshService {}
|
||||
|
||||
test('connects to server', () async {
|
||||
final mockSsh = MockSshService();
|
||||
when(mockSsh.connect(any)).thenAnswer((_) async => true);
|
||||
|
||||
// Test with mock
|
||||
});
|
||||
```
|
||||
|
||||
## Integration Tests
|
||||
|
||||
Test complete user flows (in `integration_test/`):
|
||||
|
||||
```dart
|
||||
testWidgets('add server flow', (tester) async {
|
||||
await tester.pumpWidget(MyApp());
|
||||
|
||||
// Tap add button
|
||||
await tester.tap(find.byIcon(Icons.add));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Fill form
|
||||
await tester.enterText(find.byKey(Key('name')), 'Test Server');
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Arrange-Act-Assert**: Structure tests clearly
|
||||
2. **Descriptive names**: Test names should describe behavior
|
||||
3. **One assertion per test**: Keep tests focused
|
||||
4. **Mock external deps**: Don't depend on real servers
|
||||
5. **Test edge cases**: Empty lists, null values, etc.
|
||||
55
docs/src/content/docs/features/docker.md
Normal file
55
docs/src/content/docs/features/docker.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: Docker Management
|
||||
description: Monitor and manage Docker containers
|
||||
---
|
||||
|
||||
Flutter Server Box provides an intuitive interface for managing Docker containers on your servers.
|
||||
|
||||
## Features
|
||||
|
||||
### Container List
|
||||
|
||||
- View all containers (running and stopped)
|
||||
- Container ID and name display
|
||||
- Image information
|
||||
- Status indicators
|
||||
- Creation time
|
||||
|
||||
### Container Actions
|
||||
|
||||
- **Start**: Launch stopped containers
|
||||
- **Stop**: Gracefully stop running containers
|
||||
- **Restart**: Restart containers
|
||||
- **Remove**: Delete containers
|
||||
- **View Logs**: Check container logs
|
||||
- **Inspect**: View container details
|
||||
|
||||
### Container Details
|
||||
|
||||
- Environment variables
|
||||
- Port mappings
|
||||
- Volume mounts
|
||||
- Network configuration
|
||||
- Resource usage
|
||||
|
||||
## Requirements
|
||||
|
||||
- Docker must be installed on your server
|
||||
- SSH user must have Docker permissions
|
||||
- For non-root users, add to docker group:
|
||||
```bash
|
||||
sudo usermod -aG docker your_username
|
||||
```
|
||||
|
||||
## Quick Actions
|
||||
|
||||
- Single tap: View container details
|
||||
- Long press: Quick action menu
|
||||
- Swipe: Quick start/stop
|
||||
- Bulk select: Multiple container operations
|
||||
|
||||
## Tips
|
||||
|
||||
- Use **auto-refresh** to monitor container status changes
|
||||
- Filter by running/stopped containers
|
||||
- Search containers by name or ID
|
||||
73
docs/src/content/docs/features/monitoring.md
Normal file
73
docs/src/content/docs/features/monitoring.md
Normal file
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: Server Monitoring
|
||||
description: Real-time server status monitoring with beautiful charts
|
||||
---
|
||||
|
||||
Flutter Server Box provides comprehensive real-time monitoring of your server's health and performance.
|
||||
|
||||
## Status Cards
|
||||
|
||||
The server detail page displays configurable status cards for different system metrics. You can enable/disable cards in settings.
|
||||
|
||||
### CPU Monitoring
|
||||
|
||||
- Real-time CPU usage percentage
|
||||
- Per-core CPU usage breakdown
|
||||
- Historical usage charts
|
||||
- CPU frequency information
|
||||
|
||||
### Memory Monitoring
|
||||
|
||||
- **RAM Usage**: Used vs total memory with percentage
|
||||
- **Swap Usage**: Swap memory utilization
|
||||
- Memory pressure indicators
|
||||
- Historical memory trends
|
||||
|
||||
### Disk Monitoring
|
||||
|
||||
- Mount point usage with percentage
|
||||
- Total, used, and free space
|
||||
- I/O statistics
|
||||
- Multiple disk support
|
||||
|
||||
### Network Monitoring
|
||||
|
||||
- Real-time upload/download speeds
|
||||
- Bandwidth usage charts
|
||||
- Network interface statistics
|
||||
- Total data transferred
|
||||
|
||||
### Advanced Metrics
|
||||
|
||||
- **GPU Status**: NVIDIA and AMD GPU monitoring
|
||||
- **Temperature**: CPU, GPU, and system temperatures
|
||||
- **Sensors**: Fan speeds, voltages, and other sensor data
|
||||
- **S.M.A.R.T**: Disk health monitoring
|
||||
- **Battery**: UPS or battery status (if available)
|
||||
|
||||
## Customizing Display
|
||||
|
||||
### Reordering Cards
|
||||
|
||||
1. Go to Settings
|
||||
2. Select Server Settings
|
||||
3. Drag cards to reorder them on the server detail page
|
||||
|
||||
### Enabling/Disabling Cards
|
||||
|
||||
1. Open a server's detail page
|
||||
2. Tap the edit/menu button
|
||||
3. Toggle individual cards on or off
|
||||
|
||||
## Auto-Refresh
|
||||
|
||||
- Status cards automatically refresh
|
||||
- Refresh interval is configurable in settings
|
||||
- Manual refresh available with pull-to-refresh gesture
|
||||
|
||||
## Charts and Visualizations
|
||||
|
||||
- **Line Charts**: Historical data trends
|
||||
- **Gauge Charts**: Current usage percentage
|
||||
- **Color Coding**: Visual indicators for status levels
|
||||
- **Zoom**: Pinch to zoom on charts for detailed views
|
||||
67
docs/src/content/docs/features/network.md
Normal file
67
docs/src/content/docs/features/network.md
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Network Tools
|
||||
description: Network testing and diagnostic tools
|
||||
---
|
||||
|
||||
Flutter Server Box includes several network tools for testing and diagnostics.
|
||||
|
||||
## iPerf
|
||||
|
||||
Perform network speed tests between your device and server.
|
||||
|
||||
### Features
|
||||
|
||||
- **Upload/Download Speed**: Test bandwidth
|
||||
- **Server Mode**: Use server as iPerf server
|
||||
- **Client Mode**: Connect to iPerf servers
|
||||
- **Custom Parameters**: Duration, parallel streams, etc.
|
||||
|
||||
### Usage
|
||||
|
||||
1. Open a server
|
||||
2. Tap **iPerf**
|
||||
3. Choose server or client mode
|
||||
4. Configure parameters
|
||||
5. Start test
|
||||
|
||||
## Ping
|
||||
|
||||
Test network connectivity and latency.
|
||||
|
||||
### Features
|
||||
|
||||
- **ICMP Ping**: Standard ping tool
|
||||
- **Packet Count**: Specify number of packets
|
||||
- **Packet Size**: Custom packet size
|
||||
- **Interval**: Time between pings
|
||||
|
||||
### Usage
|
||||
|
||||
1. Open a server
|
||||
2. Tap **Ping**
|
||||
3. Enter target host
|
||||
4. Configure parameters
|
||||
5. Start pinging
|
||||
|
||||
## Wake on LAN
|
||||
|
||||
Wake up remote servers via magic packet.
|
||||
|
||||
### Features
|
||||
|
||||
- **MAC Address**: Target device MAC
|
||||
- **Broadcast**: Send broadcast magic packet
|
||||
- **Saved Profiles**: Store WoL configurations
|
||||
|
||||
### Requirements
|
||||
|
||||
- Target device must support Wake-on-LAN
|
||||
- WoL must be enabled in BIOS/UEFI
|
||||
- Device must be in sleep/soft-off state
|
||||
- Device must be on the same network or reachable via broadcast
|
||||
|
||||
## Tips
|
||||
|
||||
- Use iPerf to diagnose network bottlenecks
|
||||
- Ping multiple hosts to compare latency
|
||||
- Save WoL profiles for frequently woken devices
|
||||
56
docs/src/content/docs/features/process.md
Normal file
56
docs/src/content/docs/features/process.md
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: Process & Services
|
||||
description: Monitor processes and manage systemd services
|
||||
---
|
||||
|
||||
## Process Management
|
||||
|
||||
View and manage running processes on your servers.
|
||||
|
||||
### Process List
|
||||
|
||||
- All running processes with details
|
||||
- PID (Process ID)
|
||||
- CPU and memory usage
|
||||
- User ownership
|
||||
- Process command
|
||||
|
||||
### Process Actions
|
||||
|
||||
- **Kill**: Terminate processes
|
||||
- **Filter**: By name or user
|
||||
- **Sort**: By CPU, memory, or PID
|
||||
- **Search**: Find specific processes
|
||||
|
||||
## Systemd Services
|
||||
|
||||
Manage systemd services for service control.
|
||||
|
||||
### Service List
|
||||
|
||||
- All systemd services
|
||||
- Active/inactive status
|
||||
- Enabled/disabled state
|
||||
- Service description
|
||||
|
||||
### Service Actions
|
||||
|
||||
- **Start**: Launch a stopped service
|
||||
- **Stop**: Stop a running service
|
||||
- **Restart**: Restart a service
|
||||
- **Enable**: Enable auto-start on boot
|
||||
- **Disable**: Disable auto-start
|
||||
- **View Status**: Check service status and logs
|
||||
- **Reload**: Reload service configuration
|
||||
|
||||
## Requirements
|
||||
|
||||
- SSH user must have appropriate permissions
|
||||
- For service management: `sudo` access may be required
|
||||
- Process viewing: Standard user permissions usually sufficient
|
||||
|
||||
## Tips
|
||||
|
||||
- Use process list to identify resource hogs
|
||||
- Check service logs for troubleshooting
|
||||
- Monitor critical services with auto-refresh
|
||||
105
docs/src/content/docs/features/pve.md
Normal file
105
docs/src/content/docs/features/pve.md
Normal file
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: Proxmox (PVE)
|
||||
description: Proxmox Virtual Environment management
|
||||
---
|
||||
|
||||
Flutter Server Box includes support for managing Proxmox VE virtualization platform.
|
||||
|
||||
## Features
|
||||
|
||||
### VM Management
|
||||
|
||||
- **List VMs**: View all virtual machines
|
||||
- **VM Status**: Check running/stopped states
|
||||
- **VM Actions**: Start, stop, restart VMs
|
||||
- **VM Details**: View configuration and resources
|
||||
|
||||
### Container (LXC) Management
|
||||
|
||||
- **List Containers**: View all LXC containers
|
||||
- **Container Status**: Monitor container states
|
||||
- **Container Actions**: Start, stop, restart containers
|
||||
- **Console Access**: Terminal access to containers
|
||||
|
||||
### Node Monitoring
|
||||
|
||||
- **Resource Usage**: CPU, memory, disk, network
|
||||
- **Node Status**: Check node health
|
||||
- **Cluster View**: Multi-node cluster overview
|
||||
|
||||
## Setup
|
||||
|
||||
### Adding PVE Server
|
||||
|
||||
1. Add server as normal SSH connection
|
||||
2. Ensure user has PVE permissions
|
||||
3. Access PVE features from server detail page
|
||||
|
||||
### Permissions Required
|
||||
|
||||
PVE user needs:
|
||||
|
||||
- **VM.Audit**: View VM status
|
||||
- **VM.PowerMgmt**: Start/stop VMs
|
||||
- **VM.Console**: Console access
|
||||
|
||||
Example permissions setup:
|
||||
|
||||
```bash
|
||||
pveum useradd myuser -password mypass
|
||||
pveum aclmod /vms -user myuser@pve -role VMAdmin
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### VM Management
|
||||
|
||||
1. Open server with PVE
|
||||
2. Tap **PVE** button
|
||||
3. View VM list
|
||||
4. Tap VM for details
|
||||
5. Use action buttons for management
|
||||
|
||||
### Container Management
|
||||
|
||||
1. Open server with PVE
|
||||
2. Tap **PVE** button
|
||||
3. Switch to Containers tab
|
||||
4. View and manage LXC containers
|
||||
|
||||
### Monitoring
|
||||
|
||||
- Real-time resource usage
|
||||
- Historical data charts
|
||||
- Multiple node support
|
||||
|
||||
## Features by Status
|
||||
|
||||
### Implemented
|
||||
|
||||
- VM listing and status
|
||||
- Container listing and status
|
||||
- Basic VM operations (start/stop/restart)
|
||||
- Resource monitoring
|
||||
|
||||
### Planned
|
||||
|
||||
- VM creation from templates
|
||||
- Snapshot management
|
||||
- Console access
|
||||
- Storage management
|
||||
- Network configuration
|
||||
|
||||
## Requirements
|
||||
|
||||
- **PVE Version**: 6.x or 7.x
|
||||
- **Access**: SSH access to PVE host
|
||||
- **Permissions**: Appropriate PVE user roles
|
||||
- **Network**: Connectivity to PVE API (via SSH)
|
||||
|
||||
## Tips
|
||||
|
||||
- Use **dedicated PVE user** with limited permissions
|
||||
- Monitor **resource usage** for optimal performance
|
||||
- Check **VM status** before maintenance
|
||||
- Use **snapshots** before major changes
|
||||
60
docs/src/content/docs/features/snippets.md
Normal file
60
docs/src/content/docs/features/snippets.md
Normal file
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: Snippets
|
||||
description: Save and execute custom shell commands
|
||||
---
|
||||
|
||||
Snippets allow you to save frequently used shell commands for quick execution.
|
||||
|
||||
## Creating Snippets
|
||||
|
||||
1. Go to the **Snippets** tab
|
||||
2. Tap the **+** button
|
||||
3. Fill in snippet details:
|
||||
- **Name**: Friendly name for the snippet
|
||||
- **Command**: The shell command to execute
|
||||
- **Description**: Optional notes
|
||||
4. Save the snippet
|
||||
|
||||
## Using Snippets
|
||||
|
||||
1. Open a server
|
||||
2. Tap the **Snippet** button
|
||||
3. Select a snippet to execute
|
||||
4. View output in the terminal
|
||||
|
||||
## Snippet Features
|
||||
|
||||
- **Quick Execute**: One-tap command execution
|
||||
- **Variables**: Use server-specific variables
|
||||
- **Organization**: Group related snippets
|
||||
- **Import/Export**: Share snippets between devices
|
||||
- **Sync**: Optional cloud sync
|
||||
|
||||
## Example Snippets
|
||||
|
||||
### System Update
|
||||
```bash
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
```
|
||||
|
||||
### Disk Cleanup
|
||||
```bash
|
||||
sudo apt autoremove -y && sudo apt clean
|
||||
```
|
||||
|
||||
### Docker Cleanup
|
||||
```bash
|
||||
docker system prune -a
|
||||
```
|
||||
|
||||
### View System Logs
|
||||
```bash
|
||||
journalctl -n 50 -f
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
- Use **descriptive names** for easy identification
|
||||
- Add **comments** for complex commands
|
||||
- Test commands before saving as snippets
|
||||
- Organize snippets by category or server type
|
||||
46
docs/src/content/docs/index.mdx
Normal file
46
docs/src/content/docs/index.mdx
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: Flutter Server Box
|
||||
description: A comprehensive cross-platform server management application
|
||||
hero:
|
||||
tagline: Manage your Linux servers from anywhere
|
||||
actions:
|
||||
- text: Get Started
|
||||
link: /introduction/
|
||||
icon: right-arrow
|
||||
variant: primary
|
||||
- text: View on GitHub
|
||||
link: https://github.com/lollipopkit/flutter_server_box
|
||||
icon: github
|
||||
variant: minimal
|
||||
---
|
||||
|
||||
import { Card, CardGrid } from '@astrojs/starlight/components';
|
||||
|
||||
## Features
|
||||
|
||||
<CardGrid stagger>
|
||||
<Card title="Real-time Monitoring" icon="chart">
|
||||
Monitor CPU, memory, disk, network, GPU, and temperature with beautiful real-time charts.
|
||||
</Card>
|
||||
<Card title="SSH Terminal" icon="terminal">
|
||||
Full-featured SSH terminal with multi-tab support and virtual keyboard for mobile devices.
|
||||
</Card>
|
||||
<Card title="SFTP File Browser" icon="folder">
|
||||
Manage files on your servers with the built-in SFTP client and local file browser.
|
||||
</Card>
|
||||
<Card title="Docker Management" icon="box">
|
||||
Start, stop, and monitor Docker containers with an intuitive interface.
|
||||
</Card>
|
||||
<Card title="Cross-Platform" icon="device-mobile">
|
||||
Available on iOS, Android, macOS, Linux, Windows, and watchOS.
|
||||
</Card>
|
||||
<Card title="12+ Languages" icon="globe">
|
||||
Full localization support including English, Chinese, German, French, and more.
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
## Quick Links
|
||||
|
||||
- **Download**: Available on [App Store](https://apps.apple.com/app/flutter-server-box), [Google Play](https://play.google.com/store/apps/details), [GitHub](https://github.com/lollipopkit/flutter_server_box/releases), and [F-Droid](https://f-droid.org/)
|
||||
- **Documentation**: Explore the guides to get started with Flutter Server Box
|
||||
- **Support**: Join our community on GitHub for discussions and issues
|
||||
52
docs/src/content/docs/installation.mdx
Normal file
52
docs/src/content/docs/installation.mdx
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: Installation
|
||||
description: Download and install Flutter Server Box on your device
|
||||
---
|
||||
|
||||
Flutter Server Box is available on multiple platforms. Choose your preferred method of installation.
|
||||
|
||||
## Mobile Apps
|
||||
|
||||
### iOS
|
||||
|
||||
Download from the **[App Store](https://apps.apple.com/app/flutter-server-box)**.
|
||||
|
||||
### Android
|
||||
|
||||
Choose your preferred source:
|
||||
|
||||
- **[Google Play](https://play.google.com/store/apps/details)** - Recommended for most users
|
||||
- **[F-Droid](https://f-droid.org/)** - For users who prefer FOSS-only sources
|
||||
- **[GitHub Releases](https://github.com/lollipopkit/flutter_server_box/releases)** - For the latest version directly from the source
|
||||
|
||||
## Desktop Apps
|
||||
|
||||
### macOS
|
||||
|
||||
Download from **[GitHub Releases](https://github.com/lollipopkit/flutter_server_box/releases)**.
|
||||
|
||||
Features:
|
||||
- Native menu bar integration
|
||||
- Support for both Intel and Apple Silicon
|
||||
|
||||
### Linux
|
||||
|
||||
Download from **[GitHub Releases](https://github.com/lollipopkit/flutter_server_box/releases)**.
|
||||
|
||||
Available as AppImage, deb, or tar.gz packages.
|
||||
|
||||
### Windows
|
||||
|
||||
Download from **[GitHub Releases](https://github.com/lollipopkit/flutter_server_box/releases)**.
|
||||
|
||||
## watchOS
|
||||
|
||||
Available on the **[App Store](https://apps.apple.com/app/flutter-server-box)** as part of the iOS app.
|
||||
|
||||
## Building from Source
|
||||
|
||||
To build Flutter Server Box from source, see the [Building](/development/building) section in the Development documentation.
|
||||
|
||||
## Version Information
|
||||
|
||||
Check the [GitHub Releases](https://github.com/lollipopkit/flutter_server_box/releases) page for the latest version and changelog.
|
||||
33
docs/src/content/docs/introduction.mdx
Normal file
33
docs/src/content/docs/introduction.mdx
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
title: Introduction
|
||||
description: Learn what Flutter Server Box is and what it can do
|
||||
---
|
||||
|
||||
Flutter Server Box is a comprehensive cross-platform server management application built with Flutter. It allows you to monitor, manage, and control your Linux, Unix, and Windows servers from anywhere.
|
||||
|
||||
## What is Flutter Server Box?
|
||||
|
||||
Flutter Server Box provides a unified interface for server administration tasks through SSH connections. Whether you're a system administrator, developer, or hobbyist running home servers, this app puts powerful server management tools in your pocket.
|
||||
|
||||
## Key Capabilities
|
||||
|
||||
- **Real-time Monitoring**: Track CPU, memory, disk usage, network speed, GPU status, and system temperatures
|
||||
- **SSH Terminal**: Full terminal access with multi-tab support and customizable appearance
|
||||
- **SFTP Client**: Browse and manage files on your servers
|
||||
- **Docker Management**: Control containers with ease
|
||||
- **Process Management**: View and manage system processes
|
||||
- **Systemd Services**: Start, stop, and monitor systemd services
|
||||
- **Network Tools**: iPerf testing, ping, and Wake-on-LAN
|
||||
- **Snippets**: Save and execute custom shell commands
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
Flutter Server Box is truly cross-platform:
|
||||
|
||||
- **Mobile**: iOS and Android
|
||||
- **Desktop**: macOS, Linux, and Windows
|
||||
- **Wearable**: watchOS (Apple Watch)
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under AGPL v3. Source code is available on [GitHub](https://github.com/lollipopkit/flutter_server_box).
|
||||
80
docs/src/content/docs/platforms/desktop.md
Normal file
80
docs/src/content/docs/platforms/desktop.md
Normal file
@@ -0,0 +1,80 @@
|
||||
---
|
||||
title: Desktop Features
|
||||
description: macOS, Linux, and Windows specific features
|
||||
---
|
||||
|
||||
Flutter Server Box on desktop platforms provides additional productivity features.
|
||||
|
||||
## macOS
|
||||
|
||||
### Menu Bar Integration
|
||||
|
||||
- Quick server status in menu bar
|
||||
- One-click server access
|
||||
- Compact mode for minimal distraction
|
||||
- Native macOS menu bar styling
|
||||
|
||||
### Window State Persistence
|
||||
|
||||
- Remembers window position and size
|
||||
- Restore previous session on launch
|
||||
- Multiple monitor support
|
||||
|
||||
### Native Features
|
||||
|
||||
- **Title Bar**: Custom or system title bar option
|
||||
- **Full Screen Mode**: Dedicated server monitoring
|
||||
- **Keyboard Shortcuts**: macOS-native shortcuts
|
||||
- **Touch Bar** (supported devices): Quick actions
|
||||
|
||||
## Linux
|
||||
|
||||
### Native Integration
|
||||
|
||||
- System tray support
|
||||
- Desktop notification integration
|
||||
- File picker integration
|
||||
|
||||
### Window Management
|
||||
|
||||
- X11 and Wayland support
|
||||
- Tiling window manager friendly
|
||||
- Custom window decorations option
|
||||
|
||||
## Windows
|
||||
|
||||
### Features
|
||||
|
||||
- System tray integration
|
||||
- Jump List quick actions
|
||||
- Native window controls
|
||||
- Auto-start on boot option
|
||||
|
||||
## Cross-Platform Desktop Features
|
||||
|
||||
### Keyboard Shortcuts
|
||||
|
||||
- **Cmd/Ctrl + N**: New server
|
||||
- **Cmd/Ctrl + W**: Close tab
|
||||
- **Cmd/Ctrl + T**: New terminal tab
|
||||
- **Cmd/Ctrl + ,**: Settings
|
||||
|
||||
### Themes
|
||||
|
||||
- Light theme
|
||||
- Dark theme
|
||||
- AMOLED theme (pure black)
|
||||
- System theme (follows OS)
|
||||
|
||||
### Multiple Windows
|
||||
|
||||
- Open multiple servers in separate windows
|
||||
- Drag tabs to new window
|
||||
- Compare server stats side-by-side
|
||||
|
||||
### Advantages Over Mobile
|
||||
|
||||
- Larger screen for monitoring
|
||||
- Full keyboard for terminal
|
||||
- Faster file operations
|
||||
- Better multitasking
|
||||
77
docs/src/content/docs/platforms/mobile.md
Normal file
77
docs/src/content/docs/platforms/mobile.md
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: Mobile Features
|
||||
description: iOS and Android specific features
|
||||
---
|
||||
|
||||
Flutter Server Box provides several mobile-specific features for iOS and Android devices.
|
||||
|
||||
## Biometric Authentication
|
||||
|
||||
Secure your servers with biometric authentication:
|
||||
|
||||
- **iOS**: Face ID or Touch ID
|
||||
- **Android**: Fingerprint authentication
|
||||
|
||||
Enable in Settings > Security > Biometric Authentication
|
||||
|
||||
## Home Screen Widgets
|
||||
|
||||
Add server status widgets to your home screen for quick monitoring.
|
||||
|
||||
### iOS
|
||||
|
||||
- Long press on home screen
|
||||
- Tap **+** to add widget
|
||||
- Search for "Flutter Server Box"
|
||||
- Choose widget size:
|
||||
- Small: Single server status
|
||||
- Medium: Multiple servers
|
||||
- Large: Detailed info
|
||||
|
||||
### Android
|
||||
|
||||
- Long press on home screen
|
||||
- Tap **Widgets**
|
||||
- Find "Flutter Server Box"
|
||||
- Select widget type
|
||||
|
||||
## Background Running
|
||||
|
||||
### Android
|
||||
|
||||
Keep connections alive in the background:
|
||||
|
||||
- Enable in Settings > Advanced > Background Running
|
||||
- Requires battery optimization exclusion
|
||||
- Persistent notifications for active connections
|
||||
|
||||
### iOS
|
||||
|
||||
Background limitations apply:
|
||||
|
||||
- Connections may pause in background
|
||||
- Quick reconnect on return to app
|
||||
- Background refresh support
|
||||
|
||||
## Push Notifications
|
||||
|
||||
Receive notifications for:
|
||||
|
||||
- Server offline alerts
|
||||
- High resource usage warnings
|
||||
- Task completion alerts
|
||||
|
||||
Configure in Settings > Notifications
|
||||
|
||||
## Mobile UI Features
|
||||
|
||||
- **Pull to Refresh**: Update server status
|
||||
- **Swipe Actions**: Quick server operations
|
||||
- **Landscape Mode**: Better terminal experience
|
||||
- **Virtual Keyboard**: Terminal shortcuts
|
||||
|
||||
## File Integration
|
||||
|
||||
- **Files App (iOS)**: Direct SFTP access from Files
|
||||
- **Storage Access Framework (Android)**: Share files with other apps
|
||||
- **Document Picker**: Easy file selection
|
||||
55
docs/src/content/docs/platforms/watchos.md
Normal file
55
docs/src/content/docs/platforms/watchos.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: watchOS App
|
||||
description: Apple Watch companion app
|
||||
---
|
||||
|
||||
Flutter Server Box includes a companion app for Apple Watch, providing quick server monitoring on your wrist.
|
||||
|
||||
## Features
|
||||
|
||||
### Server Status at a Glance
|
||||
|
||||
- View server online/offline status
|
||||
- Quick CPU and memory stats
|
||||
- One-tap server connectivity check
|
||||
|
||||
### Complications
|
||||
|
||||
Add server information to your watch face:
|
||||
|
||||
- **Modular**: Server status icon
|
||||
- **Infograph**: Small complication
|
||||
- **Extra Large**: Large complication
|
||||
|
||||
### Quick Actions
|
||||
|
||||
- Ping servers
|
||||
- Wake on LAN
|
||||
- Quick terminal command
|
||||
|
||||
## Requirements
|
||||
|
||||
- Apple Watch Series 3 or later
|
||||
- watchOS 8.0 or later
|
||||
- Paired iPhone with Flutter Server Box installed
|
||||
- iPhone and Watch on same Wi-Fi
|
||||
|
||||
## Setup
|
||||
|
||||
1. Install Flutter Server Box on iPhone
|
||||
2. Open the Watch app on iPhone
|
||||
3. Find Flutter Server Box under "Available Apps"
|
||||
4. Toggle "Show App on Apple Watch"
|
||||
|
||||
## Limitations
|
||||
|
||||
- No full terminal access
|
||||
- Reduced SFTP functionality
|
||||
- Dependent on iPhone connection
|
||||
- Simplified server management
|
||||
|
||||
## Tips
|
||||
|
||||
- Use complications for persistent status
|
||||
- Quick actions for emergency checks
|
||||
- Sync servers from iPhone app
|
||||
214
docs/src/content/docs/principles/architecture.md
Normal file
214
docs/src/content/docs/principles/architecture.md
Normal file
@@ -0,0 +1,214 @@
|
||||
---
|
||||
title: Architecture Overview
|
||||
description: High-level application architecture
|
||||
---
|
||||
|
||||
Flutter Server Box follows a layered architecture with clear separation of concerns.
|
||||
|
||||
## Architecture Layers
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Presentation Layer (UI) │
|
||||
│ lib/view/page/, lib/view/widget/ │
|
||||
│ - Pages, Widgets, Controllers │
|
||||
└─────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Business Logic Layer │
|
||||
│ lib/data/provider/ │
|
||||
│ - Riverpod Providers, State Notifiers │
|
||||
└─────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Data Access Layer │
|
||||
│ lib/data/store/, lib/data/model/ │
|
||||
│ - Hive Stores, Data Models │
|
||||
└─────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ External Integration Layer │
|
||||
│ - SSH (dartssh2), Terminal (xterm), SFTP │
|
||||
│ - Platform-specific code (iOS, Android, etc.) │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Application Foundation
|
||||
|
||||
### Main Entry Point
|
||||
|
||||
`lib/main.dart` initializes the app:
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(
|
||||
ProviderScope(
|
||||
child: MyApp(),
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Root Widget
|
||||
|
||||
`MyApp` provides:
|
||||
- **Theme Management**: Light/dark theme switching
|
||||
- **Routing Configuration**: Navigation structure
|
||||
- **Provider Scope**: Dependency injection root
|
||||
|
||||
### Home Page
|
||||
|
||||
`HomePage` serves as navigation hub:
|
||||
- **Tabbed Interface**: Server, Snippet, Container, SSH
|
||||
- **State Management**: Per-tab state
|
||||
- **Navigation**: Feature access
|
||||
|
||||
## Core Systems
|
||||
|
||||
### State Management: Riverpod
|
||||
|
||||
**Why Riverpod?**
|
||||
- Compile-time safety
|
||||
- Easy testing
|
||||
- No Build context dependency
|
||||
- Works across platforms
|
||||
|
||||
**Provider Types Used:**
|
||||
- `StateProvider`: Simple mutable state
|
||||
- `AsyncNotifierProvider`: Loading/error/data states
|
||||
- `StreamProvider`: Real-time data streams
|
||||
- Future providers: One-time async operations
|
||||
|
||||
### Data Persistence: Hive CE
|
||||
|
||||
**Why Hive CE?**
|
||||
- No native code dependencies
|
||||
- Fast key-value storage
|
||||
- Type-safe with code generation
|
||||
- No manual field annotations needed
|
||||
|
||||
**Stores:**
|
||||
- `SettingStore`: App preferences
|
||||
- `ServerStore`: Server configurations
|
||||
- `SnippetStore`: Command snippets
|
||||
- `KeyStore`: SSH keys
|
||||
|
||||
### Immutable Models: Freezed
|
||||
|
||||
**Benefits:**
|
||||
- Compile-time immutability
|
||||
- Union types for state
|
||||
- Built-in JSON serialization
|
||||
- CopyWith extensions
|
||||
|
||||
## Cross-Platform Strategy
|
||||
|
||||
### Plugin System
|
||||
|
||||
Flutter plugins provide platform integration:
|
||||
|
||||
| Platform | Integration Method |
|
||||
|----------|-------------------|
|
||||
| iOS | CocoaPods, Swift/Obj-C |
|
||||
| Android | Gradle, Kotlin/Java |
|
||||
| macOS | CocoaPods, Swift |
|
||||
| Linux | CMake, C++ |
|
||||
| Windows | CMake, C# |
|
||||
|
||||
### Platform-Specific Features
|
||||
|
||||
**iOS Only:**
|
||||
- Home screen widgets
|
||||
- Live Activities
|
||||
- Apple Watch companion
|
||||
|
||||
**Android Only:**
|
||||
- Background service
|
||||
- Push notifications
|
||||
- File system access
|
||||
|
||||
**Desktop Only:**
|
||||
- Menu bar integration
|
||||
- Multiple windows
|
||||
- Custom title bar
|
||||
|
||||
## Custom Dependencies
|
||||
|
||||
### dartssh2 Fork
|
||||
|
||||
Enhanced SSH client with:
|
||||
- Better mobile support
|
||||
- Enhanced error handling
|
||||
- Performance optimizations
|
||||
|
||||
### xterm.dart Fork
|
||||
|
||||
Terminal emulator with:
|
||||
- Mobile-optimized rendering
|
||||
- Touch gesture support
|
||||
- Virtual keyboard integration
|
||||
|
||||
### fl_lib
|
||||
|
||||
Shared utilities package with:
|
||||
- Common widgets
|
||||
- Extensions
|
||||
- Helper functions
|
||||
|
||||
## Build System
|
||||
|
||||
### fl_build Package
|
||||
|
||||
Custom build system for:
|
||||
- Multi-platform builds
|
||||
- Code signing
|
||||
- Asset bundling
|
||||
- Version management
|
||||
|
||||
### Build Process
|
||||
|
||||
```
|
||||
make.dart (version) → fl_build (build) → Platform output
|
||||
```
|
||||
|
||||
1. **Pre-build**: Calculate version from Git
|
||||
2. **Build**: Compile for target platform
|
||||
3. **Post-build**: Package and sign
|
||||
|
||||
## Data Flow Example
|
||||
|
||||
### Server Status Update
|
||||
|
||||
```
|
||||
1. Timer triggers →
|
||||
2. Provider calls service →
|
||||
3. Service executes SSH command →
|
||||
4. Response parsed to model →
|
||||
5. State updated →
|
||||
6. UI rebuilds with new data
|
||||
```
|
||||
|
||||
### User Action Flow
|
||||
|
||||
```
|
||||
1. User taps button →
|
||||
2. Widget calls provider method →
|
||||
3. Provider updates state →
|
||||
4. State change triggers rebuild →
|
||||
5. New state reflected in UI
|
||||
```
|
||||
|
||||
## Security Architecture
|
||||
|
||||
### Data Protection
|
||||
|
||||
- **Passwords**: Encrypted with flutter_secure_storage
|
||||
- **SSH Keys**: Encrypted at rest
|
||||
- **Host Fingerprints**: Stored securely
|
||||
- **Session Data**: Not persisted
|
||||
|
||||
### Connection Security
|
||||
|
||||
- **Host Key Verification**: MITM detection
|
||||
- **Encryption**: Standard SSH encryption
|
||||
- **No Plain Text**: Sensitive data never stored plain
|
||||
490
docs/src/content/docs/principles/sftp.md
Normal file
490
docs/src/content/docs/principles/sftp.md
Normal file
@@ -0,0 +1,490 @@
|
||||
---
|
||||
title: SFTP System
|
||||
description: How the SFTP file browser works
|
||||
---
|
||||
|
||||
The SFTP system provides file management capabilities over SSH.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ SFTP UI Layer │
|
||||
│ - File browser (remote) │
|
||||
│ - File browser (local) │
|
||||
│ - Transfer queue │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ SFTP State Management │
|
||||
│ - sftpProvider │
|
||||
│ - Path management │
|
||||
│ - Operation queue │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ SFTP Protocol Layer │
|
||||
│ - SSH subsystem │
|
||||
│ - File operations │
|
||||
│ - Directory listing │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ SSH Transport │
|
||||
│ - Secure channel │
|
||||
│ - Data streaming │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Connection Establishment
|
||||
|
||||
### SFTP Client Creation
|
||||
|
||||
```dart
|
||||
Future<SftpClient> createSftpClient(Spi spi) async {
|
||||
// 1. Get SSH client (reuse if available)
|
||||
final sshClient = await genClient(spi);
|
||||
|
||||
// 2. Open SFTP subsystem
|
||||
final sftp = await sshClient.openSftp();
|
||||
|
||||
return sftp;
|
||||
}
|
||||
```
|
||||
|
||||
### Connection Reuse
|
||||
|
||||
SFTP reuses existing SSH connections:
|
||||
|
||||
```dart
|
||||
class ServerProvider {
|
||||
SSHClient? _sshClient;
|
||||
SftpClient? _sftpClient;
|
||||
|
||||
Future<SftpClient> getSftpClient(String spiId) async {
|
||||
_sftpClient ??= await _sshClient!.openSftp();
|
||||
return _sftpClient!;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## File System Operations
|
||||
|
||||
### Directory Listing
|
||||
|
||||
```dart
|
||||
Future<List<SftpFile>> listDirectory(String path) async {
|
||||
final sftp = await getSftpClient(spiId);
|
||||
|
||||
// List directory
|
||||
final files = await sftp.listDir(path);
|
||||
|
||||
// Sort based on settings
|
||||
files.sort((a, b) {
|
||||
switch (sortOption) {
|
||||
case SortOption.name:
|
||||
return a.name.toLowerCase().compareTo(b.name.toLowerCase());
|
||||
case SortOption.size:
|
||||
return a.size.compareTo(b.size);
|
||||
case SortOption.time:
|
||||
return a.modified.compareTo(b.modified);
|
||||
}
|
||||
});
|
||||
|
||||
// Folders first if enabled
|
||||
if (showFoldersFirst) {
|
||||
final dirs = files.where((f) => f.isDirectory);
|
||||
final regular = files.where((f) => !f.isDirectory);
|
||||
return [...dirs, ...regular];
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
```
|
||||
|
||||
### File Metadata
|
||||
|
||||
```dart
|
||||
class SftpFile {
|
||||
final String name;
|
||||
final String path;
|
||||
final int size; // Bytes
|
||||
final int modified; // Unix timestamp
|
||||
final String permissions; // e.g., "rwxr-xr-x"
|
||||
final String owner;
|
||||
final String group;
|
||||
final bool isDirectory;
|
||||
final bool isSymlink;
|
||||
|
||||
String get sizeFormatted => formatBytes(size);
|
||||
String get modifiedFormatted => formatDate(modified);
|
||||
}
|
||||
```
|
||||
|
||||
## File Operations
|
||||
|
||||
### Upload
|
||||
|
||||
```dart
|
||||
Future<void> uploadFile(
|
||||
String localPath,
|
||||
String remotePath,
|
||||
) async {
|
||||
final sftp = await getSftpClient(spiId);
|
||||
|
||||
// Create request
|
||||
final req = SftpReq(
|
||||
spi: spi,
|
||||
remotePath: remotePath,
|
||||
localPath: localPath,
|
||||
type: SftpReqType.upload,
|
||||
);
|
||||
|
||||
// Add to queue
|
||||
_transferQueue.add(req);
|
||||
|
||||
// Execute transfer with progress
|
||||
final file = File(localPath);
|
||||
final size = await file.length();
|
||||
final stream = file.openRead();
|
||||
|
||||
await sftp.upload(
|
||||
stream: stream,
|
||||
toPath: remotePath,
|
||||
onProgress: (transferred) {
|
||||
_updateProgress(req, transferred, size);
|
||||
},
|
||||
);
|
||||
|
||||
// Complete
|
||||
_transferQueue.remove(req);
|
||||
}
|
||||
```
|
||||
|
||||
### Download
|
||||
|
||||
```dart
|
||||
Future<void> downloadFile(
|
||||
String remotePath,
|
||||
String localPath,
|
||||
) async {
|
||||
final sftp = await getSftpClient(spiId);
|
||||
|
||||
// Create local file
|
||||
final file = File(localPath);
|
||||
final sink = file.openWrite();
|
||||
|
||||
// Download with progress
|
||||
final stat = await sftp.stat(remotePath);
|
||||
|
||||
await sftp.download(
|
||||
fromPath: remotePath,
|
||||
toSink: sink,
|
||||
onProgress: (transferred) {
|
||||
_updateProgress(
|
||||
SftpReq(...),
|
||||
transferred,
|
||||
stat.size,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
await sink.close();
|
||||
}
|
||||
```
|
||||
|
||||
### Permission Editing
|
||||
|
||||
```dart
|
||||
Future<void> setPermissions(
|
||||
String path,
|
||||
String permissions,
|
||||
) async {
|
||||
final sftp = await getSftpClient(spiId);
|
||||
|
||||
// Parse permissions (e.g., "rwxr-xr-x" or "755")
|
||||
final mode = parsePermissions(permissions);
|
||||
|
||||
// Set via SSH command (more reliable than SFTP)
|
||||
final ssh = await getSshClient(spiId);
|
||||
await ssh.exec('chmod $mode "$path"');
|
||||
}
|
||||
```
|
||||
|
||||
## Path Management
|
||||
|
||||
### Path Structure
|
||||
|
||||
```dart
|
||||
class PathWithPrefix {
|
||||
final String prefix; // e.g., "/home/user"
|
||||
final String path; // Relative or absolute
|
||||
|
||||
String get fullPath {
|
||||
if (path.startsWith('/')) {
|
||||
return path; // Absolute path
|
||||
}
|
||||
return '$prefix/$path'; // Relative path
|
||||
}
|
||||
|
||||
PathWithPrefix cd(String subPath) {
|
||||
return PathWithPrefix(
|
||||
prefix: fullPath,
|
||||
path: subPath,
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Navigation History
|
||||
|
||||
```dart
|
||||
class PathHistory {
|
||||
final List<String> _history = [];
|
||||
int _index = -1;
|
||||
|
||||
void push(String path) {
|
||||
// Remove forward history
|
||||
_history.removeRange(_index + 1, _history.length);
|
||||
_history.add(path);
|
||||
_index = _history.length - 1;
|
||||
}
|
||||
|
||||
String? back() {
|
||||
if (_index > 0) {
|
||||
_index--;
|
||||
return _history[_index];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? forward() {
|
||||
if (_index < _history.length - 1) {
|
||||
_index++;
|
||||
return _history[_index];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Transfer System
|
||||
|
||||
### Transfer Request
|
||||
|
||||
```dart
|
||||
class SftpReq {
|
||||
final Spi spi;
|
||||
final String remotePath;
|
||||
final String localPath;
|
||||
final SftpReqType type;
|
||||
final DateTime createdAt;
|
||||
|
||||
int? totalBytes;
|
||||
int? transferredBytes;
|
||||
String? error;
|
||||
}
|
||||
```
|
||||
|
||||
### Progress Tracking
|
||||
|
||||
```dart
|
||||
class TransferProgress {
|
||||
final SftpReq request;
|
||||
final int total;
|
||||
final int transferred;
|
||||
final DateTime startTime;
|
||||
|
||||
double get percentage => (transferred / total) * 100;
|
||||
Duration get elapsed => DateTime.now().difference(startTime);
|
||||
|
||||
String get speedFormatted {
|
||||
final bytesPerSecond = transferred / elapsed.inSeconds;
|
||||
return formatSpeed(bytesPerSecond);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Queue Management
|
||||
|
||||
```dart
|
||||
class TransferQueue {
|
||||
final List<SftpReq> _queue = [];
|
||||
final Map<String, TransferProgress> _progress = {};
|
||||
int _concurrent = 3; // Max concurrent transfers
|
||||
|
||||
Future<void> process() async {
|
||||
final active = _progress.values.where((p) => p.isInProgress);
|
||||
if (active.length >= _concurrent) return;
|
||||
|
||||
final pending = _queue.where((r) => !_progress.containsKey(r.id));
|
||||
for (final req in pending.take(_concurrent - active.length)) {
|
||||
_executeTransfer(req);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _executeTransfer(SftpReq req) async {
|
||||
try {
|
||||
_progress[req.id] = TransferProgress.inProgress(req);
|
||||
|
||||
if (req.type == SftpReqType.upload) {
|
||||
await uploadFile(req.localPath, req.remotePath);
|
||||
} else {
|
||||
await downloadFile(req.remotePath, req.localPath);
|
||||
}
|
||||
|
||||
_progress[req.id] = TransferProgress.completed(req);
|
||||
} catch (e) {
|
||||
_progress[req.id] = TransferProgress.failed(req, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Local Storage Pattern
|
||||
|
||||
### Download Cache
|
||||
|
||||
Downloaded files stored at:
|
||||
|
||||
```dart
|
||||
String getLocalDownloadPath(String spiId, String remotePath) {
|
||||
final normalized = remotePath.replaceAll('/', '_');
|
||||
return 'Paths.file/$spiId/$normalized';
|
||||
}
|
||||
```
|
||||
|
||||
Example:
|
||||
- Remote: `/var/log/nginx/access.log`
|
||||
- spiId: `server-123`
|
||||
- Local: `Paths.file/server-123/_var_log_nginx_access.log`
|
||||
|
||||
## File Editing
|
||||
|
||||
### Edit Workflow
|
||||
|
||||
```dart
|
||||
Future<void> editFile(String path) async {
|
||||
final sftp = await getSftpClient(spiId);
|
||||
|
||||
// 1. Check size
|
||||
final stat = await sftp.stat(path);
|
||||
if (stat.size > editorMaxSize) {
|
||||
showWarning('File too large for built-in editor');
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Download to temp
|
||||
final temp = await downloadToTemp(path);
|
||||
|
||||
// 3. Open in editor
|
||||
final content = await openEditor(temp.path);
|
||||
|
||||
// 4. Upload back
|
||||
await uploadFile(temp.path, path);
|
||||
|
||||
// 5. Cleanup
|
||||
await temp.delete();
|
||||
}
|
||||
```
|
||||
|
||||
### External Editor Integration
|
||||
|
||||
```dart
|
||||
Future<void> editInExternalEditor(String path) async {
|
||||
final ssh = await getSshClient(spiId);
|
||||
|
||||
// Open terminal with editor
|
||||
final editor = getSetting('sftpEditor', 'vim');
|
||||
await ssh.exec('$editor "$path"');
|
||||
|
||||
// User edits in terminal
|
||||
// After save, refresh SFTP view
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Permission Errors
|
||||
|
||||
```dart
|
||||
try {
|
||||
await sftp.upload(...);
|
||||
} on SftpPermissionException {
|
||||
showError('Permission denied: ${stat.path}');
|
||||
showHint('Check file permissions and ownership');
|
||||
}
|
||||
```
|
||||
|
||||
### Connection Errors
|
||||
|
||||
```dart
|
||||
try {
|
||||
await sftp.listDir(path);
|
||||
} on SftpConnectionException {
|
||||
showError('Connection lost');
|
||||
await reconnect();
|
||||
}
|
||||
```
|
||||
|
||||
### Space Errors
|
||||
|
||||
```dart
|
||||
try {
|
||||
await sftp.upload(...);
|
||||
} on SftpNoSpaceException {
|
||||
showError('Disk full on remote server');
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Optimizations
|
||||
|
||||
### Directory Caching
|
||||
|
||||
```dart
|
||||
class DirectoryCache {
|
||||
final Map<String, CachedDirectory> _cache = {};
|
||||
final Duration ttl = Duration(minutes: 5);
|
||||
|
||||
Future<List<SftpFile>> list(String path) async {
|
||||
final cached = _cache[path];
|
||||
if (cached != null && !cached.isExpired) {
|
||||
return cached.files;
|
||||
}
|
||||
|
||||
final files = await sftp.listDir(path);
|
||||
_cache[path] = CachedDirectory(files);
|
||||
return files;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Lazy Loading
|
||||
|
||||
For large directories (>1000 items):
|
||||
|
||||
```dart
|
||||
List<SftpFile> loadPage(String path, int page, int pageSize) {
|
||||
final all = cache[path] ?? [];
|
||||
final start = page * pageSize;
|
||||
final end = start + pageSize;
|
||||
return all.sublist(start, end.clamp(0, all.length));
|
||||
}
|
||||
```
|
||||
|
||||
### Pagination
|
||||
|
||||
```dart
|
||||
class PaginatedDirectory {
|
||||
static const pageSize = 100;
|
||||
|
||||
Future<List<SftpFile>> getPage(int page) async {
|
||||
final offset = page * pageSize;
|
||||
return await sftp.listDir(
|
||||
path,
|
||||
offset: offset,
|
||||
limit: pageSize,
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
299
docs/src/content/docs/principles/ssh.md
Normal file
299
docs/src/content/docs/principles/ssh.md
Normal file
@@ -0,0 +1,299 @@
|
||||
---
|
||||
title: SSH Connection
|
||||
description: How SSH connections are established and managed
|
||||
---
|
||||
|
||||
Understanding SSH connections in Flutter Server Box.
|
||||
|
||||
## Connection Flow
|
||||
|
||||
```
|
||||
User Input → Spi Config → genClient() → SSH Client → Session
|
||||
```
|
||||
|
||||
### Step 1: Configuration
|
||||
|
||||
The `Spi` (Server Parameter Info) model contains:
|
||||
|
||||
```dart
|
||||
class Spi {
|
||||
String name; // Server name
|
||||
String ip; // IP address
|
||||
int port; // SSH port (default 22)
|
||||
String user; // Username
|
||||
String? pwd; // Password (encrypted)
|
||||
String? keyId; // SSH key ID
|
||||
String? jumpId; // Jump server ID
|
||||
String? alterUrl; // Alternative URL
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Client Generation
|
||||
|
||||
`genClient(spi)` creates SSH client:
|
||||
|
||||
```dart
|
||||
Future<SSHClient> genClient(Spi spi) async {
|
||||
// 1. Establish socket
|
||||
final socket = await connect(spi.ip, spi.port);
|
||||
|
||||
// 2. Try alternative URL if failed
|
||||
if (socket == null && spi.alterUrl != null) {
|
||||
socket = await connect(spi.alterUrl, spi.port);
|
||||
}
|
||||
|
||||
// 3. Authenticate
|
||||
final client = SSHClient(
|
||||
socket: socket,
|
||||
username: spi.user,
|
||||
onPasswordRequest: () => spi.pwd,
|
||||
onIdentityRequest: () => loadKey(spi.keyId),
|
||||
);
|
||||
|
||||
// 4. Verify host key
|
||||
await verifyHostKey(client, spi);
|
||||
|
||||
return client;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Jump Server (if configured)
|
||||
|
||||
For jump servers, recursive connection:
|
||||
|
||||
```dart
|
||||
if (spi.jumpId != null) {
|
||||
final jumpClient = await genClient(getJumpSpi(spi.jumpId));
|
||||
final forwarded = await jumpClient.forwardLocal(
|
||||
spi.ip,
|
||||
spi.port,
|
||||
);
|
||||
// Connect through forwarded socket
|
||||
}
|
||||
```
|
||||
|
||||
## Authentication Methods
|
||||
|
||||
### Password Authentication
|
||||
|
||||
```dart
|
||||
onPasswordRequest: () => spi.pwd
|
||||
```
|
||||
|
||||
- Password stored encrypted in Hive
|
||||
- Decrypted on connection
|
||||
- Sent to server for verification
|
||||
|
||||
### Private Key Authentication
|
||||
|
||||
```dart
|
||||
onIdentityRequest: () async {
|
||||
final key = await KeyStore.get(spi.keyId);
|
||||
return decyptPem(key.pem, key.password);
|
||||
}
|
||||
```
|
||||
|
||||
**Key Loading Process:**
|
||||
1. Retrieve encrypted key from `KeyStore`
|
||||
2. Decrypt password (biometric/prompt)
|
||||
3. Parse PEM format
|
||||
4. Standardize line endings (LF)
|
||||
5. Return for authentication
|
||||
|
||||
### Keyboard-Interactive
|
||||
|
||||
```dart
|
||||
onUserInfoRequest: (instructions) async {
|
||||
// Handle challenge-response
|
||||
return responses;
|
||||
}
|
||||
```
|
||||
|
||||
Supports:
|
||||
- Password authentication
|
||||
- OTP tokens
|
||||
- Two-factor authentication
|
||||
|
||||
## Host Key Verification
|
||||
|
||||
### Why Verify Host Keys?
|
||||
|
||||
Prevents **Man-in-the-Middle (MITM)** attacks by ensuring you're connecting to the same server.
|
||||
|
||||
### Storage Format
|
||||
|
||||
```
|
||||
{spi.id}::{keyType}
|
||||
```
|
||||
|
||||
Example:
|
||||
```
|
||||
my-server::ssh-ed25519
|
||||
my-server::ecdsa-sha2-nistp256
|
||||
```
|
||||
|
||||
### Fingerprint Formats
|
||||
|
||||
**MD5 Hex:**
|
||||
```
|
||||
aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99
|
||||
```
|
||||
|
||||
**Base64:**
|
||||
```
|
||||
SHA256:AbCdEf1234567890...=
|
||||
```
|
||||
|
||||
### Verification Flow
|
||||
|
||||
```dart
|
||||
Future<void> verifyHostKey(SSHClient client, Spi spi) async {
|
||||
final key = await client.hostKey;
|
||||
final fingerprint = md5Hex(key); // or base64
|
||||
|
||||
final stored = SettingStore.sshKnownHostsFingerprints
|
||||
['$keyId::$keyType'];
|
||||
|
||||
if (stored == null) {
|
||||
// New host - prompt user
|
||||
final trust = await promptUser(
|
||||
'Unknown host',
|
||||
'Fingerprint: $fingerprint',
|
||||
);
|
||||
if (trust) {
|
||||
SettingStore.sshKnownHostsFingerprints
|
||||
['$keyId::$keyType'] = fingerprint;
|
||||
}
|
||||
} else if (stored != fingerprint) {
|
||||
// Changed - warn user
|
||||
await warnUser(
|
||||
'Host key changed!',
|
||||
'Possible MITM attack',
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Session Management
|
||||
|
||||
### Connection Pooling
|
||||
|
||||
Active clients maintained in `ServerProvider`:
|
||||
|
||||
```dart
|
||||
class ServerProvider {
|
||||
final Map<String, SSHClient> _clients = {};
|
||||
|
||||
SSHClient getClient(String spiId) {
|
||||
return _clients[spiId] ??= connect(spiId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Keep-Alive
|
||||
|
||||
Maintain connection during inactivity:
|
||||
|
||||
```dart
|
||||
Timer.periodic(
|
||||
Duration(seconds: 30),
|
||||
(_) => client.sendKeepAlive(),
|
||||
);
|
||||
```
|
||||
|
||||
### Auto-Reconnect
|
||||
|
||||
On connection loss:
|
||||
|
||||
```dart
|
||||
client.onError.listen((error) async {
|
||||
await Future.delayed(Duration(seconds: 5));
|
||||
reconnect();
|
||||
});
|
||||
```
|
||||
|
||||
## Connection Lifecycle
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ Initial │
|
||||
└──────┬──────┘
|
||||
│ connect()
|
||||
↓
|
||||
┌─────────────┐
|
||||
│ Connecting │ ←──┐
|
||||
└──────┬──────┘ │
|
||||
│ success │
|
||||
↓ │ fail (retry)
|
||||
┌─────────────┐ │
|
||||
│ Connected │───┘
|
||||
└──────┬──────┘
|
||||
│
|
||||
↓
|
||||
┌─────────────┐
|
||||
│ Active │ ──→ Send commands
|
||||
└──────┬──────┘
|
||||
│
|
||||
↓ (error/disconnect)
|
||||
┌─────────────┐
|
||||
│ Disconnected│
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Connection Timeout
|
||||
|
||||
```dart
|
||||
try {
|
||||
await client.connect().timeout(
|
||||
Duration(seconds: 30),
|
||||
);
|
||||
} on TimeoutException {
|
||||
throw ConnectionException('Connection timeout');
|
||||
}
|
||||
```
|
||||
|
||||
### Authentication Failure
|
||||
|
||||
```dart
|
||||
onAuthFail: (error) {
|
||||
if (error.contains('password')) {
|
||||
return 'Invalid password';
|
||||
} else if (error.contains('key')) {
|
||||
return 'Invalid SSH key';
|
||||
}
|
||||
return 'Authentication failed';
|
||||
}
|
||||
```
|
||||
|
||||
### Host Key Mismatch
|
||||
|
||||
```dart
|
||||
onHostKeyMismatch: (stored, current) {
|
||||
showSecurityWarning(
|
||||
'Host key has changed!',
|
||||
'Possible MITM attack',
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Connection Reuse
|
||||
|
||||
- Reuse clients across features
|
||||
- Don't disconnect/reconnect unnecessarily
|
||||
- Pool connections for concurrent operations
|
||||
|
||||
### Optimal Settings
|
||||
|
||||
- **Timeout**: 30 seconds (adjustable)
|
||||
- **Keep-alive**: Every 30 seconds
|
||||
- **Retry delay**: 5 seconds
|
||||
|
||||
### Network Efficiency
|
||||
|
||||
- Single connection for multiple operations
|
||||
- Pipeline commands when possible
|
||||
- Avoid opening multiple connections
|
||||
405
docs/src/content/docs/principles/state.md
Normal file
405
docs/src/content/docs/principles/state.md
Normal file
@@ -0,0 +1,405 @@
|
||||
---
|
||||
title: State Management
|
||||
description: How state is managed with Riverpod
|
||||
---
|
||||
|
||||
Understanding the state management architecture in Flutter Server Box.
|
||||
|
||||
## Why Riverpod?
|
||||
|
||||
**Key Benefits:**
|
||||
- **Compile-time safety**: Catch errors at compile time
|
||||
- **No BuildContext needed**: Access state anywhere
|
||||
- **Easy testing**: Simple to test providers in isolation
|
||||
- **Code generation**: Less boilerplate, type-safe
|
||||
|
||||
## Provider Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ UI Layer (Widgets) │
|
||||
│ - ConsumerWidget / ConsumerStatefulWidget │
|
||||
│ - ref.watch() / ref.read() │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓ watches
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Provider Layer │
|
||||
│ - @riverpod annotations │
|
||||
│ - Generated *.g.dart files │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓ uses
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Service / Store Layer │
|
||||
│ - Business logic │
|
||||
│ - Data access │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Provider Types Used
|
||||
|
||||
### 1. StateProvider (Simple State)
|
||||
|
||||
For simple, observable state:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class ThemeNotifier extends _$ThemeNotifier {
|
||||
@override
|
||||
ThemeMode build() {
|
||||
// Load from settings
|
||||
return SettingStore.themeMode;
|
||||
}
|
||||
|
||||
void setTheme(ThemeMode mode) {
|
||||
state = mode;
|
||||
SettingStore.themeMode = mode; // Persist
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```dart
|
||||
class MyWidget extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.watch(themeNotifierProvider);
|
||||
return Text('Theme: $theme');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. AsyncNotifierProvider (Async State)
|
||||
|
||||
For data that loads asynchronously:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class ServerStatus extends _$ServerStatus {
|
||||
@override
|
||||
Future<StatusModel> build(Server server) async {
|
||||
// Initial load
|
||||
return await fetchStatus(server);
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
state = const AsyncValue.loading();
|
||||
state = await AsyncValue.guard(() async {
|
||||
return await fetchStatus(server);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```dart
|
||||
final status = ref.watch(serverStatusProvider(server));
|
||||
|
||||
status.when(
|
||||
data: (data) => StatusWidget(data),
|
||||
loading: () => LoadingWidget(),
|
||||
error: (error, stack) => ErrorWidget(error),
|
||||
)
|
||||
```
|
||||
|
||||
### 3. StreamProvider (Real-time Data)
|
||||
|
||||
For continuous data streams:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
Stream<CpuUsage> cpuUsage(CpuUsageRef ref, Server server) {
|
||||
final client = ref.watch(sshClientProvider(server));
|
||||
final stream = client.monitorCpu();
|
||||
|
||||
// Auto-dispose when not watched
|
||||
ref.onDispose(() {
|
||||
client.stopMonitoring();
|
||||
});
|
||||
|
||||
return stream;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```dart
|
||||
final cpu = ref.watch(cpuUsageProvider(server));
|
||||
|
||||
cpu.when(
|
||||
data: (usage) => CpuChart(usage),
|
||||
loading: () => CircularProgressIndicator(),
|
||||
error: (error, stack) => ErrorWidget(error),
|
||||
)
|
||||
```
|
||||
|
||||
### 4. Family Providers (Parameterized)
|
||||
|
||||
Providers that accept parameters:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
Future<List<Container>> containers(ContainersRef ref, Server server) async {
|
||||
final client = await ref.watch(sshClientProvider(server).future);
|
||||
return await client.listContainers();
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```dart
|
||||
final containers = ref.watch(containersProvider(server));
|
||||
|
||||
// Different servers = different cached states
|
||||
final containers2 = ref.watch(containersProvider(server2));
|
||||
```
|
||||
|
||||
## State Update Patterns
|
||||
|
||||
### Direct State Update
|
||||
|
||||
```dart
|
||||
ref.read(settingsProvider.notifier).updateTheme(darkMode);
|
||||
```
|
||||
|
||||
### Computed State
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
int totalServers(TotalServersRef ref) {
|
||||
final servers = ref.watch(serversProvider);
|
||||
return servers.length;
|
||||
}
|
||||
```
|
||||
|
||||
### Derived State
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
List<Server> onlineServers(OnlineServersRef ref) {
|
||||
final all = ref.watch(serversProvider);
|
||||
return all.where((s) => s.isOnline).toList();
|
||||
}
|
||||
```
|
||||
|
||||
## Server-Specific State
|
||||
|
||||
### Per-Server Providers
|
||||
|
||||
Each server has isolated state:
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class ServerProvider extends _$ServerProvider {
|
||||
@override
|
||||
ServerState build(Server server) {
|
||||
return ServerState.disconnected();
|
||||
}
|
||||
|
||||
Future<void> connect() async {
|
||||
state = ServerState.connecting();
|
||||
try {
|
||||
final client = await genClient(server.spi);
|
||||
state = ServerState.connected(client);
|
||||
} catch (e) {
|
||||
state = ServerState.error(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Provider Keys
|
||||
|
||||
```dart
|
||||
// Unique provider per server
|
||||
@riverpod
|
||||
ServerStatus serverStatus(ServerStatusRef ref, Server server) {
|
||||
// server.id used as key
|
||||
}
|
||||
```
|
||||
|
||||
## Reactive Patterns
|
||||
|
||||
### Auto-Refresh
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class AutoRefreshServerStatus extends _$AutoRefreshServerStatus {
|
||||
Timer? _timer;
|
||||
|
||||
@override
|
||||
Future<StatusModel> build(Server server) async {
|
||||
// Start timer
|
||||
_timer = Timer.periodic(Duration(seconds: 5), (_) {
|
||||
refresh();
|
||||
});
|
||||
|
||||
ref.onDispose(() {
|
||||
_timer?.cancel();
|
||||
});
|
||||
|
||||
return await fetchStatus(server);
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
state = const AsyncValue.loading();
|
||||
state = await AsyncValue.guard(() => fetchStatus(server));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Multi-Provider Dependencies
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
Future<SystemInfo> systemInfo(SystemInfoRef ref, Server server) async {
|
||||
// Wait for SSH client first
|
||||
final client = await ref.watch(sshClientProvider(server).future);
|
||||
|
||||
// Then fetch system info
|
||||
return await client.getSystemInfo();
|
||||
}
|
||||
```
|
||||
|
||||
## State Persistence
|
||||
|
||||
### Hive Integration
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class ServerStoreNotifier extends _$ServerStoreNotifier {
|
||||
@override
|
||||
List<Server> build() {
|
||||
// Load from Hive
|
||||
return Hive.box<Server>('servers').values.toList();
|
||||
}
|
||||
|
||||
void addServer(Server server) {
|
||||
state = [...state, server];
|
||||
// Persist to Hive
|
||||
Hive.box<Server>('servers').put(server.id, server);
|
||||
}
|
||||
|
||||
void removeServer(String id) {
|
||||
state = state.where((s) => s.id != id).toList();
|
||||
// Remove from Hive
|
||||
Hive.box<Server>('servers').delete(id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Error States
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class ConnectionManager extends _$ConnectionManager {
|
||||
@override
|
||||
ConnectionState build() {
|
||||
return ConnectionState.idle();
|
||||
}
|
||||
|
||||
Future<void> connect(Server server) async {
|
||||
state = ConnectionState.connecting();
|
||||
try {
|
||||
final client = await genClient(server.spi);
|
||||
state = ConnectionState.connected(client);
|
||||
} on SocketException catch (e) {
|
||||
state = ConnectionState.error('Network error: $e');
|
||||
} on AuthenticationException catch (e) {
|
||||
state = ConnectionState.error('Auth failed: $e');
|
||||
} catch (e) {
|
||||
state = ConnectionState.error('Unknown error: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Error Recovery
|
||||
|
||||
```dart
|
||||
@riverpod
|
||||
class ResilientFetcher extends _$ResilientFetcher {
|
||||
int _retryCount = 0;
|
||||
|
||||
@override
|
||||
Future<Data> build(Server server) async {
|
||||
return await _fetchWithRetry();
|
||||
}
|
||||
|
||||
Future<Data> _fetchWithRetry() async {
|
||||
try {
|
||||
return await fetchData(server);
|
||||
} catch (e) {
|
||||
if (_retryCount < 3) {
|
||||
_retryCount++;
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
return await _fetchWithRetry();
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Optimizations
|
||||
|
||||
### Provider Keep-Alive
|
||||
|
||||
```dart
|
||||
@Riverpod(keepAlive: true) // Don't dispose when no listeners
|
||||
class GlobalSettings extends _$GlobalSettings {
|
||||
@override
|
||||
Settings build() {
|
||||
return Settings.defaults();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Selective Watching
|
||||
|
||||
```dart
|
||||
// Watch only specific part of state
|
||||
final name = ref.watch(serverProvider.select((s) => s.name));
|
||||
```
|
||||
|
||||
### Provider Caching
|
||||
|
||||
Family providers cache results per parameter:
|
||||
|
||||
```dart
|
||||
// Cached per server ID
|
||||
final status1 = ref.watch(serverStatusProvider(server1));
|
||||
final status2 = ref.watch(serverStatusProvider(server2));
|
||||
// Different states, both cached
|
||||
```
|
||||
|
||||
## Testing with Riverpod
|
||||
|
||||
### Provider Container
|
||||
|
||||
```dart
|
||||
test('fetch server status', () async {
|
||||
final container = ProviderContainer();
|
||||
addTearDown(container.dispose);
|
||||
|
||||
// Override provider
|
||||
container.overrideFactory(
|
||||
sshClientProvider,
|
||||
(ref, server) => MockSshClient(),
|
||||
);
|
||||
|
||||
final status = await container.read(
|
||||
serverStatusProvider(testServer).future,
|
||||
);
|
||||
|
||||
expect(status, isA<StatusModel>());
|
||||
});
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Co-locate providers**: Place near consuming widgets
|
||||
2. **Use code generation**: Always use `@riverpod`
|
||||
3. **Keep providers focused**: Single responsibility
|
||||
4. **Handle loading states**: Always handle AsyncValue states
|
||||
5. **Dispose resources**: Use `ref.onDispose()` for cleanup
|
||||
6. **Avoid deep provider trees**: Keep provider graph flat
|
||||
343
docs/src/content/docs/principles/terminal.md
Normal file
343
docs/src/content/docs/principles/terminal.md
Normal file
@@ -0,0 +1,343 @@
|
||||
---
|
||||
title: Terminal Implementation
|
||||
description: How the SSH terminal works internally
|
||||
---
|
||||
|
||||
The SSH terminal is one of the most complex features, built on a custom xterm.dart fork.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Terminal UI Layer │
|
||||
│ - Tab management │
|
||||
│ - Virtual keyboard │
|
||||
│ - Text selection │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ xterm.dart Emulator │
|
||||
│ - PTY (Pseudo Terminal) │
|
||||
│ - VT100/ANSI emulation │
|
||||
│ - Rendering engine │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ SSH Client Layer │
|
||||
│ - SSH session │
|
||||
│ - Channel management │
|
||||
│ - Data streaming │
|
||||
└─────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Remote Server │
|
||||
│ - Shell process │
|
||||
│ - Command execution │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Terminal Session Lifecycle
|
||||
|
||||
### 1. Session Creation
|
||||
|
||||
```dart
|
||||
Future<TerminalSession> createSession(Spi spi) async {
|
||||
// 1. Get SSH client
|
||||
final client = await genClient(spi);
|
||||
|
||||
// 2. Create PTY
|
||||
final pty = await client.openPty(
|
||||
term: 'xterm-256color',
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
);
|
||||
|
||||
// 3. Initialize terminal emulator
|
||||
final terminal = Terminal(
|
||||
backend: PtyBackend(pty),
|
||||
);
|
||||
|
||||
// 4. Setup resize handler
|
||||
terminal.onResize.listen((size) {
|
||||
pty.resize(size.cols, size.rows);
|
||||
});
|
||||
|
||||
return TerminalSession(
|
||||
terminal: terminal,
|
||||
pty: pty,
|
||||
client: client,
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Terminal Emulation
|
||||
|
||||
The xterm.dart fork provides:
|
||||
|
||||
**VT100/ANSI Emulation:**
|
||||
- Cursor movement
|
||||
- Colors (256-color support)
|
||||
- Text attributes (bold, underline, etc.)
|
||||
- Scrolling regions
|
||||
- Alternate screen buffer
|
||||
|
||||
**Rendering:**
|
||||
- Line-based rendering
|
||||
- Bidirectional text support
|
||||
- Unicode/emoji support
|
||||
- Optimized redraws
|
||||
|
||||
### 3. Data Flow
|
||||
|
||||
```
|
||||
User Input
|
||||
↓
|
||||
Virtual Keyboard / Physical Keyboard
|
||||
↓
|
||||
Terminal Emulator (key → escape sequence)
|
||||
↓
|
||||
SSH Channel (send)
|
||||
↓
|
||||
Remote PTY
|
||||
↓
|
||||
Remote Shell
|
||||
↓
|
||||
Command Output
|
||||
↓
|
||||
SSH Channel (receive)
|
||||
↓
|
||||
Terminal Emulator (parse ANSI codes)
|
||||
↓
|
||||
Render to Screen
|
||||
```
|
||||
|
||||
## Multi-Tab System
|
||||
|
||||
### Tab Management
|
||||
|
||||
```dart
|
||||
class TerminalTabs {
|
||||
final Map<String, TabData> _tabs = {};
|
||||
String? _activeTabId;
|
||||
|
||||
void createTab(Server server) {
|
||||
final id = _generateTabId(server);
|
||||
_tabs[id] = TabData(
|
||||
id: id,
|
||||
name: _generateTabName(server),
|
||||
session: createSession(server),
|
||||
);
|
||||
_activeTabId = id;
|
||||
}
|
||||
|
||||
String _generateTabName(Server server) {
|
||||
final count = _tabs.values
|
||||
.where((t) => t.name.startsWith(server.name))
|
||||
.length;
|
||||
return count == 0 ? server.name : '${server.name}($count)';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Session Persistence
|
||||
|
||||
Tabs maintain state across navigation:
|
||||
|
||||
- SSH connection kept alive
|
||||
- Terminal state preserved
|
||||
- Scroll buffer maintained
|
||||
- Input history retained
|
||||
|
||||
## Virtual Keyboard
|
||||
|
||||
### Platform-Specific Implementation
|
||||
|
||||
**iOS:**
|
||||
- UIView-based custom keyboard
|
||||
- Toggleable with keyboard button
|
||||
- Auto-show/hide based on focus
|
||||
|
||||
**Android:**
|
||||
- Custom input method
|
||||
- Integrated with system keyboard
|
||||
- Quick action buttons
|
||||
|
||||
### Keyboard Buttons
|
||||
|
||||
| Button | Action |
|
||||
|--------|--------|
|
||||
| **Toggle** | Show/hide system keyboard |
|
||||
| **Ctrl** | Send Ctrl modifier |
|
||||
| **Alt** | Send Alt modifier |
|
||||
| **SFTP** | Open current directory |
|
||||
| **Clipboard** | Copy/Paste context-aware |
|
||||
| **Snippets** | Execute snippet |
|
||||
|
||||
### Key Encoding
|
||||
|
||||
```dart
|
||||
String encodeKey(Key key) {
|
||||
switch (key) {
|
||||
case Key.enter:
|
||||
return '\r';
|
||||
case Key.tab:
|
||||
return '\t';
|
||||
case Key.escape:
|
||||
return '\x1b';
|
||||
case Key.ctrlC:
|
||||
return '\x03';
|
||||
// ... more keys
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Text Selection
|
||||
|
||||
### Selection Mode
|
||||
|
||||
1. **Long press**: Enter selection mode
|
||||
2. **Drag**: Extend selection
|
||||
3. **Release**: Copy to clipboard
|
||||
|
||||
### Selection Storage
|
||||
|
||||
```dart
|
||||
class TextSelection {
|
||||
final BufferRange range;
|
||||
final String text;
|
||||
|
||||
void copyToClipboard() {
|
||||
Clipboard.setData(ClipboardData(text: text));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Font and Dimensions
|
||||
|
||||
### Size Calculation
|
||||
|
||||
```dart
|
||||
class TerminalDimensions {
|
||||
static Size calculate(double fontSize, Size screenSize) {
|
||||
final charWidth = fontSize * 0.6; // Monospace aspect ratio
|
||||
final charHeight = fontSize * 1.2;
|
||||
|
||||
final cols = (screenSize.width / charWidth).floor();
|
||||
final rows = (screenSize.height / charHeight).floor();
|
||||
|
||||
return Size(cols.toDouble(), rows.toDouble());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pinch-to-Zoom
|
||||
|
||||
```dart
|
||||
GestureDetector(
|
||||
onScaleStart: () => _baseFontSize = currentFontSize,
|
||||
onScaleUpdate: (details) {
|
||||
final newFontSize = _baseFontSize * details.scale;
|
||||
resize(newFontSize);
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
## Color Scheme
|
||||
|
||||
### ANSI Color Mapping
|
||||
|
||||
```dart
|
||||
const colorMap = {
|
||||
0: Color(0x000000), // Black
|
||||
1: Color(0x800000), // Red
|
||||
2: Color(0x008000), // Green
|
||||
3: Color(0x808000), // Yellow
|
||||
4: Color(0x000080), // Blue
|
||||
5: Color(0x800080), // Magenta
|
||||
6: Color(0x008080), // Cyan
|
||||
7: Color(0xC0C0C0), // White
|
||||
// ... 256-color palette
|
||||
};
|
||||
```
|
||||
|
||||
### Theme Support
|
||||
|
||||
- **Light**: Light background, dark text
|
||||
- **Dark**: Dark background, light text
|
||||
- **AMOLED**: Pure black background
|
||||
|
||||
## Performance Optimizations
|
||||
|
||||
### Rendering Optimizations
|
||||
|
||||
- **Dirty rectangle**: Only redraw changed regions
|
||||
- **Line caching**: Cache rendered lines
|
||||
- **Lazy scrolling**: Virtual scrolling for long buffers
|
||||
|
||||
### Data Optimizations
|
||||
|
||||
- **Batch updates**: Coalesce multiple writes
|
||||
- **Compression**: Compress scroll buffer
|
||||
- **Debouncing**: Debounce rapid inputs
|
||||
|
||||
## Clipboard Integration
|
||||
|
||||
### Copy Selection
|
||||
|
||||
```dart
|
||||
void copySelection() {
|
||||
final selected = terminal.getSelection();
|
||||
Clipboard.setData(ClipboardData(text: selected));
|
||||
}
|
||||
```
|
||||
|
||||
### Paste Clipboard
|
||||
|
||||
```dart
|
||||
Future<void> pasteClipboard() async {
|
||||
final data = await Clipboard.getData('text/plain');
|
||||
if (data?.text != null) {
|
||||
terminal.paste(data!.text!);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Context-Aware Button
|
||||
|
||||
- **Has selection**: Show "Copy"
|
||||
- **Has clipboard**: Show "Paste"
|
||||
- **Both**: Show primary action
|
||||
|
||||
## Special Features
|
||||
|
||||
### Snippet Execution
|
||||
|
||||
```dart
|
||||
void executeSnippet(Snippet snippet) {
|
||||
final formatted = formatSnippet(snippet);
|
||||
terminal.paste(formatted);
|
||||
terminal.paste('\r'); // Execute
|
||||
}
|
||||
```
|
||||
|
||||
### SFTP Quick Access
|
||||
|
||||
```dart
|
||||
void openSftp() async {
|
||||
final cwd = await terminal.getCurrentWorkingDirectory();
|
||||
Navigator.push(
|
||||
context,
|
||||
SftpPage(initialPath: cwd),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Keep-Alive
|
||||
|
||||
```dart
|
||||
Timer.periodic(Duration(seconds: 30), (_) {
|
||||
if (terminal.isActive) {
|
||||
terminal.send('\x00'); // NUL - no-op keep-alive
|
||||
}
|
||||
});
|
||||
```
|
||||
51
docs/src/content/docs/quick-start.mdx
Normal file
51
docs/src/content/docs/quick-start.mdx
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: Quick Start
|
||||
description: Get up and running with Flutter Server Box in minutes
|
||||
---
|
||||
|
||||
Follow this quick start guide to connect to your first server and start monitoring.
|
||||
|
||||
## Step 1: Add a Server
|
||||
|
||||
1. Open Flutter Server Box
|
||||
2. Tap the **+** button to add a new server
|
||||
3. Fill in the server information:
|
||||
- **Name**: A friendly name for your server
|
||||
- **Host**: IP address or domain name
|
||||
- **Port**: SSH port (default: 22)
|
||||
- **User**: SSH username
|
||||
- **Password or Key**: Authentication method
|
||||
|
||||
4. Tap **Save** to add the server
|
||||
|
||||
## Step 2: Connect and Monitor
|
||||
|
||||
1. Tap on your server card to connect
|
||||
2. The app will establish an SSH connection
|
||||
3. You'll see real-time status for:
|
||||
- CPU usage
|
||||
- Memory (RAM) and Swap
|
||||
- Disk usage
|
||||
- Network speed
|
||||
|
||||
## Step 3: Explore Features
|
||||
|
||||
Once connected, you can:
|
||||
|
||||
- **Open Terminal**: Tap the terminal button for full SSH access
|
||||
- **Browse Files**: Use SFTP to manage files
|
||||
- **Manage Containers**: View and control Docker containers
|
||||
- **View Processes**: Check running processes
|
||||
- **Run Snippets**: Execute saved commands
|
||||
|
||||
## Tips
|
||||
|
||||
- **Biometric Authentication**: Enable Face ID / Touch ID / Fingerprint for quick access (mobile)
|
||||
- **Home Screen Widgets**: Add server status widgets to your home screen (iOS/Android)
|
||||
- **Background Running**: Keep connections alive in the background (Android)
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Explore the [Features](/features/) section for detailed guides
|
||||
- Configure [SSH Keys](/configuration/ssh-keys/) for passwordless authentication
|
||||
- Customize your experience in [Settings](/configuration/appearance/)
|
||||
7
docs/src/styles/custom.css
Normal file
7
docs/src/styles/custom.css
Normal file
@@ -0,0 +1,7 @@
|
||||
/* Flutter Server Box Custom Styles */
|
||||
|
||||
:root {
|
||||
--sl-color-accent: #02569b;
|
||||
--sl-color-accent-low: #02569b15;
|
||||
--starlight-cards--border: var(--sl-color-accent-low);
|
||||
}
|
||||
5
docs/tsconfig.json
Normal file
5
docs/tsconfig.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"include": [".astro/types.d.ts", "**/*"],
|
||||
"exclude": ["dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user