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:
Lorenzo Lewis
2023-08-05 12:07:14 +01:00
committed by GitHub
parent d2a48f8240
commit 341b49cd68
20 changed files with 1536 additions and 1538 deletions

34
.github/workflows/format.yml vendored Normal file
View 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
View 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
View 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
}
}
]
}

View File

@@ -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' } },
});

View File

@@ -7,4 +7,4 @@
"label": "Français",
"lang": "fr"
}
}
}

View File

@@ -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"

View File

@@ -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();

View File

@@ -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>🔄&nbsp; 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>❌&nbsp; Missing</h5>`);
lines.push(`<ul>`);
lines.push(
...missing.map(
(content) =>
`<li>` +
`${this.renderLink(
content.githubUrl,
content.subpath
)} &nbsp; ${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>🔄&nbsp; 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>❌&nbsp; Missing</h5>`);
lines.push(`<ul>`);
lines.push(
...missing.map(
(content) =>
`<li>` +
`${this.renderLink(
content.githubUrl,
content.subpath
)} &nbsp; ${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 &nbsp; 🔄 Needs updating &nbsp; ✔ Completed</sup>`
);
lines.push('</div>');
lines.push(`\n<sup>❌ Missing &nbsp; 🔄 Needs updating &nbsp; ✔ 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];
}

View File

@@ -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>

View File

@@ -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;
};
};
};

View File

@@ -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
View File

@@ -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'}

View File

@@ -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"
/>

View File

@@ -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>

View File

@@ -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() }),
};

View File

@@ -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',
},
};
```

View File

@@ -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">

View File

@@ -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>

View File

@@ -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.'
);
}
}

View File

@@ -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;
}
}