mirror of
https://github.com/tauri-apps/tauri-docs.git
synced 2026-01-31 00:35:16 +01:00
Add Prettier formatting (#1360)
* add prettier Signed-off-by: Lorenzo Lewis <lorenzo_lewis@icloud.com> * add prettier script Signed-off-by: Lorenzo Lewis <lorenzo_lewis@icloud.com> * add prettier configs Signed-off-by: Lorenzo Lewis <lorenzo_lewis@icloud.com> * add git modules to ignore Signed-off-by: Lorenzo Lewis <lorenzo_lewis@icloud.com> * ignore formatting autogenerated references Signed-off-by: Lorenzo Lewis <lorenzo_lewis@icloud.com> * format Signed-off-by: Lorenzo Lewis <lorenzo_lewis@icloud.com> * add format workflow for testing Signed-off-by: Lorenzo Lewis <lorenzo_lewis@icloud.com> * set package manager Signed-off-by: Lorenzo Lewis <lorenzo_lewis@icloud.com> * valid package manager? Signed-off-by: Lorenzo Lewis <lorenzo_lewis@icloud.com> * test applying formatting Signed-off-by: Lorenzo Lewis <lorenzo_lewis@icloud.com> * [ci] format * update action target branch Signed-off-by: Lorenzo Lewis <lorenzo_lewis@icloud.com> * line break Signed-off-by: Lorenzo Lewis <lorenzo_lewis@icloud.com> --------- Signed-off-by: Lorenzo Lewis <lorenzo_lewis@icloud.com> Co-authored-by: lorenzolewis <lorenzolewis@users.noreply.github.com>
This commit is contained in:
34
.github/workflows/format.yml
vendored
Normal file
34
.github/workflows/format.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# Adapted from https://github.com/withastro/starlight/blob/main/.github/workflows/format.yml
|
||||
|
||||
name: Format
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- next
|
||||
|
||||
jobs:
|
||||
format:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code using Git
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
# Needs access to push to main
|
||||
token: ${{ secrets.ORG_TAURI_BOT_PAT }}
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
cache: 'pnpm'
|
||||
- run: pnpm i
|
||||
- name: Format with Prettier
|
||||
run: pnpm format
|
||||
- name: Commit changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
commit_message: '[ci] format'
|
||||
branch: ${{ github.head_ref }}
|
||||
commit_user_name: fredkbot
|
||||
commit_user_email: fred+astrobot@astro.build
|
||||
14
.prettierignore
Normal file
14
.prettierignore
Normal file
@@ -0,0 +1,14 @@
|
||||
# Deep Directories
|
||||
**/node_modules
|
||||
|
||||
# Generated Directories
|
||||
**/dist
|
||||
**/.astro
|
||||
src/content/docs/2/reference
|
||||
|
||||
# Git Modules
|
||||
packages/tauri
|
||||
packages/plugins-workspace
|
||||
|
||||
# Files
|
||||
pnpm-lock.yaml
|
||||
23
.prettierrc
Normal file
23
.prettierrc
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"printWidth": 100,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5",
|
||||
"useTabs": true,
|
||||
"plugins": ["prettier-plugin-astro"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [".*", "*.json", "*.md", "*.toml", "*.yml"],
|
||||
"options": {
|
||||
"useTabs": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.md", "*.mdx"],
|
||||
"options": {
|
||||
"printWidth": 80
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
258
astro.config.mjs
258
astro.config.mjs
@@ -9,133 +9,133 @@ await configGenerator();
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: 'https://beta.tauri.app',
|
||||
compressHTML: true,
|
||||
build: {
|
||||
inlineStylesheets: 'always',
|
||||
},
|
||||
integrations: [
|
||||
starlight({
|
||||
title: 'Tauri',
|
||||
description: 'The cross-platform app building toolkit',
|
||||
logo: {
|
||||
src: './src/assets/logo.svg',
|
||||
// light: './src/assets/logo_light.svg',
|
||||
replacesTitle: true,
|
||||
},
|
||||
social: {
|
||||
github: 'https://github.com/tauri-apps/tauri',
|
||||
discord: 'https://discord.com/invite/tauri',
|
||||
twitter: 'https://twitter.com/TauriApps',
|
||||
mastodon: 'https://fosstodon.org/@TauriApps',
|
||||
},
|
||||
editLink: {
|
||||
baseUrl: 'https://github.com/tauri-apps/tauri-docs/edit/starlight',
|
||||
},
|
||||
customCss: ['./src/styles/custom.css'],
|
||||
sidebar: [
|
||||
{
|
||||
label: 'Quick Start',
|
||||
items: [
|
||||
{ label: 'Why Tauri?', link: '/2/guide/' },
|
||||
{ label: 'Prerequisites', link: '/2/guide/prerequisites' },
|
||||
{
|
||||
label: 'Create a Project',
|
||||
link: '/2/guide/create/',
|
||||
},
|
||||
{
|
||||
label: 'Upgrade & Migrate',
|
||||
link: '2/guide/upgrade-migrate',
|
||||
},
|
||||
{
|
||||
label: 'Core Concepts',
|
||||
link: '2/guide/core-concepts',
|
||||
},
|
||||
{
|
||||
label: 'Plugins',
|
||||
link: '2/guide/plugins',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Guides',
|
||||
items: [
|
||||
{
|
||||
label: 'Develop',
|
||||
link: '2/guide/develop',
|
||||
},
|
||||
{
|
||||
label: 'Debug',
|
||||
link: '2/guide/debug',
|
||||
},
|
||||
{
|
||||
label: 'Test',
|
||||
link: '2/guide/test',
|
||||
},
|
||||
{
|
||||
label: 'Build & Distribute',
|
||||
link: '2/guide/build-distribute',
|
||||
},
|
||||
{
|
||||
label: 'Troubleshoot',
|
||||
link: '2/guide/troubleshoot',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Features & Recipes',
|
||||
link: '2/recipe',
|
||||
},
|
||||
{
|
||||
label: 'References',
|
||||
items: [
|
||||
{
|
||||
label: 'Configuration',
|
||||
link: '2/reference/config',
|
||||
},
|
||||
{
|
||||
label: 'Command Line Interface (CLI)',
|
||||
link: '2/reference/cli',
|
||||
},
|
||||
{
|
||||
label: 'Core JavaScript API',
|
||||
link: '2/reference/core/js',
|
||||
},
|
||||
{
|
||||
label: 'Core Rust API (via Docs.rs)',
|
||||
// TODO: Is there a way to link this to the latest pre-released version?
|
||||
link: 'https://docs.rs/tauri/~2.0.0-alpha',
|
||||
},
|
||||
{
|
||||
label: 'Plugin References',
|
||||
link: '2/reference/plugin',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Tauri v1',
|
||||
link: 'https://tauri.app',
|
||||
},
|
||||
],
|
||||
locales,
|
||||
lastUpdated: true,
|
||||
}),
|
||||
],
|
||||
markdown: {
|
||||
shikiConfig: {
|
||||
langs: ['powershell'],
|
||||
},
|
||||
rehypePlugins: [
|
||||
rehypeHeadingIds,
|
||||
[
|
||||
rehypeAutolinkHeadings,
|
||||
{
|
||||
behavior: 'wrap',
|
||||
properties: { ariaHidden: true, tabIndex: -1, class: 'heading-link' },
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
// Process images with sharp: https://docs.astro.build/en/guides/assets/#using-sharp
|
||||
image: { service: { entrypoint: 'astro/assets/services/sharp' } },
|
||||
site: 'https://beta.tauri.app',
|
||||
compressHTML: true,
|
||||
build: {
|
||||
inlineStylesheets: 'always',
|
||||
},
|
||||
integrations: [
|
||||
starlight({
|
||||
title: 'Tauri',
|
||||
description: 'The cross-platform app building toolkit',
|
||||
logo: {
|
||||
src: './src/assets/logo.svg',
|
||||
// light: './src/assets/logo_light.svg',
|
||||
replacesTitle: true,
|
||||
},
|
||||
social: {
|
||||
github: 'https://github.com/tauri-apps/tauri',
|
||||
discord: 'https://discord.com/invite/tauri',
|
||||
twitter: 'https://twitter.com/TauriApps',
|
||||
mastodon: 'https://fosstodon.org/@TauriApps',
|
||||
},
|
||||
editLink: {
|
||||
baseUrl: 'https://github.com/tauri-apps/tauri-docs/edit/starlight',
|
||||
},
|
||||
customCss: ['./src/styles/custom.css'],
|
||||
sidebar: [
|
||||
{
|
||||
label: 'Quick Start',
|
||||
items: [
|
||||
{ label: 'Why Tauri?', link: '/2/guide/' },
|
||||
{ label: 'Prerequisites', link: '/2/guide/prerequisites' },
|
||||
{
|
||||
label: 'Create a Project',
|
||||
link: '/2/guide/create/',
|
||||
},
|
||||
{
|
||||
label: 'Upgrade & Migrate',
|
||||
link: '2/guide/upgrade-migrate',
|
||||
},
|
||||
{
|
||||
label: 'Core Concepts',
|
||||
link: '2/guide/core-concepts',
|
||||
},
|
||||
{
|
||||
label: 'Plugins',
|
||||
link: '2/guide/plugins',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Guides',
|
||||
items: [
|
||||
{
|
||||
label: 'Develop',
|
||||
link: '2/guide/develop',
|
||||
},
|
||||
{
|
||||
label: 'Debug',
|
||||
link: '2/guide/debug',
|
||||
},
|
||||
{
|
||||
label: 'Test',
|
||||
link: '2/guide/test',
|
||||
},
|
||||
{
|
||||
label: 'Build & Distribute',
|
||||
link: '2/guide/build-distribute',
|
||||
},
|
||||
{
|
||||
label: 'Troubleshoot',
|
||||
link: '2/guide/troubleshoot',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Features & Recipes',
|
||||
link: '2/recipe',
|
||||
},
|
||||
{
|
||||
label: 'References',
|
||||
items: [
|
||||
{
|
||||
label: 'Configuration',
|
||||
link: '2/reference/config',
|
||||
},
|
||||
{
|
||||
label: 'Command Line Interface (CLI)',
|
||||
link: '2/reference/cli',
|
||||
},
|
||||
{
|
||||
label: 'Core JavaScript API',
|
||||
link: '2/reference/core/js',
|
||||
},
|
||||
{
|
||||
label: 'Core Rust API (via Docs.rs)',
|
||||
// TODO: Is there a way to link this to the latest pre-released version?
|
||||
link: 'https://docs.rs/tauri/~2.0.0-alpha',
|
||||
},
|
||||
{
|
||||
label: 'Plugin References',
|
||||
link: '2/reference/plugin',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Tauri v1',
|
||||
link: 'https://tauri.app',
|
||||
},
|
||||
],
|
||||
locales,
|
||||
lastUpdated: true,
|
||||
}),
|
||||
],
|
||||
markdown: {
|
||||
shikiConfig: {
|
||||
langs: ['powershell'],
|
||||
},
|
||||
rehypePlugins: [
|
||||
rehypeHeadingIds,
|
||||
[
|
||||
rehypeAutolinkHeadings,
|
||||
{
|
||||
behavior: 'wrap',
|
||||
properties: { ariaHidden: true, tabIndex: -1, class: 'heading-link' },
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
// Process images with sharp: https://docs.astro.build/en/guides/assets/#using-sharp
|
||||
image: { service: { entrypoint: 'astro/assets/services/sharp' } },
|
||||
});
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
"label": "Français",
|
||||
"lang": "fr"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"dev:setup:plugins-workspace": "pnpm --prefix packages/plugins-workspace install",
|
||||
"dev:setup": "pnpm dev:setup:submodules && pnpm dev:setup:tauri && pnpm dev:setup:plugins-workspace",
|
||||
"dev": "astro dev",
|
||||
"format": "prettier -w --cache --plugin prettier-plugin-astro .",
|
||||
"build:reference": "pnpm --filter js-api-generator run build",
|
||||
"build:astro": "astro build",
|
||||
"build:i18n": "pnpm --filter docs-i18n-tracker run build",
|
||||
@@ -22,9 +23,12 @@
|
||||
"@types/json-schema": "^7.0.12",
|
||||
"astro": "^2.10.1",
|
||||
"astro-feelback": "^0.3.4",
|
||||
"prettier": "^3.0.1",
|
||||
"prettier-plugin-astro": "^0.11.0",
|
||||
"rehype-autolink-headings": "^6.1.1",
|
||||
"sharp": "^0.32.4"
|
||||
},
|
||||
"packageManager": "pnpm@8.2.0",
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"typedoc-plugin-markdown@4.0.0-next.17": "patches/typedoc-plugin-markdown@4.0.0-next.17.patch"
|
||||
|
||||
@@ -2,18 +2,18 @@ import { TranslationStatusBuilder } from './lib/translation-status/builder';
|
||||
import locales from '../../locales.json';
|
||||
|
||||
const translationStatusBuilder = new TranslationStatusBuilder({
|
||||
pageSourceDir: '../../src/content/docs',
|
||||
htmlOutputFilePath: '../../dist/contribute/translate-status.html',
|
||||
sourceLanguage: 'en',
|
||||
targetLanguages: Object.values(locales)
|
||||
.map((el) => el.lang)
|
||||
.filter((lang) => lang !== 'en')
|
||||
.sort(),
|
||||
languageLabels: Object.values(locales)
|
||||
.filter((loc) => loc.lang !== 'en')
|
||||
.reduce((acc, curr) => ({ [curr.lang]: curr.label, ...acc }), {}),
|
||||
githubRepo: process.env.GITHUB_REPOSITORY || 'tauri-apps/tauri-docs',
|
||||
githubToken: process.env.GITHUB_TOKEN,
|
||||
pageSourceDir: '../../src/content/docs',
|
||||
htmlOutputFilePath: '../../dist/contribute/translate-status.html',
|
||||
sourceLanguage: 'en',
|
||||
targetLanguages: Object.values(locales)
|
||||
.map((el) => el.lang)
|
||||
.filter((lang) => lang !== 'en')
|
||||
.sort(),
|
||||
languageLabels: Object.values(locales)
|
||||
.filter((loc) => loc.lang !== 'en')
|
||||
.reduce((acc, curr) => ({ [curr.lang]: curr.label, ...acc }), {}),
|
||||
githubRepo: process.env.GITHUB_REPOSITORY || 'tauri-apps/tauri-docs',
|
||||
githubToken: process.env.GITHUB_TOKEN,
|
||||
});
|
||||
|
||||
await translationStatusBuilder.run();
|
||||
|
||||
@@ -13,11 +13,11 @@ import type { PageData, PageIndex, PageTranslationStatus } from './types';
|
||||
export const COMMIT_IGNORE = /(en-only|typo|broken link|i18nReady|i18nIgnore)/i;
|
||||
|
||||
interface PullRequest {
|
||||
html_url: string;
|
||||
title: string;
|
||||
labels: {
|
||||
name: string;
|
||||
}[];
|
||||
html_url: string;
|
||||
title: string;
|
||||
labels: {
|
||||
name: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,489 +27,452 @@ interface PullRequest {
|
||||
* This code is designed to be run on every push to the `main` branch.
|
||||
*/
|
||||
export class TranslationStatusBuilder {
|
||||
constructor(config: {
|
||||
pageSourceDir: string;
|
||||
/**
|
||||
* Full path & file name of the HTML file that the translation status should be written to.
|
||||
* If the parent path does not exist yet, it will be created.
|
||||
* */
|
||||
htmlOutputFilePath: string;
|
||||
sourceLanguage: string;
|
||||
targetLanguages: string[];
|
||||
languageLabels: { [key: string]: string };
|
||||
githubRepo: string;
|
||||
githubToken?: string | undefined;
|
||||
}) {
|
||||
this.pageSourceDir = config.pageSourceDir;
|
||||
this.htmlOutputFilePath = path.resolve(config.htmlOutputFilePath);
|
||||
this.sourceLanguage = config.sourceLanguage;
|
||||
this.targetLanguages = config.targetLanguages;
|
||||
this.languageLabels = config.languageLabels;
|
||||
this.githubRepo = config.githubRepo;
|
||||
this.githubToken = config.githubToken ?? '';
|
||||
this.git = simpleGit({
|
||||
maxConcurrentProcesses: Math.max(2, Math.min(32, os.cpus().length)),
|
||||
});
|
||||
}
|
||||
constructor(config: {
|
||||
pageSourceDir: string;
|
||||
/**
|
||||
* Full path & file name of the HTML file that the translation status should be written to.
|
||||
* If the parent path does not exist yet, it will be created.
|
||||
* */
|
||||
htmlOutputFilePath: string;
|
||||
sourceLanguage: string;
|
||||
targetLanguages: string[];
|
||||
languageLabels: { [key: string]: string };
|
||||
githubRepo: string;
|
||||
githubToken?: string | undefined;
|
||||
}) {
|
||||
this.pageSourceDir = config.pageSourceDir;
|
||||
this.htmlOutputFilePath = path.resolve(config.htmlOutputFilePath);
|
||||
this.sourceLanguage = config.sourceLanguage;
|
||||
this.targetLanguages = config.targetLanguages;
|
||||
this.languageLabels = config.languageLabels;
|
||||
this.githubRepo = config.githubRepo;
|
||||
this.githubToken = config.githubToken ?? '';
|
||||
this.git = simpleGit({
|
||||
maxConcurrentProcesses: Math.max(2, Math.min(32, os.cpus().length)),
|
||||
});
|
||||
}
|
||||
|
||||
readonly pageSourceDir;
|
||||
readonly htmlOutputFilePath;
|
||||
readonly sourceLanguage;
|
||||
readonly targetLanguages;
|
||||
readonly languageLabels;
|
||||
readonly githubRepo;
|
||||
readonly githubToken;
|
||||
readonly git;
|
||||
readonly pageSourceDir;
|
||||
readonly htmlOutputFilePath;
|
||||
readonly sourceLanguage;
|
||||
readonly targetLanguages;
|
||||
readonly languageLabels;
|
||||
readonly githubRepo;
|
||||
readonly githubToken;
|
||||
readonly git;
|
||||
|
||||
async run() {
|
||||
// Before we start, validate that this is not a shallow clone of the repo
|
||||
const isShallowRepo = await this.git.revparse(['--is-shallow-repository']);
|
||||
if (isShallowRepo !== 'false') {
|
||||
output.error(dedent`This script cannot operate on a shallow clone of the git repository.
|
||||
async run() {
|
||||
// Before we start, validate that this is not a shallow clone of the repo
|
||||
const isShallowRepo = await this.git.revparse(['--is-shallow-repository']);
|
||||
if (isShallowRepo !== 'false') {
|
||||
output.error(dedent`This script cannot operate on a shallow clone of the git repository.
|
||||
Please add the checkout setting "fetch-depth: 0" to your GitHub workflow:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
`);
|
||||
process.exit(1);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
output.debug(`*** Building translation status`);
|
||||
output.debug(`*** Building translation status`);
|
||||
|
||||
// Ensure that the output directory exists before continuing
|
||||
output.debug(`- Output file path: ${this.htmlOutputFilePath}`);
|
||||
const outputDir = path.dirname(this.htmlOutputFilePath);
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
// Ensure that the output directory exists before continuing
|
||||
output.debug(`- Output file path: ${this.htmlOutputFilePath}`);
|
||||
const outputDir = path.dirname(this.htmlOutputFilePath);
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Create an index of all Markdown/MDX pages grouped by language,
|
||||
// with information about the last minor & major commit per page
|
||||
output.debug(`- Generating page index...`);
|
||||
const pages = await this.createPageIndex();
|
||||
// Create an index of all Markdown/MDX pages grouped by language,
|
||||
// with information about the last minor & major commit per page
|
||||
output.debug(`- Generating page index...`);
|
||||
const pages = await this.createPageIndex();
|
||||
|
||||
// Determine translation status by source page
|
||||
const statusByPage = this.getTranslationStatusByPage(pages);
|
||||
// Determine translation status by source page
|
||||
const statusByPage = this.getTranslationStatusByPage(pages);
|
||||
|
||||
// Fetch all pull requests
|
||||
const pullRequests = await this.getPullRequests();
|
||||
// Fetch all pull requests
|
||||
const pullRequests = await this.getPullRequests();
|
||||
|
||||
// Render a human-friendly summary
|
||||
output.debug(`- Building HTML file...`);
|
||||
const html = this.renderHtmlStatusPage(statusByPage, pullRequests);
|
||||
// Render a human-friendly summary
|
||||
output.debug(`- Building HTML file...`);
|
||||
const html = this.renderHtmlStatusPage(statusByPage, pullRequests);
|
||||
|
||||
// Write HTML output to file
|
||||
fs.writeFileSync(this.htmlOutputFilePath, html);
|
||||
// Write HTML output to file
|
||||
fs.writeFileSync(this.htmlOutputFilePath, html);
|
||||
|
||||
output.debug('');
|
||||
output.debug('*** Success!');
|
||||
output.debug('');
|
||||
}
|
||||
output.debug('');
|
||||
output.debug('*** Success!');
|
||||
output.debug('');
|
||||
}
|
||||
|
||||
/** Get all pull requests with the `i18n` tag */
|
||||
async getPullRequests() {
|
||||
const pullRequests: PullRequest[] = await githubGet({
|
||||
url: `https://api.github.com/repos/${this.githubRepo}/pulls?state=open&per_page=100`,
|
||||
githubToken: this.githubToken,
|
||||
});
|
||||
/** Get all pull requests with the `i18n` tag */
|
||||
async getPullRequests() {
|
||||
const pullRequests: PullRequest[] = await githubGet({
|
||||
url: `https://api.github.com/repos/${this.githubRepo}/pulls?state=open&per_page=100`,
|
||||
githubToken: this.githubToken,
|
||||
});
|
||||
|
||||
return pullRequests.filter((pr) =>
|
||||
pr.labels.find((label) => label.name === 'i18n')
|
||||
);
|
||||
}
|
||||
return pullRequests.filter((pr) => pr.labels.find((label) => label.name === 'i18n'));
|
||||
}
|
||||
|
||||
async createPageIndex(): Promise<PageIndex> {
|
||||
// Initialize a new page index with a stable key order
|
||||
const pages: PageIndex = {
|
||||
[this.sourceLanguage]: {},
|
||||
};
|
||||
this.targetLanguages.forEach((lang) => (pages[lang] = {}));
|
||||
async createPageIndex(): Promise<PageIndex> {
|
||||
// Initialize a new page index with a stable key order
|
||||
const pages: PageIndex = {
|
||||
[this.sourceLanguage]: {},
|
||||
};
|
||||
this.targetLanguages.forEach((lang) => (pages[lang] = {}));
|
||||
|
||||
// Enumerate all markdown pages with supported languages in pageSourceDir,
|
||||
// retrieve their page data and update them
|
||||
const pagePaths = await glob(`**/*.{md,mdx}`, {
|
||||
cwd: this.pageSourceDir,
|
||||
});
|
||||
const updatedPages = await Promise.all(
|
||||
pagePaths.sort().map(async (pagePath) => {
|
||||
const pathParts = pagePath.split('/');
|
||||
const isLanguageSubpathIncluded = this.targetLanguages.includes(
|
||||
pathParts[0]!
|
||||
);
|
||||
// Enumerate all markdown pages with supported languages in pageSourceDir,
|
||||
// retrieve their page data and update them
|
||||
const pagePaths = await glob(`**/*.{md,mdx}`, {
|
||||
cwd: this.pageSourceDir,
|
||||
});
|
||||
const updatedPages = await Promise.all(
|
||||
pagePaths.sort().map(async (pagePath) => {
|
||||
const pathParts = pagePath.split('/');
|
||||
const isLanguageSubpathIncluded = this.targetLanguages.includes(pathParts[0]!);
|
||||
|
||||
// If the first path of a file does not belong to a language, it will be by default a page of the original language set.
|
||||
const lang = isLanguageSubpathIncluded
|
||||
? pathParts[0]
|
||||
: this.sourceLanguage;
|
||||
const subpath = pathParts.splice(1).join('/');
|
||||
// If the first path of a file does not belong to a language, it will be by default a page of the original language set.
|
||||
const lang = isLanguageSubpathIncluded ? pathParts[0] : this.sourceLanguage;
|
||||
const subpath = pathParts.splice(1).join('/');
|
||||
|
||||
// Create or update page data for the page
|
||||
return {
|
||||
lang,
|
||||
subpath: isLanguageSubpathIncluded ? subpath : pagePath,
|
||||
pageData: await this.getSinglePageData(pagePath),
|
||||
};
|
||||
})
|
||||
);
|
||||
// Create or update page data for the page
|
||||
return {
|
||||
lang,
|
||||
subpath: isLanguageSubpathIncluded ? subpath : pagePath,
|
||||
pageData: await this.getSinglePageData(pagePath),
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
// Write the updated pages to the index
|
||||
updatedPages.forEach((page) => {
|
||||
if (!page) return;
|
||||
const { lang, subpath, pageData } = page;
|
||||
if (!pageData) return;
|
||||
pages[lang!]![subpath] = pageData;
|
||||
});
|
||||
// Write the updated pages to the index
|
||||
updatedPages.forEach((page) => {
|
||||
if (!page) return;
|
||||
const { lang, subpath, pageData } = page;
|
||||
if (!pageData) return;
|
||||
pages[lang!]![subpath] = pageData;
|
||||
});
|
||||
|
||||
return pages;
|
||||
}
|
||||
return pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the markdown page located in the pageSourceDir subpath `pagePath`
|
||||
* and creates a new page data object based on its frontmatter and git history.
|
||||
*/
|
||||
async getSinglePageData(pagePath: string): Promise<PageData | undefined> {
|
||||
const fullFilePath = `${this.pageSourceDir}/${pagePath}`;
|
||||
/**
|
||||
* Processes the markdown page located in the pageSourceDir subpath `pagePath`
|
||||
* and creates a new page data object based on its frontmatter and git history.
|
||||
*/
|
||||
async getSinglePageData(pagePath: string): Promise<PageData | undefined> {
|
||||
const fullFilePath = `${this.pageSourceDir}/${pagePath}`;
|
||||
|
||||
// Retrieve git history for the current page
|
||||
const gitHistory = await this.getGitHistory(fullFilePath);
|
||||
// Retrieve git history for the current page
|
||||
const gitHistory = await this.getGitHistory(fullFilePath);
|
||||
|
||||
if (!gitHistory) return;
|
||||
if (!gitHistory) return;
|
||||
|
||||
// Retrieve i18nReady flag from frontmatter
|
||||
const frontMatterBlock = tryGetFrontMatterBlock(fullFilePath);
|
||||
const i18nReady = /^\s*i18nReady:\s*true\s*$/m.test(frontMatterBlock);
|
||||
// Retrieve i18nReady flag from frontmatter
|
||||
const frontMatterBlock = tryGetFrontMatterBlock(fullFilePath);
|
||||
const i18nReady = /^\s*i18nReady:\s*true\s*$/m.test(frontMatterBlock);
|
||||
|
||||
return {
|
||||
...(i18nReady ? { i18nReady: true } : {}),
|
||||
lastChange: gitHistory.lastCommitDate,
|
||||
lastCommitMsg: gitHistory.lastCommitMessage,
|
||||
lastMajorChange: gitHistory.lastMajorCommitDate,
|
||||
lastMajorCommitMsg: gitHistory.lastMajorCommitMessage,
|
||||
};
|
||||
}
|
||||
return {
|
||||
...(i18nReady ? { i18nReady: true } : {}),
|
||||
lastChange: gitHistory.lastCommitDate,
|
||||
lastCommitMsg: gitHistory.lastCommitMessage,
|
||||
lastMajorChange: gitHistory.lastMajorCommitDate,
|
||||
lastMajorCommitMsg: gitHistory.lastMajorCommitMessage,
|
||||
};
|
||||
}
|
||||
|
||||
async getGitHistory(filePath: string) {
|
||||
const gitLog = await this.git.log({
|
||||
file: filePath,
|
||||
strictDate: true,
|
||||
});
|
||||
async getGitHistory(filePath: string) {
|
||||
const gitLog = await this.git.log({
|
||||
file: filePath,
|
||||
strictDate: true,
|
||||
});
|
||||
|
||||
const lastCommit = gitLog.latest;
|
||||
if (!lastCommit) {
|
||||
return;
|
||||
// Disabled since we have generated reference files
|
||||
// throw new Error(dedent`Failed to retrieve last commit information for file
|
||||
// "${filePath}". Your working copy should not contain uncommitted new pages
|
||||
// when running this script.`);
|
||||
}
|
||||
const lastCommit = gitLog.latest;
|
||||
if (!lastCommit) {
|
||||
return;
|
||||
// Disabled since we have generated reference files
|
||||
// throw new Error(dedent`Failed to retrieve last commit information for file
|
||||
// "${filePath}". Your working copy should not contain uncommitted new pages
|
||||
// when running this script.`);
|
||||
}
|
||||
|
||||
// Attempt to find the last "major" commit, ignoring any commits that
|
||||
// usually do not require translations to be updated
|
||||
const lastMajorCommit =
|
||||
gitLog.all.find((logEntry) => {
|
||||
return !logEntry.message.match(COMMIT_IGNORE);
|
||||
}) || lastCommit;
|
||||
// Attempt to find the last "major" commit, ignoring any commits that
|
||||
// usually do not require translations to be updated
|
||||
const lastMajorCommit =
|
||||
gitLog.all.find((logEntry) => {
|
||||
return !logEntry.message.match(COMMIT_IGNORE);
|
||||
}) || lastCommit;
|
||||
|
||||
return {
|
||||
lastCommitMessage: lastCommit.message,
|
||||
lastCommitDate: toUtcString(lastCommit.date),
|
||||
lastMajorCommitMessage: lastMajorCommit.message,
|
||||
lastMajorCommitDate: toUtcString(lastMajorCommit.date),
|
||||
};
|
||||
}
|
||||
return {
|
||||
lastCommitMessage: lastCommit.message,
|
||||
lastCommitDate: toUtcString(lastCommit.date),
|
||||
lastMajorCommitMessage: lastMajorCommit.message,
|
||||
lastMajorCommitDate: toUtcString(lastMajorCommit.date),
|
||||
};
|
||||
}
|
||||
|
||||
getTranslationStatusByPage(pages: PageIndex): PageTranslationStatus[] {
|
||||
const sourcePages = pages[this.sourceLanguage];
|
||||
const arrContent: PageTranslationStatus[] = [];
|
||||
getTranslationStatusByPage(pages: PageIndex): PageTranslationStatus[] {
|
||||
const sourcePages = pages[this.sourceLanguage];
|
||||
const arrContent: PageTranslationStatus[] = [];
|
||||
|
||||
Object.keys(sourcePages!).forEach((subpath) => {
|
||||
const sourcePage = sourcePages![subpath]!;
|
||||
if (!sourcePage.i18nReady) return;
|
||||
Object.keys(sourcePages!).forEach((subpath) => {
|
||||
const sourcePage = sourcePages![subpath]!;
|
||||
if (!sourcePage.i18nReady) return;
|
||||
|
||||
const content: PageTranslationStatus = {
|
||||
subpath,
|
||||
sourcePage,
|
||||
githubUrl: this.getPageUrl({ lang: this.sourceLanguage, subpath }),
|
||||
translations: {},
|
||||
};
|
||||
const content: PageTranslationStatus = {
|
||||
subpath,
|
||||
sourcePage,
|
||||
githubUrl: this.getPageUrl({ lang: this.sourceLanguage, subpath }),
|
||||
translations: {},
|
||||
};
|
||||
|
||||
this.targetLanguages.forEach((lang) => {
|
||||
const i18nPage = pages[lang]![subpath]!;
|
||||
content.translations[lang] = {
|
||||
page: i18nPage,
|
||||
isMissing: !i18nPage,
|
||||
isOutdated:
|
||||
i18nPage && sourcePage.lastMajorChange > i18nPage.lastMajorChange,
|
||||
githubUrl: this.getPageUrl({ lang, subpath }),
|
||||
sourceHistoryUrl: this.getPageUrl({
|
||||
lang: 'en',
|
||||
subpath,
|
||||
type: 'commits',
|
||||
query: i18nPage ? `?since=${i18nPage.lastMajorChange}` : '',
|
||||
}),
|
||||
};
|
||||
});
|
||||
this.targetLanguages.forEach((lang) => {
|
||||
const i18nPage = pages[lang]![subpath]!;
|
||||
content.translations[lang] = {
|
||||
page: i18nPage,
|
||||
isMissing: !i18nPage,
|
||||
isOutdated: i18nPage && sourcePage.lastMajorChange > i18nPage.lastMajorChange,
|
||||
githubUrl: this.getPageUrl({ lang, subpath }),
|
||||
sourceHistoryUrl: this.getPageUrl({
|
||||
lang: 'en',
|
||||
subpath,
|
||||
type: 'commits',
|
||||
query: i18nPage ? `?since=${i18nPage.lastMajorChange}` : '',
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
arrContent.push(content);
|
||||
});
|
||||
arrContent.push(content);
|
||||
});
|
||||
|
||||
return arrContent;
|
||||
}
|
||||
return arrContent;
|
||||
}
|
||||
|
||||
getPageUrl({
|
||||
type = 'blob',
|
||||
refName = 'main',
|
||||
lang,
|
||||
subpath,
|
||||
query = '',
|
||||
}: {
|
||||
type?: string;
|
||||
refName?: string;
|
||||
lang: string;
|
||||
subpath: string;
|
||||
query?: string;
|
||||
}) {
|
||||
const noDotSrcDir = this.pageSourceDir.replace(/^\.+\//, '');
|
||||
const isSrcLang = lang === this.sourceLanguage;
|
||||
return `https://github.com/${
|
||||
this.githubRepo
|
||||
}/${type}/${refName}/${noDotSrcDir}${
|
||||
isSrcLang ? '' : `/${lang}`
|
||||
}/${subpath}${query}`;
|
||||
}
|
||||
getPageUrl({
|
||||
type = 'blob',
|
||||
refName = 'main',
|
||||
lang,
|
||||
subpath,
|
||||
query = '',
|
||||
}: {
|
||||
type?: string;
|
||||
refName?: string;
|
||||
lang: string;
|
||||
subpath: string;
|
||||
query?: string;
|
||||
}) {
|
||||
const noDotSrcDir = this.pageSourceDir.replace(/^\.+\//, '');
|
||||
const isSrcLang = lang === this.sourceLanguage;
|
||||
return `https://github.com/${this.githubRepo}/${type}/${refName}/${noDotSrcDir}${
|
||||
isSrcLang ? '' : `/${lang}`
|
||||
}/${subpath}${query}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the primary HTML output of this script by loading a template from disk,
|
||||
* rendering the individual views to HTML, and inserting them into the template.
|
||||
*/
|
||||
renderHtmlStatusPage(
|
||||
statusByPage: PageTranslationStatus[],
|
||||
prs: PullRequest[]
|
||||
) {
|
||||
// Load HTML template
|
||||
const templateFilePath = path.join(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
'template.html'
|
||||
);
|
||||
const html = fs.readFileSync(templateFilePath, { encoding: 'utf8' });
|
||||
/**
|
||||
* Renders the primary HTML output of this script by loading a template from disk,
|
||||
* rendering the individual views to HTML, and inserting them into the template.
|
||||
*/
|
||||
renderHtmlStatusPage(statusByPage: PageTranslationStatus[], prs: PullRequest[]) {
|
||||
// Load HTML template
|
||||
const templateFilePath = path.join(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
'template.html'
|
||||
);
|
||||
const html = fs.readFileSync(templateFilePath, { encoding: 'utf8' });
|
||||
|
||||
// Replace placeholders in the template with the rendered views
|
||||
// and return the resulting HTML page
|
||||
return html
|
||||
.replace(
|
||||
'<!-- TranslationStatusByLanguage -->',
|
||||
this.renderTranslationStatusByLanguage(statusByPage)
|
||||
)
|
||||
.replace(
|
||||
'<!-- TranslationNeedsReview -->',
|
||||
this.renderTranslationNeedsReview(prs)
|
||||
)
|
||||
.replace(
|
||||
'<!-- TranslationStatusByPage -->',
|
||||
this.renderTranslationStatusByPage(statusByPage)
|
||||
);
|
||||
}
|
||||
// Replace placeholders in the template with the rendered views
|
||||
// and return the resulting HTML page
|
||||
return html
|
||||
.replace(
|
||||
'<!-- TranslationStatusByLanguage -->',
|
||||
this.renderTranslationStatusByLanguage(statusByPage)
|
||||
)
|
||||
.replace('<!-- TranslationNeedsReview -->', this.renderTranslationNeedsReview(prs))
|
||||
.replace(
|
||||
'<!-- TranslationStatusByPage -->',
|
||||
this.renderTranslationStatusByPage(statusByPage)
|
||||
);
|
||||
}
|
||||
|
||||
renderTranslationStatusByLanguage(statusByPage: PageTranslationStatus[]) {
|
||||
const lines: string[] = [];
|
||||
renderTranslationStatusByLanguage(statusByPage: PageTranslationStatus[]) {
|
||||
const lines: string[] = [];
|
||||
|
||||
this.targetLanguages.forEach((lang) => {
|
||||
const missing = statusByPage.filter(
|
||||
(content) => content.translations[lang]!.isMissing
|
||||
);
|
||||
const outdated = statusByPage.filter(
|
||||
(content) => content.translations[lang]!.isOutdated
|
||||
);
|
||||
lines.push('<details>');
|
||||
lines.push(
|
||||
`<summary><strong>` +
|
||||
`${this.languageLabels[lang]} (${lang})` +
|
||||
`</strong><br>` +
|
||||
`<span class="progress-summary">` +
|
||||
`${statusByPage.length - outdated.length - missing.length} done, ` +
|
||||
`${outdated.length} need${
|
||||
outdated.length === 1 ? 's' : ''
|
||||
} updating, ` +
|
||||
`${missing.length} missing` +
|
||||
`</span>` +
|
||||
'<br>' +
|
||||
this.renderProgressBar(
|
||||
statusByPage.length,
|
||||
outdated.length,
|
||||
missing.length
|
||||
) +
|
||||
`</summary>`
|
||||
);
|
||||
lines.push(``);
|
||||
if (outdated.length > 0) {
|
||||
lines.push(`<h5>🔄 Needs updating</h5>`);
|
||||
lines.push(`<ul>`);
|
||||
lines.push(
|
||||
...outdated.map(
|
||||
(content) =>
|
||||
`<li>` +
|
||||
`${this.renderLink(content.githubUrl, content.subpath)} ` +
|
||||
`(${this.renderLink(
|
||||
content.translations[lang]!.githubUrl,
|
||||
'outdated translation'
|
||||
)}, ${this.renderLink(
|
||||
content.translations[lang]!.sourceHistoryUrl,
|
||||
'source change history'
|
||||
)})` +
|
||||
`</li>`
|
||||
)
|
||||
);
|
||||
lines.push(`</ul>`);
|
||||
}
|
||||
if (missing.length > 0) {
|
||||
lines.push(`<h5>❌ Missing</h5>`);
|
||||
lines.push(`<ul>`);
|
||||
lines.push(
|
||||
...missing.map(
|
||||
(content) =>
|
||||
`<li>` +
|
||||
`${this.renderLink(
|
||||
content.githubUrl,
|
||||
content.subpath
|
||||
)} ${this.renderCreatePageButton(lang, content.subpath)}` +
|
||||
`</li>`
|
||||
)
|
||||
);
|
||||
lines.push(`</ul>`);
|
||||
}
|
||||
lines.push(`</details>`);
|
||||
lines.push(``);
|
||||
});
|
||||
this.targetLanguages.forEach((lang) => {
|
||||
const missing = statusByPage.filter((content) => content.translations[lang]!.isMissing);
|
||||
const outdated = statusByPage.filter((content) => content.translations[lang]!.isOutdated);
|
||||
lines.push('<details>');
|
||||
lines.push(
|
||||
`<summary><strong>` +
|
||||
`${this.languageLabels[lang]} (${lang})` +
|
||||
`</strong><br>` +
|
||||
`<span class="progress-summary">` +
|
||||
`${statusByPage.length - outdated.length - missing.length} done, ` +
|
||||
`${outdated.length} need${outdated.length === 1 ? 's' : ''} updating, ` +
|
||||
`${missing.length} missing` +
|
||||
`</span>` +
|
||||
'<br>' +
|
||||
this.renderProgressBar(statusByPage.length, outdated.length, missing.length) +
|
||||
`</summary>`
|
||||
);
|
||||
lines.push(``);
|
||||
if (outdated.length > 0) {
|
||||
lines.push(`<h5>🔄 Needs updating</h5>`);
|
||||
lines.push(`<ul>`);
|
||||
lines.push(
|
||||
...outdated.map(
|
||||
(content) =>
|
||||
`<li>` +
|
||||
`${this.renderLink(content.githubUrl, content.subpath)} ` +
|
||||
`(${this.renderLink(
|
||||
content.translations[lang]!.githubUrl,
|
||||
'outdated translation'
|
||||
)}, ${this.renderLink(
|
||||
content.translations[lang]!.sourceHistoryUrl,
|
||||
'source change history'
|
||||
)})` +
|
||||
`</li>`
|
||||
)
|
||||
);
|
||||
lines.push(`</ul>`);
|
||||
}
|
||||
if (missing.length > 0) {
|
||||
lines.push(`<h5>❌ Missing</h5>`);
|
||||
lines.push(`<ul>`);
|
||||
lines.push(
|
||||
...missing.map(
|
||||
(content) =>
|
||||
`<li>` +
|
||||
`${this.renderLink(
|
||||
content.githubUrl,
|
||||
content.subpath
|
||||
)} ${this.renderCreatePageButton(lang, content.subpath)}` +
|
||||
`</li>`
|
||||
)
|
||||
);
|
||||
lines.push(`</ul>`);
|
||||
}
|
||||
lines.push(`</details>`);
|
||||
lines.push(``);
|
||||
});
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
renderTranslationNeedsReview(prs: PullRequest[]) {
|
||||
const lines: string[] = [];
|
||||
renderTranslationNeedsReview(prs: PullRequest[]) {
|
||||
const lines: string[] = [];
|
||||
|
||||
if (prs.length > 0) {
|
||||
lines.push(`<ul>`);
|
||||
lines.push(
|
||||
...prs.map((pr) => {
|
||||
const title = pr.title.replaceAll('`', '');
|
||||
return `<li>` + this.renderLink(pr.html_url, title) + `</li>`;
|
||||
})
|
||||
);
|
||||
lines.push(`</ul>`);
|
||||
}
|
||||
lines.push(``);
|
||||
if (prs.length > 0) {
|
||||
lines.push(`<ul>`);
|
||||
lines.push(
|
||||
...prs.map((pr) => {
|
||||
const title = pr.title.replaceAll('`', '');
|
||||
return `<li>` + this.renderLink(pr.html_url, title) + `</li>`;
|
||||
})
|
||||
);
|
||||
lines.push(`</ul>`);
|
||||
}
|
||||
lines.push(``);
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
renderTranslationStatusByPage(statusByPage: PageTranslationStatus[]) {
|
||||
const lines: string[] = [];
|
||||
renderTranslationStatusByPage(statusByPage: PageTranslationStatus[]) {
|
||||
const lines: string[] = [];
|
||||
|
||||
lines.push('<div class="table-container"/>');
|
||||
lines.push('<table role="table" class="status-by-page">');
|
||||
lines.push('<div class="table-container"/>');
|
||||
lines.push('<table role="table" class="status-by-page">');
|
||||
|
||||
lines.push('<thead><tr>');
|
||||
lines.push(
|
||||
['Page', ...this.targetLanguages].map((col) => `<th>${col}</th>`).join('')
|
||||
);
|
||||
lines.push('</tr></thead>');
|
||||
lines.push('<thead><tr>');
|
||||
lines.push(['Page', ...this.targetLanguages].map((col) => `<th>${col}</th>`).join(''));
|
||||
lines.push('</tr></thead>');
|
||||
|
||||
lines.push('<tbody>');
|
||||
const spacer = `<tr class="spacer">\n${this.targetLanguages
|
||||
.map(() => `<td></td>`)
|
||||
.join('\n')}\n</tr>`;
|
||||
lines.push(spacer);
|
||||
statusByPage.forEach((content) => {
|
||||
const cols = [];
|
||||
cols.push(this.renderLink(content.githubUrl, content.subpath));
|
||||
cols.push(
|
||||
...this.targetLanguages.map((lang) => {
|
||||
const translation = content.translations[lang]!;
|
||||
if (translation.isMissing)
|
||||
return `<span title="${lang}: Missing"><span aria-hidden="true">❌</span></span>`;
|
||||
if (translation.isOutdated)
|
||||
return `<a href="${translation.githubUrl}" title="${lang}: Needs updating"><span aria-hidden="true">🔄</span></a>`;
|
||||
return `<a href="${translation.githubUrl}" title="${lang}: Completed"><span aria-hidden="true">✔</span></a>`;
|
||||
})
|
||||
);
|
||||
lines.push(
|
||||
`<tr>\n${cols.map((col) => `<td>${col}</td>`).join('\n')}\n</tr>`
|
||||
);
|
||||
});
|
||||
lines.push(spacer);
|
||||
lines.push('</tbody>');
|
||||
lines.push('<tbody>');
|
||||
const spacer = `<tr class="spacer">\n${this.targetLanguages
|
||||
.map(() => `<td></td>`)
|
||||
.join('\n')}\n</tr>`;
|
||||
lines.push(spacer);
|
||||
statusByPage.forEach((content) => {
|
||||
const cols = [];
|
||||
cols.push(this.renderLink(content.githubUrl, content.subpath));
|
||||
cols.push(
|
||||
...this.targetLanguages.map((lang) => {
|
||||
const translation = content.translations[lang]!;
|
||||
if (translation.isMissing)
|
||||
return `<span title="${lang}: Missing"><span aria-hidden="true">❌</span></span>`;
|
||||
if (translation.isOutdated)
|
||||
return `<a href="${translation.githubUrl}" title="${lang}: Needs updating"><span aria-hidden="true">🔄</span></a>`;
|
||||
return `<a href="${translation.githubUrl}" title="${lang}: Completed"><span aria-hidden="true">✔</span></a>`;
|
||||
})
|
||||
);
|
||||
lines.push(`<tr>\n${cols.map((col) => `<td>${col}</td>`).join('\n')}\n</tr>`);
|
||||
});
|
||||
lines.push(spacer);
|
||||
lines.push('</tbody>');
|
||||
|
||||
lines.push('</table>');
|
||||
lines.push('</table>');
|
||||
|
||||
lines.push(
|
||||
`\n<sup>❌ Missing 🔄 Needs updating ✔ Completed</sup>`
|
||||
);
|
||||
lines.push('</div>');
|
||||
lines.push(`\n<sup>❌ Missing 🔄 Needs updating ✔ Completed</sup>`);
|
||||
lines.push('</div>');
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a link to a pre-filled GitHub UI for creating a new file.
|
||||
*
|
||||
* @param lang Language tag to create page for
|
||||
* @param filename Subpath of page to create
|
||||
*/
|
||||
renderCreatePageButton(lang: string, filename: string): string {
|
||||
// We include `lang` twice because GitHub eats the last path segment when setting filename.
|
||||
const createUrl = new URL(
|
||||
`https://github.com/${this.githubRepo}/new/main/src/content/docs`
|
||||
);
|
||||
createUrl.searchParams.set('filename', lang + '/' + filename);
|
||||
createUrl.searchParams.set('value', '---\ntitle:\ndescription:\n---\n');
|
||||
return this.renderLink(
|
||||
createUrl.href,
|
||||
`Create\xa0page\xa0+`,
|
||||
'create-button'
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Render a link to a pre-filled GitHub UI for creating a new file.
|
||||
*
|
||||
* @param lang Language tag to create page for
|
||||
* @param filename Subpath of page to create
|
||||
*/
|
||||
renderCreatePageButton(lang: string, filename: string): string {
|
||||
// We include `lang` twice because GitHub eats the last path segment when setting filename.
|
||||
const createUrl = new URL(`https://github.com/${this.githubRepo}/new/main/src/content/docs`);
|
||||
createUrl.searchParams.set('filename', lang + '/' + filename);
|
||||
createUrl.searchParams.set('value', '---\ntitle:\ndescription:\n---\n');
|
||||
return this.renderLink(createUrl.href, `Create\xa0page\xa0+`, 'create-button');
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a progress bar with emoji.
|
||||
*/
|
||||
renderProgressBar(
|
||||
total: number,
|
||||
outdated: number,
|
||||
missing: number,
|
||||
{ size = 20 }: { size?: number } = {}
|
||||
) {
|
||||
const outdatedLength = Math.round((outdated / total) * size);
|
||||
const missingLength = Math.round((missing / total) * size);
|
||||
const doneLength = size - outdatedLength - missingLength;
|
||||
return (
|
||||
'<span class="progress-bar" aria-hidden="true">' +
|
||||
[
|
||||
[doneLength, '🟦'],
|
||||
[outdatedLength, '🟧'],
|
||||
[missingLength, '⬜'],
|
||||
]
|
||||
.map(([length, icon]) => Array(length).fill(icon))
|
||||
.flat()
|
||||
.join('') +
|
||||
'</span>'
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Render a progress bar with emoji.
|
||||
*/
|
||||
renderProgressBar(
|
||||
total: number,
|
||||
outdated: number,
|
||||
missing: number,
|
||||
{ size = 20 }: { size?: number } = {}
|
||||
) {
|
||||
const outdatedLength = Math.round((outdated / total) * size);
|
||||
const missingLength = Math.round((missing / total) * size);
|
||||
const doneLength = size - outdatedLength - missingLength;
|
||||
return (
|
||||
'<span class="progress-bar" aria-hidden="true">' +
|
||||
[
|
||||
[doneLength, '🟦'],
|
||||
[outdatedLength, '🟧'],
|
||||
[missingLength, '⬜'],
|
||||
]
|
||||
.map(([length, icon]) => Array(length).fill(icon))
|
||||
.flat()
|
||||
.join('') +
|
||||
'</span>'
|
||||
);
|
||||
}
|
||||
|
||||
renderLink(href: string, text: string, className = ''): string {
|
||||
return `<a href="${escape(href)}" class="${escape(
|
||||
className
|
||||
)}" target="_blank" rel="noopener noreferrer">${escape(text)}</a>`;
|
||||
}
|
||||
renderLink(href: string, text: string, className = ''): string {
|
||||
return `<a href="${escape(href)}" class="${escape(
|
||||
className
|
||||
)}" target="_blank" rel="noopener noreferrer">${escape(text)}</a>`;
|
||||
}
|
||||
}
|
||||
|
||||
function toUtcString(date: string) {
|
||||
return new Date(date).toISOString();
|
||||
return new Date(date).toISOString();
|
||||
}
|
||||
|
||||
function tryGetFrontMatterBlock(filePath: string): string {
|
||||
const contents = fs.readFileSync(filePath, 'utf8');
|
||||
const matches = contents.match(/^\s*---([\S\s]*?)\n---/);
|
||||
if (!matches) return '';
|
||||
return matches[1];
|
||||
const contents = fs.readFileSync(filePath, 'utf8');
|
||||
const matches = contents.match(/^\s*---([\S\s]*?)\n---/);
|
||||
if (!matches) return '';
|
||||
return matches[1];
|
||||
}
|
||||
|
||||
@@ -249,9 +249,9 @@
|
||||
|
||||
<p>
|
||||
If you're interested in helping us translate
|
||||
<a href="https://tauri.app/">tauri.app</a> into one of the
|
||||
languages listed below, you've come to the right place! This auto-updating page always
|
||||
lists all the content that could use your help right now.
|
||||
<a href="https://tauri.app/">tauri.app</a> into one of the languages listed below, you've
|
||||
come to the right place! This auto-updating page always lists all the content that could
|
||||
use your help right now.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
export type PageIndex = {
|
||||
[language: string]: {
|
||||
[pagePath: string]: PageData;
|
||||
};
|
||||
[language: string]: {
|
||||
[pagePath: string]: PageData;
|
||||
};
|
||||
};
|
||||
|
||||
export type PageData = {
|
||||
lastChange: string;
|
||||
lastCommitMsg: string;
|
||||
lastMajorChange: string;
|
||||
lastMajorCommitMsg: string;
|
||||
i18nReady?: boolean;
|
||||
lastChange: string;
|
||||
lastCommitMsg: string;
|
||||
lastMajorChange: string;
|
||||
lastMajorCommitMsg: string;
|
||||
i18nReady?: boolean;
|
||||
};
|
||||
|
||||
export type PageTranslationStatus = {
|
||||
subpath: string;
|
||||
sourcePage: PageData;
|
||||
githubUrl: string;
|
||||
translations: {
|
||||
[language: string]: {
|
||||
page: PageData;
|
||||
isMissing: boolean;
|
||||
isOutdated: boolean;
|
||||
githubUrl: string;
|
||||
sourceHistoryUrl: string;
|
||||
};
|
||||
};
|
||||
subpath: string;
|
||||
sourcePage: PageData;
|
||||
githubUrl: string;
|
||||
translations: {
|
||||
[language: string]: {
|
||||
page: PageData;
|
||||
isMissing: boolean;
|
||||
isOutdated: boolean;
|
||||
githubUrl: string;
|
||||
sourceHistoryUrl: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,257 +1,244 @@
|
||||
import {
|
||||
Application,
|
||||
Comment,
|
||||
DeclarationReflection,
|
||||
Options,
|
||||
PageEvent,
|
||||
Reflection,
|
||||
SignatureReflection,
|
||||
TSConfigReader,
|
||||
type TypeDocOptions,
|
||||
Application,
|
||||
Comment,
|
||||
DeclarationReflection,
|
||||
Options,
|
||||
PageEvent,
|
||||
Reflection,
|
||||
SignatureReflection,
|
||||
TSConfigReader,
|
||||
type TypeDocOptions,
|
||||
} from 'typedoc';
|
||||
import {
|
||||
MarkdownTheme,
|
||||
MarkdownThemeRenderContext,
|
||||
type PluginOptions,
|
||||
load as loadMarkdownPlugin,
|
||||
MarkdownTheme,
|
||||
MarkdownThemeRenderContext,
|
||||
type PluginOptions,
|
||||
load as loadMarkdownPlugin,
|
||||
} from 'typedoc-plugin-markdown';
|
||||
import path from 'node:path';
|
||||
import { slug } from 'github-slugger';
|
||||
import { existsSync } from 'node:fs';
|
||||
|
||||
const typeDocConfigBaseOptions: Partial<TypeDocOptions | PluginOptions> = {
|
||||
// TypeDoc options
|
||||
// https://typedoc.org/options/
|
||||
githubPages: false,
|
||||
hideGenerator: true,
|
||||
theme: 'tauri-theme',
|
||||
plugin: ['typedoc-plugin-mdn-links'],
|
||||
// typedoc-plugin-markdown options
|
||||
// https://github.com/tgreyuk/typedoc-plugin-markdown/blob/next/packages/typedoc-plugin-markdown/docs/usage/options.md
|
||||
outputFileStrategy: 'modules',
|
||||
flattenOutputFiles: true,
|
||||
entryFileName: '../index.md',
|
||||
// TODO: Pending https://github.com/tgreyuk/typedoc-plugin-markdown/pull/455 being released so that the patch can be removed
|
||||
indexFileName: 'index.md',
|
||||
hidePageHeader: true,
|
||||
hidePageTitle: true,
|
||||
hideBreadcrumbs: true,
|
||||
hideInPageTOC: true,
|
||||
identifiersAsCodeBlocks: true,
|
||||
propertiesFormat: 'table',
|
||||
enumMembersFormat: 'table',
|
||||
typeDeclarationFormat: 'table',
|
||||
// TypeDoc options
|
||||
// https://typedoc.org/options/
|
||||
githubPages: false,
|
||||
hideGenerator: true,
|
||||
theme: 'tauri-theme',
|
||||
plugin: ['typedoc-plugin-mdn-links'],
|
||||
// typedoc-plugin-markdown options
|
||||
// https://github.com/tgreyuk/typedoc-plugin-markdown/blob/next/packages/typedoc-plugin-markdown/docs/usage/options.md
|
||||
outputFileStrategy: 'modules',
|
||||
flattenOutputFiles: true,
|
||||
entryFileName: '../index.md',
|
||||
// TODO: Pending https://github.com/tgreyuk/typedoc-plugin-markdown/pull/455 being released so that the patch can be removed
|
||||
indexFileName: 'index.md',
|
||||
hidePageHeader: true,
|
||||
hidePageTitle: true,
|
||||
hideBreadcrumbs: true,
|
||||
hideInPageTOC: true,
|
||||
identifiersAsCodeBlocks: true,
|
||||
propertiesFormat: 'table',
|
||||
enumMembersFormat: 'table',
|
||||
typeDeclarationFormat: 'table',
|
||||
};
|
||||
|
||||
async function generator() {
|
||||
if (existsSync('../tauri/tooling/api/node_modules')) {
|
||||
const coreJsOptions: Partial<TypeDocOptions> = {
|
||||
entryPoints: ['../tauri/tooling/api/src/index.ts'],
|
||||
tsconfig: '../tauri/tooling/api/tsconfig.json',
|
||||
gitRevision: 'dev',
|
||||
baseUrl: '/2/reference/core/js/',
|
||||
...typeDocConfigBaseOptions,
|
||||
};
|
||||
if (existsSync('../tauri/tooling/api/node_modules')) {
|
||||
const coreJsOptions: Partial<TypeDocOptions> = {
|
||||
entryPoints: ['../tauri/tooling/api/src/index.ts'],
|
||||
tsconfig: '../tauri/tooling/api/tsconfig.json',
|
||||
gitRevision: 'dev',
|
||||
baseUrl: '/2/reference/core/js/',
|
||||
...typeDocConfigBaseOptions,
|
||||
};
|
||||
|
||||
await generateDocs(coreJsOptions);
|
||||
} else {
|
||||
console.log(
|
||||
'Tauri submodule is not initialized, respective API routes will not be rendered.'
|
||||
);
|
||||
}
|
||||
await generateDocs(coreJsOptions);
|
||||
} else {
|
||||
console.log('Tauri submodule is not initialized, respective API routes will not be rendered.');
|
||||
}
|
||||
|
||||
// TODO: the following plugins don't have a JS API:
|
||||
// 'localhost',
|
||||
// 'persisted-scope',
|
||||
// 'single-instance',
|
||||
// TODO: the following plugins don't have a JS API:
|
||||
// 'localhost',
|
||||
// 'persisted-scope',
|
||||
// 'single-instance',
|
||||
|
||||
const plugins = [
|
||||
'app',
|
||||
'authenticator',
|
||||
'autostart',
|
||||
'cli',
|
||||
'clipboard-manager',
|
||||
'dialog',
|
||||
'fs',
|
||||
'global-shortcut',
|
||||
'http',
|
||||
'log',
|
||||
'notification',
|
||||
'os',
|
||||
'positioner',
|
||||
'process',
|
||||
'shell',
|
||||
'sql',
|
||||
'store',
|
||||
'stronghold',
|
||||
'updater',
|
||||
'upload',
|
||||
'websocket',
|
||||
'window',
|
||||
'window-state',
|
||||
];
|
||||
const plugins = [
|
||||
'app',
|
||||
'authenticator',
|
||||
'autostart',
|
||||
'cli',
|
||||
'clipboard-manager',
|
||||
'dialog',
|
||||
'fs',
|
||||
'global-shortcut',
|
||||
'http',
|
||||
'log',
|
||||
'notification',
|
||||
'os',
|
||||
'positioner',
|
||||
'process',
|
||||
'shell',
|
||||
'sql',
|
||||
'store',
|
||||
'stronghold',
|
||||
'updater',
|
||||
'upload',
|
||||
'websocket',
|
||||
'window',
|
||||
'window-state',
|
||||
];
|
||||
|
||||
if (existsSync('../plugins-workspace/node_modules')) {
|
||||
plugins.forEach(async (plugin) => {
|
||||
const pluginJsOptions: Partial<TypeDocOptions> = {
|
||||
entryPoints: [
|
||||
`../plugins-workspace/plugins/${plugin}/guest-js/index.ts`,
|
||||
],
|
||||
tsconfig: `../plugins-workspace/plugins/${plugin}/tsconfig.json`,
|
||||
gitRevision: 'v2',
|
||||
baseUrl: `/2/reference/plugin/${plugin}/js`,
|
||||
...typeDocConfigBaseOptions,
|
||||
};
|
||||
if (existsSync('../plugins-workspace/node_modules')) {
|
||||
plugins.forEach(async (plugin) => {
|
||||
const pluginJsOptions: Partial<TypeDocOptions> = {
|
||||
entryPoints: [`../plugins-workspace/plugins/${plugin}/guest-js/index.ts`],
|
||||
tsconfig: `../plugins-workspace/plugins/${plugin}/tsconfig.json`,
|
||||
gitRevision: 'v2',
|
||||
baseUrl: `/2/reference/plugin/${plugin}/js`,
|
||||
...typeDocConfigBaseOptions,
|
||||
};
|
||||
|
||||
await generateDocs(pluginJsOptions);
|
||||
});
|
||||
} else {
|
||||
console.log(
|
||||
'Plugins workspace submodule is not initialized, respective API routes will not be rendered.'
|
||||
);
|
||||
}
|
||||
await generateDocs(pluginJsOptions);
|
||||
});
|
||||
} else {
|
||||
console.log(
|
||||
'Plugins workspace submodule is not initialized, respective API routes will not be rendered.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Adapted from https://github.com/HiDeoo/starlight-typedoc
|
||||
async function generateDocs(options: Partial<TypeDocOptions>) {
|
||||
const outputDir = `../../src/content/docs${options.baseUrl}`;
|
||||
const outputDir = `../../src/content/docs${options.baseUrl}`;
|
||||
|
||||
const app = new Application();
|
||||
app.options.addReader(new TSConfigReader());
|
||||
app.renderer.defineTheme('tauri-theme', TauriTheme);
|
||||
loadMarkdownPlugin(app);
|
||||
const app = new Application();
|
||||
app.options.addReader(new TSConfigReader());
|
||||
app.renderer.defineTheme('tauri-theme', TauriTheme);
|
||||
loadMarkdownPlugin(app);
|
||||
|
||||
app.renderer.on(PageEvent.END, (event: PageEvent<DeclarationReflection>) => {
|
||||
pageEventEnd(event);
|
||||
});
|
||||
app.renderer.on(PageEvent.END, (event: PageEvent<DeclarationReflection>) => {
|
||||
pageEventEnd(event);
|
||||
});
|
||||
|
||||
await app.bootstrapWithPlugins(options);
|
||||
const project = app.convert();
|
||||
await app.bootstrapWithPlugins(options);
|
||||
const project = app.convert();
|
||||
|
||||
if (project) {
|
||||
await app.generateDocs(project, outputDir);
|
||||
}
|
||||
if (project) {
|
||||
await app.generateDocs(project, outputDir);
|
||||
}
|
||||
}
|
||||
|
||||
// Adds frontmatter to the top of the file
|
||||
// Adapted from https://github.com/HiDeoo/starlight-typedoc
|
||||
function pageEventEnd(event: PageEvent<DeclarationReflection>) {
|
||||
if (!event.contents) {
|
||||
return;
|
||||
}
|
||||
const frontmatter = [
|
||||
'---',
|
||||
`title: "${event.model.name}"`,
|
||||
'editUrl: false',
|
||||
'prev: false',
|
||||
'next: false',
|
||||
'---',
|
||||
'',
|
||||
event.contents,
|
||||
];
|
||||
event.contents = frontmatter.join('\n');
|
||||
if (!event.contents) {
|
||||
return;
|
||||
}
|
||||
const frontmatter = [
|
||||
'---',
|
||||
`title: "${event.model.name}"`,
|
||||
'editUrl: false',
|
||||
'prev: false',
|
||||
'next: false',
|
||||
'---',
|
||||
'',
|
||||
event.contents,
|
||||
];
|
||||
event.contents = frontmatter.join('\n');
|
||||
}
|
||||
|
||||
// Overrides and extensions based on https://github.com/tgreyuk/typedoc-plugin-markdown/blob/next/packages/typedoc-plugin-markdown/docs/usage/customizing.md
|
||||
class TauriTheme extends MarkdownTheme {
|
||||
override getRenderContext(
|
||||
pageEvent: PageEvent<Reflection>
|
||||
): MarkdownThemeRenderContext {
|
||||
return new TauriThemeRenderContext(pageEvent, this.application.options);
|
||||
}
|
||||
override getRenderContext(pageEvent: PageEvent<Reflection>): MarkdownThemeRenderContext {
|
||||
return new TauriThemeRenderContext(pageEvent, this.application.options);
|
||||
}
|
||||
}
|
||||
|
||||
class TauriThemeRenderContext extends MarkdownThemeRenderContext {
|
||||
#markdownThemeRenderContext: MarkdownThemeRenderContext;
|
||||
#markdownThemeRenderContext: MarkdownThemeRenderContext;
|
||||
|
||||
constructor(event: PageEvent<Reflection>, options: Options) {
|
||||
super(event, options);
|
||||
this.#markdownThemeRenderContext = new MarkdownThemeRenderContext(
|
||||
event,
|
||||
options
|
||||
);
|
||||
}
|
||||
constructor(event: PageEvent<Reflection>, options: Options) {
|
||||
super(event, options);
|
||||
this.#markdownThemeRenderContext = new MarkdownThemeRenderContext(event, options);
|
||||
}
|
||||
|
||||
// Formats `@since` to be a single line
|
||||
override comment: (
|
||||
comment: Comment,
|
||||
headingLevel?: number | undefined
|
||||
) => string = (comment, headingLevel) => {
|
||||
const filteredComment = { ...comment } as Comment;
|
||||
filteredComment.blockTags = [];
|
||||
// Formats `@since` to be a single line
|
||||
override comment: (comment: Comment, headingLevel?: number | undefined) => string = (
|
||||
comment,
|
||||
headingLevel
|
||||
) => {
|
||||
const filteredComment = { ...comment } as Comment;
|
||||
filteredComment.blockTags = [];
|
||||
|
||||
const customBlockTags = [];
|
||||
const customBlockTags = [];
|
||||
|
||||
for (const blockTag of comment.blockTags) {
|
||||
if (blockTag.tag === '@since') {
|
||||
customBlockTags.push(blockTag);
|
||||
} else {
|
||||
filteredComment.blockTags.push(blockTag);
|
||||
}
|
||||
}
|
||||
for (const blockTag of comment.blockTags) {
|
||||
if (blockTag.tag === '@since') {
|
||||
customBlockTags.push(blockTag);
|
||||
} else {
|
||||
filteredComment.blockTags.push(blockTag);
|
||||
}
|
||||
}
|
||||
|
||||
let markdown = this.#markdownThemeRenderContext.comment(
|
||||
filteredComment,
|
||||
headingLevel
|
||||
);
|
||||
let markdown = this.#markdownThemeRenderContext.comment(filteredComment, headingLevel);
|
||||
|
||||
for (const customCommentTag of customBlockTags) {
|
||||
markdown += `\n**Since**: ${customCommentTag.content
|
||||
.map((content) => content.text)
|
||||
.join(', ')}\n\n`;
|
||||
}
|
||||
for (const customCommentTag of customBlockTags) {
|
||||
markdown += `\n**Since**: ${customCommentTag.content
|
||||
.map((content) => content.text)
|
||||
.join(', ')}\n\n`;
|
||||
}
|
||||
|
||||
return markdown;
|
||||
};
|
||||
return markdown;
|
||||
};
|
||||
|
||||
// Formats `@source` to be a single line
|
||||
override sources: (
|
||||
reflection: DeclarationReflection | SignatureReflection,
|
||||
headingLevel: number
|
||||
) => string = (reflection, _) => {
|
||||
if (!reflection.sources) {
|
||||
return '';
|
||||
}
|
||||
let label =
|
||||
reflection.sources.length > 1 ? '**Sources**: ' : '**Source**: ';
|
||||
const sources = reflection.sources.map(
|
||||
(source) => `[${source.fileName}:${source.line}](${source.url})`
|
||||
);
|
||||
// Formats `@source` to be a single line
|
||||
override sources: (
|
||||
reflection: DeclarationReflection | SignatureReflection,
|
||||
headingLevel: number
|
||||
) => string = (reflection, _) => {
|
||||
if (!reflection.sources) {
|
||||
return '';
|
||||
}
|
||||
let label = reflection.sources.length > 1 ? '**Sources**: ' : '**Source**: ';
|
||||
const sources = reflection.sources.map(
|
||||
(source) => `[${source.fileName}:${source.line}](${source.url})`
|
||||
);
|
||||
|
||||
return label + sources.join(', ');
|
||||
};
|
||||
return label + sources.join(', ');
|
||||
};
|
||||
|
||||
// Adapted from https://github.com/HiDeoo/starlight-typedoc/blob/d95072e218004276942a5132ec8a4e3561425903/packages/starlight-typedoc/src/libs/theme.ts#L28
|
||||
override relativeURL = (url: string | undefined) => {
|
||||
if (!url) {
|
||||
return null;
|
||||
}
|
||||
// Adapted from https://github.com/HiDeoo/starlight-typedoc/blob/d95072e218004276942a5132ec8a4e3561425903/packages/starlight-typedoc/src/libs/theme.ts#L28
|
||||
override relativeURL = (url: string | undefined) => {
|
||||
if (!url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const filePath = path.parse(url);
|
||||
const [, anchor] = filePath.base.split('#');
|
||||
const segments = filePath.dir
|
||||
.split('/')
|
||||
.map((segment) => slug(segment))
|
||||
.filter((segment) => segment !== '');
|
||||
const filePath = path.parse(url);
|
||||
const [, anchor] = filePath.base.split('#');
|
||||
const segments = filePath.dir
|
||||
.split('/')
|
||||
.map((segment) => slug(segment))
|
||||
.filter((segment) => segment !== '');
|
||||
|
||||
let baseUrl = this.options.getValue('baseUrl');
|
||||
let baseUrl = this.options.getValue('baseUrl');
|
||||
|
||||
if (!baseUrl.startsWith('/')) {
|
||||
baseUrl = `/${baseUrl}`;
|
||||
}
|
||||
if (!baseUrl.startsWith('/')) {
|
||||
baseUrl = `/${baseUrl}`;
|
||||
}
|
||||
|
||||
if (!baseUrl.endsWith('/')) {
|
||||
baseUrl = `${baseUrl}/`;
|
||||
}
|
||||
if (!baseUrl.endsWith('/')) {
|
||||
baseUrl = `${baseUrl}/`;
|
||||
}
|
||||
|
||||
let constructedUrl = typeof baseUrl === 'string' ? baseUrl : '';
|
||||
constructedUrl += segments.length > 0 ? `${segments.join('/')}/` : '';
|
||||
constructedUrl += slug(filePath.name);
|
||||
constructedUrl += '/';
|
||||
constructedUrl += anchor && anchor.length > 0 ? `#${anchor}` : '';
|
||||
let constructedUrl = typeof baseUrl === 'string' ? baseUrl : '';
|
||||
constructedUrl += segments.length > 0 ? `${segments.join('/')}/` : '';
|
||||
constructedUrl += slug(filePath.name);
|
||||
constructedUrl += '/';
|
||||
constructedUrl += anchor && anchor.length > 0 ? `#${anchor}` : '';
|
||||
|
||||
return constructedUrl;
|
||||
};
|
||||
return constructedUrl;
|
||||
};
|
||||
}
|
||||
|
||||
generator()
|
||||
generator();
|
||||
|
||||
21
pnpm-lock.yaml
generated
21
pnpm-lock.yaml
generated
@@ -28,6 +28,12 @@ importers:
|
||||
astro-feelback:
|
||||
specifier: ^0.3.4
|
||||
version: 0.3.4
|
||||
prettier:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
prettier-plugin-astro:
|
||||
specifier: ^0.11.0
|
||||
version: 0.11.0
|
||||
rehype-autolink-headings:
|
||||
specifier: ^6.1.1
|
||||
version: 6.1.1
|
||||
@@ -3666,6 +3672,15 @@ packages:
|
||||
which-pm: 2.0.0
|
||||
dev: false
|
||||
|
||||
/prettier-plugin-astro@0.11.0:
|
||||
resolution: {integrity: sha512-rl2hJ4Kty/aEfGjk3i4JS+bpng9MjgvwqLRNzeb9NqYhqKoWNwOR39cIJXFjU1vR3zYOPnwWNRMelKb0orunYA==}
|
||||
engines: {node: ^14.15.0 || >=16.0.0, pnpm: '>=7.14.0'}
|
||||
dependencies:
|
||||
'@astrojs/compiler': 1.8.0
|
||||
prettier: 3.0.1
|
||||
sass-formatter: 0.7.6
|
||||
dev: false
|
||||
|
||||
/prettier-plugin-astro@0.9.1:
|
||||
resolution: {integrity: sha512-pYZXSbdq0eElvzoIMArzv1SBn1NUXzopjlcnt6Ql8VW32PjC12NovwBjXJ6rh8qQLi7vF8jNqAbraKW03UPfag==}
|
||||
engines: {node: ^14.15.0 || >=16.0.0, pnpm: '>=7.14.0'}
|
||||
@@ -3682,6 +3697,12 @@ packages:
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/prettier@3.0.1:
|
||||
resolution: {integrity: sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/prismjs@1.29.0:
|
||||
resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
import FeelbackReaction from "astro-feelback/components/FeelbackReaction.astro";
|
||||
import "astro-feelback/styles/feelback.css";
|
||||
import FeelbackReaction from 'astro-feelback/components/FeelbackReaction.astro';
|
||||
import 'astro-feelback/styles/feelback.css';
|
||||
|
||||
// Waiting for https://discord.com/channels/830184174198718474/1134090586052378624 to be build out to dynamically insert into the layout for every page
|
||||
const CONTENT_SET_ID = "54faa8e2-cd0d-4997-9a3c-d9fed28f358a"
|
||||
const CONTENT_SET_ID = '54faa8e2-cd0d-4997-9a3c-d9fed28f358a';
|
||||
---
|
||||
|
||||
<FeelbackReaction
|
||||
contentSetId={CONTENT_SET_ID}
|
||||
key={Astro.url.pathname}
|
||||
layout="list"
|
||||
preset="feeling"
|
||||
/>
|
||||
contentSetId={CONTENT_SET_ID}
|
||||
key={Astro.url.pathname}
|
||||
layout="list"
|
||||
preset="feeling"
|
||||
/>
|
||||
|
||||
@@ -5,19 +5,19 @@ const baseUrl = 'https://github.com/tauri-apps/tauri-docs/tree/starlight';
|
||||
---
|
||||
|
||||
<Card title="Contribute" icon="pencil">
|
||||
<p>
|
||||
This page is a stub and is waiting for contributions. Get involved by
|
||||
<a href={baseUrl}>visiting us on GitHub</a> or <a
|
||||
href="https://discord.com/invite/tauri">joining us on Discord</a
|
||||
>.
|
||||
</p>
|
||||
<p>
|
||||
This page is a stub and is waiting for contributions. Get involved by
|
||||
<a href={baseUrl}>visiting us on GitHub</a> or <a href="https://discord.com/invite/tauri"
|
||||
>joining us on Discord</a
|
||||
>.
|
||||
</p>
|
||||
|
||||
{
|
||||
Astro.slots.has('default') && (
|
||||
<>
|
||||
<div>Here are some ideas to get you started:</div>
|
||||
<slot />
|
||||
</>
|
||||
)
|
||||
}
|
||||
{
|
||||
Astro.slots.has('default') && (
|
||||
<>
|
||||
<div>Here are some ideas to get you started:</div>
|
||||
<slot />
|
||||
</>
|
||||
)
|
||||
}
|
||||
</Card>
|
||||
|
||||
@@ -2,6 +2,6 @@ import { defineCollection } from 'astro:content';
|
||||
import { docsSchema, i18nSchema } from '@astrojs/starlight/schema';
|
||||
|
||||
export const collections = {
|
||||
docs: defineCollection({ schema: docsSchema() }),
|
||||
i18n: defineCollection({ type: 'data', schema: i18nSchema() }),
|
||||
docs: defineCollection({ schema: docsSchema() }),
|
||||
i18n: defineCollection({ type: 'data', schema: i18nSchema() }),
|
||||
};
|
||||
|
||||
@@ -117,23 +117,23 @@ import { internalIpV4 } from 'internal-ip';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(async () => {
|
||||
const host = await internalIpV4();
|
||||
const host = await internalIpV4();
|
||||
|
||||
/** @type {import('vite').UserConfig} */
|
||||
const config = {
|
||||
server: {
|
||||
host: '0.0.0.0', // listen on all addresses
|
||||
port: 5173,
|
||||
strictPort: true,
|
||||
hmr: {
|
||||
protocol: 'ws',
|
||||
host,
|
||||
port: 5183,
|
||||
},
|
||||
},
|
||||
};
|
||||
/** @type {import('vite').UserConfig} */
|
||||
const config = {
|
||||
server: {
|
||||
host: '0.0.0.0', // listen on all addresses
|
||||
port: 5173,
|
||||
strictPort: true,
|
||||
hmr: {
|
||||
protocol: 'ws',
|
||||
host,
|
||||
port: 5183,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return config;
|
||||
return config;
|
||||
});
|
||||
```
|
||||
|
||||
@@ -144,25 +144,25 @@ For Next.js, you need to configure the [assetPrefix] to use the internal IP so t
|
||||
```typescript title=next.config.js
|
||||
const isProd = process.env.NODE_ENV === 'production';
|
||||
module.exports = async (phase, { defaultConfig }) => {
|
||||
let internalHost = null;
|
||||
if (!isProd) {
|
||||
const { internalIpV4 } = await import('internal-ip');
|
||||
internalHost = await internalIpV4();
|
||||
}
|
||||
/**
|
||||
* @type {import('next').NextConfig}
|
||||
*/
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
swcMinify: true,
|
||||
// Note: This experimental feature is required to use NextJS Image in SSG mode.
|
||||
// See https://nextjs.org/docs/messages/export-image-api for different workarounds.
|
||||
images: {
|
||||
unoptimized: true,
|
||||
},
|
||||
assetPrefix: isProd ? null : `http://${internalHost}:3000`,
|
||||
};
|
||||
return nextConfig;
|
||||
let internalHost = null;
|
||||
if (!isProd) {
|
||||
const { internalIpV4 } = await import('internal-ip');
|
||||
internalHost = await internalIpV4();
|
||||
}
|
||||
/**
|
||||
* @type {import('next').NextConfig}
|
||||
*/
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
swcMinify: true,
|
||||
// Note: This experimental feature is required to use NextJS Image in SSG mode.
|
||||
// See https://nextjs.org/docs/messages/export-image-api for different workarounds.
|
||||
images: {
|
||||
unoptimized: true,
|
||||
},
|
||||
assetPrefix: isProd ? null : `http://${internalHost}:3000`,
|
||||
};
|
||||
return nextConfig;
|
||||
};
|
||||
```
|
||||
|
||||
@@ -175,9 +175,9 @@ Webpack has a built-in option to use the local IP address as the host for the de
|
||||
|
||||
```typescript title=webpack.config.js
|
||||
export default {
|
||||
devServer: {
|
||||
host: 'local-ipv4',
|
||||
},
|
||||
devServer: {
|
||||
host: 'local-ipv4',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ This documentation is for the prereleased version of Tauri 2.0 and is subject to
|
||||
:::
|
||||
|
||||
<Card title="Next Steps" icon="rocket">
|
||||
Get ready to make a Tauri project by installing the
|
||||
[prerequisites](/2/guide/prerequisites).
|
||||
Get ready to make a Tauri project by installing the
|
||||
[prerequisites](/2/guide/prerequisites).
|
||||
</Card>
|
||||
|
||||
<Card title="Additional Resources" icon="document">
|
||||
|
||||
@@ -23,35 +23,35 @@ hero:
|
||||
import { Card, CardGrid } from '@astrojs/starlight/components';
|
||||
|
||||
<div class="hero-bg">
|
||||
<div class="bg-logo"></div>
|
||||
<div class="bg-grad"></div>
|
||||
<div class="bg-grad-red"></div>
|
||||
<div class="bg-logo"></div>
|
||||
<div class="bg-grad"></div>
|
||||
<div class="bg-grad-red"></div>
|
||||
</div>
|
||||
|
||||
<CardGrid stagger>
|
||||
<Card title="Frontend Independent" icon="rocket">
|
||||
Bring your existing web stack to Tauri or start that new dream project.
|
||||
Tauri supports any frontend framework so you don't need to change your
|
||||
stack.
|
||||
</Card>
|
||||
<Card title="Cross Platform" icon="rocket">
|
||||
Build your app for Linux, macOS, Windows, Android and iOS - all from a
|
||||
single codebase.
|
||||
</Card>
|
||||
<Card title="Inter-Process Communication" icon="rocket">
|
||||
Write your frontend in JavaScript, application logic in Rust, and integrate
|
||||
deep into the system with Swift and Kotlin.
|
||||
</Card>
|
||||
<Card title="Maximum Security" icon="rocket">
|
||||
Front-of-mind for the Tauri Team driving our highest priorities and biggest
|
||||
innovations.
|
||||
</Card>
|
||||
<Card title="Minimal Size" icon="rocket">
|
||||
By using the OS's native web renderer, the size of a Tauri app can be less
|
||||
than 600KB.
|
||||
</Card>
|
||||
<Card title="Powered by Rust" icon="rocket">
|
||||
With performance and security at the center, Rust is the language for the
|
||||
next generation of apps.
|
||||
</Card>
|
||||
<Card title="Frontend Independent" icon="rocket">
|
||||
Bring your existing web stack to Tauri or start that new dream project.
|
||||
Tauri supports any frontend framework so you don't need to change your
|
||||
stack.
|
||||
</Card>
|
||||
<Card title="Cross Platform" icon="rocket">
|
||||
Build your app for Linux, macOS, Windows, Android and iOS - all from a
|
||||
single codebase.
|
||||
</Card>
|
||||
<Card title="Inter-Process Communication" icon="rocket">
|
||||
Write your frontend in JavaScript, application logic in Rust, and integrate
|
||||
deep into the system with Swift and Kotlin.
|
||||
</Card>
|
||||
<Card title="Maximum Security" icon="rocket">
|
||||
Front-of-mind for the Tauri Team driving our highest priorities and biggest
|
||||
innovations.
|
||||
</Card>
|
||||
<Card title="Minimal Size" icon="rocket">
|
||||
By using the OS's native web renderer, the size of a Tauri app can be less
|
||||
than 600KB.
|
||||
</Card>
|
||||
<Card title="Powered by Rust" icon="rocket">
|
||||
With performance and security at the center, Rust is the language for the
|
||||
next generation of apps.
|
||||
</Card>
|
||||
</CardGrid>
|
||||
|
||||
@@ -1,502 +1,470 @@
|
||||
import fs from 'node:fs';
|
||||
|
||||
export default async function configGenerator() {
|
||||
if (fs.existsSync('packages/tauri/tooling/api/node_modules')) {
|
||||
const schemaPath = 'packages/tauri/core/tauri-config-schema/schema.json';
|
||||
const schemaString = fs
|
||||
.readFileSync(schemaPath)
|
||||
.toString()
|
||||
// Fixes any angle brackets that aren't escaped propertly
|
||||
.replaceAll('(?<!\\)<', '<')
|
||||
.replaceAll('(?!\\)>', '>');
|
||||
const schema = JSON.parse(schemaString);
|
||||
const targetPath = 'src/content/docs/2/reference/config.md';
|
||||
const nullMarkdown = '_null_';
|
||||
if (fs.existsSync('packages/tauri/tooling/api/node_modules')) {
|
||||
const schemaPath = 'packages/tauri/core/tauri-config-schema/schema.json';
|
||||
const schemaString = fs
|
||||
.readFileSync(schemaPath)
|
||||
.toString()
|
||||
// Fixes any angle brackets that aren't escaped propertly
|
||||
.replaceAll('(?<!\\)<', '<')
|
||||
.replaceAll('(?!\\)>', '>');
|
||||
const schema = JSON.parse(schemaString);
|
||||
const targetPath = 'src/content/docs/2/reference/config.md';
|
||||
const nullMarkdown = '_null_';
|
||||
|
||||
const header = `---
|
||||
const header = `---
|
||||
title: Tauri Configuration
|
||||
---
|
||||
|
||||
`;
|
||||
|
||||
const builtDefinitions = [];
|
||||
const builtDefinitions = [];
|
||||
|
||||
function buildObject(key, value, headerLevel) {
|
||||
let out = [];
|
||||
headerLevel = Math.min(headerLevel, 6);
|
||||
function buildObject(key, value, headerLevel) {
|
||||
let out = [];
|
||||
headerLevel = Math.min(headerLevel, 6);
|
||||
|
||||
var headerTitle = value.title ? 'Configuration' : key;
|
||||
var headerTitle = value.title ? 'Configuration' : key;
|
||||
|
||||
var header = `${'#'.repeat(headerLevel)} ${headerTitle}\n`;
|
||||
var header = `${'#'.repeat(headerLevel)} ${headerTitle}\n`;
|
||||
|
||||
if (headerLevel === 1) {
|
||||
headerLevel = 2;
|
||||
}
|
||||
if (headerLevel === 1) {
|
||||
headerLevel = 2;
|
||||
}
|
||||
|
||||
out.push(header);
|
||||
out.push(`${descriptionConstructor(value.description)}\n`);
|
||||
out.push(header);
|
||||
out.push(`${descriptionConstructor(value.description)}\n`);
|
||||
|
||||
out.push(`${longFormTypeConstructor(key, value, headerLevel)}\n`);
|
||||
out = out.concat(buildProperties(headerTitle, value, headerLevel));
|
||||
out.push(`${longFormTypeConstructor(key, value, headerLevel)}\n`);
|
||||
out = out.concat(buildProperties(headerTitle, value, headerLevel));
|
||||
|
||||
out = out.concat(inspectRef(value, headerLevel + 1));
|
||||
out = out.concat(inspectRef(value, headerLevel + 1));
|
||||
|
||||
return out;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function buildDef(name, headerLevel) {
|
||||
const def = name.replace('#/definitions/', '');
|
||||
if (!builtDefinitions.includes(def)) {
|
||||
builtDefinitions.push(def);
|
||||
const obj = schema.definitions[def];
|
||||
return buildObject(def, obj, headerLevel);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
function buildDef(name, headerLevel) {
|
||||
const def = name.replace('#/definitions/', '');
|
||||
if (!builtDefinitions.includes(def)) {
|
||||
builtDefinitions.push(def);
|
||||
const obj = schema.definitions[def];
|
||||
return buildObject(def, obj, headerLevel);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function inspectRef(object, headerLevel) {
|
||||
let out = [];
|
||||
|
||||
if (object.$ref) {
|
||||
out = out.concat(buildDef(object.$ref, headerLevel));
|
||||
}
|
||||
|
||||
if (object.additionalProperties && object.additionalProperties.$ref) {
|
||||
out = out.concat(
|
||||
buildDef(object.additionalProperties.$ref, headerLevel)
|
||||
);
|
||||
}
|
||||
|
||||
if (object.items && object.items.$ref) {
|
||||
out = out.concat(buildDef(object.items.$ref, headerLevel));
|
||||
}
|
||||
|
||||
for (const opt of object.allOf || []) {
|
||||
out = out.concat(inspectRef(opt, headerLevel));
|
||||
}
|
||||
|
||||
for (const opt of object.anyOf || []) {
|
||||
out = out.concat(inspectRef(opt, headerLevel));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function buildProperties(parentName, object, headerLevel) {
|
||||
const out = [];
|
||||
if (!object.properties) return out;
|
||||
|
||||
const required = object.required || [];
|
||||
|
||||
// Build table header
|
||||
out.push('| Name | Type | Default | Description |');
|
||||
out.push('| ---- | ---- | ------- | ----------- |');
|
||||
|
||||
let definitions = [];
|
||||
|
||||
// Populate table
|
||||
Object.entries(object.properties).forEach(([key, value]) => {
|
||||
if (key == '$schema') return;
|
||||
|
||||
let propertyType = typeConstructor(value, true);
|
||||
let propertyDefault = defaultConstructor(value);
|
||||
|
||||
if (required.includes(key)) {
|
||||
propertyType += ' (required)';
|
||||
if (propertyDefault === nullMarkdown) {
|
||||
propertyDefault = '';
|
||||
}
|
||||
}
|
||||
|
||||
const url = `${parentName.toLowerCase()}.${key.toLowerCase()}`;
|
||||
const name = `<div className="anchor-with-padding" id="${url}">\`${key}\`<a class="hash-link" href="#${url}"></a></div>`;
|
||||
out.push(
|
||||
`| ${name} | ${propertyType} | ${propertyDefault} | ${descriptionConstructor(
|
||||
value.description,
|
||||
true
|
||||
)} |`
|
||||
);
|
||||
|
||||
definitions = definitions.concat(inspectRef(value, headerLevel + 1));
|
||||
});
|
||||
|
||||
out.push('\n');
|
||||
|
||||
return out.concat(definitions);
|
||||
}
|
||||
|
||||
function descriptionConstructor(description, fixNewlines = false) {
|
||||
if (!description) return;
|
||||
|
||||
// Remove links to current page
|
||||
description = description.replaceAll(
|
||||
/\n\nSee more: https:\/\/tauri\.app\/v[0-9]\/api\/config.*$/g,
|
||||
''
|
||||
);
|
||||
|
||||
// fix Rust doc style links
|
||||
description = description.replaceAll(/\[`Self::(\S+)`\]/g, '`$1`');
|
||||
|
||||
// Fix bullet points not being on a newline
|
||||
description = description.replaceAll(' - ', '\n- ');
|
||||
|
||||
// Parse any json code blocks
|
||||
if (description.includes('```json ')) {
|
||||
let newDescription = '';
|
||||
const s = description.split('```');
|
||||
|
||||
for (const text of s) {
|
||||
if (text.startsWith('json')) {
|
||||
const description = text.match(/([^{]+)/)[0];
|
||||
const json = JSON.stringify(
|
||||
JSON.parse(text.replace(description, '')),
|
||||
null,
|
||||
2
|
||||
);
|
||||
newDescription += `${description}\n${json}\n`;
|
||||
} else {
|
||||
newDescription += text + '```';
|
||||
}
|
||||
}
|
||||
description = newDescription;
|
||||
}
|
||||
|
||||
const referenceStyleLinksRegex = /(\[[A-Za-z0-9 ]+\]): (.+)/g;
|
||||
const referenceStyleLinksMatches =
|
||||
referenceStyleLinksRegex.exec(description);
|
||||
if (referenceStyleLinksMatches) {
|
||||
let link = referenceStyleLinksMatches[2];
|
||||
// strip `<` and `>` from `<$url>`
|
||||
if (link.startsWith('<')) {
|
||||
link = link.substring(1, link.length - 1);
|
||||
}
|
||||
description = description
|
||||
.replace(referenceStyleLinksMatches[0], '')
|
||||
.replace(
|
||||
referenceStyleLinksMatches[1],
|
||||
`${referenceStyleLinksMatches[1]}(${link})`
|
||||
)
|
||||
.trim();
|
||||
}
|
||||
|
||||
// Fix any embedded new lines
|
||||
if (fixNewlines) {
|
||||
description = description.replaceAll('\n', '<br />');
|
||||
}
|
||||
|
||||
const markdownLinkRegex = /\[([^\[]+)\]\((.*)\)/gm;
|
||||
const markdownLinkMatches = markdownLinkRegex.exec(description);
|
||||
|
||||
if (markdownLinkMatches) {
|
||||
const url = markdownLinkMatches[2];
|
||||
if (!url.startsWith('http')) {
|
||||
description = description.replace(
|
||||
url,
|
||||
`#${url.toLowerCase().replaceAll('_', '')}`
|
||||
);
|
||||
}
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
function typeConstructor(object, describeObject = false) {
|
||||
const canBeNull =
|
||||
(object.type && object.type.includes('null')) ||
|
||||
(object.anyOf && object.anyOf.some((item) => item.type === 'null'));
|
||||
|
||||
if (object.$ref) {
|
||||
return refLinkConstructor(object.$ref, canBeNull);
|
||||
}
|
||||
|
||||
if (object.additionalProperties && object.additionalProperties.$ref) {
|
||||
return refLinkConstructor(object.additionalProperties.$ref, canBeNull);
|
||||
}
|
||||
|
||||
if (object.items && object.items.$ref) {
|
||||
return refLinkConstructor(object.items.$ref, canBeNull);
|
||||
}
|
||||
|
||||
if (object.anyOf) {
|
||||
// Removes any null values
|
||||
const items = object.anyOf.filter(
|
||||
(item) => !(item.type && item.type == 'null')
|
||||
);
|
||||
|
||||
if (canBeNull && items.length == 1) {
|
||||
return `${items.map((t) => typeConstructor(t, describeObject))}?`;
|
||||
}
|
||||
|
||||
return items
|
||||
.map((t) => typeConstructor(t, describeObject))
|
||||
.join(' \\| ');
|
||||
}
|
||||
|
||||
if (object.allOf) {
|
||||
return refLinkConstructor(object.allOf[0].$ref);
|
||||
}
|
||||
|
||||
if (object.oneOf) {
|
||||
return object.oneOf
|
||||
.map((t) => typeConstructor(t, describeObject))
|
||||
.join(' | ');
|
||||
}
|
||||
|
||||
const m = describeObject ? '' : '`';
|
||||
|
||||
if (object.type) {
|
||||
var typeString = '';
|
||||
|
||||
// See what the type is
|
||||
switch (typeof object.type) {
|
||||
case 'string':
|
||||
// See if referencing a different type
|
||||
switch (object.type) {
|
||||
case 'string':
|
||||
typeString = object.enum
|
||||
? object.enum.map((e) => `"${e}"`).join(', ')
|
||||
: `${m}${object.type}${m}`;
|
||||
break;
|
||||
case 'number':
|
||||
case 'integer':
|
||||
case 'boolean':
|
||||
typeString = `${m}${object.type}${m}`;
|
||||
break;
|
||||
case 'object':
|
||||
if (describeObject && object.properties) {
|
||||
typeString = `${m}{`;
|
||||
const len = Object.keys(object.properties).length;
|
||||
let i = 0;
|
||||
for (const prop in object.properties) {
|
||||
typeString += ` "${prop}": ${typeConstructor(
|
||||
object.properties[prop],
|
||||
describeObject
|
||||
)}`;
|
||||
i++;
|
||||
if (i < len) typeString += ',';
|
||||
}
|
||||
typeString += ` }${m}`;
|
||||
} else {
|
||||
typeString = `${m}${object.type}${m}`;
|
||||
}
|
||||
break;
|
||||
case 'array':
|
||||
if (object.items) {
|
||||
if (describeObject) {
|
||||
typeString = `${typeConstructor(
|
||||
object.items,
|
||||
describeObject
|
||||
)}[]`;
|
||||
} else {
|
||||
const type = typeConstructor(object.items, true);
|
||||
const hasLink = type.includes('(#');
|
||||
typeString = hasLink
|
||||
? type.replace(/\[`(.*)`\]/, '[`$1[]`]')
|
||||
: `${m}${type}[]${m}`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'undefined':
|
||||
typeString = nullMarkdown;
|
||||
break;
|
||||
case 'object':
|
||||
if (Array.isArray(object.type)) {
|
||||
// Check if it should just be an optional value
|
||||
if (object.type.length == 2 && object.type.includes('null')) {
|
||||
typeString = `${m}${object.type.filter(
|
||||
(item) => item != 'null'
|
||||
)}${m}?`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
var additionalProperties = [];
|
||||
|
||||
if (object.format) {
|
||||
additionalProperties.push(`format: \`${object.format}\``);
|
||||
}
|
||||
|
||||
if (object.multipleOf) {
|
||||
additionalProperties.push(`multiple of: \`${object.multipleOf}\``);
|
||||
}
|
||||
|
||||
if (object.minimum) {
|
||||
additionalProperties.push(`minimum: \`${object.minimum}\``);
|
||||
}
|
||||
|
||||
if (object.exclusiveMinimum) {
|
||||
additionalProperties.push(
|
||||
`exclusive minimum: \`${object.exclusiveMinimum}\``
|
||||
);
|
||||
}
|
||||
|
||||
if (object.maximum) {
|
||||
additionalProperties.push(`maximum: \`${object.maximum}\``);
|
||||
}
|
||||
|
||||
if (object.exclusiveMaximum) {
|
||||
additionalProperties.push(
|
||||
`exclusive maximum: \`${object.exclusiveMaximum}\``
|
||||
);
|
||||
}
|
||||
|
||||
if (typeString != '') {
|
||||
if (additionalProperties.length > 0) {
|
||||
const props = `_(${additionalProperties.join(', ')})_`;
|
||||
return `${typeString} ${props}`;
|
||||
}
|
||||
return typeString;
|
||||
}
|
||||
}
|
||||
|
||||
if (object.enum) {
|
||||
return `${m}${object.enum.map((e) => `"${e}"`).join(', ')}${m}`;
|
||||
}
|
||||
|
||||
if (Array.isArray(object)) {
|
||||
if (describeObject) {
|
||||
const type = [];
|
||||
for (const obj of object) {
|
||||
type.push(typeConstructor(obj));
|
||||
}
|
||||
if (type.every((t) => t === type[0])) {
|
||||
return `${m}${type[0]}${m}`;
|
||||
}
|
||||
return `${m}[${type.join(', ')}]${m}`;
|
||||
} else {
|
||||
return `${m}array${m}`;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('A type was not able to be parsed:', object);
|
||||
return JSON.stringify(object);
|
||||
}
|
||||
|
||||
/** prepares a description to be added to a markdown bullet point list */
|
||||
function listDescription(description) {
|
||||
return description.replace('\n\n', '\n\n\t');
|
||||
}
|
||||
|
||||
function longFormTypeConstructor(key, object, headerLevel) {
|
||||
if (object.enum) {
|
||||
var buffer = [];
|
||||
buffer.push(`Can be any of the following \`${object.type}\` values:`);
|
||||
object.enum.forEach((item) => {
|
||||
buffer.push(`- ${item}`);
|
||||
});
|
||||
return buffer.join('\n');
|
||||
}
|
||||
if (object.anyOf) {
|
||||
var buffer = [];
|
||||
buffer.push('Can be any of the following types:\n');
|
||||
object.anyOf.forEach((item) => {
|
||||
var description = ':';
|
||||
if (item.description) {
|
||||
description = `: ${descriptionConstructor(item.description)}`;
|
||||
}
|
||||
const hasProperties = 'properties' in item;
|
||||
let typeDef = typeConstructor(item, hasProperties);
|
||||
if (hasProperties) {
|
||||
typeDef = '`' + typeDef + '`';
|
||||
}
|
||||
buffer.push(`- ${typeDef}${listDescription(description)}`);
|
||||
if (hasProperties) {
|
||||
buffer.push('\n\t');
|
||||
buffer.push(
|
||||
buildProperties(key, item, headerLevel)
|
||||
.map((line) => `\t${line}`)
|
||||
.join('\n')
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return buffer.join(`\n`);
|
||||
}
|
||||
|
||||
if (object.oneOf) {
|
||||
var buffer = [];
|
||||
buffer.push('Can be any **ONE** of the following types:\n');
|
||||
object.oneOf.forEach((item) => {
|
||||
var description = ':';
|
||||
if (item.description) {
|
||||
description = `: ${descriptionConstructor(item.description)}`;
|
||||
}
|
||||
const hasProperties = 'properties' in item;
|
||||
let typeDef = typeConstructor(item, hasProperties);
|
||||
if (hasProperties) {
|
||||
typeDef = '`' + typeDef + '`';
|
||||
}
|
||||
buffer.push(`- ${typeDef}${listDescription(description)}`);
|
||||
if ('properties' in item) {
|
||||
buffer.push('\n\t');
|
||||
buffer.push(
|
||||
buildProperties(key, item, headerLevel)
|
||||
.map((line) => `\t${line}`)
|
||||
.join('\n')
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return buffer.join(`\n`);
|
||||
}
|
||||
|
||||
return `Type: ${typeConstructor(object)}`;
|
||||
}
|
||||
|
||||
function defaultConstructor(object) {
|
||||
switch (typeof object.default) {
|
||||
case 'boolean':
|
||||
case 'number':
|
||||
return `\`${object.default}\``;
|
||||
case 'object':
|
||||
// Check if empty array
|
||||
if (Array.isArray(object.default) && object.default.length == 0) {
|
||||
return '[]';
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
if (object.$ref) {
|
||||
console.error('Found $ref default:', object.$ref);
|
||||
}
|
||||
|
||||
if (object.anyOf) {
|
||||
const link = object.anyOf[0].$ref
|
||||
.replace('#/definitions/', '')
|
||||
.toLowerCase();
|
||||
return `[view](#${link})`;
|
||||
}
|
||||
|
||||
if (object.allOf) {
|
||||
const link = object.allOf[0].$ref
|
||||
.replace('#/definitions/', '')
|
||||
.toLowerCase();
|
||||
return `[view](#${link})`;
|
||||
}
|
||||
|
||||
if (object.oneOf) {
|
||||
console.error('Found oneOf default:', object.oneOf);
|
||||
}
|
||||
|
||||
return nullMarkdown;
|
||||
}
|
||||
|
||||
function refLinkConstructor(string, nullable = false) {
|
||||
const name = string.replace('#/definitions/', '');
|
||||
return `[\`${name}\`](#${name.toLowerCase()})${nullable ? '?' : ''}`;
|
||||
}
|
||||
|
||||
const config = buildObject(null, schema, 1).join('\n');
|
||||
fs.writeFileSync(targetPath, `${header}${config}`);
|
||||
} else {
|
||||
console.log(
|
||||
'Tauri submodule is not initialized, respective core config routes will not be rendered.'
|
||||
);
|
||||
}
|
||||
function inspectRef(object, headerLevel) {
|
||||
let out = [];
|
||||
|
||||
if (object.$ref) {
|
||||
out = out.concat(buildDef(object.$ref, headerLevel));
|
||||
}
|
||||
|
||||
if (object.additionalProperties && object.additionalProperties.$ref) {
|
||||
out = out.concat(buildDef(object.additionalProperties.$ref, headerLevel));
|
||||
}
|
||||
|
||||
if (object.items && object.items.$ref) {
|
||||
out = out.concat(buildDef(object.items.$ref, headerLevel));
|
||||
}
|
||||
|
||||
for (const opt of object.allOf || []) {
|
||||
out = out.concat(inspectRef(opt, headerLevel));
|
||||
}
|
||||
|
||||
for (const opt of object.anyOf || []) {
|
||||
out = out.concat(inspectRef(opt, headerLevel));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function buildProperties(parentName, object, headerLevel) {
|
||||
const out = [];
|
||||
if (!object.properties) return out;
|
||||
|
||||
const required = object.required || [];
|
||||
|
||||
// Build table header
|
||||
out.push('| Name | Type | Default | Description |');
|
||||
out.push('| ---- | ---- | ------- | ----------- |');
|
||||
|
||||
let definitions = [];
|
||||
|
||||
// Populate table
|
||||
Object.entries(object.properties).forEach(([key, value]) => {
|
||||
if (key == '$schema') return;
|
||||
|
||||
let propertyType = typeConstructor(value, true);
|
||||
let propertyDefault = defaultConstructor(value);
|
||||
|
||||
if (required.includes(key)) {
|
||||
propertyType += ' (required)';
|
||||
if (propertyDefault === nullMarkdown) {
|
||||
propertyDefault = '';
|
||||
}
|
||||
}
|
||||
|
||||
const url = `${parentName.toLowerCase()}.${key.toLowerCase()}`;
|
||||
const name = `<div className="anchor-with-padding" id="${url}">\`${key}\`<a class="hash-link" href="#${url}"></a></div>`;
|
||||
out.push(
|
||||
`| ${name} | ${propertyType} | ${propertyDefault} | ${descriptionConstructor(
|
||||
value.description,
|
||||
true
|
||||
)} |`
|
||||
);
|
||||
|
||||
definitions = definitions.concat(inspectRef(value, headerLevel + 1));
|
||||
});
|
||||
|
||||
out.push('\n');
|
||||
|
||||
return out.concat(definitions);
|
||||
}
|
||||
|
||||
function descriptionConstructor(description, fixNewlines = false) {
|
||||
if (!description) return;
|
||||
|
||||
// Remove links to current page
|
||||
description = description.replaceAll(
|
||||
/\n\nSee more: https:\/\/tauri\.app\/v[0-9]\/api\/config.*$/g,
|
||||
''
|
||||
);
|
||||
|
||||
// fix Rust doc style links
|
||||
description = description.replaceAll(/\[`Self::(\S+)`\]/g, '`$1`');
|
||||
|
||||
// Fix bullet points not being on a newline
|
||||
description = description.replaceAll(' - ', '\n- ');
|
||||
|
||||
// Parse any json code blocks
|
||||
if (description.includes('```json ')) {
|
||||
let newDescription = '';
|
||||
const s = description.split('```');
|
||||
|
||||
for (const text of s) {
|
||||
if (text.startsWith('json')) {
|
||||
const description = text.match(/([^{]+)/)[0];
|
||||
const json = JSON.stringify(JSON.parse(text.replace(description, '')), null, 2);
|
||||
newDescription += `${description}\n${json}\n`;
|
||||
} else {
|
||||
newDescription += text + '```';
|
||||
}
|
||||
}
|
||||
description = newDescription;
|
||||
}
|
||||
|
||||
const referenceStyleLinksRegex = /(\[[A-Za-z0-9 ]+\]): (.+)/g;
|
||||
const referenceStyleLinksMatches = referenceStyleLinksRegex.exec(description);
|
||||
if (referenceStyleLinksMatches) {
|
||||
let link = referenceStyleLinksMatches[2];
|
||||
// strip `<` and `>` from `<$url>`
|
||||
if (link.startsWith('<')) {
|
||||
link = link.substring(1, link.length - 1);
|
||||
}
|
||||
description = description
|
||||
.replace(referenceStyleLinksMatches[0], '')
|
||||
.replace(referenceStyleLinksMatches[1], `${referenceStyleLinksMatches[1]}(${link})`)
|
||||
.trim();
|
||||
}
|
||||
|
||||
// Fix any embedded new lines
|
||||
if (fixNewlines) {
|
||||
description = description.replaceAll('\n', '<br />');
|
||||
}
|
||||
|
||||
const markdownLinkRegex = /\[([^\[]+)\]\((.*)\)/gm;
|
||||
const markdownLinkMatches = markdownLinkRegex.exec(description);
|
||||
|
||||
if (markdownLinkMatches) {
|
||||
const url = markdownLinkMatches[2];
|
||||
if (!url.startsWith('http')) {
|
||||
description = description.replace(url, `#${url.toLowerCase().replaceAll('_', '')}`);
|
||||
}
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
function typeConstructor(object, describeObject = false) {
|
||||
const canBeNull =
|
||||
(object.type && object.type.includes('null')) ||
|
||||
(object.anyOf && object.anyOf.some((item) => item.type === 'null'));
|
||||
|
||||
if (object.$ref) {
|
||||
return refLinkConstructor(object.$ref, canBeNull);
|
||||
}
|
||||
|
||||
if (object.additionalProperties && object.additionalProperties.$ref) {
|
||||
return refLinkConstructor(object.additionalProperties.$ref, canBeNull);
|
||||
}
|
||||
|
||||
if (object.items && object.items.$ref) {
|
||||
return refLinkConstructor(object.items.$ref, canBeNull);
|
||||
}
|
||||
|
||||
if (object.anyOf) {
|
||||
// Removes any null values
|
||||
const items = object.anyOf.filter((item) => !(item.type && item.type == 'null'));
|
||||
|
||||
if (canBeNull && items.length == 1) {
|
||||
return `${items.map((t) => typeConstructor(t, describeObject))}?`;
|
||||
}
|
||||
|
||||
return items.map((t) => typeConstructor(t, describeObject)).join(' \\| ');
|
||||
}
|
||||
|
||||
if (object.allOf) {
|
||||
return refLinkConstructor(object.allOf[0].$ref);
|
||||
}
|
||||
|
||||
if (object.oneOf) {
|
||||
return object.oneOf.map((t) => typeConstructor(t, describeObject)).join(' | ');
|
||||
}
|
||||
|
||||
const m = describeObject ? '' : '`';
|
||||
|
||||
if (object.type) {
|
||||
var typeString = '';
|
||||
|
||||
// See what the type is
|
||||
switch (typeof object.type) {
|
||||
case 'string':
|
||||
// See if referencing a different type
|
||||
switch (object.type) {
|
||||
case 'string':
|
||||
typeString = object.enum
|
||||
? object.enum.map((e) => `"${e}"`).join(', ')
|
||||
: `${m}${object.type}${m}`;
|
||||
break;
|
||||
case 'number':
|
||||
case 'integer':
|
||||
case 'boolean':
|
||||
typeString = `${m}${object.type}${m}`;
|
||||
break;
|
||||
case 'object':
|
||||
if (describeObject && object.properties) {
|
||||
typeString = `${m}{`;
|
||||
const len = Object.keys(object.properties).length;
|
||||
let i = 0;
|
||||
for (const prop in object.properties) {
|
||||
typeString += ` "${prop}": ${typeConstructor(
|
||||
object.properties[prop],
|
||||
describeObject
|
||||
)}`;
|
||||
i++;
|
||||
if (i < len) typeString += ',';
|
||||
}
|
||||
typeString += ` }${m}`;
|
||||
} else {
|
||||
typeString = `${m}${object.type}${m}`;
|
||||
}
|
||||
break;
|
||||
case 'array':
|
||||
if (object.items) {
|
||||
if (describeObject) {
|
||||
typeString = `${typeConstructor(object.items, describeObject)}[]`;
|
||||
} else {
|
||||
const type = typeConstructor(object.items, true);
|
||||
const hasLink = type.includes('(#');
|
||||
typeString = hasLink
|
||||
? type.replace(/\[`(.*)`\]/, '[`$1[]`]')
|
||||
: `${m}${type}[]${m}`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'undefined':
|
||||
typeString = nullMarkdown;
|
||||
break;
|
||||
case 'object':
|
||||
if (Array.isArray(object.type)) {
|
||||
// Check if it should just be an optional value
|
||||
if (object.type.length == 2 && object.type.includes('null')) {
|
||||
typeString = `${m}${object.type.filter((item) => item != 'null')}${m}?`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
var additionalProperties = [];
|
||||
|
||||
if (object.format) {
|
||||
additionalProperties.push(`format: \`${object.format}\``);
|
||||
}
|
||||
|
||||
if (object.multipleOf) {
|
||||
additionalProperties.push(`multiple of: \`${object.multipleOf}\``);
|
||||
}
|
||||
|
||||
if (object.minimum) {
|
||||
additionalProperties.push(`minimum: \`${object.minimum}\``);
|
||||
}
|
||||
|
||||
if (object.exclusiveMinimum) {
|
||||
additionalProperties.push(`exclusive minimum: \`${object.exclusiveMinimum}\``);
|
||||
}
|
||||
|
||||
if (object.maximum) {
|
||||
additionalProperties.push(`maximum: \`${object.maximum}\``);
|
||||
}
|
||||
|
||||
if (object.exclusiveMaximum) {
|
||||
additionalProperties.push(`exclusive maximum: \`${object.exclusiveMaximum}\``);
|
||||
}
|
||||
|
||||
if (typeString != '') {
|
||||
if (additionalProperties.length > 0) {
|
||||
const props = `_(${additionalProperties.join(', ')})_`;
|
||||
return `${typeString} ${props}`;
|
||||
}
|
||||
return typeString;
|
||||
}
|
||||
}
|
||||
|
||||
if (object.enum) {
|
||||
return `${m}${object.enum.map((e) => `"${e}"`).join(', ')}${m}`;
|
||||
}
|
||||
|
||||
if (Array.isArray(object)) {
|
||||
if (describeObject) {
|
||||
const type = [];
|
||||
for (const obj of object) {
|
||||
type.push(typeConstructor(obj));
|
||||
}
|
||||
if (type.every((t) => t === type[0])) {
|
||||
return `${m}${type[0]}${m}`;
|
||||
}
|
||||
return `${m}[${type.join(', ')}]${m}`;
|
||||
} else {
|
||||
return `${m}array${m}`;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('A type was not able to be parsed:', object);
|
||||
return JSON.stringify(object);
|
||||
}
|
||||
|
||||
/** prepares a description to be added to a markdown bullet point list */
|
||||
function listDescription(description) {
|
||||
return description.replace('\n\n', '\n\n\t');
|
||||
}
|
||||
|
||||
function longFormTypeConstructor(key, object, headerLevel) {
|
||||
if (object.enum) {
|
||||
var buffer = [];
|
||||
buffer.push(`Can be any of the following \`${object.type}\` values:`);
|
||||
object.enum.forEach((item) => {
|
||||
buffer.push(`- ${item}`);
|
||||
});
|
||||
return buffer.join('\n');
|
||||
}
|
||||
if (object.anyOf) {
|
||||
var buffer = [];
|
||||
buffer.push('Can be any of the following types:\n');
|
||||
object.anyOf.forEach((item) => {
|
||||
var description = ':';
|
||||
if (item.description) {
|
||||
description = `: ${descriptionConstructor(item.description)}`;
|
||||
}
|
||||
const hasProperties = 'properties' in item;
|
||||
let typeDef = typeConstructor(item, hasProperties);
|
||||
if (hasProperties) {
|
||||
typeDef = '`' + typeDef + '`';
|
||||
}
|
||||
buffer.push(`- ${typeDef}${listDescription(description)}`);
|
||||
if (hasProperties) {
|
||||
buffer.push('\n\t');
|
||||
buffer.push(
|
||||
buildProperties(key, item, headerLevel)
|
||||
.map((line) => `\t${line}`)
|
||||
.join('\n')
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return buffer.join(`\n`);
|
||||
}
|
||||
|
||||
if (object.oneOf) {
|
||||
var buffer = [];
|
||||
buffer.push('Can be any **ONE** of the following types:\n');
|
||||
object.oneOf.forEach((item) => {
|
||||
var description = ':';
|
||||
if (item.description) {
|
||||
description = `: ${descriptionConstructor(item.description)}`;
|
||||
}
|
||||
const hasProperties = 'properties' in item;
|
||||
let typeDef = typeConstructor(item, hasProperties);
|
||||
if (hasProperties) {
|
||||
typeDef = '`' + typeDef + '`';
|
||||
}
|
||||
buffer.push(`- ${typeDef}${listDescription(description)}`);
|
||||
if ('properties' in item) {
|
||||
buffer.push('\n\t');
|
||||
buffer.push(
|
||||
buildProperties(key, item, headerLevel)
|
||||
.map((line) => `\t${line}`)
|
||||
.join('\n')
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return buffer.join(`\n`);
|
||||
}
|
||||
|
||||
return `Type: ${typeConstructor(object)}`;
|
||||
}
|
||||
|
||||
function defaultConstructor(object) {
|
||||
switch (typeof object.default) {
|
||||
case 'boolean':
|
||||
case 'number':
|
||||
return `\`${object.default}\``;
|
||||
case 'object':
|
||||
// Check if empty array
|
||||
if (Array.isArray(object.default) && object.default.length == 0) {
|
||||
return '[]';
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
if (object.$ref) {
|
||||
console.error('Found $ref default:', object.$ref);
|
||||
}
|
||||
|
||||
if (object.anyOf) {
|
||||
const link = object.anyOf[0].$ref.replace('#/definitions/', '').toLowerCase();
|
||||
return `[view](#${link})`;
|
||||
}
|
||||
|
||||
if (object.allOf) {
|
||||
const link = object.allOf[0].$ref.replace('#/definitions/', '').toLowerCase();
|
||||
return `[view](#${link})`;
|
||||
}
|
||||
|
||||
if (object.oneOf) {
|
||||
console.error('Found oneOf default:', object.oneOf);
|
||||
}
|
||||
|
||||
return nullMarkdown;
|
||||
}
|
||||
|
||||
function refLinkConstructor(string, nullable = false) {
|
||||
const name = string.replace('#/definitions/', '');
|
||||
return `[\`${name}\`](#${name.toLowerCase()})${nullable ? '?' : ''}`;
|
||||
}
|
||||
|
||||
const config = buildObject(null, schema, 1).join('\n');
|
||||
fs.writeFileSync(targetPath, `${header}${config}`);
|
||||
} else {
|
||||
console.log(
|
||||
'Tauri submodule is not initialized, respective core config routes will not be rendered.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,224 +1,208 @@
|
||||
:root {
|
||||
--sl-color-white: hsl(0, 0%, 100%);
|
||||
--sl-color-gray-1: hsl(231, 23%, 94%);
|
||||
--sl-color-gray-2: hsl(228, 8%, 77%);
|
||||
--sl-color-gray-3: hsl(221, 8%, 56%);
|
||||
--sl-color-gray-4: hsl(225, 9%, 36%);
|
||||
--sl-color-gray-5: hsl(222, 11%, 23%);
|
||||
--sl-color-gray-6: hsl(180, 2%, 10%);
|
||||
--sl-color-black: hsl(0, 0%, 7%);
|
||||
--sl-color-white: hsl(0, 0%, 100%);
|
||||
--sl-color-gray-1: hsl(231, 23%, 94%);
|
||||
--sl-color-gray-2: hsl(228, 8%, 77%);
|
||||
--sl-color-gray-3: hsl(221, 8%, 56%);
|
||||
--sl-color-gray-4: hsl(225, 9%, 36%);
|
||||
--sl-color-gray-5: hsl(222, 11%, 23%);
|
||||
--sl-color-gray-6: hsl(180, 2%, 10%);
|
||||
--sl-color-black: hsl(0, 0%, 7%);
|
||||
|
||||
--sl-hue-orange: 41;
|
||||
--sl-color-orange-low: hsl(var(--sl-hue-orange), 39%, 22%);
|
||||
--sl-color-orange: hsl(var(--sl-hue-orange), 82%, 63%);
|
||||
--sl-color-orange-high: hsl(var(--sl-hue-orange), 82%, 87%);
|
||||
--sl-hue-green: 101;
|
||||
--sl-color-green-low: hsl(var(--sl-hue-green), 39%, 22%);
|
||||
--sl-color-green: hsl(var(--sl-hue-green), 82%, 63%);
|
||||
--sl-color-green-high: hsl(var(--sl-hue-green), 82%, 80%);
|
||||
--sl-hue-blue: 234;
|
||||
--sl-color-blue-low: hsl(var(--sl-hue-blue), 54%, 20%);
|
||||
--sl-color-blue: hsl(var(--sl-hue-blue), 100%, 60%);
|
||||
--sl-color-blue-high: hsl(var(--sl-hue-blue), 100%, 87%);
|
||||
--sl-hue-purple: 281;
|
||||
--sl-color-purple-low: hsl(var(--sl-hue-purple), 39%, 22%);
|
||||
--sl-color-purple: hsl(var(--sl-hue-purple), 82%, 63%);
|
||||
--sl-color-purple-high: hsl(var(--sl-hue-purple), 82%, 89%);
|
||||
--sl-hue-red: 339;
|
||||
--sl-color-red-low: hsl(var(--sl-hue-red), 39%, 22%);
|
||||
--sl-color-red: hsl(var(--sl-hue-red), 82%, 63%);
|
||||
--sl-color-red-high: hsl(var(--sl-hue-red), 82%, 87%);
|
||||
--sl-hue-orange: 41;
|
||||
--sl-color-orange-low: hsl(var(--sl-hue-orange), 39%, 22%);
|
||||
--sl-color-orange: hsl(var(--sl-hue-orange), 82%, 63%);
|
||||
--sl-color-orange-high: hsl(var(--sl-hue-orange), 82%, 87%);
|
||||
--sl-hue-green: 101;
|
||||
--sl-color-green-low: hsl(var(--sl-hue-green), 39%, 22%);
|
||||
--sl-color-green: hsl(var(--sl-hue-green), 82%, 63%);
|
||||
--sl-color-green-high: hsl(var(--sl-hue-green), 82%, 80%);
|
||||
--sl-hue-blue: 234;
|
||||
--sl-color-blue-low: hsl(var(--sl-hue-blue), 54%, 20%);
|
||||
--sl-color-blue: hsl(var(--sl-hue-blue), 100%, 60%);
|
||||
--sl-color-blue-high: hsl(var(--sl-hue-blue), 100%, 87%);
|
||||
--sl-hue-purple: 281;
|
||||
--sl-color-purple-low: hsl(var(--sl-hue-purple), 39%, 22%);
|
||||
--sl-color-purple: hsl(var(--sl-hue-purple), 82%, 63%);
|
||||
--sl-color-purple-high: hsl(var(--sl-hue-purple), 82%, 89%);
|
||||
--sl-hue-red: 339;
|
||||
--sl-color-red-low: hsl(var(--sl-hue-red), 39%, 22%);
|
||||
--sl-color-red: hsl(var(--sl-hue-red), 82%, 63%);
|
||||
--sl-color-red-high: hsl(var(--sl-hue-red), 82%, 87%);
|
||||
|
||||
--sl-hue-accent: var(--sl-hue-blue);
|
||||
--sl-color-accent-low: hsl(var(--sl-hue-accent), 54%, 20%);
|
||||
--sl-color-accent: hsl(var(--sl-hue-accent), 100%, 60%);
|
||||
--sl-color-accent-high: hsl(var(--sl-hue-accent), 100%, 87%);
|
||||
--sl-hue-accent: var(--sl-hue-blue);
|
||||
--sl-color-accent-low: hsl(var(--sl-hue-accent), 54%, 20%);
|
||||
--sl-color-accent: hsl(var(--sl-hue-accent), 100%, 60%);
|
||||
--sl-color-accent-high: hsl(var(--sl-hue-accent), 100%, 87%);
|
||||
|
||||
--sl-color-text: var(--sl-color-gray-2);
|
||||
--sl-color-text-accent: var(--sl-color-accent-high);
|
||||
--sl-color-text-invert: var(--sl-color-accent-low);
|
||||
--sl-color-bg: var(--sl-color-black);
|
||||
--sl-color-bg-nav: var(--sl-color-gray-6);
|
||||
--sl-color-bg-sidebar: var(--sl-color-gray-6);
|
||||
--sl-color-bg-inline-code: var(--sl-color-gray-5);
|
||||
--sl-color-hairline-light: var(--sl-color-gray-5);
|
||||
--sl-color-hairline: var(--sl-color-gray-6);
|
||||
--sl-color-hairline-shade: var(--sl-color-black);
|
||||
--sl-color-text: var(--sl-color-gray-2);
|
||||
--sl-color-text-accent: var(--sl-color-accent-high);
|
||||
--sl-color-text-invert: var(--sl-color-accent-low);
|
||||
--sl-color-bg: var(--sl-color-black);
|
||||
--sl-color-bg-nav: var(--sl-color-gray-6);
|
||||
--sl-color-bg-sidebar: var(--sl-color-gray-6);
|
||||
--sl-color-bg-inline-code: var(--sl-color-gray-5);
|
||||
--sl-color-hairline-light: var(--sl-color-gray-5);
|
||||
--sl-color-hairline: var(--sl-color-gray-6);
|
||||
--sl-color-hairline-shade: var(--sl-color-black);
|
||||
|
||||
--sl-hue-blue: 186;
|
||||
--sl-hue-orange: 42;
|
||||
--sl-hue-blue: 186;
|
||||
--sl-hue-orange: 42;
|
||||
|
||||
/* Custom */
|
||||
--tauri-transition-speed: 200ms;
|
||||
/* Custom */
|
||||
--tauri-transition-speed: 200ms;
|
||||
}
|
||||
|
||||
:root[data-theme='light'] {
|
||||
--sl-color-white: hsl(0, 0%, 100%);
|
||||
--sl-color-gray-1: hsl(231, 23%, 94%);
|
||||
--sl-color-gray-2: hsl(228, 8%, 77%);
|
||||
--sl-color-gray-3: hsl(221, 8%, 56%);
|
||||
--sl-color-gray-4: hsl(225, 9%, 36%);
|
||||
--sl-color-gray-5: hsl(222, 11%, 23%);
|
||||
--sl-color-gray-6: hsl(180, 2%, 10%);
|
||||
--sl-color-black: hsl(0, 0%, 7%);
|
||||
--sl-color-white: hsl(0, 0%, 100%);
|
||||
--sl-color-gray-1: hsl(231, 23%, 94%);
|
||||
--sl-color-gray-2: hsl(228, 8%, 77%);
|
||||
--sl-color-gray-3: hsl(221, 8%, 56%);
|
||||
--sl-color-gray-4: hsl(225, 9%, 36%);
|
||||
--sl-color-gray-5: hsl(222, 11%, 23%);
|
||||
--sl-color-gray-6: hsl(180, 2%, 10%);
|
||||
--sl-color-black: hsl(0, 0%, 7%);
|
||||
|
||||
--sl-hue-orange: 41;
|
||||
--sl-color-orange-low: hsl(var(--sl-hue-orange), 39%, 22%);
|
||||
--sl-color-orange: hsl(var(--sl-hue-orange), 82%, 63%);
|
||||
--sl-color-orange-high: hsl(var(--sl-hue-orange), 82%, 87%);
|
||||
--sl-hue-green: 101;
|
||||
--sl-color-green-low: hsl(var(--sl-hue-green), 39%, 22%);
|
||||
--sl-color-green: hsl(var(--sl-hue-green), 82%, 63%);
|
||||
--sl-color-green-high: hsl(var(--sl-hue-green), 82%, 80%);
|
||||
--sl-hue-blue: 234;
|
||||
--sl-color-blue-low: hsl(var(--sl-hue-blue), 54%, 20%);
|
||||
--sl-color-blue: hsl(var(--sl-hue-blue), 100%, 60%);
|
||||
--sl-color-blue-high: hsl(var(--sl-hue-blue), 100%, 87%);
|
||||
--sl-hue-purple: 281;
|
||||
--sl-color-purple-low: hsl(var(--sl-hue-purple), 39%, 22%);
|
||||
--sl-color-purple: hsl(var(--sl-hue-purple), 82%, 63%);
|
||||
--sl-color-purple-high: hsl(var(--sl-hue-purple), 82%, 89%);
|
||||
--sl-hue-red: 339;
|
||||
--sl-color-red-low: hsl(var(--sl-hue-red), 39%, 22%);
|
||||
--sl-color-red: hsl(var(--sl-hue-red), 82%, 63%);
|
||||
--sl-color-red-high: hsl(var(--sl-hue-red), 82%, 87%);
|
||||
--sl-hue-orange: 41;
|
||||
--sl-color-orange-low: hsl(var(--sl-hue-orange), 39%, 22%);
|
||||
--sl-color-orange: hsl(var(--sl-hue-orange), 82%, 63%);
|
||||
--sl-color-orange-high: hsl(var(--sl-hue-orange), 82%, 87%);
|
||||
--sl-hue-green: 101;
|
||||
--sl-color-green-low: hsl(var(--sl-hue-green), 39%, 22%);
|
||||
--sl-color-green: hsl(var(--sl-hue-green), 82%, 63%);
|
||||
--sl-color-green-high: hsl(var(--sl-hue-green), 82%, 80%);
|
||||
--sl-hue-blue: 234;
|
||||
--sl-color-blue-low: hsl(var(--sl-hue-blue), 54%, 20%);
|
||||
--sl-color-blue: hsl(var(--sl-hue-blue), 100%, 60%);
|
||||
--sl-color-blue-high: hsl(var(--sl-hue-blue), 100%, 87%);
|
||||
--sl-hue-purple: 281;
|
||||
--sl-color-purple-low: hsl(var(--sl-hue-purple), 39%, 22%);
|
||||
--sl-color-purple: hsl(var(--sl-hue-purple), 82%, 63%);
|
||||
--sl-color-purple-high: hsl(var(--sl-hue-purple), 82%, 89%);
|
||||
--sl-hue-red: 339;
|
||||
--sl-color-red-low: hsl(var(--sl-hue-red), 39%, 22%);
|
||||
--sl-color-red: hsl(var(--sl-hue-red), 82%, 63%);
|
||||
--sl-color-red-high: hsl(var(--sl-hue-red), 82%, 87%);
|
||||
|
||||
--sl-hue-accent: var(--sl-hue-blue);
|
||||
--sl-color-accent-low: hsl(var(--sl-hue-accent), 54%, 20%);
|
||||
--sl-color-accent: hsl(var(--sl-hue-accent), 100%, 60%);
|
||||
--sl-color-accent-high: hsl(var(--sl-hue-accent), 100%, 87%);
|
||||
--sl-hue-accent: var(--sl-hue-blue);
|
||||
--sl-color-accent-low: hsl(var(--sl-hue-accent), 54%, 20%);
|
||||
--sl-color-accent: hsl(var(--sl-hue-accent), 100%, 60%);
|
||||
--sl-color-accent-high: hsl(var(--sl-hue-accent), 100%, 87%);
|
||||
|
||||
--sl-color-text: var(--sl-color-gray-2);
|
||||
--sl-color-text-accent: var(--sl-color-accent-high);
|
||||
--sl-color-text-invert: var(--sl-color-accent-low);
|
||||
--sl-color-bg: var(--sl-color-black);
|
||||
--sl-color-bg-nav: var(--sl-color-gray-6);
|
||||
--sl-color-bg-sidebar: var(--sl-color-gray-6);
|
||||
--sl-color-bg-inline-code: var(--sl-color-gray-5);
|
||||
--sl-color-hairline-light: var(--sl-color-gray-5);
|
||||
--sl-color-hairline: var(--sl-color-gray-6);
|
||||
--sl-color-hairline-shade: var(--sl-color-black);
|
||||
--sl-color-text: var(--sl-color-gray-2);
|
||||
--sl-color-text-accent: var(--sl-color-accent-high);
|
||||
--sl-color-text-invert: var(--sl-color-accent-low);
|
||||
--sl-color-bg: var(--sl-color-black);
|
||||
--sl-color-bg-nav: var(--sl-color-gray-6);
|
||||
--sl-color-bg-sidebar: var(--sl-color-gray-6);
|
||||
--sl-color-bg-inline-code: var(--sl-color-gray-5);
|
||||
--sl-color-hairline-light: var(--sl-color-gray-5);
|
||||
--sl-color-hairline: var(--sl-color-gray-6);
|
||||
--sl-color-hairline-shade: var(--sl-color-black);
|
||||
|
||||
--sl-hue-blue: 186;
|
||||
--sl-hue-orange: 42;
|
||||
--sl-hue-blue: 186;
|
||||
--sl-hue-orange: 42;
|
||||
}
|
||||
|
||||
.content details {
|
||||
padding: 0 1rem;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.content details[open] {
|
||||
background-color: var(--sl-color-gray-6);
|
||||
padding-bottom: rem;
|
||||
background-color: var(--sl-color-gray-6);
|
||||
padding-bottom: rem;
|
||||
}
|
||||
|
||||
.content summary {
|
||||
cursor: pointer;
|
||||
padding: 1rem 0;
|
||||
cursor: pointer;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
/* Heading link styling */
|
||||
.heading-link::after {
|
||||
content: '#';
|
||||
padding-inline-start: 0.25em;
|
||||
opacity: 0;
|
||||
transition: var(--tauri-transition-speed);
|
||||
content: '#';
|
||||
padding-inline-start: 0.25em;
|
||||
opacity: 0;
|
||||
transition: var(--tauri-transition-speed);
|
||||
}
|
||||
|
||||
.heading-link:hover::after {
|
||||
color: var(--sl-color-text-accent);
|
||||
opacity: 1;
|
||||
color: var(--sl-color-text-accent);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.heading-link {
|
||||
text-decoration: none;
|
||||
color: var(--sl-color-white) !important;
|
||||
text-decoration: none;
|
||||
color: var(--sl-color-white) !important;
|
||||
}
|
||||
|
||||
.hero-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
z-index: -1;
|
||||
opacity: 0.7;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(
|
||||
-90deg,
|
||||
rgba(192, 192, 192, 0.2) 1px,
|
||||
transparent 1px
|
||||
),
|
||||
linear-gradient(rgba(192, 192, 192, 0.2) 1px, transparent 1px),
|
||||
linear-gradient(-90deg, rgba(192, 192, 192, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(rgba(192, 192, 192, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(
|
||||
transparent 6px,
|
||||
transparent 6px,
|
||||
transparent 156px,
|
||||
transparent 156px
|
||||
),
|
||||
linear-gradient(-90deg, rgba(192, 192, 192, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(
|
||||
-90deg,
|
||||
transparent 6px,
|
||||
transparent 6px,
|
||||
transparent 156px,
|
||||
transparent 156px
|
||||
),
|
||||
linear-gradient(rgba(192, 192, 192, 0.1) 1px, transparent 1px), transparent;
|
||||
background-size: 32px 32px, 32px 32px, 256px 256px, 256px 256px, 256px 256px,
|
||||
256px 256px, 256px 256px, 256px 256px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
z-index: -1;
|
||||
opacity: 0.7;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(-90deg, rgba(192, 192, 192, 0.2) 1px, transparent 1px),
|
||||
linear-gradient(rgba(192, 192, 192, 0.2) 1px, transparent 1px),
|
||||
linear-gradient(-90deg, rgba(192, 192, 192, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(rgba(192, 192, 192, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(transparent 6px, transparent 6px, transparent 156px, transparent 156px),
|
||||
linear-gradient(-90deg, rgba(192, 192, 192, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(-90deg, transparent 6px, transparent 6px, transparent 156px, transparent 156px),
|
||||
linear-gradient(rgba(192, 192, 192, 0.1) 1px, transparent 1px), transparent;
|
||||
background-size:
|
||||
32px 32px,
|
||||
32px 32px,
|
||||
256px 256px,
|
||||
256px 256px,
|
||||
256px 256px,
|
||||
256px 256px,
|
||||
256px 256px,
|
||||
256px 256px;
|
||||
}
|
||||
|
||||
.hero > img {
|
||||
opacity: 0.3;
|
||||
animation: 3s intro;
|
||||
opacity: 0.3;
|
||||
animation: 3s intro;
|
||||
}
|
||||
|
||||
.bg-grad {
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: 50%;
|
||||
width: 150vh;
|
||||
height: 150vh;
|
||||
opacity: 0.3;
|
||||
background: radial-gradient(
|
||||
circle,
|
||||
rgba(2, 87, 247, 1) 0%,
|
||||
rgba(0, 0, 0, 0) 70%
|
||||
);
|
||||
animation: 6s intro;
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: 50%;
|
||||
width: 150vh;
|
||||
height: 150vh;
|
||||
opacity: 0.3;
|
||||
background: radial-gradient(circle, rgba(2, 87, 247, 1) 0%, rgba(0, 0, 0, 0) 70%);
|
||||
animation: 6s intro;
|
||||
}
|
||||
|
||||
.bg-grad-red {
|
||||
position: absolute;
|
||||
top: 0%;
|
||||
left: -50%;
|
||||
width: 150vh;
|
||||
height: 150vh;
|
||||
opacity: 0.3;
|
||||
background: radial-gradient(
|
||||
circle,
|
||||
rgb(247, 169, 2) 0%,
|
||||
rgba(0, 0, 0, 0) 70%
|
||||
);
|
||||
animation: 14s intro;
|
||||
position: absolute;
|
||||
top: 0%;
|
||||
left: -50%;
|
||||
width: 150vh;
|
||||
height: 150vh;
|
||||
opacity: 0.3;
|
||||
background: radial-gradient(circle, rgb(247, 169, 2) 0%, rgba(0, 0, 0, 0) 70%);
|
||||
animation: 14s intro;
|
||||
}
|
||||
|
||||
@keyframes intro {
|
||||
0% {
|
||||
scale: 1.2;
|
||||
opacity: 0%;
|
||||
}
|
||||
0% {
|
||||
scale: 1.2;
|
||||
opacity: 0%;
|
||||
}
|
||||
|
||||
100% {
|
||||
}
|
||||
100% {
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion) {
|
||||
* {
|
||||
transition: none !important;
|
||||
}
|
||||
* {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user