fix: tabs are a sin

This commit is contained in:
Simon Hyll
2024-05-24 20:22:18 +02:00
parent 1ce00557e4
commit b387bc22fc
151 changed files with 5707 additions and 5707 deletions

View File

@@ -1,29 +1,29 @@
{
"printWidth": 100,
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": true,
"plugins": ["prettier-plugin-astro"],
"overrides": [
{
"files": "*.astro",
"options": {
"parser": "astro"
}
},
{
"files": ["*.json", "*.md", "*.toml", "*.yml"],
"options": {
"useTabs": false
}
},
{
"files": ["*.md", "*.mdx"],
"options": {
"printWidth": 80
}
}
]
"printWidth": 100,
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": false,
"plugins": ["prettier-plugin-astro"],
"overrides": [
{
"files": "*.astro",
"options": {
"parser": "astro"
}
},
{
"files": ["*.json", "*.md", "*.toml", "*.yml"],
"options": {
"useTabs": false
}
},
{
"files": ["*.md", "*.mdx"],
"options": {
"printWidth": 80
}
}
]
}

View File

@@ -8,395 +8,395 @@ import starlightBlog from 'starlight-blog';
import serviceWorker from 'astrojs-service-worker';
const authors = {
nothingismagick: {
name: 'Daniel Thompson-Yvetot',
title: 'Tauri Co-Founder',
picture: '/authors/nothingismagick.jpeg',
},
lucasfernog: {
name: 'Lucas Nogueira',
title: 'Tauri Co-Founder',
picture: '/authors/lucasfernog.jpeg',
},
beanow: {
name: 'Robin van Boven',
title: 'Tauri Board Director',
picture: '/authors/Beanow.png',
},
jbolda: {
name: 'Jacob Bolda',
title: 'Tauri Board Director',
picture: '/authors/jbolda.jpeg',
},
lorenzolewis: {
name: 'Lorenzo Lewis',
title: 'Tauri Community Lead',
picture: '/authors/lorenzolewis.png',
},
tweidinger: {
name: 'Tillmann Weidinger',
title: 'Tauri Security',
picture: '/authors/tweidinger.png',
},
amrbashir: {
name: 'Amr Bashir',
title: 'Tauri Development',
picture: '/authors/amrbashir.png',
},
wusyong: {
name: 'Wu Yu Wei',
title: 'Tauri Development Lead',
picture: '/authors/wusyong.png',
},
chip: {
name: 'Chip Reed',
title: 'Tauri Security',
picture: '/authors/chip.png',
},
nothingismagick: {
name: 'Daniel Thompson-Yvetot',
title: 'Tauri Co-Founder',
picture: '/authors/nothingismagick.jpeg',
},
lucasfernog: {
name: 'Lucas Nogueira',
title: 'Tauri Co-Founder',
picture: '/authors/lucasfernog.jpeg',
},
beanow: {
name: 'Robin van Boven',
title: 'Tauri Board Director',
picture: '/authors/Beanow.png',
},
jbolda: {
name: 'Jacob Bolda',
title: 'Tauri Board Director',
picture: '/authors/jbolda.jpeg',
},
lorenzolewis: {
name: 'Lorenzo Lewis',
title: 'Tauri Community Lead',
picture: '/authors/lorenzolewis.png',
},
tweidinger: {
name: 'Tillmann Weidinger',
title: 'Tauri Security',
picture: '/authors/tweidinger.png',
},
amrbashir: {
name: 'Amr Bashir',
title: 'Tauri Development',
picture: '/authors/amrbashir.png',
},
wusyong: {
name: 'Wu Yu Wei',
title: 'Tauri Development Lead',
picture: '/authors/wusyong.png',
},
chip: {
name: 'Chip Reed',
title: 'Tauri Security',
picture: '/authors/chip.png',
},
};
const site = 'https://v2.tauri.app';
// https://astro.build/config
export default defineConfig({
site,
integrations: [
starlight({
plugins: [
starlightBlog({ authors }),
starlightLinksValidator({ errorOnRelativeLinks: false }),
],
title: 'Tauri',
description: 'The cross-platform app building toolkit',
logo: {
dark: './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',
rss: `${site}/rss`,
},
components: {
Sidebar: './src/components/overrides/Sidebar.astro',
Header: './src/components/overrides/Header.astro',
Footer: 'src/components/overrides/Footer.astro',
ThemeSelect: 'src/components/overrides/ThemeSelect.astro',
PageFrame: 'src/components/overrides/PageFrame.astro',
},
head: [
{
tag: 'meta',
attrs: { property: 'og:image', content: site + '/og.png?v=1' },
},
{
tag: 'meta',
attrs: { property: 'twitter:image', content: site + '/og.png?v=1' },
},
{
tag: 'script',
attrs: {
src: '/navigate.js',
},
},
{
tag: 'link',
attrs: {
rel: 'manifest',
href: '/manifest.json',
},
},
{
tag: 'meta',
attrs: { name: 'theme-color', content: '#181818' },
},
],
editLink: {
baseUrl: 'https://github.com/tauri-apps/tauri-docs/edit/v2',
},
customCss: ['./src/styles/custom.scss'],
sidebar: [
{
label: 'Guides',
collapsed: true,
items: [
{
label: 'Quick Start',
collapsed: true,
items: [
{
label: 'What is Tauri?',
link: '/start/',
},
{
label: 'Prerequisites',
link: '/start/prerequisites/',
},
{
label: 'Create a Project',
link: '/start/create-project/',
badge: {
text: 'WIP',
variant: 'caution',
},
},
{
label: 'Frontend Configuration',
collapsed: true,
autogenerate: { directory: 'start/frontend' },
},
{
label: 'Upgrade & Migrate',
collapsed: true,
autogenerate: { directory: 'start/migrate' },
},
],
},
{
label: 'Core Concepts',
collapsed: true,
autogenerate: { directory: 'concept' },
},
{
label: 'Security',
collapsed: true,
autogenerate: { directory: 'security' },
},
{
label: 'Develop',
collapsed: true,
autogenerate: { directory: 'develop' },
},
{
label: 'Distribute',
collapsed: true,
autogenerate: { directory: 'distribute' },
},
{
label: 'Learn',
collapsed: true,
autogenerate: { directory: 'learn' },
},
{
label: 'Plugins',
collapsed: true,
autogenerate: { directory: 'plugin' },
},
{
label: 'About',
collapsed: true,
autogenerate: { directory: 'about' },
},
],
},
{
label: 'References',
collapsed: true,
items: [
{
label: 'Access Control List',
link: '/reference/acl/',
},
{
label: 'Command Line Interface (CLI)',
link: '/reference/cli/',
},
{
label: 'Configuration',
link: '/reference/config/',
},
{
label: 'Environment Variables',
link: '/reference/environment-variables/',
},
{
label: 'Webview Versions',
link: '/reference/webview-versions/',
},
{
label: 'Releases',
collapsed: true,
autogenerate: { directory: 'release' },
},
{
label: 'JavaScript',
collapsed: true,
autogenerate: { directory: 'reference/javascript' },
},
{
label: 'Rust (docs.rs)',
link: 'https://docs.rs/tauri/2.0.0-beta.19/tauri/index.html',
},
],
},
{
label: 'Blog',
collapsed: true,
items: [
{
label: 'All posts',
link: '/blog/',
},
{
label: 'Recent posts',
collapsed: false,
autogenerate: { directory: 'blog' }, // TODO: Manually construct `items` to sort by dates
},
],
},
],
locales,
lastUpdated: true,
}),
serviceWorker({
workbox: {
cleanupOutdatedCaches: true,
clientsClaim: true,
inlineWorkboxRuntime: true,
skipWaiting: true,
globIgnores: ['**_redirects**', '**_headers**'],
globPatterns: ['**/*.js', '**/*.css'],
runtimeCaching: [
{
urlPattern: new RegExp('.*'),
handler: 'CacheFirst',
options: {
cacheName: 'tauri-runtime',
expiration: {
maxAgeSeconds: 30 * 60, // 30 minutes
},
},
},
],
},
}),
],
markdown: {
shikiConfig: {
langs: ['powershell', 'ts', 'rust', 'bash', 'json', 'toml', 'html', 'js'],
},
rehypePlugins: [
rehypeHeadingIds,
[
rehypeAutolinkHeadings,
{
behavior: 'wrap',
properties: { ariaHidden: true, tabIndex: -1, class: 'heading-link' },
},
],
],
},
redirects: {
// Old blog url schema redirects
'/blog/2022/06/19/tauri-1-0': '/blog/tauri-1-0',
'/blog/tauri_1_0': '/blog/tauri-1-0',
'/blog/2022/07/12/tauri-programme-turns-1-and-board-elections':
'/blog/tauri-programme-turns-1-and-board-elections',
'/blog/2022/09/15/tauri-1-1': '/blog/tauri-1-1',
'/blog/2022/09/19/tauri-egui-0-1': '/blog/tauri-egui-0-1',
'/blog/2022/11/18/tauri-1-2': '/blog/tauri-1-2',
'/blog/2022/12/09/tauri-mobile-alpha': '/blog/tauri-mobile-alpha',
'/blog/2023/02/03/tauri-2-0-0-alpha-3': '/blog/tauri-2-0-0-alpha-3',
'/blog/2023/02/09/tauri-community-growth-and-feedback':
'/blog/tauri-community-growth-and-feedback',
'/blog/2023/03/01/create-tauri-app-version-3-released':
'/blog/create-tauri-app-version-3-released',
'/blog/2023/03/20/tauri-2-0-0-alpha-4': '/blog/tauri-2-0-0-alpha-4',
'/blog/2023/05/03/tauri-1-3': '/blog/tauri-1-3',
'/blog/2023/06/14/tauri-1-4': '/blog/tauri-1-4',
'/blog/2023/06/15/tauri-board-elections-and-governance-updates':
'/blog/tauri-board-elections-and-governance-updates',
'about/intro': 'about/philosophy',
// v1 /guides/debugging -> /guides/debug
...i18nRedirect('/v1/guides/debugging/application', '/guides/debug/application'),
...i18nRedirect('/v1/guides/debugging/vs-code', '/guides/debug/vs-code'),
...i18nRedirect('/v1/guides/debugging/clion', '/guides/debug/clion'),
// v1 /guides/development -> /guides/develop
...i18nRedirect(
'/v1/guides/development/development-cycle',
'/guides/develop/development-cycle'
),
...i18nRedirect(
'/v1/guides/development/updating-dependencies',
'/guides/develop/updating-dependencies'
),
// v1 /guides/testing -> /guides/test
...i18nRedirect('/v1/guides/testing/mocking', '/guides/test/mocking'),
...i18nRedirect('/v1/guides/testing/webdriver/ci', '/guides/test/webdriver/ci'),
...i18nRedirect('/v1/guides/testing/webdriver/introduction', '/guides/test/webdriver/'),
...i18nRedirect(
'/v1/guides/testing/webdriver/example/setup',
'/guides/test/webdriver/example/'
),
...i18nRedirect(
'/v1/guides/testing/webdriver/example/selenium',
'/guides/test/webdriver/example/selenium'
),
...i18nRedirect(
'/v1/guides/testing/webdriver/example/webdriverio',
'/test/webdriver/example/webdriverio'
),
site,
integrations: [
starlight({
plugins: [
starlightBlog({ authors }),
starlightLinksValidator({ errorOnRelativeLinks: false }),
],
title: 'Tauri',
description: 'The cross-platform app building toolkit',
logo: {
dark: './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',
rss: `${site}/rss`,
},
components: {
Sidebar: './src/components/overrides/Sidebar.astro',
Header: './src/components/overrides/Header.astro',
Footer: 'src/components/overrides/Footer.astro',
ThemeSelect: 'src/components/overrides/ThemeSelect.astro',
PageFrame: 'src/components/overrides/PageFrame.astro',
},
head: [
{
tag: 'meta',
attrs: { property: 'og:image', content: site + '/og.png?v=1' },
},
{
tag: 'meta',
attrs: { property: 'twitter:image', content: site + '/og.png?v=1' },
},
{
tag: 'script',
attrs: {
src: '/navigate.js',
},
},
{
tag: 'link',
attrs: {
rel: 'manifest',
href: '/manifest.json',
},
},
{
tag: 'meta',
attrs: { name: 'theme-color', content: '#181818' },
},
],
editLink: {
baseUrl: 'https://github.com/tauri-apps/tauri-docs/edit/v2',
},
customCss: ['./src/styles/custom.scss'],
sidebar: [
{
label: 'Guides',
collapsed: true,
items: [
{
label: 'Quick Start',
collapsed: true,
items: [
{
label: 'What is Tauri?',
link: '/start/',
},
{
label: 'Prerequisites',
link: '/start/prerequisites/',
},
{
label: 'Create a Project',
link: '/start/create-project/',
badge: {
text: 'WIP',
variant: 'caution',
},
},
{
label: 'Frontend Configuration',
collapsed: true,
autogenerate: { directory: 'start/frontend' },
},
{
label: 'Upgrade & Migrate',
collapsed: true,
autogenerate: { directory: 'start/migrate' },
},
],
},
{
label: 'Core Concepts',
collapsed: true,
autogenerate: { directory: 'concept' },
},
{
label: 'Security',
collapsed: true,
autogenerate: { directory: 'security' },
},
{
label: 'Develop',
collapsed: true,
autogenerate: { directory: 'develop' },
},
{
label: 'Distribute',
collapsed: true,
autogenerate: { directory: 'distribute' },
},
{
label: 'Learn',
collapsed: true,
autogenerate: { directory: 'learn' },
},
{
label: 'Plugins',
collapsed: true,
autogenerate: { directory: 'plugin' },
},
{
label: 'About',
collapsed: true,
autogenerate: { directory: 'about' },
},
],
},
{
label: 'References',
collapsed: true,
items: [
{
label: 'Access Control List',
link: '/reference/acl/',
},
{
label: 'Command Line Interface (CLI)',
link: '/reference/cli/',
},
{
label: 'Configuration',
link: '/reference/config/',
},
{
label: 'Environment Variables',
link: '/reference/environment-variables/',
},
{
label: 'Webview Versions',
link: '/reference/webview-versions/',
},
{
label: 'Releases',
collapsed: true,
autogenerate: { directory: 'release' },
},
{
label: 'JavaScript',
collapsed: true,
autogenerate: { directory: 'reference/javascript' },
},
{
label: 'Rust (docs.rs)',
link: 'https://docs.rs/tauri/2.0.0-beta.19/tauri/index.html',
},
],
},
{
label: 'Blog',
collapsed: true,
items: [
{
label: 'All posts',
link: '/blog/',
},
{
label: 'Recent posts',
collapsed: false,
autogenerate: { directory: 'blog' }, // TODO: Manually construct `items` to sort by dates
},
],
},
],
locales,
lastUpdated: true,
}),
serviceWorker({
workbox: {
cleanupOutdatedCaches: true,
clientsClaim: true,
inlineWorkboxRuntime: true,
skipWaiting: true,
globIgnores: ['**_redirects**', '**_headers**'],
globPatterns: ['**/*.js', '**/*.css'],
runtimeCaching: [
{
urlPattern: new RegExp('.*'),
handler: 'CacheFirst',
options: {
cacheName: 'tauri-runtime',
expiration: {
maxAgeSeconds: 30 * 60, // 30 minutes
},
},
},
],
},
}),
],
markdown: {
shikiConfig: {
langs: ['powershell', 'ts', 'rust', 'bash', 'json', 'toml', 'html', 'js'],
},
rehypePlugins: [
rehypeHeadingIds,
[
rehypeAutolinkHeadings,
{
behavior: 'wrap',
properties: { ariaHidden: true, tabIndex: -1, class: 'heading-link' },
},
],
],
},
redirects: {
// Old blog url schema redirects
'/blog/2022/06/19/tauri-1-0': '/blog/tauri-1-0',
'/blog/tauri_1_0': '/blog/tauri-1-0',
'/blog/2022/07/12/tauri-programme-turns-1-and-board-elections':
'/blog/tauri-programme-turns-1-and-board-elections',
'/blog/2022/09/15/tauri-1-1': '/blog/tauri-1-1',
'/blog/2022/09/19/tauri-egui-0-1': '/blog/tauri-egui-0-1',
'/blog/2022/11/18/tauri-1-2': '/blog/tauri-1-2',
'/blog/2022/12/09/tauri-mobile-alpha': '/blog/tauri-mobile-alpha',
'/blog/2023/02/03/tauri-2-0-0-alpha-3': '/blog/tauri-2-0-0-alpha-3',
'/blog/2023/02/09/tauri-community-growth-and-feedback':
'/blog/tauri-community-growth-and-feedback',
'/blog/2023/03/01/create-tauri-app-version-3-released':
'/blog/create-tauri-app-version-3-released',
'/blog/2023/03/20/tauri-2-0-0-alpha-4': '/blog/tauri-2-0-0-alpha-4',
'/blog/2023/05/03/tauri-1-3': '/blog/tauri-1-3',
'/blog/2023/06/14/tauri-1-4': '/blog/tauri-1-4',
'/blog/2023/06/15/tauri-board-elections-and-governance-updates':
'/blog/tauri-board-elections-and-governance-updates',
'about/intro': 'about/philosophy',
// v1 /guides/debugging -> /guides/debug
...i18nRedirect('/v1/guides/debugging/application', '/guides/debug/application'),
...i18nRedirect('/v1/guides/debugging/vs-code', '/guides/debug/vs-code'),
...i18nRedirect('/v1/guides/debugging/clion', '/guides/debug/clion'),
// v1 /guides/development -> /guides/develop
...i18nRedirect(
'/v1/guides/development/development-cycle',
'/guides/develop/development-cycle'
),
...i18nRedirect(
'/v1/guides/development/updating-dependencies',
'/guides/develop/updating-dependencies'
),
// v1 /guides/testing -> /guides/test
...i18nRedirect('/v1/guides/testing/mocking', '/guides/test/mocking'),
...i18nRedirect('/v1/guides/testing/webdriver/ci', '/guides/test/webdriver/ci'),
...i18nRedirect('/v1/guides/testing/webdriver/introduction', '/guides/test/webdriver/'),
...i18nRedirect(
'/v1/guides/testing/webdriver/example/setup',
'/guides/test/webdriver/example/'
),
...i18nRedirect(
'/v1/guides/testing/webdriver/example/selenium',
'/guides/test/webdriver/example/selenium'
),
...i18nRedirect(
'/v1/guides/testing/webdriver/example/webdriverio',
'/test/webdriver/example/webdriverio'
),
// v1 /references
...i18nRedirect('/v1/references', '/concepts'),
...i18nRedirect('/v1/reference/architecture', '/concepts/architecture'),
...i18nRedirect('/v1/reference/architecture/process-model', '/concepts/process-model'),
...i18nRedirect('/v1/reference/architecture/security', '/concepts/tauri-security'),
...i18nRedirect(
'/v1/reference/architecture/inter-process-communication',
'/concepts/inter-process-communication'
),
...i18nRedirect(
'/v1/reference/architecture/inter-process-communication/brownfield',
'/concepts/inter-process-communication/brownfield'
),
...i18nRedirect(
'/v1/reference/architecture/inter-process-communication/isolation',
'/concepts/inter-process-communication/isolation'
),
...i18nRedirect('/v1/reference/security', '/concepts/development-security'),
...i18nRedirect('/v1/reference/configuration-files', '/reference/configuration-files'),
...i18nRedirect('/v1/reference/webview-versions', '/reference/webview-versions'),
// v1 /references
...i18nRedirect('/v1/references', '/concepts'),
...i18nRedirect('/v1/reference/architecture', '/concepts/architecture'),
...i18nRedirect('/v1/reference/architecture/process-model', '/concepts/process-model'),
...i18nRedirect('/v1/reference/architecture/security', '/concepts/tauri-security'),
...i18nRedirect(
'/v1/reference/architecture/inter-process-communication',
'/concepts/inter-process-communication'
),
...i18nRedirect(
'/v1/reference/architecture/inter-process-communication/brownfield',
'/concepts/inter-process-communication/brownfield'
),
...i18nRedirect(
'/v1/reference/architecture/inter-process-communication/isolation',
'/concepts/inter-process-communication/isolation'
),
...i18nRedirect('/v1/reference/security', '/concepts/development-security'),
...i18nRedirect('/v1/reference/configuration-files', '/reference/configuration-files'),
...i18nRedirect('/v1/reference/webview-versions', '/reference/webview-versions'),
// Decommissioned locales -> refer to /public/_redirects file
// '/ko/[...slug]': '/[...slug]',
// '/it/[...slug]': '/[...slug]',
},
server: {
headers: readHeaders(),
},
//
// Decommissioned locales -> refer to /public/_redirects file
// '/ko/[...slug]': '/[...slug]',
// '/it/[...slug]': '/[...slug]',
},
server: {
headers: readHeaders(),
},
//
});
// Generates a redirect for each locale.
function i18nRedirect(from, to) {
const routes = {};
Object.keys(locales).map((locale) =>
locale === 'root'
? (routes[from] = to)
: (routes[`/${locale}/${from.replaceAll(/^\/*/g, '')}`] = `/${locale}/${to.replaceAll(
/^\/*/g,
''
)}`)
);
return routes;
const routes = {};
Object.keys(locales).map((locale) =>
locale === 'root'
? (routes[from] = to)
: (routes[`/${locale}/${from.replaceAll(/^\/*/g, '')}`] = `/${locale}/${to.replaceAll(
/^\/*/g,
''
)}`)
);
return routes;
}
// Read the HTTP header file in `public/_headers`
function readHeaders() {
const header_file = fs
.readFileSync('public/_headers', { encoding: 'utf8' })
.split('\n')
.filter(Boolean);
const headers = {};
for (const line of header_file) {
const [key, val] = line.trim().split(/\s*:\s*(.+)/);
if (key != undefined && val != undefined) {
headers[key] = val.toString();
}
}
return headers;
const header_file = fs
.readFileSync('public/_headers', { encoding: 'utf8' })
.split('\n')
.filter(Boolean);
const headers = {};
for (const line of header_file) {
const [key, val] = line.trim().split(/\s*:\s*(.+)/);
if (key != undefined && val != undefined) {
headers[key] = val.toString();
}
}
return headers;
}

View File

@@ -3,69 +3,69 @@ import { existsSync, writeFileSync } from 'node:fs';
import { slug } from 'github-slugger';
buildConfig(
'../tauri/core/tauri-config-schema/schema.json',
'../../src/content/docs/reference/config.md'
'../tauri/core/tauri-config-schema/schema.json',
'../../src/content/docs/reference/config.md'
);
async function buildConfig(schemaFile: string, outputFile: string) {
if (!existsSync(schemaFile)) {
throw Error('Could not find the Tauri config schema. Is the Tauri submodule initialized?');
}
if (!existsSync(schemaFile)) {
throw Error('Could not find the Tauri config schema. Is the Tauri submodule initialized?');
}
let schema: JSONSchema7 = (await import(schemaFile)).default;
let schema: JSONSchema7 = (await import(schemaFile)).default;
const output = [
'---\n# NOTE: This file is auto-generated in packages/config-generator/build.ts\n# For corrections please edit https://github.com/tauri-apps/tauri/blob/dev/core/tauri-utils/src/config.rs directly\n\ntitle: Configuration\nsidebar:\n order: 1\n---',
];
const output = [
'---\n# NOTE: This file is auto-generated in packages/config-generator/build.ts\n# For corrections please edit https://github.com/tauri-apps/tauri/blob/dev/core/tauri-utils/src/config.rs directly\n\ntitle: Configuration\nsidebar:\n order: 1\n---',
];
output.push(
...buildSchemaDefinition(schema, {
headingLevel: 2,
renderTitle: false,
})
);
output.push(
...buildSchemaDefinition(schema, {
headingLevel: 2,
renderTitle: false,
})
);
writeFileSync(outputFile, output.join('\n\n'));
writeFileSync(outputFile, output.join('\n\n'));
}
interface Options {
headingLevel: number;
renderTitle: boolean;
leadWithType: boolean;
headingLevel: number;
renderTitle: boolean;
leadWithType: boolean;
}
function buildSchemaDefinition(
schema: JSONSchema7Definition,
passedOptions: Partial<Options> = {}
schema: JSONSchema7Definition,
passedOptions: Partial<Options> = {}
): string[] {
// Note: $id, $schema, and $comment are explicitly not rendered
// Note: $id, $schema, and $comment are explicitly not rendered
// Assign default values for any missing options
const opts = Object.assign(
{
headingLevel: 1,
renderTitle: true,
leadWithType: false,
},
passedOptions
);
// Assign default values for any missing options
const opts = Object.assign(
{
headingLevel: 1,
renderTitle: true,
leadWithType: false,
},
passedOptions
);
if (typeof schema === 'boolean') {
return [`\`${schema}\``];
}
if (typeof schema === 'boolean') {
return [`\`${schema}\``];
}
const out: string[] = [];
const out: string[] = [];
out.push(...buildType(schema, opts));
out.push(...buildExtendedItems(schema, opts));
out.push(...buildConditionalSubschemas(schema, opts));
out.push(...buildBooleanSubschemas(schema, opts));
out.push(...buildMetadata(schema, opts));
out.push(...buildProperties(schema, opts));
out.push(...buildExtendedMetadata(schema, opts));
out.push(...buildDefinitions(schema, opts));
out.push(...buildType(schema, opts));
out.push(...buildExtendedItems(schema, opts));
out.push(...buildConditionalSubschemas(schema, opts));
out.push(...buildBooleanSubschemas(schema, opts));
out.push(...buildMetadata(schema, opts));
out.push(...buildProperties(schema, opts));
out.push(...buildExtendedMetadata(schema, opts));
out.push(...buildDefinitions(schema, opts));
return out;
return out;
}
/**
@@ -74,37 +74,37 @@ function buildSchemaDefinition(
* Also see: buildExtendedMetadata()
*/
function buildMetadata(schema: JSONSchema7Definition, opts: Options): string[] {
if (typeof schema !== 'object') {
return [];
}
if (typeof schema !== 'object') {
return [];
}
const out: string[] = [];
const out: string[] = [];
opts.renderTitle &&
schema.title &&
out.push(`${'#'.repeat(Math.min(opts.headingLevel, 6))} ${schema.title}`);
opts.renderTitle &&
schema.title &&
out.push(`${'#'.repeat(Math.min(opts.headingLevel, 6))} ${schema.title}`);
if (schema.readOnly || schema.writeOnly) {
const line = [];
schema.readOnly && line.push('Read only');
schema.writeOnly && line.push('Write only');
out.push(line.join(' & '));
}
if (schema.readOnly || schema.writeOnly) {
const line = [];
schema.readOnly && line.push('Read only');
schema.writeOnly && line.push('Write only');
out.push(line.join(' & '));
}
schema.description &&
out.push(
schema.description
// Set headings to appropriate level
.replaceAll(/#{1,6}(?=.+[\n\\n])/g, '#'.repeat(Math.min(opts.headingLevel + 1, 6)))
// Fix improperly formatted heading links
.replaceAll(/#{1,6}(?=[^\s#])/g, '#')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
// Fix for link at https://github.com/tauri-apps/tauri/blob/713f84db2b5bf17e4217053a229f9c11cbb22c74/core/tauri-config-schema/schema.json#L1863-L1864
.replace('#SecurityConfig.devCsp', '#securityconfig')
);
schema.description &&
out.push(
schema.description
// Set headings to appropriate level
.replaceAll(/#{1,6}(?=.+[\n\\n])/g, '#'.repeat(Math.min(opts.headingLevel + 1, 6)))
// Fix improperly formatted heading links
.replaceAll(/#{1,6}(?=[^\s#])/g, '#')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
// Fix for link at https://github.com/tauri-apps/tauri/blob/713f84db2b5bf17e4217053a229f9c11cbb22c74/core/tauri-config-schema/schema.json#L1863-L1864
.replace('#SecurityConfig.devCsp', '#securityconfig')
);
return out;
return out;
}
/**
@@ -117,318 +117,318 @@ function buildMetadata(schema: JSONSchema7Definition, opts: Options): string[] {
* Non-JSON data validation: contentMediaType, contentEncoding
*/
function buildType(schema: JSONSchema7Definition, opts: Options): string[] {
if (typeof schema !== 'object') {
return [];
}
if (typeof schema !== 'object') {
return [];
}
let line: string = '';
let line: string = '';
if (schema.type) {
if (Array.isArray(schema.type)) {
line += schema.type.map((value) => buildTypeName(value, schema, opts)).join(' | ');
} else {
line += buildTypeName(schema.type, schema, opts);
}
}
if (schema.type) {
if (Array.isArray(schema.type)) {
line += schema.type.map((value) => buildTypeName(value, schema, opts)).join(' | ');
} else {
line += buildTypeName(schema.type, schema, opts);
}
}
if (schema.$ref) {
const reference = schema.$ref.split('/').pop();
if (schema.$ref) {
const reference = schema.$ref.split('/').pop();
if (!reference) {
throw Error(`Invalid reference: ${schema.$ref}`);
}
if (!reference) {
throw Error(`Invalid reference: ${schema.$ref}`);
}
line += `[\`${reference}\`](#${slug(reference)})`;
}
line += `[\`${reference}\`](#${slug(reference)})`;
}
if (schema.const) {
switch (typeof schema.const) {
case 'string':
line += `\`"${schema.const}"\``;
break;
default:
line += `\`${schema.const}\``;
}
}
if (schema.const) {
switch (typeof schema.const) {
case 'string':
line += `\`"${schema.const}"\``;
break;
default:
line += `\`${schema.const}\``;
}
}
if (schema.enum) {
const enumValues: string[] = [];
schema.enum.forEach((value) => {
switch (typeof value) {
case 'string':
enumValues.push(`\`"${value}"\``);
break;
default:
enumValues.push(`\`${value}\``);
}
});
line += enumValues.join(' | ');
}
if (schema.enum) {
const enumValues: string[] = [];
schema.enum.forEach((value) => {
switch (typeof value) {
case 'string':
enumValues.push(`\`"${value}"\``);
break;
default:
enumValues.push(`\`${value}\``);
}
});
line += enumValues.join(' | ');
}
const validation = [];
const validation = [];
// Number validation
schema.multipleOf && validation.push(`multiple of \`${schema.multipleOf}\``);
schema.maximum && validation.push(`maximum of \`${schema.maximum}\``);
schema.exclusiveMaximum && validation.push(`exclusive maximum of \`${schema.exclusiveMaximum}\``);
schema.minimum && validation.push(`minimum of \`${schema.minimum}\``);
schema.exclusiveMinimum && validation.push(`exclusive minimum of \`${schema.exclusiveMinimum}\``);
// Number validation
schema.multipleOf && validation.push(`multiple of \`${schema.multipleOf}\``);
schema.maximum && validation.push(`maximum of \`${schema.maximum}\``);
schema.exclusiveMaximum && validation.push(`exclusive maximum of \`${schema.exclusiveMaximum}\``);
schema.minimum && validation.push(`minimum of \`${schema.minimum}\``);
schema.exclusiveMinimum && validation.push(`exclusive minimum of \`${schema.exclusiveMinimum}\``);
// String validation
schema.maxLength && validation.push(`maximum length of \`${schema.maxLength}\``);
schema.minLength && validation.push(`minimum length of \`${schema.minLength}\``);
schema.pattern && validation.push(`pattern of \`${schema.pattern}\``);
// String validation
schema.maxLength && validation.push(`maximum length of \`${schema.maxLength}\``);
schema.minLength && validation.push(`minimum length of \`${schema.minLength}\``);
schema.pattern && validation.push(`pattern of \`${schema.pattern}\``);
// Array validation
schema.maxItems && validation.push(`maximum of \`${schema.maxItems}\` items`);
schema.minItems && validation.push(`minimum of \`${schema.minItems}\` items`);
schema.uniqueItems && validation.push(`each item must be unique`);
// Array validation
schema.maxItems && validation.push(`maximum of \`${schema.maxItems}\` items`);
schema.minItems && validation.push(`minimum of \`${schema.minItems}\` items`);
schema.uniqueItems && validation.push(`each item must be unique`);
// Object validation
schema.maxProperties && validation.push(`maximum of \`${schema.maxProperties}\` properties`);
schema.minProperties && validation.push(`minimum of \`${schema.minProperties}\` properties`);
// Object validation
schema.maxProperties && validation.push(`maximum of \`${schema.maxProperties}\` properties`);
schema.minProperties && validation.push(`minimum of \`${schema.minProperties}\` properties`);
// Semantic validation
schema.format && validation.push(`formatted as \`${schema.format}\``);
// Semantic validation
schema.format && validation.push(`formatted as \`${schema.format}\``);
// Non-JSON data validation
schema.contentMediaType &&
validation.push(`content media type of \`${schema.contentMediaType}\``);
schema.contentEncoding && validation.push(`content encoding of \`${schema.contentEncoding}\``);
// Non-JSON data validation
schema.contentMediaType &&
validation.push(`content media type of \`${schema.contentMediaType}\``);
schema.contentEncoding && validation.push(`content encoding of \`${schema.contentEncoding}\``);
if (validation.length > 0) {
line += ' ' + validation.join(', ');
}
if (validation.length > 0) {
line += ' ' + validation.join(', ');
}
return [line];
return [line];
function buildTypeName(
typeName: JSONSchema7TypeName,
parentSchema: JSONSchema7Definition,
opts: Options
): string {
// Rendering logic for enums and consts are handled separately
if (typeof parentSchema === 'object' && (parentSchema.enum || parentSchema.const)) {
return '';
}
function buildTypeName(
typeName: JSONSchema7TypeName,
parentSchema: JSONSchema7Definition,
opts: Options
): string {
// Rendering logic for enums and consts are handled separately
if (typeof parentSchema === 'object' && (parentSchema.enum || parentSchema.const)) {
return '';
}
let line = '';
switch (typeName) {
case 'object':
break;
case 'array':
if (typeof parentSchema !== 'object' || !parentSchema.items) {
throw Error('Invalid array');
}
if (Array.isArray(parentSchema.items)) {
line += parentSchema.items
.map((value) => {
const definition = buildSchemaDefinition(value, opts);
if (definition.length > 1) {
// Format additional information to be in parenthesis
const [first, ...rest] = definition;
return [first, ' (', rest.join(', '), ')'];
} else {
return definition.join();
}
})
.join(' | ');
} else {
line += buildSchemaDefinition(parentSchema.items, opts);
}
line += '[]';
break;
default:
line += '`' + typeName + '`';
}
return line;
}
let line = '';
switch (typeName) {
case 'object':
break;
case 'array':
if (typeof parentSchema !== 'object' || !parentSchema.items) {
throw Error('Invalid array');
}
if (Array.isArray(parentSchema.items)) {
line += parentSchema.items
.map((value) => {
const definition = buildSchemaDefinition(value, opts);
if (definition.length > 1) {
// Format additional information to be in parenthesis
const [first, ...rest] = definition;
return [first, ' (', rest.join(', '), ')'];
} else {
return definition.join();
}
})
.join(' | ');
} else {
line += buildSchemaDefinition(parentSchema.items, opts);
}
line += '[]';
break;
default:
line += '`' + typeName + '`';
}
return line;
}
}
/**
* Builds: additionalItems, contains
*/
function buildExtendedItems(schema: JSONSchema7Definition, opts: Options): string[] {
if (typeof schema !== 'object') {
return [];
}
if (typeof schema !== 'object') {
return [];
}
if (schema.additionalItems) {
throw Error('Not implemented');
}
if (schema.contains) {
throw Error('Not implemented');
}
if (schema.additionalItems) {
throw Error('Not implemented');
}
if (schema.contains) {
throw Error('Not implemented');
}
const out: string[] = [];
const out: string[] = [];
schema.additionalItems && out.push(`additionalItems: ${JSON.stringify(schema.additionalItems)}`);
schema.contains && out.push(`contains: ${JSON.stringify(schema.contains)}`);
schema.additionalItems && out.push(`additionalItems: ${JSON.stringify(schema.additionalItems)}`);
schema.contains && out.push(`contains: ${JSON.stringify(schema.contains)}`);
return out;
return out;
}
/**
* Builds: required, properties, patternProperties, additionalProperties, dependencies, propertyNames
*/
function buildProperties(schema: JSONSchema7Definition, opts: Options): string[] {
if (typeof schema !== 'object') {
return [];
}
if (typeof schema !== 'object') {
return [];
}
const out: string[] = [];
const out: string[] = [];
if (schema.properties) {
out.push(`**Object Properties**:`);
if (schema.properties) {
out.push(`**Object Properties**:`);
const properties = Object.entries(schema.properties)
.filter(([key]) => key !== '$schema')
.sort(([a], [b]) => a.localeCompare(b));
const properties = Object.entries(schema.properties)
.filter(([key]) => key !== '$schema')
.sort(([a], [b]) => a.localeCompare(b));
out.push(
properties
.map(
([key]) =>
`- ${key} ${schema.required && schema.required.includes(key) ? '(required)' : ''}`
)
.join('\n')
);
out.push(
properties
.map(
([key]) =>
`- ${key} ${schema.required && schema.required.includes(key) ? '(required)' : ''}`
)
.join('\n')
);
properties.forEach(([key, value]) => {
out.push(`${'#'.repeat(Math.min(6, opts.headingLevel + 1))} ${key}`);
out.push(...buildSchemaDefinition(value, { ...opts, headingLevel: opts.headingLevel + 1 }));
});
}
properties.forEach(([key, value]) => {
out.push(`${'#'.repeat(Math.min(6, opts.headingLevel + 1))} ${key}`);
out.push(...buildSchemaDefinition(value, { ...opts, headingLevel: opts.headingLevel + 1 }));
});
}
schema.additionalProperties &&
out.push(
`**Allows additional properties**: ${buildSchemaDefinition(
schema.additionalProperties,
opts
)}`
);
schema.additionalProperties &&
out.push(
`**Allows additional properties**: ${buildSchemaDefinition(
schema.additionalProperties,
opts
)}`
);
if (schema.patternProperties || schema.dependencies || schema.propertyNames) {
throw Error('Not implemented');
}
if (schema.patternProperties || schema.dependencies || schema.propertyNames) {
throw Error('Not implemented');
}
schema.patternProperties &&
out.push(`patternProperties: ${JSON.stringify(schema.patternProperties)}`);
schema.dependencies && out.push(`dependencies: ${JSON.stringify(schema.dependencies)}`);
schema.propertyNames && out.push(`propertyNames: ${JSON.stringify(schema.propertyNames)}`);
schema.patternProperties &&
out.push(`patternProperties: ${JSON.stringify(schema.patternProperties)}`);
schema.dependencies && out.push(`dependencies: ${JSON.stringify(schema.dependencies)}`);
schema.propertyNames && out.push(`propertyNames: ${JSON.stringify(schema.propertyNames)}`);
return out;
return out;
}
/**
* Builds: if, then, else
*/
function buildConditionalSubschemas(schema: JSONSchema7Definition, opts: Options): string[] {
if (typeof schema !== 'object') {
return [];
}
if (typeof schema !== 'object') {
return [];
}
const out: string[] = [];
const out: string[] = [];
if (schema.if || schema.then || schema.else) {
throw Error('Not implemented');
}
if (schema.if || schema.then || schema.else) {
throw Error('Not implemented');
}
schema.if && out.push(`if: ${JSON.stringify(schema.if)}`);
schema.then && out.push(`then: ${JSON.stringify(schema.then)}`);
schema.else && out.push(`else: ${JSON.stringify(schema.else)}`);
schema.if && out.push(`if: ${JSON.stringify(schema.if)}`);
schema.then && out.push(`then: ${JSON.stringify(schema.then)}`);
schema.else && out.push(`else: ${JSON.stringify(schema.else)}`);
return out;
return out;
}
/**
* Builds: allOf, anyOf, oneOf, not
*/
function buildBooleanSubschemas(schema: JSONSchema7Definition, opts: Options): string[] {
if (typeof schema !== 'object') {
return [];
}
if (typeof schema !== 'object') {
return [];
}
const out: string[] = [];
const out: string[] = [];
if (schema.allOf) {
out.push(...buildXOf('All', schema.allOf, opts));
}
if (schema.allOf) {
out.push(...buildXOf('All', schema.allOf, opts));
}
if (schema.anyOf) {
out.push(...buildXOf('Any', schema.anyOf, opts));
}
if (schema.anyOf) {
out.push(...buildXOf('Any', schema.anyOf, opts));
}
if (schema.oneOf) {
out.push(...buildXOf('One', schema.oneOf, opts));
}
if (schema.oneOf) {
out.push(...buildXOf('One', schema.oneOf, opts));
}
if (schema.not) {
throw Error('Not implemented');
}
if (schema.not) {
throw Error('Not implemented');
}
schema.not && out.push(`not: ${JSON.stringify(schema.not)}`);
schema.not && out.push(`not: ${JSON.stringify(schema.not)}`);
return out;
return out;
function buildXOf(
label: 'One' | 'Any' | 'All',
schemas: JSONSchema7Definition[],
opts: Options
): string[] {
const definitions = schemas.map((value) =>
buildSchemaDefinition(value, { ...opts, leadWithType: true })
);
function buildXOf(
label: 'One' | 'Any' | 'All',
schemas: JSONSchema7Definition[],
opts: Options
): string[] {
const definitions = schemas.map((value) =>
buildSchemaDefinition(value, { ...opts, leadWithType: true })
);
if (definitions.every((definition) => definition.length == 1)) {
// Short definition, can be rendered on a single line
return [definitions.join(' | ')];
}
if (definitions.every((definition) => definition.length == 1)) {
// Short definition, can be rendered on a single line
return [definitions.join(' | ')];
}
const out: string[] = [];
const out: string[] = [];
if (definitions.length > 1) {
// Render as a list
out.push(`**${label} of the following**:`);
const list: string[] = [];
if (definitions.length > 1) {
// Render as a list
out.push(`**${label} of the following**:`);
const list: string[] = [];
const additionalDefinitions: string[][] = [];
const additionalDefinitions: string[][] = [];
definitions.forEach((definition) => {
if (definition[0].startsWith('`object`')) {
// Is an object, need to render subdefinitions
const [first] = definition;
list.push(`- ${first}: Subdefinition ${additionalDefinitions.length + 1}`);
additionalDefinitions.push(definition);
} else {
// Render inline in list
list.push(`- ${definition.map((value) => value.replaceAll('\n', ' ')).join(' ')}`);
}
});
definitions.forEach((definition) => {
if (definition[0].startsWith('`object`')) {
// Is an object, need to render subdefinitions
const [first] = definition;
list.push(`- ${first}: Subdefinition ${additionalDefinitions.length + 1}`);
additionalDefinitions.push(definition);
} else {
// Render inline in list
list.push(`- ${definition.map((value) => value.replaceAll('\n', ' ')).join(' ')}`);
}
});
out.push(list.join('\n'));
out.push(list.join('\n'));
if (additionalDefinitions.length > 0) {
out.push(`${'#'.repeat(Math.min(6, opts.headingLevel))} Subdefinitions`);
additionalDefinitions.forEach((definition, index) => {
// Render heading
out.push(`${'#'.repeat(Math.min(6, opts.headingLevel + 1))} Subdefinition ${index + 1}`);
if (additionalDefinitions.length > 0) {
out.push(`${'#'.repeat(Math.min(6, opts.headingLevel))} Subdefinitions`);
additionalDefinitions.forEach((definition, index) => {
// Render heading
out.push(`${'#'.repeat(Math.min(6, opts.headingLevel + 1))} Subdefinition ${index + 1}`);
// Render definition, giving additional heading indention as needed
definition.forEach((line) => {
out.push(
line.replaceAll(/#{1,6}\s(?=.+)/g, '#'.repeat(Math.min(opts.headingLevel + 2, 6)))
);
});
});
}
} else {
// Render as a block
// Mapping might need some fixing...
out.push(...definitions.map((definition) => definition.join()));
}
return out;
}
// Render definition, giving additional heading indention as needed
definition.forEach((line) => {
out.push(
line.replaceAll(/#{1,6}\s(?=.+)/g, '#'.repeat(Math.min(opts.headingLevel + 2, 6)))
);
});
});
}
} else {
// Render as a block
// Mapping might need some fixing...
out.push(...definitions.map((definition) => definition.join()));
}
return out;
}
}
/**
@@ -438,34 +438,34 @@ function buildBooleanSubschemas(schema: JSONSchema7Definition, opts: Options): s
* https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-9
*/
function buildExtendedMetadata(schema: JSONSchema7Definition, opts: Options): string[] {
if (typeof schema !== 'object') {
return [];
}
if (typeof schema !== 'object') {
return [];
}
const out: string[] = [];
const out: string[] = [];
if (schema.default) {
if (typeof schema.default === 'string') {
out.push(`**Default**: \`"${schema.default}"\``);
} else if (typeof schema.default !== 'object') {
// Render on single line
out.push(`**Default**: \`${schema.default}\``);
} else if (Object.keys(schema.default).length == 0) {
// Empty object, render on a single line
out.push(`**Default**: \`${JSON.stringify(schema.default)}\``);
} else {
// Object with properties, render in code block
out.push(
`\n\`\`\`json title='Default'\n${JSON.stringify(schema.default, null, '\t')}\n\`\`\`\n`
);
}
}
if (schema.default) {
if (typeof schema.default === 'string') {
out.push(`**Default**: \`"${schema.default}"\``);
} else if (typeof schema.default !== 'object') {
// Render on single line
out.push(`**Default**: \`${schema.default}\``);
} else if (Object.keys(schema.default).length == 0) {
// Empty object, render on a single line
out.push(`**Default**: \`${JSON.stringify(schema.default)}\``);
} else {
// Object with properties, render in code block
out.push(
`\n\`\`\`json title='Default'\n${JSON.stringify(schema.default, null, '\t')}\n\`\`\`\n`
);
}
}
if (schema.examples) {
throw Error('Examples not implemented');
}
if (schema.examples) {
throw Error('Examples not implemented');
}
return out;
return out;
}
/**
@@ -475,28 +475,28 @@ function buildExtendedMetadata(schema: JSONSchema7Definition, opts: Options): st
* https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-9
*/
function buildDefinitions(schema: JSONSchema7Definition, opts: Options): string[] {
if (typeof schema !== 'object') {
return [];
}
if (typeof schema !== 'object') {
return [];
}
const out: string[] = [];
const out: string[] = [];
// Combine definitions together
// `definitions` was renamed to `$defs` in Draft 7 but still allowed in either place
const definitions = { ...schema.$defs, ...schema.definitions };
if (Object.keys(definitions).length > 0) {
out.push(`${'#'.repeat(Math.min(opts.headingLevel, 6))} Definitions`);
Object.entries(definitions)
.sort(([a], [b]) => a.localeCompare(b))
.forEach(([key, value]) => {
out.push(`${'#'.repeat(Math.min(opts.headingLevel, 6) + 1)} ${key}`);
out.push(
...buildSchemaDefinition(value, {
...opts,
headingLevel: Math.min(opts.headingLevel, 6) + 2,
})
);
});
}
return out;
// Combine definitions together
// `definitions` was renamed to `$defs` in Draft 7 but still allowed in either place
const definitions = { ...schema.$defs, ...schema.definitions };
if (Object.keys(definitions).length > 0) {
out.push(`${'#'.repeat(Math.min(opts.headingLevel, 6))} Definitions`);
Object.entries(definitions)
.sort(([a], [b]) => a.localeCompare(b))
.forEach(([key, value]) => {
out.push(`${'#'.repeat(Math.min(opts.headingLevel, 6) + 1)} ${key}`);
out.push(
...buildSchemaDefinition(value, {
...opts,
headingLevel: Math.min(opts.headingLevel, 6) + 2,
})
);
});
}
return out;
}

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)
.reduce((acc, { lang }) => (lang !== 'en' ? [lang, ...acc] : acc), [])
.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',
gitHubRef: 'v2',
githubToken: process.env.GITHUB_TOKEN,
pageSourceDir: '../../src/content/docs',
htmlOutputFilePath: '../../dist/contribute/translate-status.html',
sourceLanguage: 'en',
targetLanguages: Object.values(locales)
.reduce((acc, { lang }) => (lang !== 'en' ? [lang, ...acc] : acc), [])
.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',
gitHubRef: 'v2',
githubToken: process.env.GITHUB_TOKEN,
});
await translationStatusBuilder.run();

View File

@@ -7,27 +7,27 @@ import pRetry, { AbortError } from 'p-retry';
* @returns {Promise<any>}
*/
export async function githubGet({ url, githubToken = undefined }) {
return await pRetry(
async () => {
const headers = {
Accept: 'application/vnd.github.v3+json',
};
if (githubToken) {
headers.Authorization = `token ${githubToken}`;
}
const response = await fetch(url, { headers });
const json = await response.json();
return await pRetry(
async () => {
const headers = {
Accept: 'application/vnd.github.v3+json',
};
if (githubToken) {
headers.Authorization = `token ${githubToken}`;
}
const response = await fetch(url, { headers });
const json = await response.json();
if (!response.ok) {
throw new AbortError(
`GitHub API call failed: GET "${url}" returned status ${
response.status
}: ${JSON.stringify(json)}`
);
}
if (!response.ok) {
throw new AbortError(
`GitHub API call failed: GET "${url}" returned status ${
response.status
}: ${JSON.stringify(json)}`
);
}
return json;
},
{ retries: 5 }
);
return json;
},
{ retries: 5 }
);
}

View File

@@ -8,7 +8,7 @@ export const isCi = process.env.CI;
* @param {...any} params
*/
export function debug(message, ...params) {
console.log(message, ...params);
console.log(message, ...params);
}
/**
@@ -16,7 +16,7 @@ export function debug(message, ...params) {
* @param {...any} params
*/
export function warning(message, ...params) {
console.warn(kleur.yellow().bold(`*** WARNING: ${message}`), ...params);
console.warn(kleur.yellow().bold(`*** WARNING: ${message}`), ...params);
}
/**
@@ -24,7 +24,7 @@ export function warning(message, ...params) {
* @param {...any} params
*/
export function error(message, ...params) {
console.error(kleur.red().bold(`*** ERROR: ${message}`), ...params);
console.error(kleur.red().bold(`*** ERROR: ${message}`), ...params);
}
/**
@@ -32,7 +32,7 @@ export function error(message, ...params) {
* while leaving new paragraphs intact.
*/
export function dedentMd(...markdown) {
return dedent(...markdown).replace(/(\S)\n(?!\n)/g, '$1 ');
return dedent(...markdown).replace(/(\S)\n(?!\n)/g, '$1 ');
}
/**
@@ -52,27 +52,27 @@ export function dedentMd(...markdown) {
* @param {string} template
*/
export function formatCount(count, template) {
/** @param {string} text */
const wrapWithCount = (text) => {
// If no count was given, we're outputting a single issue in annotations,
// so omit count and capitalize the first letter of the issue type description
if (count === undefined) return text[0].toUpperCase() + text.slice(1);
/** @param {string} text */
const wrapWithCount = (text) => {
// If no count was given, we're outputting a single issue in annotations,
// so omit count and capitalize the first letter of the issue type description
if (count === undefined) return text[0].toUpperCase() + text.slice(1);
// Otherwise, prefix the issue type description with count
return `${count} ${text}`;
};
// Otherwise, prefix the issue type description with count
return `${count} ${text}`;
};
const usePlural = count !== undefined && count !== 1;
const templateParts = template.split('|');
const usedTemplate = templateParts.length === 2 ? templateParts[usePlural ? 1 : 0] : template;
return wrapWithCount(usedTemplate.replace(/\(s\)/g, usePlural ? 's' : ''));
const usePlural = count !== undefined && count !== 1;
const templateParts = template.split('|');
const usedTemplate = templateParts.length === 2 ? templateParts[usePlural ? 1 : 0] : template;
return wrapWithCount(usedTemplate.replace(/\(s\)/g, usePlural ? 's' : ''));
}
export default {
debug,
warning,
error,
dedentMd,
formatCount,
isCi,
debug,
warning,
error,
dedentMd,
formatCount,
isCi,
};

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,458 +27,458 @@ 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;
gitHubRef: 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.githubRef = config.gitHubRef;
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;
gitHubRef: 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.githubRef = config.gitHubRef;
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 githubRef;
readonly githubToken;
readonly git;
readonly pageSourceDir;
readonly htmlOutputFilePath;
readonly sourceLanguage;
readonly targetLanguages;
readonly languageLabels;
readonly githubRepo;
readonly githubRef;
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.toLowerCase()] = {}));
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.toLowerCase()] = {}));
// 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
.map((el) => el.toLowerCase())
.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
.map((el) => el.toLowerCase())
.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.toLowerCase()]![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.toLowerCase()]![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',
lang,
subpath,
query = '',
}: {
type?: string;
refName?: string;
lang: string;
subpath: string;
query?: string;
}) {
const noDotSrcDir = this.pageSourceDir.replaceAll(/\.+\//g, '');
const isSrcLang = lang === this.sourceLanguage;
return `https://github.com/${this.githubRepo}/${type}/${this.githubRef}/${noDotSrcDir}${
isSrcLang ? '' : `/${lang}`
}/${subpath}${query}`;
}
getPageUrl({
type = 'blob',
lang,
subpath,
query = '',
}: {
type?: string;
refName?: string;
lang: string;
subpath: string;
query?: string;
}) {
const noDotSrcDir = this.pageSourceDir.replaceAll(/\.+\//g, '');
const isSrcLang = lang === this.sourceLanguage;
return `https://github.com/${this.githubRepo}/${type}/${this.githubRef}/${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/${this.githubRef}/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/${this.githubRef}/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

@@ -1,285 +1,285 @@
<!doctype html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<meta name="theme-color" content="hsl(186, 44%, 15%)" media="(prefers-color-scheme: dark)" />
<meta name="theme-color" content="hsl(186, 37%, 93%)" media="(prefers-color-scheme: light)" />
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<meta name="theme-color" content="hsl(186, 44%, 15%)" media="(prefers-color-scheme: dark)" />
<meta name="theme-color" content="hsl(186, 37%, 93%)" media="(prefers-color-scheme: light)" />
<title>Tauri Docs Translation Status</title>
<meta
name="description"
content="Translation progress tracker for the Tauri Docs site. See how much has been translated in your language and get involved!"
/>
<link rel="canonical" href="https://v2.tauri.app" />
<meta property="og:title" content="Tauri Docs Translation Status" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://v2.tauri.app/" />
<meta
property="og:description"
content="Translation progress tracker for the Tauri Docs site. See how much has been translated in your language and get involved!"
/>
<title>Tauri Docs Translation Status</title>
<meta
name="description"
content="Translation progress tracker for the Tauri Docs site. See how much has been translated in your language and get involved!"
/>
<link rel="canonical" href="https://v2.tauri.app" />
<meta property="og:title" content="Tauri Docs Translation Status" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://v2.tauri.app/" />
<meta
property="og:description"
content="Translation progress tracker for the Tauri Docs site. See how much has been translated in your language and get involved!"
/>
<style>
:root {
color-scheme: dark;
<style>
:root {
color-scheme: dark;
--font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif,
Apple Color Emoji, Segoe UI Emoji;
--font-body: system-ui, var(--font-fallback);
--theme-accent: hsl(186, 100%, 87%);
--theme-bg: hsl(186, 13%, 10%);
--theme-table-header: hsl(186, 14%, 16%);
--theme-table-border: 1px solid hsl(186, 13%, 16%);
--theme-table-hover: hsl(186, 13%, 16%);
--theme-text: hsl(186, 8%, 77%);
--theme-text-bright: hsl(0, 0%, 100%);
--overlay-blurple: hsla(186, 60%, 60%, 0.2);
}
--font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif,
Apple Color Emoji, Segoe UI Emoji;
--font-body: system-ui, var(--font-fallback);
--theme-accent: hsl(186, 100%, 87%);
--theme-bg: hsl(186, 13%, 10%);
--theme-table-header: hsl(186, 14%, 16%);
--theme-table-border: 1px solid hsl(186, 13%, 16%);
--theme-table-hover: hsl(186, 13%, 16%);
--theme-text: hsl(186, 8%, 77%);
--theme-text-bright: hsl(0, 0%, 100%);
--overlay-blurple: hsla(186, 60%, 60%, 0.2);
}
* {
box-sizing: border-box;
margin: 0;
}
* {
box-sizing: border-box;
margin: 0;
}
body {
color: var(--theme-text);
display: flex;
flex-direction: column;
min-height: 100vh;
font-family: var(--font-body);
font-size: 16px;
line-height: 1.5;
margin-block: 2rem;
margin-inline: 1rem;
background:
linear-gradient(215deg, var(--overlay-blurple), transparent 40%),
radial-gradient(var(--overlay-blurple), transparent 40%) no-repeat -60vw -40vh / 105vw
200vh,
radial-gradient(var(--overlay-blurple), transparent 65%) no-repeat 50% calc(100% + 20rem) /
60rem 30rem,
var(--theme-bg);
}
body {
color: var(--theme-text);
display: flex;
flex-direction: column;
min-height: 100vh;
font-family: var(--font-body);
font-size: 16px;
line-height: 1.5;
margin-block: 2rem;
margin-inline: 1rem;
background:
linear-gradient(215deg, var(--overlay-blurple), transparent 40%),
radial-gradient(var(--overlay-blurple), transparent 40%) no-repeat -60vw -40vh / 105vw
200vh,
radial-gradient(var(--overlay-blurple), transparent 65%) no-repeat 50% calc(100% + 20rem) /
60rem 30rem,
var(--theme-bg);
}
:is(h1, h2, h3, h4, h5, h6) {
color: var(--theme-text-bright);
margin-bottom: 1rem;
font-weight: bold;
line-height: 1.3;
}
:is(h1, h2, h3, h4, h5, h6) {
color: var(--theme-text-bright);
margin-bottom: 1rem;
font-weight: bold;
line-height: 1.3;
}
:is(h2):not(:first-child) {
margin-top: 4rem;
}
:is(h2):not(:first-child) {
margin-top: 4rem;
}
:is(h3, h4):not(:first-child) {
margin-top: 3rem;
}
:is(h3, h4):not(:first-child) {
margin-top: 3rem;
}
:is(h5, h6):not(:first-child) {
margin-top: 2rem;
}
:is(h5, h6):not(:first-child) {
margin-top: 2rem;
}
h1,
h2 {
max-width: 40ch;
}
h1,
h2 {
max-width: 40ch;
}
h1 {
font-size: 2.25rem;
font-weight: 900;
}
h1 {
font-size: 2.25rem;
font-weight: 900;
}
h2 {
font-size: 1.875rem;
}
h2 {
font-size: 1.875rem;
}
@media (min-width: 37.75em) {
h1 {
font-size: 2.5rem;
}
}
@media (min-width: 37.75em) {
h1 {
font-size: 2.5rem;
}
}
main {
max-width: 80ch;
margin-inline: auto;
}
main {
max-width: 80ch;
margin-inline: auto;
}
.limit-to-viewport {
max-width: calc(100vw - 2rem);
}
.limit-to-viewport {
max-width: calc(100vw - 2rem);
}
p + p {
margin-top: 1.25rem;
}
p + p {
margin-top: 1.25rem;
}
a {
color: var(--theme-accent);
text-decoration: none;
}
a {
color: var(--theme-accent);
text-decoration: none;
}
h2 a {
color: inherit;
}
h2 a {
color: inherit;
}
p a {
color: var(--theme-accent);
text-decoration: underline;
}
p a {
color: var(--theme-accent);
text-decoration: underline;
}
a:hover {
text-decoration: underline;
color: var(--theme-text-bright);
}
a:hover {
text-decoration: underline;
color: var(--theme-text-bright);
}
ul {
font-size: 0.875rem;
}
ul {
font-size: 0.875rem;
}
details {
margin-bottom: 1.25rem;
}
details {
margin-bottom: 1.25rem;
}
details summary {
cursor: pointer;
user-select: none;
}
details summary {
cursor: pointer;
user-select: none;
}
details summary:hover strong {
color: var(--theme-accent-secondary);
}
details summary:hover strong {
color: var(--theme-accent-secondary);
}
details h5 {
margin-top: 1rem !important;
}
details h5 {
margin-top: 1rem !important;
}
details > :last-child {
margin-bottom: 1rem;
}
details > :last-child {
margin-bottom: 1rem;
}
.create-button {
padding: 0.1em 0.5em;
background-color: hsl(213deg 89% 64% / 20%);
display: inline-block;
border-radius: 0.5em;
font-weight: bold;
font-size: 0.75rem;
}
.create-button {
padding: 0.1em 0.5em;
background-color: hsl(213deg 89% 64% / 20%);
display: inline-block;
border-radius: 0.5em;
font-weight: bold;
font-size: 0.75rem;
}
.status-by-page {
margin-bottom: 1rem;
border-collapse: collapse;
margin-inline: -1rem;
border: var(--theme-table-border);
font-size: 0.8125rem;
width: 85%;
}
.status-by-page {
margin-bottom: 1rem;
border-collapse: collapse;
margin-inline: -1rem;
border: var(--theme-table-border);
font-size: 0.8125rem;
width: 85%;
}
.table-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 30px;
width: 100%;
gap: 5px;
}
.table-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 30px;
width: 100%;
gap: 5px;
}
.status-by-page th {
position: sticky;
top: -1px;
background: var(--theme-table-header);
white-space: nowrap;
padding-inline: 0.3rem;
}
.status-by-page th {
position: sticky;
top: -1px;
background: var(--theme-table-header);
white-space: nowrap;
padding-inline: 0.3rem;
}
.status-by-page th,
.status-by-page td {
padding-block: 0.2rem;
}
.status-by-page th,
.status-by-page td {
padding-block: 0.2rem;
}
.status-by-page tbody tr:not(.spacer):hover td {
background: var(--theme-table-hover);
}
.status-by-page tbody tr:not(.spacer):hover td {
background: var(--theme-table-hover);
}
.status-by-page .spacer td {
height: 0.5rem;
}
.status-by-page .spacer td {
height: 0.5rem;
}
.status-by-page th:first-of-type,
.status-by-page td:first-of-type {
text-align: left;
padding-inline-start: 1rem;
}
.status-by-page th:first-of-type,
.status-by-page td:first-of-type {
text-align: left;
padding-inline-start: 1rem;
}
.status-by-page th:last-of-type,
.status-by-page td:last-of-type {
text-align: center;
padding-inline-end: 1rem;
}
.status-by-page th:last-of-type,
.status-by-page td:last-of-type {
text-align: center;
padding-inline-end: 1rem;
}
.status-by-page td:not(:first-of-type) {
min-width: 2rem;
text-align: center;
cursor: default;
}
.status-by-page td:not(:first-of-type) {
min-width: 2rem;
text-align: center;
cursor: default;
}
.status-by-page td:not(:first-of-type) a {
text-decoration: none;
}
.status-by-page td:not(:first-of-type) a {
text-decoration: none;
}
.progress-summary {
font-size: 0.8125rem;
}
.progress-summary {
font-size: 0.8125rem;
}
.progress-bar {
font-size: 0.6875rem;
}
.progress-bar {
font-size: 0.6875rem;
}
.main-container {
position: relative;
}
</style>
</head>
.main-container {
position: relative;
}
</style>
</head>
<body>
<main class="main-container">
<div class="limit-to-viewport">
<h1>Tauri Docs Translation Status</h1>
<body>
<main class="main-container">
<div class="limit-to-viewport">
<h1>Tauri Docs Translation Status</h1>
<p>
If you're interested in helping us translate
<a href="https://v2.tauri.app/">v2.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>
If you're interested in helping us translate
<a href="https://v2.tauri.app/">v2.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>
Read the
<a href="https://github.com/tauri-apps/tauri-docs/blob/v2/.github/TRANSLATING.md"
>Translations Guide</a
>
for how to translate a document. Before starting a new translation, be sure to check the
<a href="#needs-review">existing Tauri Docs PRs</a> to see if this page has already been
translated, and consider reviewing any open PRs in your language!
</p>
<p>
Read the
<a href="https://github.com/tauri-apps/tauri-docs/blob/v2/.github/TRANSLATING.md"
>Translations Guide</a
>
for how to translate a document. Before starting a new translation, be sure to check the
<a href="#needs-review">existing Tauri Docs PRs</a> to see if this page has already been
translated, and consider reviewing any open PRs in your language!
</p>
<h2 id="by-language">
<a href="#by-language">Translation progress by language</a>
</h2>
<!-- TranslationStatusByLanguage -->
<h2 id="by-language">
<a href="#by-language">Translation progress by language</a>
</h2>
<!-- TranslationStatusByLanguage -->
<h2 id="needs-review">
<a href="#needs-review">Translations that need reviews</a>
</h2>
<!-- TranslationNeedsReview -->
<h2 id="needs-review">
<a href="#needs-review">Translations that need reviews</a>
</h2>
<!-- TranslationNeedsReview -->
<h2 id="by-content">
<a href="#by-content">Translation status by content</a>
</h2>
</div>
<!-- This table is not limited to the viewport to allow horizontal scrolling -->
<!-- TranslationStatusByPage -->
</main>
</body>
<h2 id="by-content">
<a href="#by-content">Translation status by content</a>
</h2>
</div>
<!-- This table is not limited to the viewport to allow horizontal scrolling -->
<!-- TranslationStatusByPage -->
</main>
</body>
</html>

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,198 +1,198 @@
import {
Application,
DeclarationReflection,
Options,
PageEvent,
Reflection,
SignatureReflection,
TSConfigReader,
type TypeDocOptions,
Application,
DeclarationReflection,
Options,
PageEvent,
Reflection,
SignatureReflection,
TSConfigReader,
type TypeDocOptions,
} from 'typedoc';
import {
MarkdownPageEvent,
MarkdownTheme,
MarkdownThemeContext,
type MarkdownApplication,
type PluginOptions,
MarkdownPageEvent,
MarkdownTheme,
MarkdownThemeContext,
type MarkdownApplication,
type PluginOptions,
} from 'typedoc-plugin-markdown';
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'],
readme: 'none',
logLevel: 'Warn',
parametersFormat: 'table',
// 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',
hidePageHeader: true,
hidePageTitle: true,
hideBreadcrumbs: true,
useCodeBlocks: true,
propertiesFormat: 'table',
typeDeclarationFormat: 'table',
useHTMLAnchors: true,
// TypeDoc options
// https://typedoc.org/options/
githubPages: false,
hideGenerator: true,
theme: 'tauri-theme',
plugin: ['typedoc-plugin-mdn-links', 'typedoc-plugin-markdown'],
readme: 'none',
logLevel: 'Warn',
parametersFormat: 'table',
// 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',
hidePageHeader: true,
hidePageTitle: true,
hideBreadcrumbs: true,
useCodeBlocks: true,
propertiesFormat: 'table',
typeDeclarationFormat: 'table',
useHTMLAnchors: true,
};
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',
publicPath: '/reference/javascript/api/',
basePath: '/reference/javascript/api/',
...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',
publicPath: '/reference/javascript/api/',
basePath: '/reference/javascript/api/',
...typeDocConfigBaseOptions,
};
await generateDocs(coreJsOptions);
} else {
console.log(
'Tauri V2 submodule is not initialized, respective API routes will not be rendered.'
);
}
await generateDocs(coreJsOptions);
} else {
console.log(
'Tauri V2 submodule is not initialized, respective API routes will not be rendered.'
);
}
const plugins = [
'authenticator',
'autostart',
'barcode-scanner',
'biometric',
'cli',
'clipboard-manager',
'deep-link',
'dialog',
'fs',
'global-shortcut',
'http',
'log',
'nfc',
'notification',
'os',
'positioner',
'process',
'shell',
'sql',
'store',
'stronghold',
'updater',
'upload',
'websocket',
'window-state',
];
const plugins = [
'authenticator',
'autostart',
'barcode-scanner',
'biometric',
'cli',
'clipboard-manager',
'deep-link',
'dialog',
'fs',
'global-shortcut',
'http',
'log',
'nfc',
'notification',
'os',
'positioner',
'process',
'shell',
'sql',
'store',
'stronghold',
'updater',
'upload',
'websocket',
'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',
publicPath: `/reference/javascript/`,
basePath: `/reference/javascript/`,
...typeDocConfigBaseOptions,
// Must go after to override base
entryFileName: `${plugin}.md`,
};
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',
publicPath: `/reference/javascript/`,
basePath: `/reference/javascript/`,
...typeDocConfigBaseOptions,
// Must go after to override base
entryFileName: `${plugin}.md`,
};
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.'
);
}
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',
publicPath: '/reference/javascript/api/',
basePath: '/reference/javascript/api/',
...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',
publicPath: '/reference/javascript/api/',
basePath: '/reference/javascript/api/',
...typeDocConfigBaseOptions,
};
await generateDocs(coreJsOptions);
} else {
console.log(
'Tauri V2 submodule is not initialized, respective API routes will not be rendered.'
);
}
await generateDocs(coreJsOptions);
} else {
console.log(
'Tauri V2 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.publicPath}`;
const outputDir = `../../src/content/docs${options.publicPath}`;
const app = await Application.bootstrapWithPlugins(options);
app.options.addReader(new TSConfigReader());
// @ts-ignore
app.renderer.defineTheme('tauri-theme', TauriTheme);
const app = await Application.bootstrapWithPlugins(options);
app.options.addReader(new TSConfigReader());
// @ts-ignore
app.renderer.defineTheme('tauri-theme', TauriTheme);
app.renderer.on(PageEvent.END, (event: PageEvent<DeclarationReflection>) => {
pageEventEnd(event);
});
app.renderer.on(PageEvent.END, (event: PageEvent<DeclarationReflection>) => {
pageEventEnd(event);
});
const project = await app.convert();
const project = await 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',
'sidebar:',
` label: "${event.model.name.replace('@tauri-apps/plugin-', '')}"`,
'---',
'',
event.contents,
];
event.contents = frontmatter.join('\n');
if (!event.contents) {
return;
}
const frontmatter = [
'---',
`title: "${event.model.name}"`,
'editUrl: false',
'sidebar:',
` label: "${event.model.name.replace('@tauri-apps/plugin-', '')}"`,
'---',
'',
event.contents,
];
event.contents = frontmatter.join('\n');
}
class TauriThemeRenderContext extends MarkdownThemeContext {
constructor(theme: MarkdownTheme, page: MarkdownPageEvent<Reflection>, options: Options) {
super(theme, page, options);
this.partials = {
...this.partials,
// Formats `@source` to be a single line
sources: (model: DeclarationReflection | SignatureReflection, options: object) => {
if (!model.sources) {
return '';
}
let label = model.sources.length > 1 ? '**Sources**: ' : '**Source**: ';
const sources = model.sources.map((source) => `${source.url}`);
return label + sources.join(', ');
},
};
}
constructor(theme: MarkdownTheme, page: MarkdownPageEvent<Reflection>, options: Options) {
super(theme, page, options);
this.partials = {
...this.partials,
// Formats `@source` to be a single line
sources: (model: DeclarationReflection | SignatureReflection, options: object) => {
if (!model.sources) {
return '';
}
let label = model.sources.length > 1 ? '**Sources**: ' : '**Source**: ';
const sources = model.sources.map((source) => `${source.url}`);
return label + sources.join(', ');
},
};
}
// Adapted from https://github.com/HiDeoo/starlight-typedoc/blob/d95072e218004276942a5132ec8a4e3561425903/packages/starlight-typedoc/src/libs/theme.ts#L28
override getRelativeUrl = (url: string) => {
url = super.getRelativeUrl(url).replaceAll('.md', '').replaceAll('.', '').toLowerCase();
return url;
};
// Adapted from https://github.com/HiDeoo/starlight-typedoc/blob/d95072e218004276942a5132ec8a4e3561425903/packages/starlight-typedoc/src/libs/theme.ts#L28
override getRelativeUrl = (url: string) => {
url = super.getRelativeUrl(url).replaceAll('.md', '').replaceAll('.', '').toLowerCase();
return url;
};
}
// 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 {
getRenderContext(page: MarkdownPageEvent<Reflection>): MarkdownThemeContext {
return new TauriThemeRenderContext(this, page, this.application.options);
}
getRenderContext(page: MarkdownPageEvent<Reflection>): MarkdownThemeContext {
return new TauriThemeRenderContext(this, page, this.application.options);
}
}
generator();

View File

@@ -2,131 +2,131 @@ import { writeFileSync, mkdirSync } from 'node:fs';
import { join } from 'node:path';
const note =
'\n# NOTE: This file is auto-generated in packages/releases-generator/build.ts\n# For corrections please edit it directly';
'\n# NOTE: This file is auto-generated in packages/releases-generator/build.ts\n# For corrections please edit it directly';
const packages = [
{
name: 'tauri',
url: 'https://raw.githubusercontent.com/tauri-apps/tauri/dev/core/tauri/CHANGELOG.md',
tag: 'https://github.com/tauri-apps/tauri/release/tag',
},
{
name: '@tauri-apps/api',
url: 'https://raw.githubusercontent.com/tauri-apps/tauri/dev/tooling/api/CHANGELOG.md',
tag: 'https://github.com/tauri-apps/tauri/release/tag',
},
{
name: 'tauri-cli',
url: 'https://raw.githubusercontent.com/tauri-apps/tauri/dev/tooling/cli/CHANGELOG.md',
tag: 'https://github.com/tauri-apps/tauri/release/tag',
},
{
name: '@tauri-apps/cli',
url: 'https://raw.githubusercontent.com/tauri-apps/tauri/dev/tooling/cli/node/CHANGELOG.md',
tag: 'https://github.com/tauri-apps/tauri/release/tag',
},
{
name: 'tauri-bundler',
url: 'https://raw.githubusercontent.com/tauri-apps/tauri/dev/tooling/bundler/CHANGELOG.md',
tag: 'https://github.com/tauri-apps/tauri/release/tag',
},
{
name: 'wry',
url: 'https://raw.githubusercontent.com/tauri-apps/wry/dev/CHANGELOG.md',
tag: 'https://github.com/tauri-apps/wry/release/tag',
},
{
name: 'tao',
url: 'https://raw.githubusercontent.com/tauri-apps/tao/dev/CHANGELOG.md',
tag: 'https://github.com/tauri-apps/tao/release/tag',
},
{
name: 'tauri',
url: 'https://raw.githubusercontent.com/tauri-apps/tauri/dev/core/tauri/CHANGELOG.md',
tag: 'https://github.com/tauri-apps/tauri/release/tag',
},
{
name: '@tauri-apps/api',
url: 'https://raw.githubusercontent.com/tauri-apps/tauri/dev/tooling/api/CHANGELOG.md',
tag: 'https://github.com/tauri-apps/tauri/release/tag',
},
{
name: 'tauri-cli',
url: 'https://raw.githubusercontent.com/tauri-apps/tauri/dev/tooling/cli/CHANGELOG.md',
tag: 'https://github.com/tauri-apps/tauri/release/tag',
},
{
name: '@tauri-apps/cli',
url: 'https://raw.githubusercontent.com/tauri-apps/tauri/dev/tooling/cli/node/CHANGELOG.md',
tag: 'https://github.com/tauri-apps/tauri/release/tag',
},
{
name: 'tauri-bundler',
url: 'https://raw.githubusercontent.com/tauri-apps/tauri/dev/tooling/bundler/CHANGELOG.md',
tag: 'https://github.com/tauri-apps/tauri/release/tag',
},
{
name: 'wry',
url: 'https://raw.githubusercontent.com/tauri-apps/wry/dev/CHANGELOG.md',
tag: 'https://github.com/tauri-apps/wry/release/tag',
},
{
name: 'tao',
url: 'https://raw.githubusercontent.com/tauri-apps/tao/dev/CHANGELOG.md',
tag: 'https://github.com/tauri-apps/tao/release/tag',
},
];
const baseDir = '../../src/content/docs/release';
let latestVersions: {
[key: string]: string;
[key: string]: string;
} = {};
async function generator() {
for (const pkg of packages) {
const response = await fetch(pkg.url);
const responseText: string = await response.text();
const releases = responseText
.split('## \\[')
.filter((item) => !item.includes('# Changelog'))
.map((section) => {
const [version, ...c] = section.split('\n');
const contents = c.join('\n');
return {
version: version.replace('\\[', '').replace(']', ''),
notes: contents,
};
})
.filter(({ version }) => !version.includes('Not Published'));
for (const pkg of packages) {
const response = await fetch(pkg.url);
const responseText: string = await response.text();
const releases = responseText
.split('## \\[')
.filter((item) => !item.includes('# Changelog'))
.map((section) => {
const [version, ...c] = section.split('\n');
const contents = c.join('\n');
return {
version: version.replace('\\[', '').replace(']', ''),
notes: contents,
};
})
.filter(({ version }) => !version.includes('Not Published'));
mkdirSync(join(baseDir, pkg.name), { recursive: true });
//
/*
* Write files for each version
*/
const len = releases.length;
for (let i = 0; i < len; i++) {
/**
* Deal with next-prev labels
*/
const thisVersion = releases[i].version;
mkdirSync(join(baseDir, pkg.name), { recursive: true });
//
/*
* Write files for each version
*/
const len = releases.length;
for (let i = 0; i < len; i++) {
/**
* Deal with next-prev labels
*/
const thisVersion = releases[i].version;
if (i === 0) {
// latest version
latestVersions[pkg.name] = `v${thisVersion}`;
}
//
const pageFrontmatter = [
note,
`title: '${pkg.name}@${thisVersion}'`,
`description: '${thisVersion}'`,
`slug: 'release/${pkg.name}/v${thisVersion}'`,
`tableOfContents: false`,
`editUrl: 'https://github.com/tauri-apps/tauri-docs/packages/releases-generator/build.ts'`,
'pagefind: false',
'sidebar:',
` label: ${thisVersion}`,
` order: ${semverToInt(thisVersion)}`,
];
if (i === 0) {
// latest version
latestVersions[pkg.name] = `v${thisVersion}`;
}
//
const pageFrontmatter = [
note,
`title: '${pkg.name}@${thisVersion}'`,
`description: '${thisVersion}'`,
`slug: 'release/${pkg.name}/v${thisVersion}'`,
`tableOfContents: false`,
`editUrl: 'https://github.com/tauri-apps/tauri-docs/packages/releases-generator/build.ts'`,
'pagefind: false',
'sidebar:',
` label: ${thisVersion}`,
` order: ${semverToInt(thisVersion)}`,
];
const frontmatter = ['---', ...pageFrontmatter, '---'].join('\n');
//
const indexLink = `[Return](/release)`;
const viewInGitHub = `<a href="${pkg.tag}/${pkg.name}-v${thisVersion}">View on GitHub</a>`;
const linksDiv = `<div style="margin-bottom:3rem; display: flex; justify-content: space-between; align-items: center"><span>${indexLink}</span><span>${viewInGitHub}</span></div>`;
//
const sidebar = `\nimport ReleaseSidebar from '@components/list/ReleaseSidebar.astro';
const frontmatter = ['---', ...pageFrontmatter, '---'].join('\n');
//
const indexLink = `[Return](/release)`;
const viewInGitHub = `<a href="${pkg.tag}/${pkg.name}-v${thisVersion}">View on GitHub</a>`;
const linksDiv = `<div style="margin-bottom:3rem; display: flex; justify-content: space-between; align-items: center"><span>${indexLink}</span><span>${viewInGitHub}</span></div>`;
//
const sidebar = `\nimport ReleaseSidebar from '@components/list/ReleaseSidebar.astro';
\n\n<ReleaseSidebar slug="release/${pkg.name}" packageName="${pkg.name}" />\n`;
writeFileSync(
join(baseDir, pkg.name, `v${thisVersion}.mdx`),
`${frontmatter}\n${linksDiv}\n${entitify(releases[i].notes)}`
);
}
}
writeFileSync(
join(baseDir, pkg.name, `v${thisVersion}.mdx`),
`${frontmatter}\n${linksDiv}\n${entitify(releases[i].notes)}`
);
}
}
// Generate index page
const extraNote =
'# To quickly preview changes, you can edit this file, them make sure you copy the changes over the source build.ts script\n';
const indexPage = [
'---',
note,
extraNote,
`title: 'Tauri Core Ecosystem Releases'`,
`editUrl: 'https://github.com/tauri-apps/tauri-docs/packages/releases-generator/build.ts'`,
'tableOfContents: false',
'sidebar:',
' label: Overview',
' order: -1000000',
'---',
].join('\n');
// Generate index page
const extraNote =
'# To quickly preview changes, you can edit this file, them make sure you copy the changes over the source build.ts script\n';
const indexPage = [
'---',
note,
extraNote,
`title: 'Tauri Core Ecosystem Releases'`,
`editUrl: 'https://github.com/tauri-apps/tauri-docs/packages/releases-generator/build.ts'`,
'tableOfContents: false',
'sidebar:',
' label: Overview',
' order: -1000000',
'---',
].join('\n');
const indexPageContent = `import { LinkCard, CardGrid } from '@astrojs/starlight/components';\n
const indexPageContent = `import { LinkCard, CardGrid } from '@astrojs/starlight/components';\n
<CardGrid>
<LinkCard title="tauri" href="/release/tauri/${latestVersions['tauri']}" />
<LinkCard title="@tauri-apps/api" href="/release/@tauri-apps/api/${latestVersions['@tauri-apps/api']}" />
@@ -137,55 +137,55 @@ async function generator() {
<LinkCard title="tao" href="/release/tao/${latestVersions['tao']}" />
</CardGrid>`;
writeFileSync(join(baseDir, 'index.mdx'), `${indexPage}\n${indexPageContent}`);
writeFileSync(join(baseDir, 'index.mdx'), `${indexPage}\n${indexPageContent}`);
}
function entitify(str: string): string {
return str
.replace(/[&<>"']/g, function (entity) {
switch (entity) {
case '&':
return '&amp;';
case '<':
return '&lt;';
case '>':
return '&gt;';
case '"':
return '&quot;';
case "'":
return '&#39;';
default:
return entity;
}
})
.replace(/\$\{/g, '$\\{');
return str
.replace(/[&<>"']/g, function (entity) {
switch (entity) {
case '&':
return '&amp;';
case '<':
return '&lt;';
case '>':
return '&gt;';
case '"':
return '&quot;';
case "'":
return '&#39;';
default:
return entity;
}
})
.replace(/\$\{/g, '$\\{');
}
const PRE_RELEASE_VALUES: any = {
alpha: 1,
'beta-rc': 100,
beta: 1000,
rc: 100000,
alpha: 1,
'beta-rc': 100,
beta: 1000,
rc: 100000,
};
function semverToInt(semver: string) {
const BASE = 1000000000;
let [version, preRelease] = semver.split('-');
const [major, minor, patch] = version.split('.').map(Number);
let preReleaseValue = 0;
if (preRelease) {
const match = preRelease.split('.');
if (match) {
const identifier = match[0];
const number = match[1] !== undefined ? parseInt(match[1]) : 0;
preReleaseValue = PRE_RELEASE_VALUES[identifier] + number;
}
}
return BASE - (major * 100000000 + minor * 1000000 + patch * 10000 + preReleaseValue);
const BASE = 1000000000;
let [version, preRelease] = semver.split('-');
const [major, minor, patch] = version.split('.').map(Number);
let preReleaseValue = 0;
if (preRelease) {
const match = preRelease.split('.');
if (match) {
const identifier = match[0];
const number = match[1] !== undefined ? parseInt(match[1]) : 0;
preReleaseValue = PRE_RELEASE_VALUES[identifier] + number;
}
}
return BASE - (major * 100000000 + minor * 1000000 + patch * 10000 + preReleaseValue);
}
if (process.env.CONTEXT === 'production' || process.env.HEAD?.startsWith('release-pages')) {
generator();
generator();
} else {
console.info('Skipping `/release` pages build');
console.info('Skipping `/release` pages build');
}

View File

@@ -1,45 +1,45 @@
async function chapterNavigation() {
let navigating = false;
document.addEventListener('keydown', function (e) {
if (navigating) return;
if (e.altKey || e.ctrlKey || e.metaKey) {
return;
}
if (window.search && document.activeElement === window.search) {
return;
}
let navigating = false;
document.addEventListener('keydown', function (e) {
if (navigating) return;
if (e.altKey || e.ctrlKey || e.metaKey) {
return;
}
if (window.search && document.activeElement === window.search) {
return;
}
switch (e.key) {
case 'ArrowLeft':
e.preventDefault();
let previousButton = document.querySelector('a[rel="prev"]');
if (!previousButton && window.location.pathname !== '/') previousButton = { href: '/' };
switch (e.key) {
case 'ArrowLeft':
e.preventDefault();
let previousButton = document.querySelector('a[rel="prev"]');
if (!previousButton && window.location.pathname !== '/') previousButton = { href: '/' };
if (document.referrer.includes(window.location.host))
if (previousButton) {
window.location.href = previousButton.href;
navigating = true;
}
break;
case 'ArrowRight':
e.preventDefault();
let nextButton = document.querySelector('a[rel="next"]');
if (!nextButton && window.location.pathname === '/') nextButton = { href: '/start/' };
if (document.referrer.includes(window.location.host))
if (previousButton) {
window.location.href = previousButton.href;
navigating = true;
}
break;
case 'ArrowRight':
e.preventDefault();
let nextButton = document.querySelector('a[rel="next"]');
if (!nextButton && window.location.pathname === '/') nextButton = { href: '/start/' };
if (nextButton) {
window.location.href = nextButton.href;
navigating = true;
}
break;
}
});
if (nextButton) {
window.location.href = nextButton.href;
navigating = true;
}
break;
}
});
}
window.addEventListener('DOMContentLoaded', () => {
chapterNavigation();
chapterNavigation();
});
window.onload = function () {
document.body.setAttribute('tabindex', '-1');
document.body.focus();
document.body.setAttribute('tabindex', '-1');
document.body.focus();
};

View File

@@ -9,11 +9,11 @@ import { readFile } from 'fs/promises';
const md = await createMarkdownProcessor();
const res = readFile(
join(
dirname(dirname(dirname(fileURLToPath(import.meta.url)))),
'packages/awesome-tauri/README.md'
),
'utf-8'
join(
dirname(dirname(dirname(fileURLToPath(import.meta.url)))),
'packages/awesome-tauri/README.md'
),
'utf-8'
);
const content = await md.render(await res);
const dom = new JSDOM('<!DOCTYPE html>' + content.code);
@@ -21,56 +21,56 @@ const document = dom.window.document;
let categories = document.querySelectorAll('h3');
let cards = [];
const sections = {
plugins: 'Plugins',
'plugins-no-official': 'Plugins',
integrations: 'Integrations',
articles: 'Articles',
guides: 'Guides',
tutorials: 'Tutorials',
templates: 'Templates',
'applications-audio-video': 'Audio & Video',
'applications-chatgpt-clients': 'ChatGPT clients',
'applications-data': 'Data',
'applications-developer-tools': 'Developer tools',
'applications-email-feeds': 'Email & Feeds',
'applications-file-management': 'File management',
'applications-finance': 'Finance',
'applications-gaming': 'Gaming',
'applications-information': 'Information',
'applications-learning': 'Learning',
'applications-networking': 'Networking',
'applications-office-writing': 'Office & Writing',
'applications-productivity': 'Productivity',
'applications-search': 'Search',
'applications-security': 'Security',
'applications-social-media': 'Social media',
'applications-utilities': 'Utilities',
plugins: 'Plugins',
'plugins-no-official': 'Plugins',
integrations: 'Integrations',
articles: 'Articles',
guides: 'Guides',
tutorials: 'Tutorials',
templates: 'Templates',
'applications-audio-video': 'Audio & Video',
'applications-chatgpt-clients': 'ChatGPT clients',
'applications-data': 'Data',
'applications-developer-tools': 'Developer tools',
'applications-email-feeds': 'Email & Feeds',
'applications-file-management': 'File management',
'applications-finance': 'Finance',
'applications-gaming': 'Gaming',
'applications-information': 'Information',
'applications-learning': 'Learning',
'applications-networking': 'Networking',
'applications-office-writing': 'Office & Writing',
'applications-productivity': 'Productivity',
'applications-search': 'Search',
'applications-security': 'Security',
'applications-social-media': 'Social media',
'applications-utilities': 'Utilities',
};
for (const header of categories) {
if (header.textContent === sections[section]) {
let list = header.nextSibling.nextSibling as HTMLElement;
for (const entry: HTMLLinkElement of list.children) {
if (section.includes('-no-official')) {
let img = entry.children[1];
if (img && img.src && img.src.includes('official')) continue;
img = entry.children[2];
if (img && img.src && img.src.includes('official')) continue;
}
cards.push({
href: entry.children[0].href,
name: entry.children[0].textContent,
description: entry.textContent.split(' - ')[1],
});
}
}
if (header.textContent === sections[section]) {
let list = header.nextSibling.nextSibling as HTMLElement;
for (const entry: HTMLLinkElement of list.children) {
if (section.includes('-no-official')) {
let img = entry.children[1];
if (img && img.src && img.src.includes('official')) continue;
img = entry.children[2];
if (img && img.src && img.src.includes('official')) continue;
}
cards.push({
href: entry.children[0].href,
name: entry.children[0].textContent,
description: entry.textContent.split(' - ')[1],
});
}
}
}
---
<CardGrid>
{
cards.map((card) => (
<LinkCard title={card.name} description={card.description} href={card.href} />
))
}
{
cards.map((card) => (
<LinkCard title={card.name} description={card.description} href={card.href} />
))
}
</CardGrid>

View File

@@ -6,7 +6,7 @@
*/
interface Props {
placeholder?: string;
placeholder?: string;
}
const { placeholder = 'Search features and community resources...' } = Astro.props;
@@ -14,49 +14,49 @@ const { placeholder = 'Search features and community resources...' } = Astro.pro
---
<card-search>
<div id="search-input" class="pagefind-ui pagefind-ui--reset">
<form
class="pagefind-ui__form"
role="search"
aria-label="search_label"
action="javascript:void(0);"
>
<input
data-wrapper="search-feature-list"
class="pagefind-ui__search-input"
id="search"
aria-label="search"
placeholder={placeholder}
title={placeholder}
autocapitalize="none"
enterkeyhint="search"
autocomplete="off"
/>
<div id="search-input" class="pagefind-ui pagefind-ui--reset">
<form
class="pagefind-ui__form"
role="search"
aria-label="search_label"
action="javascript:void(0);"
>
<input
data-wrapper="search-feature-list"
class="pagefind-ui__search-input"
id="search"
aria-label="search"
placeholder={placeholder}
title={placeholder}
autocapitalize="none"
enterkeyhint="search"
autocomplete="off"
/>
<button class="pagefind-ui__search-clear pagefind-ui__suppressed">clear</button>
</form>
</div>
<slot />
<button class="pagefind-ui__search-clear pagefind-ui__suppressed">clear</button>
</form>
</div>
<slot />
</card-search>
<script>
class CardSearch extends HTMLElement {
constructor() {
super();
window.addEventListener('DOMContentLoaded', () => {
const form = this.querySelector<HTMLFormElement>('form.pagefind-ui__form')!;
const searchBar = this.querySelector<HTMLInputElement>('input.pagefind-ui__search-input')!;
const clear = this.querySelector<HTMLButtonElement>('button.pagefind-ui__search-clear')!;
const inputs = Array.from(this.children).filter((el) => el.classList.contains('card-grid'));
const headings = Array.from(this.children).filter(
(el) => el instanceof HTMLHeadingElement || el instanceof HTMLParagraphElement
);
// Create "nothing found" for each heading
headings.forEach((heading, i) => {
const wrn = document.createElement('p');
wrn.classList.add('hidden', 'warning');
wrn.id = `warn-${i}`;
wrn.innerHTML = `
class CardSearch extends HTMLElement {
constructor() {
super();
window.addEventListener('DOMContentLoaded', () => {
const form = this.querySelector<HTMLFormElement>('form.pagefind-ui__form')!;
const searchBar = this.querySelector<HTMLInputElement>('input.pagefind-ui__search-input')!;
const clear = this.querySelector<HTMLButtonElement>('button.pagefind-ui__search-clear')!;
const inputs = Array.from(this.children).filter((el) => el.classList.contains('card-grid'));
const headings = Array.from(this.children).filter(
(el) => el instanceof HTMLHeadingElement || el instanceof HTMLParagraphElement
);
// Create "nothing found" for each heading
headings.forEach((heading, i) => {
const wrn = document.createElement('p');
wrn.classList.add('hidden', 'warning');
wrn.id = `warn-${i}`;
wrn.innerHTML = `
<style>
.warning {
font-style: italic;
@@ -69,170 +69,170 @@ const { placeholder = 'Search features and community resources...' } = Astro.pro
</style>
<span>No results found in ${heading.textContent}</span>
`;
heading.insertAdjacentElement('afterend', wrn);
});
// Select all "nothing found" created before
const warnings = document.querySelectorAll('[id^="warn-"]');
heading.insertAdjacentElement('afterend', wrn);
});
// Select all "nothing found" created before
const warnings = document.querySelectorAll('[id^="warn-"]');
// Prevent page reload
form.addEventListener('submit', (e) => {
e.preventDefault();
});
// Prevent page reload
form.addEventListener('submit', (e) => {
e.preventDefault();
});
// Prevent reload on 'Enter', and enable clean input on 'Escape'
form.addEventListener('keydown', (e) => {
clear.classList.remove('pagefind-ui__suppressed');
if (e.key === 'Escape') {
reset();
} else if (e.key === 'Enter') e.preventDefault();
});
searchBar.addEventListener('keydown', (e) => {
if (e.key === 'Enter') e.preventDefault();
});
// Prevent reload on 'Enter', and enable clean input on 'Escape'
form.addEventListener('keydown', (e) => {
clear.classList.remove('pagefind-ui__suppressed');
if (e.key === 'Escape') {
reset();
} else if (e.key === 'Enter') e.preventDefault();
});
searchBar.addEventListener('keydown', (e) => {
if (e.key === 'Enter') e.preventDefault();
});
// Search
searchBar.addEventListener('keyup', () => {
searchFilter();
});
// Clear search button 'X'
clear.addEventListener('click', () => {
clear.classList.add('pagefind-ui__suppressed');
reset();
});
// Search
searchBar.addEventListener('keyup', () => {
searchFilter();
});
// Clear search button 'X'
clear.addEventListener('click', () => {
clear.classList.add('pagefind-ui__suppressed');
reset();
});
function reset() {
searchBar.value = '';
searchFilter();
}
function reset() {
searchBar.value = '';
searchFilter();
}
/**
* Function to search and filter based on input
*/
function searchFilter() {
const query = searchBar.value.toUpperCase();
/**
* Function to search and filter based on input
*/
function searchFilter() {
const query = searchBar.value.toUpperCase();
inputs.forEach((elements, i) => {
const cards = Array.from(elements.children);
let total = cards.length;
inputs.forEach((elements, i) => {
const cards = Array.from(elements.children);
let total = cards.length;
cards.forEach((el) => {
const htmlElement = el as HTMLElement;
const span = el.querySelector('span');
const txtValue = span?.textContent || span?.innerText;
if (!txtValue) return;
cards.forEach((el) => {
const htmlElement = el as HTMLElement;
const span = el.querySelector('span');
const txtValue = span?.textContent || span?.innerText;
if (!txtValue) return;
// Filter list
if (txtValue.toUpperCase().indexOf(query) > -1) {
total++;
htmlElement.style.display = '';
} else {
total--;
htmlElement.style.display = 'none';
}
// Filter list
if (txtValue.toUpperCase().indexOf(query) > -1) {
total++;
htmlElement.style.display = '';
} else {
total--;
htmlElement.style.display = 'none';
}
// Show warning if none found
if (total === 0) {
warnings[i].classList.remove('hidden');
} else {
warnings[i].classList.add('hidden');
}
});
});
}
});
}
}
customElements.define('card-search', CardSearch);
// Show warning if none found
if (total === 0) {
warnings[i].classList.remove('hidden');
} else {
warnings[i].classList.add('hidden');
}
});
});
}
});
}
}
customElements.define('card-search', CardSearch);
</script>
<style>
:root {
--pagefind-ui-border-width: 1px;
--pagefind-ui-scale: 0.8;
--pagefind-ui-tag: #eeeeee;
--pagefind-ui-border-radius: 8px;
--pagefind-ui-image-border-radius: 8px;
--pagefind-ui-image-box-ratio: 3 / 2;
}
.pagefind-ui {
width: 100%;
color: var(--sl-color-gray-2);
font-family: var(--__sl-font);
margin-block: 2rem 1rem;
}
.pagefind-ui__form {
position: relative;
}
.pagefind-ui__form::before {
background-color: var(--sl-color-gray-2);
width: calc(18px * var(--pagefind-ui-scale));
height: calc(18px * var(--pagefind-ui-scale));
top: calc(23px * var(--pagefind-ui-scale));
left: calc(20px * var(--pagefind-ui-scale));
content: '';
position: absolute;
display: block;
opacity: 0.7;
-webkit-mask-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A");
mask-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A");
-webkit-mask-size: 100%;
mask-size: 100%;
z-index: 9;
pointer-events: none;
}
.pagefind-ui__search-input {
box-sizing: border-box;
position: relative;
display: flex;
font-weight: 400;
height: calc(64px * var(--pagefind-ui-scale));
padding: 0 calc(70px * var(--pagefind-ui-scale)) 0 calc(54px * var(--pagefind-ui-scale));
background-color: var(--sl-color-black);
border: var(--pagefind-ui-border-width) solid var(--sl-color-gray-5);
border-radius: var(--pagefind-ui-border-radius);
font-size: calc(21px * var(--pagefind-ui-scale));
appearance: none;
-webkit-appearance: none;
color: var(--sl-color-white);
width: 100%;
}
:root {
--pagefind-ui-border-width: 1px;
--pagefind-ui-scale: 0.8;
--pagefind-ui-tag: #eeeeee;
--pagefind-ui-border-radius: 8px;
--pagefind-ui-image-border-radius: 8px;
--pagefind-ui-image-box-ratio: 3 / 2;
}
.pagefind-ui {
width: 100%;
color: var(--sl-color-gray-2);
font-family: var(--__sl-font);
margin-block: 2rem 1rem;
}
.pagefind-ui__form {
position: relative;
}
.pagefind-ui__form::before {
background-color: var(--sl-color-gray-2);
width: calc(18px * var(--pagefind-ui-scale));
height: calc(18px * var(--pagefind-ui-scale));
top: calc(23px * var(--pagefind-ui-scale));
left: calc(20px * var(--pagefind-ui-scale));
content: '';
position: absolute;
display: block;
opacity: 0.7;
-webkit-mask-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A");
mask-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A");
-webkit-mask-size: 100%;
mask-size: 100%;
z-index: 9;
pointer-events: none;
}
.pagefind-ui__search-input {
box-sizing: border-box;
position: relative;
display: flex;
font-weight: 400;
height: calc(64px * var(--pagefind-ui-scale));
padding: 0 calc(70px * var(--pagefind-ui-scale)) 0 calc(54px * var(--pagefind-ui-scale));
background-color: var(--sl-color-black);
border: var(--pagefind-ui-border-width) solid var(--sl-color-gray-5);
border-radius: var(--pagefind-ui-border-radius);
font-size: calc(21px * var(--pagefind-ui-scale));
appearance: none;
-webkit-appearance: none;
color: var(--sl-color-white);
width: 100%;
}
.pagefind-ui__search-input::placeholder {
color: var(--sl-color-gray-3);
opacity: 1; /* Firefox */
}
.pagefind-ui__search-input::placeholder {
color: var(--sl-color-gray-3);
opacity: 1; /* Firefox */
}
.pagefind-ui__search-clear {
position: absolute;
top: calc(3px * var(--pagefind-ui-scale));
right: calc(3px * var(--pagefind-ui-scale));
height: calc(58px * var(--pagefind-ui-scale));
color: var(--sl-color-gray-2);
font-size: calc(14px * var(--pagefind-ui-scale));
cursor: pointer;
border-radius: var(--pagefind-ui-border-radius);
width: calc(60px * var(--pagefind-ui-scale));
padding: 0;
background-color: transparent;
overflow: hidden;
}
.pagefind-ui__search-clear {
position: absolute;
top: calc(3px * var(--pagefind-ui-scale));
right: calc(3px * var(--pagefind-ui-scale));
height: calc(58px * var(--pagefind-ui-scale));
color: var(--sl-color-gray-2);
font-size: calc(14px * var(--pagefind-ui-scale));
cursor: pointer;
border-radius: var(--pagefind-ui-border-radius);
width: calc(60px * var(--pagefind-ui-scale));
padding: 0;
background-color: transparent;
overflow: hidden;
}
.pagefind-ui__search-clear:focus {
outline: 1px solid var(--sl-color-accent);
}
.pagefind-ui__search-clear::before {
content: '';
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m13.41 12 6.3-6.29a1 1 0 1 0-1.42-1.42L12 10.59l-6.29-6.3a1 1 0 0 0-1.42 1.42l6.3 6.29-6.3 6.29a1 1 0 0 0 .33 1.64 1 1 0 0 0 1.09-.22l6.29-6.3 6.29 6.3a1 1 0 0 0 1.64-.33 1 1 0 0 0-.22-1.09L13.41 12Z'/%3E%3C/svg%3E")
center / 50% no-repeat;
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m13.41 12 6.3-6.29a1 1 0 1 0-1.42-1.42L12 10.59l-6.29-6.3a1 1 0 0 0-1.42 1.42l6.3 6.29-6.3 6.29a1 1 0 0 0 .33 1.64 1 1 0 0 0 1.09-.22l6.29-6.3 6.29 6.3a1 1 0 0 0 1.64-.33 1 1 0 0 0-.22-1.09L13.41 12Z'/%3E%3C/svg%3E")
center / 50% no-repeat;
background-color: var(--sl-color-text-accent);
display: block;
width: 100%;
height: 100%;
}
.pagefind-ui__suppressed {
opacity: 0;
pointer-events: none;
}
.pagefind-ui__search-clear:focus {
outline: 1px solid var(--sl-color-accent);
}
.pagefind-ui__search-clear::before {
content: '';
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m13.41 12 6.3-6.29a1 1 0 1 0-1.42-1.42L12 10.59l-6.29-6.3a1 1 0 0 0-1.42 1.42l6.3 6.29-6.3 6.29a1 1 0 0 0 .33 1.64 1 1 0 0 0 1.09-.22l6.29-6.3 6.29 6.3a1 1 0 0 0 1.64-.33 1 1 0 0 0-.22-1.09L13.41 12Z'/%3E%3C/svg%3E")
center / 50% no-repeat;
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m13.41 12 6.3-6.29a1 1 0 1 0-1.42-1.42L12 10.59l-6.29-6.3a1 1 0 0 0-1.42 1.42l6.3 6.29-6.3 6.29a1 1 0 0 0 .33 1.64 1 1 0 0 0 1.09-.22l6.29-6.3 6.29 6.3a1 1 0 0 0 1.64-.33 1 1 0 0 0-.22-1.09L13.41 12Z'/%3E%3C/svg%3E")
center / 50% no-repeat;
background-color: var(--sl-color-text-accent);
display: block;
width: 100%;
height: 100%;
}
.pagefind-ui__suppressed {
opacity: 0;
pointer-events: none;
}
</style>

View File

@@ -3,42 +3,42 @@ import { Tabs, TabItem } from '@astrojs/starlight/components';
import { Code } from '@astrojs/starlight/components';
interface Props {
npm?: string;
yarn?: string;
pnpm?: string;
cargo?: string;
npm?: string;
yarn?: string;
pnpm?: string;
cargo?: string;
}
const { npm, yarn, pnpm, cargo } = Astro.props;
---
<Tabs>
{
npm && (
<TabItem label="npm">
<Code code={npm} lang="sh" theme="css-variables" frame="none" />
</TabItem>
)
}
{
yarn && (
<TabItem label="Yarn">
<Code code={yarn} lang="sh" theme="css-variables" frame="none" />
</TabItem>
)
}
{
pnpm && (
<TabItem label="pnpm">
<Code code={pnpm} lang="sh" theme="css-variables" frame="none" />
</TabItem>
)
}
{
cargo && (
<TabItem label="Cargo">
<Code code={cargo} lang="sh" theme="css-variables" frame="none" />
</TabItem>
)
}
{
npm && (
<TabItem label="npm">
<Code code={npm} lang="sh" theme="css-variables" frame="none" />
</TabItem>
)
}
{
yarn && (
<TabItem label="Yarn">
<Code code={yarn} lang="sh" theme="css-variables" frame="none" />
</TabItem>
)
}
{
pnpm && (
<TabItem label="pnpm">
<Code code={pnpm} lang="sh" theme="css-variables" frame="none" />
</TabItem>
)
}
{
cargo && (
<TabItem label="Cargo">
<Code code={cargo} lang="sh" theme="css-variables" frame="none" />
</TabItem>
)
}
</Tabs>

View File

@@ -4,5 +4,5 @@ const enabled = process.env.TAURI_DEBUG && Astro.slots.has('default');
---
<Card title="Demo">
{enabled ? <slot /> : <p>Try it in the Tauri Docs app (coming soon)</p>}
{enabled ? <slot /> : <p>Try it in the Tauri Docs app (coming soon)</p>}
</Card>

View File

@@ -7,8 +7,8 @@ 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"
/>

File diff suppressed because one or more lines are too long

View File

@@ -5,19 +5,19 @@ const baseUrl = 'https://github.com/tauri-apps/tauri-docs/tree/v2';
---
<Card title="Contribute" icon="pencil">
<p>
This 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 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

@@ -7,30 +7,30 @@ interface Props extends HTMLAttributes<'a'> {}
---
<div>
<a class="not-content" {...Astro.props}>Tauri 1.0 Upgrade Guide <Icon name="rocket" /></a>
<a class="not-content" {...Astro.props}>Tauri 1.0 Upgrade Guide <Icon name="rocket" /></a>
</div>
<style>
div {
display: inline-block;
}
div {
display: inline-block;
}
a {
display: flex;
align-items: center;
gap: 0.5rem;
border: 1px solid var(--sl-color-orange-low);
border-radius: 0.5rem;
padding: 0.5rem 1rem;
text-decoration: none;
box-shadow: var(--sl-shadow-sm);
color: var(--sl-color-gray-1) !important;
font-weight: 600;
line-height: var(--sl-line-height-headings);
}
a {
display: flex;
align-items: center;
gap: 0.5rem;
border: 1px solid var(--sl-color-orange-low);
border-radius: 0.5rem;
padding: 0.5rem 1rem;
text-decoration: none;
box-shadow: var(--sl-shadow-sm);
color: var(--sl-color-gray-1) !important;
font-weight: 600;
line-height: var(--sl-line-height-headings);
}
a:hover {
background: var(--sl-color-orange-low);
border-color: var(--sl-color-orange-high);
}
a:hover {
background: var(--sl-color-orange-low);
border-color: var(--sl-color-orange-high);
}
</style>

View File

@@ -1,29 +1,29 @@
---
import { LinkCard, CardGrid } from '@astrojs/starlight/components';
type Entry = {
name: string;
description: string;
href: string;
name: string;
description: string;
href: string;
};
const list: Entry[] = [
{
name: 'Have something to share?',
description: 'Open a pull request to show us your amazing resource',
href: 'https://github.com/tauri-apps/tauri-docs/pulls',
},
{
name: 'Github OAuth with Lucia',
description: 'Authenticate users with a simple JS server',
href: 'https://lucia-auth.com/guidebook/github-oauth-native/tauri',
},
{
name: 'Have something to share?',
description: 'Open a pull request to show us your amazing resource',
href: 'https://github.com/tauri-apps/tauri-docs/pulls',
},
{
name: 'Github OAuth with Lucia',
description: 'Authenticate users with a simple JS server',
href: 'https://lucia-auth.com/guidebook/github-oauth-native/tauri',
},
];
---
<CardGrid>
{
list
.sort((a, b) => a.name.localeCompare(b.name))
.map((item) => <LinkCard title={item.name} href={item.href} description={item.description} />)
}
{
list
.sort((a, b) => a.name.localeCompare(b.name))
.map((item) => <LinkCard title={item.name} href={item.href} description={item.description} />)
}
</CardGrid>

View File

@@ -1,45 +1,45 @@
---
import config from 'virtual:starlight/user-config';
import {
stripLeadingAndTrailingSlashes,
ensureTrailingSlash,
stripLeadingAndTrailingSlashes,
ensureTrailingSlash,
} from 'node_modules/@astrojs/starlight/utils/path';
import { LinkCard, CardGrid } from '@astrojs/starlight/components';
import { routes, type Route } from 'node_modules/@astrojs/starlight/utils/routing';
interface Props {
/**
* Slug relative to /src - e.g "/zh-cn/features"
*
* Note: leading and trailing slashes are dropped "/features/" === "features"
*/
slug: string;
/**
* Filter out pages by title - case insensitive
*/
filterOutByTitle?: string[];
/**
* Filter out pages by file name - case insensitive
*/
filterOutByFileName?: string[];
/**
* Force alphabetical order - by default it sorts by `frontmatter.sidebar.order`, then alphabetically.
*
* Use this to ignore sidebar order
*/
sortAlphabetically?: boolean;
/**
* Slug relative to /src - e.g "/zh-cn/features"
*
* Note: leading and trailing slashes are dropped "/features/" === "features"
*/
slug: string;
/**
* Filter out pages by title - case insensitive
*/
filterOutByTitle?: string[];
/**
* Filter out pages by file name - case insensitive
*/
filterOutByFileName?: string[];
/**
* Force alphabetical order - by default it sorts by `frontmatter.sidebar.order`, then alphabetically.
*
* Use this to ignore sidebar order
*/
sortAlphabetically?: boolean;
}
function hasSidebarOrder(page: Route): number | undefined {
return page.entry.data.sidebar.order;
return page.entry.data.sidebar.order;
}
function alphaSort(a: Route, b: Route): number {
return a.entry.data.title.localeCompare(b.entry.data.title);
return a.entry.data.title.localeCompare(b.entry.data.title);
}
function compareOrder(a: Route, b: Route): boolean {
return a.entry.data.sidebar.order > b.entry.data.sidebar.order;
return a.entry.data.sidebar.order > b.entry.data.sidebar.order;
}
let { slug } = Astro.props;
@@ -53,7 +53,7 @@ let [currentPageLocale] = stripLeadingAndTrailingSlashes(Astro.url.pathname).spl
// Ensure currentPageLocale is a locale when it's page is on default locale
if (!localesList.includes(currentPageLocale)) {
currentPageLocale = defaultLocale;
currentPageLocale = defaultLocale;
}
const locale = currentPageLocale;
@@ -62,7 +62,7 @@ const pageLang = ensureTrailingSlash(locale);
// Ensure the locale is ignore if passed on `slug`
let [sliceLocale] = stripLeadingAndTrailingSlashes(slug).split('/');
if (localesList.includes(sliceLocale)) {
slug = slug.slice(sliceLocale.length + 1);
slug = slug.slice(sliceLocale.length + 1);
}
// Localize `slug` to the current page locale
slug = locale === defaultLocale ? slug : pageLang + slug;
@@ -71,14 +71,14 @@ let fallbackList: Route[] = [];
let mainList: Route[] = [];
routes.forEach((page) => {
// `page.slug` is the slug of the current page being looped, `slug` is a prop
if (page.slug.startsWith(slug)) {
if (page.isFallback) {
fallbackList.push(page);
} else if (page.slug !== slug) {
mainList.push(page);
}
}
// `page.slug` is the slug of the current page being looped, `slug` is a prop
if (page.slug.startsWith(slug)) {
if (page.isFallback) {
fallbackList.push(page);
} else if (page.slug !== slug) {
mainList.push(page);
}
}
});
/**
@@ -86,86 +86,86 @@ routes.forEach((page) => {
*/
if (filterOutByTitle.length > 0) {
fallbackList = fallbackList.filter(
(page) => !filterOutByTitle.some((val) => page.entry.data.title.includes(val))
);
mainList = mainList.filter(
(page) => !filterOutByTitle.some((val) => page.entry.data.title.includes(val))
);
fallbackList = fallbackList.filter(
(page) => !filterOutByTitle.some((val) => page.entry.data.title.includes(val))
);
mainList = mainList.filter(
(page) => !filterOutByTitle.some((val) => page.entry.data.title.includes(val))
);
}
if (filterOutByFileName.length > 0) {
fallbackList = fallbackList.filter(
(page) => !filterOutByFileName.some((val) => page.entry.id.includes(val))
);
mainList = mainList.filter(
(page) => !filterOutByFileName.some((val) => page.entry.id.includes(val))
);
fallbackList = fallbackList.filter(
(page) => !filterOutByFileName.some((val) => page.entry.id.includes(val))
);
mainList = mainList.filter(
(page) => !filterOutByFileName.some((val) => page.entry.id.includes(val))
);
}
if (!sortAlphabetically) {
// dumb luck this works, now why?
const sortVal = locale === defaultLocale ? 1 : -1;
// Sort list following sidebar order
mainList.sort((a, b) => {
if (!hasSidebarOrder(a) && !hasSidebarOrder(b)) {
return alphaSort(a, b);
}
if (compareOrder(a, b)) {
return sortVal;
} else {
return -sortVal;
}
});
// Sort list following sidebar order
fallbackList.sort((a, b) => {
if (!hasSidebarOrder(a) && !hasSidebarOrder(b)) {
return alphaSort(a, b);
}
if (compareOrder(a, b)) {
return -sortVal;
} else {
return sortVal;
}
});
// dumb luck this works, now why?
const sortVal = locale === defaultLocale ? 1 : -1;
// Sort list following sidebar order
mainList.sort((a, b) => {
if (!hasSidebarOrder(a) && !hasSidebarOrder(b)) {
return alphaSort(a, b);
}
if (compareOrder(a, b)) {
return sortVal;
} else {
return -sortVal;
}
});
// Sort list following sidebar order
fallbackList.sort((a, b) => {
if (!hasSidebarOrder(a) && !hasSidebarOrder(b)) {
return alphaSort(a, b);
}
if (compareOrder(a, b)) {
return -sortVal;
} else {
return sortVal;
}
});
} else {
// Sort alphabetically
mainList.sort((a, b) => alphaSort(a, b));
fallbackList.sort((a, b) => alphaSort(a, b));
// Sort alphabetically
mainList.sort((a, b) => alphaSort(a, b));
fallbackList.sort((a, b) => alphaSort(a, b));
}
---
<CardGrid>
{
mainList.map((item) => (
<LinkCard
title={item.entry.data.title}
href={`/${item.slug}`}
description={item.entry.data.description}
/>
))
}
{
locale !== defaultLocale &&
fallbackList.map((item) => (
<LinkCard
class="fallback-badge"
title={item.entry.data.title}
href={`/${item.slug}`}
description={item.entry.data.description}
/>
))
}
{
mainList.map((item) => (
<LinkCard
title={item.entry.data.title}
href={`/${item.slug}`}
description={item.entry.data.description}
/>
))
}
{
locale !== defaultLocale &&
fallbackList.map((item) => (
<LinkCard
class="fallback-badge"
title={item.entry.data.title}
href={`/${item.slug}`}
description={item.entry.data.description}
/>
))
}
</CardGrid>
<style>
.fallback-badge:after {
/* now how can this match the defaultLocale? */
/* try with attr() */
content: 'EN';
vertical-align: super;
font-size: 0.75em;
font-weight: 700;
margin-left: 1ch;
}
.fallback-badge:after {
/* now how can this match the defaultLocale? */
/* try with attr() */
content: 'EN';
vertical-align: super;
font-size: 0.75em;
font-weight: 700;
margin-left: 1ch;
}
</style>

View File

@@ -5,24 +5,24 @@ import { Icon } from '@astrojs/starlight/components';
import semver from 'semver';
interface Props {
/**
* Package name
*/
packageName: string;
/**
* Slug relative to /src - e.g "/zh-cn/features"
*
* Note: leading and trailing slashes are dropped "/features/" === "features"
*/
slug: string;
/**
* Filter out pages by title - case insensitive
*/
filterOutByTitle?: string[];
/**
* Filter out pages by file name - case insensitive
*/
filterOutByFileName?: string[];
/**
* Package name
*/
packageName: string;
/**
* Slug relative to /src - e.g "/zh-cn/features"
*
* Note: leading and trailing slashes are dropped "/features/" === "features"
*/
slug: string;
/**
* Filter out pages by title - case insensitive
*/
filterOutByTitle?: string[];
/**
* Filter out pages by file name - case insensitive
*/
filterOutByFileName?: string[];
}
let { slug } = Astro.props;
@@ -33,11 +33,11 @@ slug = stripLeadingAndTrailingSlashes(slug.toLowerCase());
let mainList: Route[] = [];
routes.forEach((page) => {
// `page.slug` is the slug of the current page being looped,
// `slug` is this component prop
if (page.slug.startsWith(slug + '/')) {
mainList.push(page);
}
// `page.slug` is the slug of the current page being looped,
// `slug` is this component prop
if (page.slug.startsWith(slug + '/')) {
mainList.push(page);
}
});
/**
@@ -45,237 +45,237 @@ routes.forEach((page) => {
*/
if (filterOutByTitle.length > 0) {
mainList = mainList.filter(
(page) => !filterOutByTitle.some((val) => page.entry.data.title.includes(val))
);
mainList = mainList.filter(
(page) => !filterOutByTitle.some((val) => page.entry.data.title.includes(val))
);
}
if (filterOutByFileName.length > 0) {
mainList = mainList.filter(
(page) => !filterOutByFileName.some((val) => page.entry.id.includes(val))
);
mainList = mainList.filter(
(page) => !filterOutByFileName.some((val) => page.entry.id.includes(val))
);
}
function sortVersions(a: Route, b: Route): number {
const t1 = a.entry.data.title;
const t2 = b.entry.data.title;
const [package1, version1] = t1.split('@').filter((w) => w.length > 0);
const [package2, version2] = t2.split('@').filter((w) => w.length > 0);
if (package1 === package2) {
return semver.lt(version1, version2) ? -1 : semver.gt(version1, version2) ? 1 : 0;
} else {
return package1.localeCompare(package2);
}
const t1 = a.entry.data.title;
const t2 = b.entry.data.title;
const [package1, version1] = t1.split('@').filter((w) => w.length > 0);
const [package2, version2] = t2.split('@').filter((w) => w.length > 0);
if (package1 === package2) {
return semver.lt(version1, version2) ? -1 : semver.gt(version1, version2) ? 1 : 0;
} else {
return package1.localeCompare(package2);
}
}
mainList.sort((a, b) => sortVersions(b, a));
---
<div class="parent">
<div class="sidenav">
<div class="sidenav-title">
<p>Changelogs: <br />{packageName}</p>
</div>
<div class="sidenav-content">
{
mainList.map((item) => (
<>
<a href={`/${item.slug}`}> {item.entry.data.description}</a>
</>
))
}
</div>
</div>
<div class="sidenav">
<div class="sidenav-title">
<p>Changelogs: <br />{packageName}</p>
</div>
<div class="sidenav-content">
{
mainList.map((item) => (
<>
<a href={`/${item.slug}`}> {item.entry.data.description}</a>
</>
))
}
</div>
</div>
</div>
<mobile-release-list>
<nav aria-labelledby="mobile-release-list">
<details>
<summary id="mobile-release-list" class="sl-flex">
<div class="toggle sl-flex">
Release List:
<span class="display-current">{packageName}</span>
<Icon name={'right-caret'} class="caret" size="1rem" />
</div>
</summary>
<div class="dropdown">
<ul>
{
mainList.map((item) => (
<li>
<a href={`/${item.slug}`}> {item.entry.data.description}</a>
</li>
))
}
</ul>
</div>
</details>
</nav>
<nav aria-labelledby="mobile-release-list">
<details>
<summary id="mobile-release-list" class="sl-flex">
<div class="toggle sl-flex">
Release List:
<span class="display-current">{packageName}</span>
<Icon name={'right-caret'} class="caret" size="1rem" />
</div>
</summary>
<div class="dropdown">
<ul>
{
mainList.map((item) => (
<li>
<a href={`/${item.slug}`}> {item.entry.data.description}</a>
</li>
))
}
</ul>
</div>
</details>
</nav>
</mobile-release-list>
<script>
// Taken from starlight's mobile TOC
class MobileReleaseList extends HTMLElement {
constructor() {
super();
const details = this.querySelector('details');
if (!details) return;
const closeToC = () => {
details.open = false;
};
// Taken from starlight's mobile TOC
class MobileReleaseList extends HTMLElement {
constructor() {
super();
const details = this.querySelector('details');
if (!details) return;
const closeToC = () => {
details.open = false;
};
// Close the table of contents when a user clicks outside of it.
window.addEventListener('click', (e) => {
if (!details.contains(e.target as Node)) closeToC();
});
// Or when they press the escape key.
window.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && details.open) {
const hasFocus = details.contains(document.activeElement);
closeToC();
if (hasFocus) {
const summary = details.querySelector('summary');
if (summary) summary.focus();
}
}
});
}
}
// Close the table of contents when a user clicks outside of it.
window.addEventListener('click', (e) => {
if (!details.contains(e.target as Node)) closeToC();
});
// Or when they press the escape key.
window.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && details.open) {
const hasFocus = details.contains(document.activeElement);
closeToC();
if (hasFocus) {
const summary = details.querySelector('summary');
if (summary) summary.focus();
}
}
});
}
}
customElements.define('mobile-release-list', MobileReleaseList);
customElements.define('mobile-release-list', MobileReleaseList);
</script>
<style>
.parent {
position: relative;
}
:root {
--release-navbar-width: 12rem;
--sl-content-width: 35rem !important;
}
.parent {
position: relative;
}
:root {
--release-navbar-width: 12rem;
--sl-content-width: 35rem !important;
}
.sidenav-title {
display: block;
padding-block: 0.5rem;
text-align: center;
background-color: var(--sl-color-gray-5);
border: 1px solid var(--sl-color-gray-6);
}
.sidenav {
display: none;
width: fit-content;
top: 0;
right: calc(var(--release-navbar-width) * -1);
box-shadow: var(--sl-shadow-md);
font-size: var(--sl-text-xs);
font-family: var(--__sl-font-mono);
background-color: var(--sl-color-bg-sidebar);
}
.sidenav-title {
display: block;
padding-block: 0.5rem;
text-align: center;
background-color: var(--sl-color-gray-5);
border: 1px solid var(--sl-color-gray-6);
}
.sidenav {
display: none;
width: fit-content;
top: 0;
right: calc(var(--release-navbar-width) * -1);
box-shadow: var(--sl-shadow-md);
font-size: var(--sl-text-xs);
font-family: var(--__sl-font-mono);
background-color: var(--sl-color-bg-sidebar);
}
.sl-markdown-content {
margin: auto;
background-color: red;
}
@media (min-width: 72rem) {
.sidenav {
display: inline;
position: absolute;
}
}
.sl-markdown-content {
margin: auto;
background-color: red;
}
@media (min-width: 72rem) {
.sidenav {
display: inline;
position: absolute;
}
}
.sidenav-content {
padding-inline: 16px 8px;
min-width: fit-content;
row-gap: 1em;
display: flex;
flex-direction: column;
max-height: 85vh;
overflow-y: auto;
overscroll-behavior: contain;
}
.sidenav-content {
padding-inline: 16px 8px;
min-width: fit-content;
row-gap: 1em;
display: flex;
flex-direction: column;
max-height: 85vh;
overflow-y: auto;
overscroll-behavior: contain;
}
/* Mobile */
/* Taken from starlight's mobile TOC */
nav {
width: fit-content;
}
@media (min-width: 72rem) {
nav {
display: none;
}
}
/* Mobile */
/* Taken from starlight's mobile TOC */
nav {
width: fit-content;
}
@media (min-width: 72rem) {
nav {
display: none;
}
}
summary {
padding-bottom: 1rem;
gap: 0.5rem;
align-items: center;
font-size: var(--sl-text-xs);
outline-offset: var(--sl-outline-offset-inside);
}
summary::marker,
summary::-webkit-details-marker {
display: none;
}
summary {
padding-bottom: 1rem;
gap: 0.5rem;
align-items: center;
font-size: var(--sl-text-xs);
outline-offset: var(--sl-outline-offset-inside);
}
summary::marker,
summary::-webkit-details-marker {
display: none;
}
.toggle {
z-index: 3;
flex-shrink: 0;
gap: 1rem;
align-items: center;
justify-content: space-between;
border: 1px solid var(--sl-color-gray-5);
border-radius: 0.5rem;
padding-block: 0.5rem;
padding-inline-start: 0.75rem;
padding-inline-end: 0.5rem;
background-color: var(--sl-color-gray-6);
user-select: none;
cursor: pointer;
}
details[open] .toggle {
color: var(--sl-color-white);
border-color: var(--sl-color-accent);
}
details .toggle:hover {
color: var(--sl-color-white);
border-color: var(--sl-color-gray-2);
}
details[open] .caret {
transform: rotateZ(90deg);
}
.display-current {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
color: var(--sl-color-white);
}
.dropdown {
--border: 1px;
z-index: 2;
row-gap: 1em;
position: absolute;
border-top-left-radius: 0.5rem;
padding-inline: 0.75rem;
padding-top: 1em;
margin-top: -1em !important;
border: var(--border) solid var(--sl-color-gray-5);
max-height: calc(85vh - var(--sl-nav-height) - var(--sl-mobile-toc-height));
overflow-y: auto;
background-color: var(--sl-color-black);
box-shadow: var(--sl-shadow-md);
overscroll-behavior: contain;
background-color: var(--sl-color-bg-sidebar);
}
.toggle {
z-index: 3;
flex-shrink: 0;
gap: 1rem;
align-items: center;
justify-content: space-between;
border: 1px solid var(--sl-color-gray-5);
border-radius: 0.5rem;
padding-block: 0.5rem;
padding-inline-start: 0.75rem;
padding-inline-end: 0.5rem;
background-color: var(--sl-color-gray-6);
user-select: none;
cursor: pointer;
}
details[open] .toggle {
color: var(--sl-color-white);
border-color: var(--sl-color-accent);
}
details .toggle:hover {
color: var(--sl-color-white);
border-color: var(--sl-color-gray-2);
}
details[open] .caret {
transform: rotateZ(90deg);
}
.display-current {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
color: var(--sl-color-white);
}
.dropdown {
--border: 1px;
z-index: 2;
row-gap: 1em;
position: absolute;
border-top-left-radius: 0.5rem;
padding-inline: 0.75rem;
padding-top: 1em;
margin-top: -1em !important;
border: var(--border) solid var(--sl-color-gray-5);
max-height: calc(85vh - var(--sl-nav-height) - var(--sl-mobile-toc-height));
overflow-y: auto;
background-color: var(--sl-color-black);
box-shadow: var(--sl-shadow-md);
overscroll-behavior: contain;
background-color: var(--sl-color-bg-sidebar);
}
ul {
all: unset;
list-style: none;
}
li {
all: unset;
}
a {
display: block;
padding-block: 0.5rem;
line-height: 1.25;
}
ul {
all: unset;
list-style: none;
}
li {
all: unset;
}
a {
display: block;
padding-block: 0.5rem;
line-height: 1.25;
}
</style>

View File

@@ -11,13 +11,13 @@ const year = new Date().getFullYear();
<p>&copy; {year} Tauri Contributors. CC-BY / MIT</p>
<style>
p {
text-align: center;
color: var(--sl-color-gray-2);
font-size: var(--sl-text-sm);
}
hr {
border: 0;
border-bottom: 1px solid var(--sl-color-hairline);
}
p {
text-align: center;
color: var(--sl-color-gray-2);
font-size: var(--sl-text-sm);
}
hr {
border: 0;
border-bottom: 1px solid var(--sl-color-hairline);
}
</style>

View File

@@ -6,96 +6,96 @@ const { labels } = Astro.props;
---
<div class="page sl-flex">
<header class="header"><slot name="header" /></header>
<nav class="sidebar" aria-label={labels['sidebarNav.accessibleLabel']}>
<MobileMenuToggle {...Astro.props} />
<div
id="starlight__sidebar"
class={Astro.props.entry.slug === '' || Astro.props.locale === Astro.props.entry.slug
? 'sidebar-pane lp-hide'
: 'sidebar-pane'}
>
<div class="sidebar-content sl-flex">
<Sidebar {...Astro.props} />
</div>
</div>
</nav>
<div class="main-frame"><slot /></div>
<header class="header"><slot name="header" /></header>
<nav class="sidebar" aria-label={labels['sidebarNav.accessibleLabel']}>
<MobileMenuToggle {...Astro.props} />
<div
id="starlight__sidebar"
class={Astro.props.entry.slug === '' || Astro.props.locale === Astro.props.entry.slug
? 'sidebar-pane lp-hide'
: 'sidebar-pane'}
>
<div class="sidebar-content sl-flex">
<Sidebar {...Astro.props} />
</div>
</div>
</nav>
<div class="main-frame"><slot /></div>
</div>
<style>
.page {
flex-direction: column;
min-height: 100vh;
}
.page {
flex-direction: column;
min-height: 100vh;
}
@media (min-width: 50rem) {
.lp-hide {
display: none;
}
}
@media (min-width: 50rem) {
.lp-hide {
display: none;
}
}
.header {
z-index: var(--sl-z-index-navbar);
position: fixed;
inset-inline-start: 0;
inset-block-start: 0;
width: 100%;
height: var(--sl-nav-height);
border-bottom: 1px solid var(--sl-color-hairline-shade);
padding: var(--sl-nav-pad-y) var(--sl-nav-pad-x);
padding-inline-end: var(--sl-nav-pad-x);
background-color: var(--sl-color-bg-nav);
}
.header {
z-index: var(--sl-z-index-navbar);
position: fixed;
inset-inline-start: 0;
inset-block-start: 0;
width: 100%;
height: var(--sl-nav-height);
border-bottom: 1px solid var(--sl-color-hairline-shade);
padding: var(--sl-nav-pad-y) var(--sl-nav-pad-x);
padding-inline-end: var(--sl-nav-pad-x);
background-color: var(--sl-color-bg-nav);
}
:global([data-has-sidebar]) .header {
padding-inline-end: calc(var(--sl-nav-gap) + var(--sl-nav-pad-x) + var(--sl-menu-button-size));
}
:global([data-has-sidebar]) .header {
padding-inline-end: calc(var(--sl-nav-gap) + var(--sl-nav-pad-x) + var(--sl-menu-button-size));
}
.sidebar-pane {
visibility: var(--sl-sidebar-visibility, hidden);
position: fixed;
z-index: var(--sl-z-index-menu);
inset-block: var(--sl-nav-height) 0;
inset-inline-start: 0;
width: 100%;
background-color: var(--sl-color-black);
overflow-y: auto;
}
.sidebar-pane {
visibility: var(--sl-sidebar-visibility, hidden);
position: fixed;
z-index: var(--sl-z-index-menu);
inset-block: var(--sl-nav-height) 0;
inset-inline-start: 0;
width: 100%;
background-color: var(--sl-color-black);
overflow-y: auto;
}
:global([aria-expanded='true']) ~ .sidebar-pane {
--sl-sidebar-visibility: visible;
}
:global([aria-expanded='true']) ~ .sidebar-pane {
--sl-sidebar-visibility: visible;
}
.sidebar-content {
height: 100%;
min-height: max-content;
padding: 1rem var(--sl-sidebar-pad-x) 0;
flex-direction: column;
gap: 1rem;
}
.sidebar-content {
height: 100%;
min-height: max-content;
padding: 1rem var(--sl-sidebar-pad-x) 0;
flex-direction: column;
gap: 1rem;
}
@media (min-width: 50rem) {
.sidebar-content::after {
content: '';
padding-bottom: 1px;
}
}
@media (min-width: 50rem) {
.sidebar-content::after {
content: '';
padding-bottom: 1px;
}
}
.main-frame {
padding-top: calc(var(--sl-nav-height) + var(--sl-mobile-toc-height));
padding-inline-start: var(--sl-content-inline-start);
}
.main-frame {
padding-top: calc(var(--sl-nav-height) + var(--sl-mobile-toc-height));
padding-inline-start: var(--sl-content-inline-start);
}
@media (min-width: 50rem) {
:global([data-has-sidebar]) .header {
padding-inline-end: var(--sl-nav-pad-x);
}
.sidebar-pane {
--sl-sidebar-visibility: visible;
width: var(--sl-sidebar-width);
background-color: var(--sl-color-bg-sidebar);
border-inline-end: 1px solid var(--sl-color-hairline-shade);
}
}
@media (min-width: 50rem) {
:global([data-has-sidebar]) .header {
padding-inline-end: var(--sl-nav-pad-x);
}
.sidebar-pane {
--sl-sidebar-visibility: visible;
width: var(--sl-sidebar-width);
background-color: var(--sl-color-bg-sidebar);
border-inline-end: 1px solid var(--sl-color-hairline-shade);
}
}
</style>

View File

@@ -12,107 +12,107 @@ import { AstroError } from 'astro/errors';
// 2. Create a new derived set of `Astro.props` that only contains one set of sidebar entries
// 3. Check if the current page being rendered is current page to determine if this sidebar group should be selected
const multiSidebarConfig: [string, boolean, Props][] = Astro.props.sidebar.map((entry) => {
if (entry.type !== 'group') {
throw new AstroError(
`\`${entry.label}\` cannot be used with multiple Starlight sidebars.
if (entry.type !== 'group') {
throw new AstroError(
`\`${entry.label}\` cannot be used with multiple Starlight sidebars.
Each top-level \`sidebar\` item in the Starlight config must be either a group or autogenerated.
See https://starlight.astro.build/guides/sidebar/#groups and https://starlight.astro.build/guides/sidebar/#autogenerated-groups`
);
}
);
}
// Recursively check if a group of sidebar entries contains the current page
const findIfIsCurrent = (entry: (typeof Astro.props.sidebar)[number]): boolean => {
if (entry.type === 'link') {
return entry.isCurrent;
}
return entry.entries.some((item) => findIfIsCurrent(item));
};
// Recursively check if a group of sidebar entries contains the current page
const findIfIsCurrent = (entry: (typeof Astro.props.sidebar)[number]): boolean => {
if (entry.type === 'link') {
return entry.isCurrent;
}
return entry.entries.some((item) => findIfIsCurrent(item));
};
const isCurrentPage = findIfIsCurrent(entry);
const isCurrentPage = findIfIsCurrent(entry);
return [entry.label, isCurrentPage, { ...Astro.props, sidebar: [...entry.entries] }];
return [entry.label, isCurrentPage, { ...Astro.props, sidebar: [...entry.entries] }];
});
let foundCurrentPage = false;
for (const page of multiSidebarConfig) {
if (page[1]) {
foundCurrentPage = true;
break;
}
if (page[1]) {
foundCurrentPage = true;
break;
}
}
if (!foundCurrentPage && multiSidebarConfig.length > 0) {
multiSidebarConfig[0][1] = true;
multiSidebarConfig[0][1] = true;
}
if (!multiSidebarConfig.some(([_label, isCurrentPage, _config]) => isCurrentPage)) {
multiSidebarConfig[0][1] = true;
multiSidebarConfig[0][1] = true;
}
---
<div class="__collapse">
{
multiSidebarConfig.map(([label, isCurrentPage, config]) => (
<>
<input type="radio" name="sidebar" role="tab" aria-label={label} checked={isCurrentPage} />
<div class="__collapse-content">
<Default {...config}>
<slot />
</Default>
</div>
</>
))
}
{
multiSidebarConfig.map(([label, isCurrentPage, config]) => (
<>
<input type="radio" name="sidebar" role="tab" aria-label={label} checked={isCurrentPage} />
<div class="__collapse-content">
<Default {...config}>
<slot />
</Default>
</div>
</>
))
}
</div>
<style>
.__collapse {
display: grid;
}
.__collapse > input {
/* Layout */
position: relative;
display: inline-flex;
grid-row-start: 1;
appearance: none;
width: 100%;
min-height: fit-content;
.__collapse {
display: grid;
}
.__collapse > input {
/* Layout */
position: relative;
display: inline-flex;
grid-row-start: 1;
appearance: none;
width: 100%;
min-height: fit-content;
/* Styles */
border-radius: 0.25rem;
padding: 0.2em 0.5rem;
line-height: 1.4;
font-size: var(--sl-text-lg);
font-weight: 600;
cursor: pointer;
user-select: none;
margin-bottom: var(--sl-nav-pad-y);
}
/* Styles */
border-radius: 0.25rem;
padding: 0.2em 0.5rem;
line-height: 1.4;
font-size: var(--sl-text-lg);
font-weight: 600;
cursor: pointer;
user-select: none;
margin-bottom: var(--sl-nav-pad-y);
}
.__collapse > input::after {
content: attr(aria-label);
}
.__collapse > input::after {
content: attr(aria-label);
}
.__collapse > input:checked {
color: var(--sl-color-text-invert);
background-color: var(--sl-color-text-accent);
}
.__collapse > input:checked {
color: var(--sl-color-text-invert);
background-color: var(--sl-color-text-accent);
}
.__collapse > .__collapse-content {
display: none;
grid-column-start: 1;
grid-column-end: span 999;
grid-row-start: 2;
border-top: 1px solid var(--sl-color-gray-5);
padding-top: 1rem;
}
.__collapse > input:checked + .__collapse-content {
display: block;
}
.__collapse > .__collapse-content {
display: none;
grid-column-start: 1;
grid-column-end: span 999;
grid-row-start: 2;
border-top: 1px solid var(--sl-color-gray-5);
padding-top: 1rem;
}
.__collapse > input:checked + .__collapse-content {
display: block;
}
</style>
<style is:global>
.top-level {
margin-bottom: 1rem;
}
.sidebar-pane {
overflow-y: scroll;
}
.top-level {
margin-bottom: 1rem;
}
.sidebar-pane {
overflow-y: scroll;
}
</style>

View File

@@ -10,150 +10,150 @@ import { Icon } from '@astrojs/starlight/components';
const curLocale = Astro.props.locale;
type Link = {
title: string;
value: string;
transfer: boolean;
title: string;
value: string;
transfer: boolean;
};
const links: Link[] = [
{
title: 'Guides',
value: '/start/',
transfer: true,
},
{
title: 'References',
value: '/reference/acl/',
transfer: true,
},
{
title: 'Blog',
value: '/blog',
transfer: false,
},
{
title: 'Releases',
value: '/release/',
transfer: true,
},
{
title: 'Guides',
value: '/start/',
transfer: true,
},
{
title: 'References',
value: '/reference/acl/',
transfer: true,
},
{
title: 'Blog',
value: '/blog',
transfer: false,
},
{
title: 'Releases',
value: '/release/',
transfer: true,
},
];
---
<>
<Default {...Astro.props}><slot /></Default>
<Default {...Astro.props}><slot /></Default>
<starlight-select class="mobile">
<label>
<select aria-label="Menu">
<option value="" disabled selected>Menu</option>
{
links.map((link) => (
<option
value={link.transfer && curLocale ? `/${curLocale}${link.value}` : link.value}
set:html={link.title}
/>
))
}
</select>
<Icon name="down-caret" class="icon caret" />
</label>
</starlight-select>
<starlight-select class="mobile">
<label>
<select aria-label="Menu">
<option value="" disabled selected>Menu</option>
{
links.map((link) => (
<option
value={link.transfer && curLocale ? `/${curLocale}${link.value}` : link.value}
set:html={link.title}
/>
))
}
</select>
<Icon name="down-caret" class="icon caret" />
</label>
</starlight-select>
</>
<script>
class StarlightLanguageSelect extends HTMLElement {
constructor() {
super();
const select = this.querySelector('select');
if (select) {
select.addEventListener('change', (e) => {
if (e.currentTarget instanceof HTMLSelectElement) {
window.location.pathname = e.currentTarget.value;
}
});
}
}
}
customElements.define('starlight-select', StarlightLanguageSelect);
class StarlightLanguageSelect extends HTMLElement {
constructor() {
super();
const select = this.querySelector('select');
if (select) {
select.addEventListener('change', (e) => {
if (e.currentTarget instanceof HTMLSelectElement) {
window.location.pathname = e.currentTarget.value;
}
});
}
}
}
customElements.define('starlight-select', StarlightLanguageSelect);
</script>
<style>
.desktop {
display: none;
align-items: center;
gap: 1rem;
padding-inline-start: 1rem;
}
@media (min-width: 72rem) {
.desktop {
display: flex;
}
.mobile {
display: none;
}
}
.desktop {
display: none;
align-items: center;
gap: 1rem;
padding-inline-start: 1rem;
}
@media (min-width: 72rem) {
.desktop {
display: flex;
}
.mobile {
display: none;
}
}
a {
color: var(--sl-color-white);
font-weight: 600;
text-decoration: none;
}
a {
color: var(--sl-color-white);
font-weight: 600;
text-decoration: none;
}
/* From https://github.com/withastro/starlight/blob/main/packages/starlight/components/Select.astro */
label {
--sl-label-icon-size: 0.875rem;
--sl-caret-size: 1.25rem;
position: relative;
display: flex;
align-items: center;
gap: 0.25rem;
color: var(--sl-color-gray-1);
}
/* From https://github.com/withastro/starlight/blob/main/packages/starlight/components/Select.astro */
label {
--sl-label-icon-size: 0.875rem;
--sl-caret-size: 1.25rem;
position: relative;
display: flex;
align-items: center;
gap: 0.25rem;
color: var(--sl-color-gray-1);
}
label:hover {
color: var(--sl-color-gray-2);
}
label:hover {
color: var(--sl-color-gray-2);
}
.icon {
position: absolute;
top: 50%;
transform: translateY(-50%);
pointer-events: none;
}
.icon {
position: absolute;
top: 50%;
transform: translateY(-50%);
pointer-events: none;
}
.label-icon {
font-size: var(--sl-label-icon-size);
inset-inline-start: 0;
}
.label-icon {
font-size: var(--sl-label-icon-size);
inset-inline-start: 0;
}
.caret {
font-size: var(--sl-caret-size);
inset-inline-end: 0;
}
.caret {
font-size: var(--sl-caret-size);
inset-inline-end: 0;
}
select {
border: 0;
padding-block: 0.625rem;
padding-inline: calc(var(--sl-label-icon-size) + 0.25rem) calc(var(--sl-caret-size) + 0.25rem);
width: var(--sl-select-width);
background-color: transparent;
text-overflow: ellipsis;
color: inherit;
cursor: pointer;
appearance: none;
}
select {
border: 0;
padding-block: 0.625rem;
padding-inline: calc(var(--sl-label-icon-size) + 0.25rem) calc(var(--sl-caret-size) + 0.25rem);
width: var(--sl-select-width);
background-color: transparent;
text-overflow: ellipsis;
color: inherit;
cursor: pointer;
appearance: none;
}
option {
background-color: var(--sl-color-bg-nav);
color: var(--sl-color-gray-1);
}
option {
background-color: var(--sl-color-bg-nav);
color: var(--sl-color-gray-1);
}
@media (min-width: 50rem) {
select {
font-size: var(--sl-text-sm);
}
}
starlight-select {
margin-left: auto;
}
@media (min-width: 50rem) {
select {
font-size: var(--sl-text-sm);
}
}
starlight-select {
margin-left: auto;
}
</style>

View File

@@ -3,6 +3,6 @@ import { docsSchema, i18nSchema } from '@astrojs/starlight/schema';
import { blogSchema } from 'starlight-blog/schema';
export const collections = {
docs: defineCollection({ schema: docsSchema({ extend: blogSchema() }) }),
i18n: defineCollection({ type: 'data', schema: i18nSchema() }),
docs: defineCollection({ schema: docsSchema({ extend: blogSchema() }) }),
i18n: defineCollection({ type: 'data', schema: i18nSchema() }),
};

View File

@@ -11,19 +11,19 @@ If you're looking for a quick technical overview and to start building an app th
import { LinkCard, CardGrid } from '@astrojs/starlight/components';
<CardGrid>
<LinkCard
title="Tauri Philosophy"
href="/about/philosophy"
description="Learn more about the approach behind Tauri"
/>
<LinkCard
title="Governance"
href="/about/governance"
description="Understand how the Tauri governance structure is setup"
/>
<LinkCard
title="Trademark"
href="/about/trademark"
description="Guidelines for using the Tauri trademark"
/>
<LinkCard
title="Tauri Philosophy"
href="/about/philosophy"
description="Learn more about the approach behind Tauri"
/>
<LinkCard
title="Governance"
href="/about/governance"
description="Understand how the Tauri governance structure is setup"
/>
<LinkCard
title="Trademark"
href="/about/trademark"
description="Guidelines for using the Tauri trademark"
/>
</CardGrid>

View File

@@ -18,10 +18,10 @@ After 113 pull requests and nearly two months of work, the Tauri team is pleased
You can update the dependencies with:
<CommandTabs
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update"
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update"
/>
## What's New in 1.1.0
@@ -79,20 +79,20 @@ In the 1.0 releases Tauri supports the `JSON` configuration format by default, a
```json title=tauri.conf.json
{
"build": {
"devPath": "http://localhost:8000",
"distDir": "../dist"
}
"build": {
"devPath": "http://localhost:8000",
"distDir": "../dist"
}
}
```
```json5
{
build: {
// devServer URL (comments are allowed!)
devPath: 'http://localhost:8000',
distDir: '../dist',
},
build: {
// devServer URL (comments are allowed!)
devPath: 'http://localhost:8000',
distDir: '../dist',
},
}
```

View File

@@ -18,10 +18,10 @@ The Tauri team is happy to announce the 1.2.0 release. It includes a security fi
Make sure to update both NPM and Cargo dependencies to the 1.2.0 release. You can update the dependencies with:
<CommandTabs
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update"
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update"
/>
## What's in 1.2.0

View File

@@ -20,10 +20,10 @@ The Tauri team is excited to announce the 1.3 release. This version includes sec
Make sure to update both NPM and Cargo dependencies to the 1.3.0 release. You can update the dependencies with:
<CommandTabs
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update"
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update"
/>
## What's in 1.3.0

View File

@@ -20,10 +20,10 @@ The Tauri team is excited to announce the 1.4 release. This version includes sev
Make sure to update both NPM and Cargo dependencies to the 1.4.0 release. You can update the dependencies with:
<CommandTabs
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update"
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update"
/>
## What's in 1.4.0

View File

@@ -20,10 +20,10 @@ The Tauri team is excited to announce the 1.5 release. This version includes sev
Make sure to update both NPM and Cargo dependencies to the 1.5.0 release. You can update the dependencies with:
<CommandTabs
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update"
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update"
/>
## What's in 1.5.0

View File

@@ -18,10 +18,10 @@ The Tauri team is happy to announce the 1.6 release. This version includes sever
Make sure to update both NPM and Cargo dependencies to the 1.6.0 release. You can update the dependencies with:
<CommandTabs
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update"
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update"
/>
## What's in 1.6.0

View File

@@ -20,10 +20,10 @@ A new alpha release for the 2.0 has been published. This release includes all ch
Make sure to update both NPM and Cargo dependencies to the latest alpha release. You can update the NPM dependencies with:
<CommandTabs
npm="npm install @tauri-apps/cli@next @tauri-apps/api@next"
yarn="yarn upgrade @tauri-apps/cli@next @tauri-apps/api@next"
pnpm="pnpm update @tauri-apps/cli@next @tauri-apps/api@next"
cargo='cargo add tauri@2.0.0-alpha.4
npm="npm install @tauri-apps/cli@next @tauri-apps/api@next"
yarn="yarn upgrade @tauri-apps/cli@next @tauri-apps/api@next"
pnpm="pnpm update @tauri-apps/cli@next @tauri-apps/api@next"
cargo='cargo add tauri@2.0.0-alpha.4
cargo add tauri-build@2.0.0-alpha.2 --build
cargo install tauri-cli --version "^2.0.0-alpha"'
/>
@@ -120,7 +120,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
```js
import { invoke } from '@tauri-apps/api/tauri';
invoke('plugin:example|ping', { value: 'Tauri' }).then(({ value }) =>
console.log('Response', value)
console.log('Response', value)
);
```

View File

@@ -20,10 +20,10 @@ Tauri mobile is here! The first alpha release 2.0.0-alpha.0 has been published.
Make sure to update both NPM and Cargo dependencies to the 2.0.0-alpha.0 release. You can update the dependencies with:
<CommandTabs
npm="npm install @tauri-apps/cli@next @tauri-apps/api@next"
yarn="yarn upgrade @tauri-apps/cli@next @tauri-apps/api@next"
pnpm="pnpm update @tauri-apps/cli@next @tauri-apps/api@next"
cargo='cargo add tauri@2.0.0-alpha.0
npm="npm install @tauri-apps/cli@next @tauri-apps/api@next"
yarn="yarn upgrade @tauri-apps/cli@next @tauri-apps/api@next"
pnpm="pnpm update @tauri-apps/cli@next @tauri-apps/api@next"
cargo='cargo add tauri@2.0.0-alpha.0
cargo add tauri-build@2.0.0-alpha.0 --build
cargo install tauri-cli --version "^2.0.0-alpha"'
/>

View File

@@ -15,14 +15,14 @@ Inter-Process Communication (IPC) allows isolated processes to communicate secur
Learn more above the specific IPC patterns in the following guides:
<CardGrid>
<LinkCard
title="Brownfield"
href="/concept/inter-process-communication/brownfield/"
/>
<LinkCard
title="Isolation"
href="/concept/inter-process-communication/isolation/"
/>
<LinkCard
title="Brownfield"
href="/concept/inter-process-communication/brownfield/"
/>
<LinkCard
title="Isolation"
href="/concept/inter-process-communication/isolation/"
/>
</CardGrid>
Tauri uses a particular style of Inter-Process Communication called [Asynchronous Message Passing], where processes exchange _requests_ and _responses_ serialized using some simple data representation. Message Passing should sound familiar to anyone with web development experience, as this paradigm is used for client-server communication on the internet.

View File

@@ -7,49 +7,49 @@ import Rater from '@theme/Rater';
import useBaseUrl from '@docusaurus/useBaseUrl';
<div className="row">
<div className="col col--4">
<table>
<tr>
<td>Ease of Use</td>
<td>
<Rater value="3" />
</td>
</tr>
<tr>
<td>Extensibility</td>
<td>
<Rater value="5" />
</td>
</tr>
<tr>
<td>Performance</td>
<td>
<Rater value="4" />
</td>
</tr>
<tr>
<td>Security</td>
<td>
<Rater value="4" />
</td>
</tr>
</table>
</div>
<div className="col col--4 pattern-logo">
<img src={useBaseUrl('img/recipes/Bridge.svg')} alt="Bridge" />
</div>
<div className="col col--4">
Pros:
<ul>
<li>Highly configurable</li>
<li>No Rust skills required</li>
</ul>
Cons:
<ul>
<li>Some WebAPIs unavailable</li>
<li>Challenge to implement</li>
</ul>
</div>
<div className="col col--4">
<table>
<tr>
<td>Ease of Use</td>
<td>
<Rater value="3" />
</td>
</tr>
<tr>
<td>Extensibility</td>
<td>
<Rater value="5" />
</td>
</tr>
<tr>
<td>Performance</td>
<td>
<Rater value="4" />
</td>
</tr>
<tr>
<td>Security</td>
<td>
<Rater value="4" />
</td>
</tr>
</table>
</div>
<div className="col col--4 pattern-logo">
<img src={useBaseUrl('img/recipes/Bridge.svg')} alt="Bridge" />
</div>
<div className="col col--4">
Pros:
<ul>
<li>Highly configurable</li>
<li>No Rust skills required</li>
</ul>
Cons:
<ul>
<li>Some WebAPIs unavailable</li>
<li>Challenge to implement</li>
</ul>
</div>
</div>
## Description
@@ -93,97 +93,97 @@ Here's what you need to add to your tauri.conf.json file:
```json
{
"tauri": {
"allowlist": {
"all": false,
"clipboard": {
"all": false,
"readText": false,
"writeText": false
},
"dialog": {
"all": false,
"ask": false,
"confirm": false,
"message": false,
"open": false,
"save": false
},
"fs": {
"all": false,
"copyFile": false,
"createDir": false,
"readDir": false,
"readFile": false,
"removeDir": false,
"removeFile": false,
"renameFile": false,
"scope": [],
"writeFile": false
},
"globalShortcut": {
"all": false
},
"http": {
"all": false,
"request": false,
"scope": []
},
"notification": {
"all": false
},
"os": {
"all": false
},
"path": {
"all": false
},
"process": {
"all": false,
"exit": false,
"relaunch": false,
"relaunchDangerousAllowSymlinkMacos": false
},
"protocol": {
"all": false,
"asset": false,
"assetScope": []
},
"shell": {
"all": false,
"execute": false,
"open": false,
"scope": [],
"sidecar": false
},
"window": {
"all": false,
"center": false,
"close": false,
"create": false,
"hide": false,
"maximize": false,
"minimize": false,
"print": false,
"requestUserAttention": false,
"setAlwaysOnTop": false,
"setDecorations": false,
"setFocus": false,
"setFullscreen": false,
"setIcon": false,
"setMaxSize": false,
"setMinSize": false,
"setPosition": false,
"setResizable": false,
"setSize": false,
"setSkipTaskbar": false,
"setTitle": false,
"show": false,
"startDragging": false,
"unmaximize": false,
"unminimize": false
}
}
}
"tauri": {
"allowlist": {
"all": false,
"clipboard": {
"all": false,
"readText": false,
"writeText": false
},
"dialog": {
"all": false,
"ask": false,
"confirm": false,
"message": false,
"open": false,
"save": false
},
"fs": {
"all": false,
"copyFile": false,
"createDir": false,
"readDir": false,
"readFile": false,
"removeDir": false,
"removeFile": false,
"renameFile": false,
"scope": [],
"writeFile": false
},
"globalShortcut": {
"all": false
},
"http": {
"all": false,
"request": false,
"scope": []
},
"notification": {
"all": false
},
"os": {
"all": false
},
"path": {
"all": false
},
"process": {
"all": false,
"exit": false,
"relaunch": false,
"relaunchDangerousAllowSymlinkMacos": false
},
"protocol": {
"all": false,
"asset": false,
"assetScope": []
},
"shell": {
"all": false,
"execute": false,
"open": false,
"scope": [],
"sidecar": false
},
"window": {
"all": false,
"center": false,
"close": false,
"create": false,
"hide": false,
"maximize": false,
"minimize": false,
"print": false,
"requestUserAttention": false,
"setAlwaysOnTop": false,
"setDecorations": false,
"setFocus": false,
"setFullscreen": false,
"setIcon": false,
"setMaxSize": false,
"setMinSize": false,
"setPosition": false,
"setResizable": false,
"setSize": false,
"setSkipTaskbar": false,
"setTitle": false,
"show": false,
"startDragging": false,
"unmaximize": false,
"unminimize": false
}
}
}
}
```

View File

@@ -7,49 +7,49 @@ import Rater from '@theme/Rater';
import useBaseUrl from '@docusaurus/useBaseUrl';
<div className="row">
<div className="col col--4">
<table>
<tr>
<td>Ease of Use</td>
<td>
<Rater value="1" />
</td>
</tr>
<tr>
<td>Extensibility</td>
<td>
<Rater value="5" />
</td>
</tr>
<tr>
<td>Performance</td>
<td>
<Rater value="3" />
</td>
</tr>
<tr>
<td>Security</td>
<td>
<Rater value="2" />
</td>
</tr>
</table>
</div>
<div className="col col--4 pattern-logo">
<img src={useBaseUrl('img/recipes/Cloudbridge.svg')} alt="Cloudbridge" />
</div>
<div className="col col--4">
Pros:
<ul>
<li>All available features</li>
<li>No Rust skills required</li>
</ul>
Cons:
<ul>
<li>Largest bundle size</li>
<li>Hard to separate concerns</li>
</ul>
</div>
<div className="col col--4">
<table>
<tr>
<td>Ease of Use</td>
<td>
<Rater value="1" />
</td>
</tr>
<tr>
<td>Extensibility</td>
<td>
<Rater value="5" />
</td>
</tr>
<tr>
<td>Performance</td>
<td>
<Rater value="3" />
</td>
</tr>
<tr>
<td>Security</td>
<td>
<Rater value="2" />
</td>
</tr>
</table>
</div>
<div className="col col--4 pattern-logo">
<img src={useBaseUrl('img/recipes/Cloudbridge.svg')} alt="Cloudbridge" />
</div>
<div className="col col--4">
Pros:
<ul>
<li>All available features</li>
<li>No Rust skills required</li>
</ul>
Cons:
<ul>
<li>Largest bundle size</li>
<li>Hard to separate concerns</li>
</ul>
</div>
</div>
## Description

View File

@@ -7,49 +7,49 @@ import Rater from '@theme/Rater';
import useBaseUrl from '@docusaurus/useBaseUrl';
<div className="row">
<div className="col col--4">
<table>
<tr>
<td>Ease of Use</td>
<td>
<Rater value="5" />
</td>
</tr>
<tr>
<td>Extensibility</td>
<td>
<Rater value="3" />
</td>
</tr>
<tr>
<td>Performance</td>
<td>
<Rater value="3" />
</td>
</tr>
<tr>
<td>Security</td>
<td>
<Rater value="2" />
</td>
</tr>
</table>
</div>
<div className="col col--4 pattern-logo">
<img src={useBaseUrl('img/recipes/Cloudish.svg')} alt="Cloudish" />
</div>
<div className="col col--4">
Pros:
<ul>
<li>Similar to a SPA web-app</li>
<li>No Rust skills required</li>
</ul>
Cons:
<ul>
<li>No access to Rust API</li>
<li>Uses a localhost server</li>
</ul>
</div>
<div className="col col--4">
<table>
<tr>
<td>Ease of Use</td>
<td>
<Rater value="5" />
</td>
</tr>
<tr>
<td>Extensibility</td>
<td>
<Rater value="3" />
</td>
</tr>
<tr>
<td>Performance</td>
<td>
<Rater value="3" />
</td>
</tr>
<tr>
<td>Security</td>
<td>
<Rater value="2" />
</td>
</tr>
</table>
</div>
<div className="col col--4 pattern-logo">
<img src={useBaseUrl('img/recipes/Cloudish.svg')} alt="Cloudish" />
</div>
<div className="col col--4">
Pros:
<ul>
<li>Similar to a SPA web-app</li>
<li>No Rust skills required</li>
</ul>
Cons:
<ul>
<li>No access to Rust API</li>
<li>Uses a localhost server</li>
</ul>
</div>
</div>
## Description

View File

@@ -12,48 +12,48 @@ This pattern is not available for now.
import Rater from '@theme/Rater';
<div className="row">
<div className="col col--4">
<table>
<tr>
<td>Ease of Use</td>
<td>
<Rater value="0" />
</td>
</tr>
<tr>
<td>Extensibility</td>
<td>
<Rater value="0" />
</td>
</tr>
<tr>
<td>Performance</td>
<td>
<Rater value="5" />
</td>
</tr>
<tr>
<td>Security</td>
<td>
<Rater value="0" />
</td>
</tr>
</table>
</div>
<div className="col col--4 pattern-logo">
<img src={useBaseUrl('img/recipes/GLUI.svg')} alt="GLUI" />
</div>
<div className="col col--4">
Pros:
<ul>
<li>Framebuffer FTW</li>
<li>Window events rigged</li>
</ul>
Cons:
<ul>
<li>Broken on your machine</li>
</ul>
</div>
<div className="col col--4">
<table>
<tr>
<td>Ease of Use</td>
<td>
<Rater value="0" />
</td>
</tr>
<tr>
<td>Extensibility</td>
<td>
<Rater value="0" />
</td>
</tr>
<tr>
<td>Performance</td>
<td>
<Rater value="5" />
</td>
</tr>
<tr>
<td>Security</td>
<td>
<Rater value="0" />
</td>
</tr>
</table>
</div>
<div className="col col--4 pattern-logo">
<img src={useBaseUrl('img/recipes/GLUI.svg')} alt="GLUI" />
</div>
<div className="col col--4">
Pros:
<ul>
<li>Framebuffer FTW</li>
<li>Window events rigged</li>
</ul>
Cons:
<ul>
<li>Broken on your machine</li>
</ul>
</div>
</div>
## Description

View File

@@ -7,49 +7,49 @@ import Rater from '@theme/Rater';
import useBaseUrl from '@docusaurus/useBaseUrl';
<div className="row">
<div className="col col--4">
<table>
<tr>
<td>Ease of Use</td>
<td>
<Rater value="5" />
</td>
</tr>
<tr>
<td>Extensibility</td>
<td>
<Rater value="0" />
</td>
</tr>
<tr>
<td>Performance</td>
<td>
<Rater value="5" />
</td>
</tr>
<tr>
<td>Security</td>
<td>
<Rater value="5" />
</td>
</tr>
</table>
</div>
<div className="col col--4 pattern-logo">
<img src={useBaseUrl('img/recipes/Hermit.svg')} alt="Hermit" />
</div>
<div className="col col--4">
Pros:
<ul>
<li>Quick to make</li>
<li>Smallest size</li>
</ul>
Cons:
<ul>
<li>No remote resources</li>
<li>No access to API</li>
</ul>
</div>
<div className="col col--4">
<table>
<tr>
<td>Ease of Use</td>
<td>
<Rater value="5" />
</td>
</tr>
<tr>
<td>Extensibility</td>
<td>
<Rater value="0" />
</td>
</tr>
<tr>
<td>Performance</td>
<td>
<Rater value="5" />
</td>
</tr>
<tr>
<td>Security</td>
<td>
<Rater value="5" />
</td>
</tr>
</table>
</div>
<div className="col col--4 pattern-logo">
<img src={useBaseUrl('img/recipes/Hermit.svg')} alt="Hermit" />
</div>
<div className="col col--4">
Pros:
<ul>
<li>Quick to make</li>
<li>Smallest size</li>
</ul>
Cons:
<ul>
<li>No remote resources</li>
<li>No access to API</li>
</ul>
</div>
</div>
## Description

View File

@@ -7,49 +7,49 @@ import Rater from '@theme/Rater';
import useBaseUrl from '@docusaurus/useBaseUrl';
<div className="row">
<div className="col col--4">
<table>
<tr>
<td>Ease of Use</td>
<td>
<Rater value="2" />
</td>
</tr>
<tr>
<td>Extensibility</td>
<td>
<Rater value="4" />
</td>
</tr>
<tr>
<td>Performance</td>
<td>
<Rater value="5" />
</td>
</tr>
<tr>
<td>Security</td>
<td>
<Rater value="5" />
</td>
</tr>
</table>
</div>
<div className="col col--4 pattern-logo">
<img src={useBaseUrl('img/recipes/Lockdown.svg')} alt="Lockdown" />
</div>
<div className="col col--4">
Pros:
<ul>
<li>Highest security rating</li>
<li>Elegant and powerful</li>
</ul>
Cons:
<ul>
<li>Rust skills required</li>
<li>No remote resources</li>
</ul>
</div>
<div className="col col--4">
<table>
<tr>
<td>Ease of Use</td>
<td>
<Rater value="2" />
</td>
</tr>
<tr>
<td>Extensibility</td>
<td>
<Rater value="4" />
</td>
</tr>
<tr>
<td>Performance</td>
<td>
<Rater value="5" />
</td>
</tr>
<tr>
<td>Security</td>
<td>
<Rater value="5" />
</td>
</tr>
</table>
</div>
<div className="col col--4 pattern-logo">
<img src={useBaseUrl('img/recipes/Lockdown.svg')} alt="Lockdown" />
</div>
<div className="col col--4">
Pros:
<ul>
<li>Highest security rating</li>
<li>Elegant and powerful</li>
</ul>
Cons:
<ul>
<li>Rust skills required</li>
<li>No remote resources</li>
</ul>
</div>
</div>
## Description

View File

@@ -7,48 +7,48 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
import Rater from '@theme/Rater';
<div className="row">
<div className="col col--4">
<table>
<tr>
<td>Ease of Use</td>
<td>
<Rater value="4" />
</td>
</tr>
<tr>
<td>Extensibility</td>
<td>
<Rater value="4" />
</td>
</tr>
<tr>
<td>Performance</td>
<td>
<Rater value="3" />
</td>
</tr>
<tr>
<td>Security</td>
<td>
<Rater value="5" />
</td>
</tr>
</table>
</div>
<div className="col col--4 pattern-logo">
<img src={useBaseUrl('img/recipes/Multiwin.svg')} alt="Multiwin" />
</div>
<div className="col col--4">
Pros:
<ul>
<li>Windows can be spawned or destroyed at runtime</li>
<li>Separation of concerns</li>
</ul>
Cons:
<ul>
<li>Somewhat complex</li>
</ul>
</div>
<div className="col col--4">
<table>
<tr>
<td>Ease of Use</td>
<td>
<Rater value="4" />
</td>
</tr>
<tr>
<td>Extensibility</td>
<td>
<Rater value="4" />
</td>
</tr>
<tr>
<td>Performance</td>
<td>
<Rater value="3" />
</td>
</tr>
<tr>
<td>Security</td>
<td>
<Rater value="5" />
</td>
</tr>
</table>
</div>
<div className="col col--4 pattern-logo">
<img src={useBaseUrl('img/recipes/Multiwin.svg')} alt="Multiwin" />
</div>
<div className="col col--4">
Pros:
<ul>
<li>Windows can be spawned or destroyed at runtime</li>
<li>Separation of concerns</li>
</ul>
Cons:
<ul>
<li>Somewhat complex</li>
</ul>
</div>
</div>
## Description

View File

@@ -13,34 +13,34 @@ import { CardGrid, LinkCard } from '@astrojs/starlight/components';
Tauri has a variety of topics that are considered to be core concepts, things any developer should be aware of when developing their applications. Here's a variety of topics that you should get more intimately familiar with if you want to get the most out of the framework.
<CardGrid>
<LinkCard
title="Tauri Architecture"
href="/concept/architecture/"
description="Architecture and ecosystem."
/>
<LinkCard
title="Inter-Process Communication (IPC)"
href="/concept/inter-process-communication/"
description="The inner workings on the IPC."
/>
<LinkCard
title="Security"
href="/security"
description="How Tauri enforces security practices."
/>
<LinkCard
title="Process Model"
href="/concept/process-model/"
description="Which processes Tauri manages and why."
/>
<LinkCard
title="App Size"
href="/concept/size/"
description="How to make your app as small as possible."
/>
<LinkCard
title="Rendering"
href="/concept/rendering/"
description="What a renderer is in Tauri."
/>
<LinkCard
title="Tauri Architecture"
href="/concept/architecture/"
description="Architecture and ecosystem."
/>
<LinkCard
title="Inter-Process Communication (IPC)"
href="/concept/inter-process-communication/"
description="The inner workings on the IPC."
/>
<LinkCard
title="Security"
href="/security"
description="How Tauri enforces security practices."
/>
<LinkCard
title="Process Model"
href="/concept/process-model/"
description="Which processes Tauri manages and why."
/>
<LinkCard
title="App Size"
href="/concept/size/"
description="How to make your app as small as possible."
/>
<LinkCard
title="Rendering"
href="/concept/rendering/"
description="What a renderer is in Tauri."
/>
</CardGrid>

View File

@@ -106,10 +106,10 @@ By default, the inspector is only enabled in development and debug builds unless
To create a debug build, run the `tauri build --debug` command.
<CommandTabs
npm="npm run tauri build -- --debug"
yarn="yarn tauri build --debug"
pnpm="pnpm tauri build --debug"
cargo="cargo tauri build --debug"
npm="npm run tauri build -- --debug"
yarn="yarn tauri build --debug"
pnpm="pnpm tauri build --debug"
cargo="cargo tauri build --debug"
/>
Like the normal build and dev processes, building takes some time the first time you run this command but is significantly faster on subsequent runs.

View File

@@ -23,36 +23,36 @@ Create a `.vscode/launch.json` file and paste the below JSON contents into it:
```json title=".vscode/launch.json"
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Tauri Development Debug",
"cargo": {
"args": [
"build",
"--manifest-path=./src-tauri/Cargo.toml",
"--no-default-features"
]
},
// task for the `beforeDevCommand` if used, must be configured in `.vscode/tasks.json`
"preLaunchTask": "ui:dev"
},
{
"type": "lldb",
"request": "launch",
"name": "Tauri Production Debug",
"cargo": {
"args": ["build", "--release", "--manifest-path=./src-tauri/Cargo.toml"]
},
// task for the `beforeBuildCommand` if used, must be configured in `.vscode/tasks.json`
"preLaunchTask": "ui:build"
}
]
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Tauri Development Debug",
"cargo": {
"args": [
"build",
"--manifest-path=./src-tauri/Cargo.toml",
"--no-default-features"
]
},
// task for the `beforeDevCommand` if used, must be configured in `.vscode/tasks.json`
"preLaunchTask": "ui:dev"
},
{
"type": "lldb",
"request": "launch",
"name": "Tauri Production Debug",
"cargo": {
"args": ["build", "--release", "--manifest-path=./src-tauri/Cargo.toml"]
},
// task for the `beforeBuildCommand` if used, must be configured in `.vscode/tasks.json`
"preLaunchTask": "ui:build"
}
]
}
```
@@ -62,29 +62,29 @@ Note that it does not use the Tauri CLI, so exclusive CLI features are not execu
```json title=".vscode/tasks.json"
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "ui:dev",
"type": "shell",
// `dev` keeps running in the background
// ideally you should also configure a `problemMatcher`
// see https://code.visualstudio.com/docs/editor/tasks#_can-a-background-task-be-used-as-a-prelaunchtask-in-launchjson
"isBackground": true,
// change this to your `beforeDevCommand`:
"command": "yarn",
"args": ["dev"]
},
{
"label": "ui:build",
"type": "shell",
// change this to your `beforeBuildCommand`:
"command": "yarn",
"args": ["build"]
}
]
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "ui:dev",
"type": "shell",
// `dev` keeps running in the background
// ideally you should also configure a `problemMatcher`
// see https://code.visualstudio.com/docs/editor/tasks#_can-a-background-task-be-used-as-a-prelaunchtask-in-launchjson
"isBackground": true,
// change this to your `beforeDevCommand`:
"command": "yarn",
"args": ["dev"]
},
{
"label": "ui:build",
"type": "shell",
// change this to your `beforeBuildCommand`:
"command": "yarn",
"args": ["build"]
}
]
}
```

View File

@@ -41,10 +41,10 @@ To bootstrap a new plugin project, run `plugin new`. If you do not need the NPM
After installing, you can run the following to create a plugin project:
<CommandTabs
npm="npm run tauri plugin new [name]"
yarn="yarn tauri plugin new [name]"
pnpm="pnpm tauri plugin new [name]"
cargo="cargo tauri plugin new [name]"
npm="npm run tauri plugin new [name]"
yarn="yarn tauri plugin new [name]"
pnpm="pnpm tauri plugin new [name]"
cargo="cargo tauri plugin new [name]"
/>
This will initialize the plugin at the directory `tauri-plugin-[name]` and, depending on the used CLI flags, the resulting project will look like this:

View File

@@ -41,30 +41,30 @@ First, let's create our Tauri `distDir` that we know we will need once building
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Hello Tauri!</title>
<style>
body {
/* Add a nice colorscheme */
background-color: #222831;
color: #ececec;
<head>
<meta charset="UTF-8" />
<title>Hello Tauri!</title>
<style>
body {
/* Add a nice colorscheme */
background-color: #222831;
color: #ececec;
/* Make the body the exact size of the window */
margin: 0;
height: 100vh;
width: 100vw;
/* Make the body the exact size of the window */
margin: 0;
height: 100vh;
width: 100vw;
/* Vertically and horizontally center children of the body tag */
display: flex;
justify-content: center;
align-items: center;
}
</style>
</head>
<body>
<h1>Hello, Tauri!</h1>
</body>
/* Vertically and horizontally center children of the body tag */
display: flex;
justify-content: center;
align-items: center;
}
</style>
</head>
<body>
<h1>Hello, Tauri!</h1>
</body>
</html>
```
@@ -150,26 +150,26 @@ here.
```json
{
"build": {
"distDir": "dist"
},
"tauri": {
"bundle": {
"identifier": "studio.tauri.hello_tauri_webdriver",
"icon": ["icon.png"]
},
"allowlist": {
"all": false
},
"windows": [
{
"width": 800,
"height": 600,
"resizable": true,
"fullscreen": false
}
]
}
"build": {
"distDir": "dist"
},
"tauri": {
"bundle": {
"identifier": "studio.tauri.hello_tauri_webdriver",
"icon": ["icon.png"]
},
"allowlist": {
"all": false
},
"windows": [
{
"width": 800,
"height": 600,
"resizable": true,
"fullscreen": false
}
]
}
}
```
@@ -187,7 +187,7 @@ will also run our WebDriver tests with a release profile. Run `cargo run --relea
see the following application pop up.
<div style={{ textAlign: 'center' }}>
<Image src={HelloTauriWebdriver} alt="Hello Tauri Webdriver" />
<Image src={HelloTauriWebdriver} alt="Hello Tauri Webdriver" />
</div>
_Note: If you are modifying the application and want to use the Devtools, then run it without `--release` and "Inspect

View File

@@ -38,17 +38,17 @@ guide on how to set it up from scratch.
```json
{
"name": "selenium",
"version": "1.0.0",
"private": true,
"scripts": {
"test": "mocha"
},
"dependencies": {
"chai": "^4.3.4",
"mocha": "^9.0.3",
"selenium-webdriver": "^4.0.0-beta.4"
}
"name": "selenium",
"version": "1.0.0",
"private": true,
"scripts": {
"test": "mocha"
},
"dependencies": {
"chai": "^4.3.4",
"mocha": "^9.0.3",
"selenium-webdriver": "^4.0.0-beta.4"
}
}
```
@@ -62,8 +62,8 @@ that we will be using to run the tests. [Mocha] as the testing framework, [Chai]
If you want to install the dependencies from scratch, just run the following command.
<CommandTabs
npm="npm install mocha chai selenium-webdriver"
yarn="yarn add mocha chai selenium-webdriver"
npm="npm install mocha chai selenium-webdriver"
yarn="yarn add mocha chai selenium-webdriver"
/>
I suggest also adding a `"test": "mocha"` item in the `package.json` `"scripts"` key so that running Mocha can be called
@@ -90,13 +90,13 @@ const { Builder, By, Capabilities } = require('selenium-webdriver');
// create the path to the expected application binary
const application = path.resolve(
__dirname,
'..',
'..',
'..',
'target',
'release',
'hello-tauri-webdriver'
__dirname,
'..',
'..',
'..',
'target',
'release',
'hello-tauri-webdriver'
);
// keep track of the webdriver instance we create
@@ -106,61 +106,61 @@ let driver;
let tauriDriver;
before(async function () {
// set timeout to 2 minutes to allow the program to build if it needs to
this.timeout(120000);
// set timeout to 2 minutes to allow the program to build if it needs to
this.timeout(120000);
// ensure the program has been built
spawnSync('cargo', ['build', '--release']);
// ensure the program has been built
spawnSync('cargo', ['build', '--release']);
// start tauri-driver
tauriDriver = spawn(
path.resolve(os.homedir(), '.cargo', 'bin', 'tauri-driver'),
[],
{ stdio: [null, process.stdout, process.stderr] }
);
// start tauri-driver
tauriDriver = spawn(
path.resolve(os.homedir(), '.cargo', 'bin', 'tauri-driver'),
[],
{ stdio: [null, process.stdout, process.stderr] }
);
const capabilities = new Capabilities();
capabilities.set('tauri:options', { application });
capabilities.setBrowserName('wry');
const capabilities = new Capabilities();
capabilities.set('tauri:options', { application });
capabilities.setBrowserName('wry');
// start the webdriver client
driver = await new Builder()
.withCapabilities(capabilities)
.usingServer('http://127.0.0.1:4444/')
.build();
// start the webdriver client
driver = await new Builder()
.withCapabilities(capabilities)
.usingServer('http://127.0.0.1:4444/')
.build();
});
after(async function () {
// stop the webdriver session
await driver.quit();
// stop the webdriver session
await driver.quit();
// kill the tauri-driver process
tauriDriver.kill();
// kill the tauri-driver process
tauriDriver.kill();
});
describe('Hello Tauri', () => {
it('should be cordial', async () => {
const text = await driver.findElement(By.css('body > h1')).getText();
expect(text).to.match(/^[hH]ello/);
});
it('should be cordial', async () => {
const text = await driver.findElement(By.css('body > h1')).getText();
expect(text).to.match(/^[hH]ello/);
});
it('should be excited', async () => {
const text = await driver.findElement(By.css('body > h1')).getText();
expect(text).to.match(/!$/);
});
it('should be excited', async () => {
const text = await driver.findElement(By.css('body > h1')).getText();
expect(text).to.match(/!$/);
});
it('should be easy on the eyes', async () => {
// selenium returns color css values as rgb(r, g, b)
const text = await driver
.findElement(By.css('body'))
.getCssValue('background-color');
it('should be easy on the eyes', async () => {
// selenium returns color css values as rgb(r, g, b)
const text = await driver
.findElement(By.css('body'))
.getCssValue('background-color');
const rgb = text.match(/^rgb\((?<r>\d+), (?<g>\d+), (?<b>\d+)\)$/).groups;
expect(rgb).to.have.all.keys('r', 'g', 'b');
const rgb = text.match(/^rgb\((?<r>\d+), (?<g>\d+), (?<b>\d+)\)$/).groups;
expect(rgb).to.have.all.keys('r', 'g', 'b');
const luma = 0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b;
expect(luma).to.be.lessThan(100);
});
const luma = 0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b;
expect(luma).to.be.lessThan(100);
});
});
```

View File

@@ -38,20 +38,20 @@ guide on setting it up from scratch.
```json
{
"name": "webdriverio",
"version": "1.0.0",
"private": true,
"scripts": {
"test": "wdio run wdio.conf.js"
},
"dependencies": {
"@wdio/cli": "^7.9.1"
},
"devDependencies": {
"@wdio/local-runner": "^7.9.1",
"@wdio/mocha-framework": "^7.9.1",
"@wdio/spec-reporter": "^7.9.0"
}
"name": "webdriverio",
"version": "1.0.0",
"private": true,
"scripts": {
"test": "wdio run wdio.conf.js"
},
"dependencies": {
"@wdio/cli": "^7.9.1"
},
"devDependencies": {
"@wdio/local-runner": "^7.9.1",
"@wdio/mocha-framework": "^7.9.1",
"@wdio/spec-reporter": "^7.9.0"
}
}
```
@@ -91,36 +91,36 @@ const { spawn, spawnSync } = require('child_process');
let tauriDriver;
exports.config = {
specs: ['./develop/tests/specs/**/*.js'],
maxInstances: 1,
capabilities: [
{
maxInstances: 1,
'tauri:options': {
application: '../../target/release/hello-tauri-webdriver',
},
},
],
reporters: ['spec'],
framework: 'mocha',
mochaOpts: {
ui: 'bdd',
timeout: 60000,
},
specs: ['./develop/tests/specs/**/*.js'],
maxInstances: 1,
capabilities: [
{
maxInstances: 1,
'tauri:options': {
application: '../../target/release/hello-tauri-webdriver',
},
},
],
reporters: ['spec'],
framework: 'mocha',
mochaOpts: {
ui: 'bdd',
timeout: 60000,
},
// ensure the rust project is built since we expect this binary to exist for the webdriver sessions
onPrepare: () => spawnSync('cargo', ['build', '--release']),
// ensure the rust project is built since we expect this binary to exist for the webdriver sessions
onPrepare: () => spawnSync('cargo', ['build', '--release']),
// ensure we are running `tauri-driver` before the session starts so that we can proxy the webdriver requests
beforeSession: () =>
(tauriDriver = spawn(
path.resolve(os.homedir(), '.cargo', 'bin', 'tauri-driver'),
[],
{ stdio: [null, process.stdout, process.stderr] }
)),
// ensure we are running `tauri-driver` before the session starts so that we can proxy the webdriver requests
beforeSession: () =>
(tauriDriver = spawn(
path.resolve(os.homedir(), '.cargo', 'bin', 'tauri-driver'),
[],
{ stdio: [null, process.stdout, process.stderr] }
)),
// clean up the `tauri-driver` process we spawned at the start of the session
afterSession: () => tauriDriver.kill(),
// clean up the `tauri-driver` process we spawned at the start of the session
afterSession: () => tauriDriver.kill(),
};
```
@@ -138,35 +138,35 @@ run them as it sees fit. Let's create our spec now in the directory we specified
```js
// calculates the luma from a hex color `#abcdef`
function luma(hex) {
if (hex.startsWith('#')) {
hex = hex.substring(1);
}
if (hex.startsWith('#')) {
hex = hex.substring(1);
}
const rgb = parseInt(hex, 16);
const r = (rgb >> 16) & 0xff;
const g = (rgb >> 8) & 0xff;
const b = (rgb >> 0) & 0xff;
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
const rgb = parseInt(hex, 16);
const r = (rgb >> 16) & 0xff;
const g = (rgb >> 8) & 0xff;
const b = (rgb >> 0) & 0xff;
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
describe('Hello Tauri', () => {
it('should be cordial', async () => {
const header = await $('body > h1');
const text = await header.getText();
expect(text).toMatch(/^[hH]ello/);
});
it('should be cordial', async () => {
const header = await $('body > h1');
const text = await header.getText();
expect(text).toMatch(/^[hH]ello/);
});
it('should be excited', async () => {
const header = await $('body > h1');
const text = await header.getText();
expect(text).toMatch(/!$/);
});
it('should be excited', async () => {
const header = await $('body > h1');
const text = await header.getText();
expect(text).toMatch(/!$/);
});
it('should be easy on the eyes', async () => {
const body = await $('body');
const backgroundColor = await body.getCSSProperty('background-color');
expect(luma(backgroundColor.parsed.hex)).toBeLessThan(100);
});
it('should be easy on the eyes', async () => {
const body = await $('body');
const backgroundColor = await body.getCSSProperty('background-color');
expect(luma(backgroundColor.parsed.hex)).toBeLessThan(100);
});
});
```

View File

@@ -60,15 +60,15 @@ can look at https://github.com/chippers/hello_tauri.
import { LinkCard, CardGrid } from '@astrojs/starlight/components';
<CardGrid>
<LinkCard title="Setup" href="/start/develop/tests/webdriver/example/" />
<LinkCard
title="Selenium"
href="/start/develop/tests/webdriver/example/selenium"
/>
<LinkCard
title="WebdriverIO"
href="/start/develop/tests/webdriver/example/webdriverio"
/>
<LinkCard title="Setup" href="/start/develop/tests/webdriver/example/" />
<LinkCard
title="Selenium"
href="/start/develop/tests/webdriver/example/selenium"
/>
<LinkCard
title="WebdriverIO"
href="/start/develop/tests/webdriver/example/webdriverio"
/>
</CardGrid>
## Continuous Integration (CI)
@@ -76,8 +76,8 @@ import { LinkCard, CardGrid } from '@astrojs/starlight/components';
The above examples also comes with a CI script to test with GitHub actions, but you may still be interested in the below WebDriver CI guide as it explains the concept a bit more.
<LinkCard
title="Continuous Integration (CI)"
href="/start/develop/tests/webdriver/ci"
title="Continuous Integration (CI)"
href="/start/develop/tests/webdriver/ci"
/>
[webdriver]: https://www.w3.org/TR/webdriver/

View File

@@ -119,8 +119,8 @@ If the command returns an error, the promise will reject, otherwise, it resolves
```js
invoke('my_custom_command')
.then((message) => console.log(message))
.catch((error) => console.error(error));
.then((message) => console.log(message))
.catch((error) => console.error(error));
```
As mentioned above, everything returned from commands must implement [`serde::Serialize`], including errors.
@@ -231,7 +231,7 @@ Since invoking the command from JavaScript already returns a promise, it works j
```js
invoke('my_custom_command', { value: 'Hello, Async!' }).then(() =>
console.log('Completed!')
console.log('Completed!')
);
```
@@ -379,12 +379,12 @@ import { invoke } from '@tauri-apps/api/core';
// Invocation from JavaScript
invoke('my_custom_command', {
number: 42,
number: 42,
})
.then((res) =>
console.log(`Message: ${res.message}, Other Val: ${res.other_val}`)
)
.catch((e) => console.error(e));
.then((res) =>
console.log(`Message: ${res.message}, Other Val: ${res.other_val}`)
)
.catch((e) => console.error(e));
```
[`async_runtime::spawn`]: https://docs.rs/tauri/2.0.0-beta/tauri/async_runtime/fn.spawn.html

View File

@@ -24,10 +24,10 @@ Every framework has its own development tooling. It is outside of the scope of t
### 2. Start Tauri Development Window
<CommandTabs
npm="npm run tauri dev"
yarn="yarn tauri dev"
pnpm="pnpm tauri dev"
cargo="cargo tauri dev"
npm="npm run tauri dev"
yarn="yarn tauri dev"
pnpm="pnpm tauri dev"
cargo="cargo tauri dev"
/>
The first time you run this command, the Rust package manager takes several minutes to download and build all the required packages. Since they are cached, subsequent builds are much faster, as only your code needs rebuilding.

View File

@@ -17,15 +17,15 @@ Here is a sample to illustrate the configuration. This is not a complete `tauri.
```json title="src-tauri/tauri.conf.json"
{
"tauri": {
"bundle": {
"externalBin": [
"/absolute/path/to/sidecar",
"relative/path/to/binary",
"binaries/my-sidecar"
]
}
}
"tauri": {
"bundle": {
"externalBin": [
"/absolute/path/to/sidecar",
"relative/path/to/binary",
"binaries/my-sidecar"
]
}
}
}
```
@@ -59,23 +59,23 @@ const fs = require('fs');
let extension = '';
if (process.platform === 'win32') {
extension = '.exe';
extension = '.exe';
}
async function main() {
const rustInfo = (await execa('rustc', ['-vV'])).stdout;
const targetTriple = /host: (\S+)/g.exec(rustInfo)[1];
if (!targetTriple) {
console.error('Failed to determine platform target triple');
}
fs.renameSync(
`src-tauri/binaries/sidecar${extension}`,
`src-tauri/binaries/sidecar-${targetTriple}${extension}`
);
const rustInfo = (await execa('rustc', ['-vV'])).stdout;
const targetTriple = /host: (\S+)/g.exec(rustInfo)[1];
if (!targetTriple) {
console.error('Failed to determine platform target triple');
}
fs.renameSync(
`src-tauri/binaries/sidecar${extension}`,
`src-tauri/binaries/sidecar-${targetTriple}${extension}`
);
}
main().catch((e) => {
throw e;
throw e;
});
```
@@ -130,38 +130,38 @@ First, define the arguments that need to be passed to the sidecar command in `sr
```json title="src-tauri/capabilities/main.json" ins={14-31}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"path:default",
"event:default",
"window:default",
"app:default",
"resources:default",
"menu:default",
"tray:default",
{
"identifier": "shell:allow-execute",
"allow": [
{
"args": [
"arg1",
"-a",
"--arg2",
{
"validator": "\\S+"
}
],
"cmd": "",
"name": "binaries/my-sidecar",
"sidecar": true
}
]
},
"shell:allow-open"
]
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"path:default",
"event:default",
"window:default",
"app:default",
"resources:default",
"menu:default",
"tray:default",
{
"identifier": "shell:allow-execute",
"allow": [
{
"args": [
"arg1",
"-a",
"--arg2",
{
"validator": "\\S+"
}
],
"cmd": "",
"name": "binaries/my-sidecar",
"sidecar": true
}
]
},
"shell:allow-open"
]
}
```
@@ -192,10 +192,10 @@ import { Command } from '@tauri-apps/plugin-shell';
// `binaries/my-sidecar` is the EXACT value specified on `tauri.conf.json > tauri > bundle > externalBin`
// notice that the args array matches EXACTLY what is specified on `tauri.conf.json`.
const command = Command.sidecar('binaries/my-sidecar', [
'arg1',
'-a',
'--arg2',
'any-string-that-matches-the-validator',
'arg1',
'-a',
'--arg2',
'any-string-that-matches-the-validator',
]);
const output = await command.execute();
```

View File

@@ -16,25 +16,25 @@ import CommandTabs from '@components/CommandTabs.astro';
If you are using the `tauri` package:
<CommandTabs
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn up @tauri-apps/cli @tauri-apps/api"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn up @tauri-apps/cli @tauri-apps/api"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
/>
You can also detect what the latest version of Tauri is on the command line, using:
<CommandTabs
npm="npm outdated @tauri-apps/cli"
yarn="yarn outdated @tauri-apps/cli"
pnpm="pnpm outdated @tauri-apps/cli"
npm="npm outdated @tauri-apps/cli"
yarn="yarn outdated @tauri-apps/cli"
pnpm="pnpm outdated @tauri-apps/cli"
/>
Alternatively, if you are using the `vue-cli-plugin-tauri` approach:
<CommandTabs
npm="npm install vue-cli-plugin-tauri@latest"
yarn="yarn up vue-cli-plugin-tauri"
pnpm="pnpm update vue-cli-plugin-tauri --latest"
npm="npm install vue-cli-plugin-tauri@latest"
yarn="yarn up vue-cli-plugin-tauri"
pnpm="pnpm update vue-cli-plugin-tauri --latest"
/>
## Update Cargo Packages

View File

@@ -27,35 +27,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 Independiente" icon="rocket">
Trae tu stack web existente a Tauri o comienza ese nuevo proyecto de en
sueño. Tauri soporta cualquier framework frontend, por lo que no necesitas
cambiar tu stack.
</Card>
<Card title="Multiplataforma" icon="rocket">
Construye tu aplicación para Linux, macOS, Windows, Android e iOS - todo
desde una sola base de código.
</Card>
<Card title="Comunicación Entre Procesos" icon="rocket">
Escribe tu frontend en JavaScript, la lógica de la aplicación en Rust, e
integra profundamente en el sistema con Swift y Kotlin.
</Card>
<Card title="Máxima Seguridad" icon="rocket">
Frente a la mente del equipo de Tauri que impulsa nuestras mayores
prioridades y mayores innovaciones.
</Card>
<Card title="Tamaño Mínimo" icon="rocket">
Al usar el renderizador web nativo del sistema operativo, el tamaño de una
aplicación Tauri puede ser tan pequeño como 600KB.
</Card>
<Card title="Impulsado por Rust" icon="rocket">
Con el rendimiento y la seguridad en el centro, Rust es el lenguaje para la
próxima generación de aplicaciones.
</Card>
<Card title="Frontend Independiente" icon="rocket">
Trae tu stack web existente a Tauri o comienza ese nuevo proyecto de en
sueño. Tauri soporta cualquier framework frontend, por lo que no necesitas
cambiar tu stack.
</Card>
<Card title="Multiplataforma" icon="rocket">
Construye tu aplicación para Linux, macOS, Windows, Android e iOS - todo
desde una sola base de código.
</Card>
<Card title="Comunicación Entre Procesos" icon="rocket">
Escribe tu frontend en JavaScript, la lógica de la aplicación en Rust, e
integra profundamente en el sistema con Swift y Kotlin.
</Card>
<Card title="Máxima Seguridad" icon="rocket">
Frente a la mente del equipo de Tauri que impulsa nuestras mayores
prioridades y mayores innovaciones.
</Card>
<Card title="Tamaño Mínimo" icon="rocket">
Al usar el renderizador web nativo del sistema operativo, el tamaño de una
aplicación Tauri puede ser tan pequeño como 600KB.
</Card>
<Card title="Impulsado por Rust" icon="rocket">
Con el rendimiento y la seguridad en el centro, Rust es el lenguaje para la
próxima generación de aplicaciones.
</Card>
</CardGrid>

View File

@@ -93,8 +93,8 @@ import { ask } from '@tauri-apps/plugin-dialog';
// Crea un diálogo de Sí/No
const answer = await ask('This action cannot be reverted. Are you sure?', {
title: 'Tauri',
type: 'warning',
title: 'Tauri',
type: 'warning',
});
console.log(answer);
@@ -112,8 +112,8 @@ import { confirm } from '@tauri-apps/plugin-dialog';
// Crea un diálogo de confirmación Ok/Cancelar
const confirmation = await confirm(
'This action cannot be reverted. Are you sure?',
{ title: 'Tauri', type: 'warning' }
'This action cannot be reverted. Are you sure?',
{ title: 'Tauri', type: 'warning' }
);
console.log(confirmation);
@@ -146,8 +146,8 @@ import { open } from '@tauri-apps/plugin-dialog';
// Abre un diálogo
const file = await open({
multiple: false,
directory: false,
multiple: false,
directory: false,
});
console.log(file);
// Imprime la ruta y el nombre del archivo en la consola
@@ -163,12 +163,12 @@ Abre un diálogo de guardar archivo/directorio.
import { save } from '@tauri-apps/plugin-dialog';
// Indica para guardar un 'My Filter' con extensión .png o .jpeg
const path = await save({
filters: [
{
name: 'My Filter',
extensions: ['png', 'jpeg'],
},
],
filters: [
{
name: 'My Filter',
extensions: ['png', 'jpeg'],
},
],
});
console.log(path);
// Imprime la ruta escogida

View File

@@ -13,10 +13,10 @@ Tauri viene con el objetivo de ser extensible. En esta página encontrarás:
- **[Recursos de la Comunidad](#recursos-de-la-comunidad)**: Más plugins y fórmulas creadas por la comunidad de Tauri
<Search>
## Características
<FeaturesList />
## Recursos de la comunidad
<CommunityList />
## Características
<FeaturesList />
## Recursos de la comunidad
<CommunityList />
</Search>
:::tip[¿Tienes algo genial que compartir?]

View File

@@ -18,21 +18,21 @@ Si un framework no aparece en esta lista puede que funcione con Tauri sin necesi
## JavaScript
<CardGrid>
<LinkCard title="Next.js" href="/start/frontend/nextjs" />
<LinkCard title="Nuxt" href="/start/frontend/nuxt" />
<LinkCard title="Qwik" href="/start/frontend/qwik" />
<LinkCard title="Svelte" href="/start/frontend/svelte" />
<LinkCard title="Vite" href="/start/frontend/vite" />
<LinkCard title="Webpack" href="/start/frontend/webpack" />
<LinkCard title="Next.js" href="/start/frontend/nextjs" />
<LinkCard title="Nuxt" href="/start/frontend/nuxt" />
<LinkCard title="Qwik" href="/start/frontend/qwik" />
<LinkCard title="Svelte" href="/start/frontend/svelte" />
<LinkCard title="Vite" href="/start/frontend/vite" />
<LinkCard title="Webpack" href="/start/frontend/webpack" />
</CardGrid>
## Rust
<CardGrid>
<LinkCard title="Leptos" href="/start/frontend/leptos" />
<LinkCard title="Sycamore" href="/start/frontend/sycamore" />
<LinkCard title="Trunk" href="/start/frontend/trunk" />
<LinkCard title="Yew" href="/start/frontend/yew" />
<LinkCard title="Leptos" href="/start/frontend/leptos" />
<LinkCard title="Sycamore" href="/start/frontend/sycamore" />
<LinkCard title="Trunk" href="/start/frontend/trunk" />
<LinkCard title="Yew" href="/start/frontend/yew" />
</CardGrid>
## Checklist de Configuración

View File

@@ -19,9 +19,9 @@ Next.js es un framework para React. Aprende más sobre Next.js en https://nextjs
1. Instala `internal-ip` para el desarrollo móvil:
<CommandTabs
npm="npm install --save-dev internal-ip"
yarn="yarn add -D internal-ip"
pnpm="pnpm add -D internal-ip"
npm="npm install --save-dev internal-ip"
yarn="yarn add -D internal-ip"
pnpm="pnpm add -D internal-ip"
/>
2. Actualiza la configuración de Tauri:
@@ -32,12 +32,12 @@ Next.js es un framework para React. Aprende más sobre Next.js en https://nextjs
```json
// tauri.conf.json
{
"build": {
"beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run build",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
"build": {
"beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run build",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
}
```
@@ -47,12 +47,12 @@ Next.js es un framework para React. Aprende más sobre Next.js en https://nextjs
```json
// tauri.conf.json
{
"build": {
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
"build": {
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
}
```
@@ -62,12 +62,12 @@ Next.js es un framework para React. Aprende más sobre Next.js en https://nextjs
```json
// tauri.conf.json
{
"build": {
"beforeDevCommand": "pnpm dev",
"beforeBuildCommand": "pnpm generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
"build": {
"beforeDevCommand": "pnpm dev",
"beforeBuildCommand": "pnpm generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
}
```
@@ -80,24 +80,24 @@ Next.js es un framework para React. Aprende más sobre Next.js en https://nextjs
// next.conf.ts
const isProd = process.env.NODE_ENV === 'production';
module.exports = async (phase, { defaultConfig }) => {
let internalHost = null;
// En modo de desarrollo usamos internal-ip para servir los archivos
if (!isProd) {
const { internalIpV4 } = await import('internal-ip');
internalHost = await internalIpV4();
}
const nextConfig = {
// Aségurate de que Next.js use SSG en lugar de SSR
// https://nextjs.org/docs/pages/building-your-application/deploying/static-exports
output: 'export',
// Nota: Esta función experimental es necesaria para usar NextJS Image en modo SSG.
// Consulta https://nextjs.org/docs/messages/export-image-api para ver diferentes soluciones.
images: {
unoptimized: true,
},
// Configura assetPrefix o el servidor no resolverá correctamente tus archivos.
assetPrefix: isProd ? null : `http://${internalHost}:3000`,
};
return nextConfig;
let internalHost = null;
// En modo de desarrollo usamos internal-ip para servir los archivos
if (!isProd) {
const { internalIpV4 } = await import('internal-ip');
internalHost = await internalIpV4();
}
const nextConfig = {
// Aségurate de que Next.js use SSG en lugar de SSR
// https://nextjs.org/docs/pages/building-your-application/deploying/static-exports
output: 'export',
// Nota: Esta función experimental es necesaria para usar NextJS Image en modo SSG.
// Consulta https://nextjs.org/docs/messages/export-image-api para ver diferentes soluciones.
images: {
unoptimized: true,
},
// Configura assetPrefix o el servidor no resolverá correctamente tus archivos.
assetPrefix: isProd ? null : `http://${internalHost}:3000`,
};
return nextConfig;
};
```

View File

@@ -25,12 +25,12 @@ Aprende más sobre Nuxt en https://nuxt.com. Esta guía es precisa a partir de N
```json
// tauri.conf.json
{
"build": {
"beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
"build": {
"beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
}
```
@@ -40,12 +40,12 @@ Aprende más sobre Nuxt en https://nuxt.com. Esta guía es precisa a partir de N
```json
// tauri.conf.json
{
"build": {
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
"build": {
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
}
```
@@ -55,12 +55,12 @@ Aprende más sobre Nuxt en https://nuxt.com. Esta guía es precisa a partir de N
```json
// tauri.conf.json
{
"build": {
"beforeDevCommand": "pnpm dev",
"beforeBuildCommand": "pnpm generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
"build": {
"beforeDevCommand": "pnpm dev",
"beforeBuildCommand": "pnpm generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
}
```
@@ -72,31 +72,31 @@ Aprende más sobre Nuxt en https://nuxt.com. Esta guía es precisa a partir de N
```ts
export default defineNuxtConfig({
// (opcional) Habilita las herramientas de desarrollo de Nuxt
devtools: { enabled: true },
// Habilita SSG
ssr: false,
vite: {
// Mejor soporte para la salida de Tauri CLI
clearScreen: false,
// Habilita las variables de entorno
// Las variables de entorno adicionales se pueden encontrar en
// https://tauri.app/2/reference/environment-variables/
envPrefix: ['VITE_', 'TAURI_'],
server: {
// Tauri requiere un puerto consistente
strictPort: true,
// Habilita el servidor de desarrollo para pueda ser accedido por otros dispositivos para el desarrollo móvil
host: '0.0.0.0',
hmr: {
// Usa un websocket para la recarga rápida en móviles
protocol: 'ws',
// Asegúrate de que esté disponible en la red
host: '0.0.0.0',
// Usa un puerto específico para hmr
port: 5183,
},
},
},
// (opcional) Habilita las herramientas de desarrollo de Nuxt
devtools: { enabled: true },
// Habilita SSG
ssr: false,
vite: {
// Mejor soporte para la salida de Tauri CLI
clearScreen: false,
// Habilita las variables de entorno
// Las variables de entorno adicionales se pueden encontrar en
// https://tauri.app/2/reference/environment-variables/
envPrefix: ['VITE_', 'TAURI_'],
server: {
// Tauri requiere un puerto consistente
strictPort: true,
// Habilita el servidor de desarrollo para pueda ser accedido por otros dispositivos para el desarrollo móvil
host: '0.0.0.0',
hmr: {
// Usa un websocket para la recarga rápida en móviles
protocol: 'ws',
// Asegúrate de que esté disponible en la red
host: '0.0.0.0',
// Usa un puerto específico para hmr
port: 5183,
},
},
},
});
```

View File

@@ -19,15 +19,15 @@ Trunk es una herramienta de empaquetado de aplicaciones web WASM para Rust. Obt
```json
// tauri.conf.json
{
"build": {
"beforeDevCommand": "trunk serve",
"beforeBuildCommand": "trunk build",
"devPath": "http://localhost:8080",
"distDir": "../dist"
},
"app": {
"withGlobalTauri": true
}
"build": {
"beforeDevCommand": "trunk serve",
"beforeBuildCommand": "trunk build",
"devPath": "http://localhost:8080",
"distDir": "../dist"
},
"app": {
"withGlobalTauri": true
}
}
```

View File

@@ -73,10 +73,10 @@ Par défaut, l'inspecteur n'est activé que dans les versions de développement
Pour créer une version de débogage, exécutez la commande `tauri build --debug`.
<CommandTabs
npm="npm run tauri build -- --debug"
yarn="yarn tauri build --debug"
pnpm="pnpm tauri build --debug"
cargo="cargo tauri build --debug"
npm="npm run tauri build -- --debug"
yarn="yarn tauri build --debug"
pnpm="pnpm tauri build --debug"
cargo="cargo tauri build --debug"
/>
Comme les processus de construction et de développement normaux, la construction prend un certain temps la première fois que vous exécutez cette commande, mais est beaucoup plus rapide lors des exécutions suivantes. L'application groupée finale a la console de développement activée et est placée dans `src-tauri/target/debug/bundle`.

View File

@@ -6,13 +6,13 @@ description: Conseils et astuces pour votre débogage
import { LinkCard, CardGrid } from '@astrojs/starlight/components';
<CardGrid>
<LinkCard
title="Débogage de l'application"
href="/fr/develop/Debug/application//"
/>
<LinkCard title="Débogage dans VS Code" href="/fr/develop/Debug/vscode" />
<LinkCard
title="Débogage dans RustRover"
href="/fr/develop/Debug/rustrover"
/>
<LinkCard
title="Débogage de l'application"
href="/fr/develop/Debug/application//"
/>
<LinkCard title="Débogage dans VS Code" href="/fr/develop/Debug/vscode" />
<LinkCard
title="Débogage dans RustRover"
href="/fr/develop/Debug/rustrover"
/>
</CardGrid>

View File

@@ -14,36 +14,36 @@ Créez un fichier `.vscode/launch.json` et collez-y le contenu JSON ci-dessous :
```json title=".vscode/launch.json"
{
// Utilisez IntelliSense pour en savoir plus sur les attributs possibles.
// Survolez pour afficher les descriptions des attributs existants.
// Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Débogage de la production de Tauri",
"cargo": {
"args": [
"build",
"--manifest-path=./src-tauri/Cargo.toml",
"--no-default-features"
]
},
// La tâche pour `beforeDevCommand` si elle est utilisée, doit être configurée dans `.vscode/tasks.json`
"preLaunchTask": "ui:dev"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug de production de Tauri",
"cargo": {
"args": ["build", "--release", "--manifest-path=./src-tauri/Cargo.toml"]
},
// La tâche pour `beforeBuildCommand` si elle est utilisée, doit être configurée dans `.vscode/tasks.json`
"preLaunchTask": "ui:build"
}
]
// Utilisez IntelliSense pour en savoir plus sur les attributs possibles.
// Survolez pour afficher les descriptions des attributs existants.
// Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Débogage de la production de Tauri",
"cargo": {
"args": [
"build",
"--manifest-path=./src-tauri/Cargo.toml",
"--no-default-features"
]
},
// La tâche pour `beforeDevCommand` si elle est utilisée, doit être configurée dans `.vscode/tasks.json`
"preLaunchTask": "ui:dev"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug de production de Tauri",
"cargo": {
"args": ["build", "--release", "--manifest-path=./src-tauri/Cargo.toml"]
},
// La tâche pour `beforeBuildCommand` si elle est utilisée, doit être configurée dans `.vscode/tasks.json`
"preLaunchTask": "ui:build"
}
]
}
```
@@ -53,29 +53,29 @@ Notez qu'il n'utilise pas la CLI Tauri, donc les fonctionnalités CLI exclusives
```json title=".vscode/tasks.json"
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "ui:dev",
"type": "shell",
// `dev` keeps running in the background
// ideally you should also configure a `problemMatcher`
// see https://code.visualstudio.com/docs/editor/tasks#_can-a-background-task-be-used-as-a-prelaunchtask-in-launchjson
"isBackground": true,
// change this to your `beforeDevCommand`:
"command": "yarn",
"args": ["dev"]
},
{
"label": "ui:build",
"type": "shell",
// change this to your `beforeBuildCommand`:
"command": "yarn",
"args": ["build"]
}
]
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "ui:dev",
"type": "shell",
// `dev` keeps running in the background
// ideally you should also configure a `problemMatcher`
// see https://code.visualstudio.com/docs/editor/tasks#_can-a-background-task-be-used-as-a-prelaunchtask-in-launchjson
"isBackground": true,
// change this to your `beforeDevCommand`:
"command": "yarn",
"args": ["dev"]
},
{
"label": "ui:build",
"type": "shell",
// change this to your `beforeBuildCommand`:
"command": "yarn",
"args": ["build"]
}
]
}
```

View File

@@ -17,10 +17,10 @@ Chaque framework possède ses propres outils de développement. Il n'entre pas d
### 2. Démarrer la fenêtre de développement de Tauri
<CommandTabs
npm="npm run tauri dev"
yarn="yarn tauri dev"
pnpm="pnpm tauri dev"
cargo="cargo tauri dev"
npm="npm run tauri dev"
yarn="yarn tauri dev"
pnpm="pnpm tauri dev"
cargo="cargo tauri dev"
/>
La première fois que vous exécutez cette commande, le gestionnaire de packages Rust prend plusieurs minutes pour télécharger et créer tous les packages requis. Comme ils sont mis en cache, les builds suivants sont beaucoup plus rapides, car seul votre code a besoin d'être reconstruit.

View File

@@ -6,12 +6,12 @@ description: Concepts de base pour le développement avec Tauri
import { LinkCard, CardGrid } from '@astrojs/starlight/components';
<CardGrid>
<LinkCard
title="Development Cycle"
href="/fr/guides/develop/development-cycle/"
/>
<LinkCard
title="Mise à jour des dépendances"
href="/fr/guides/develop/updating-dependencies"
/>
<LinkCard
title="Development Cycle"
href="/fr/guides/develop/development-cycle/"
/>
<LinkCard
title="Mise à jour des dépendances"
href="/fr/guides/develop/updating-dependencies"
/>
</CardGrid>

View File

@@ -9,25 +9,25 @@ import CommandTabs from '@components/CommandTabs.astro';
Si vous utilisez le package `tauri` :
<CommandTabs
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn up @tauri-apps/cli @tauri-apps/api"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn up @tauri-apps/cli @tauri-apps/api"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
/>
Vous pouvez également détecter la dernière version de Tauri sur la ligne de commande, en utilisant :
<CommandTabs
npm="npm outdated @tauri-apps/cli"
yarn="yarn outdated @tauri-apps/cli"
pnpm="pnpm outdated @tauri-apps/cli"
npm="npm outdated @tauri-apps/cli"
yarn="yarn outdated @tauri-apps/cli"
pnpm="pnpm outdated @tauri-apps/cli"
/>
Sinon, si vous utilisez l'approche `vue-cli-plugin-tauri` :
<CommandTabs
npm="npm install vue-cli-plugin-tauri@latest"
yarn="yarn up vue-cli-plugin-tauri"
pnpm="pnpm update vue-cli-plugin-tauri --latest"
npm="npm install vue-cli-plugin-tauri@latest"
yarn="yarn up vue-cli-plugin-tauri"
pnpm="pnpm update vue-cli-plugin-tauri --latest"
/>
## Mettre à jour les packages Cargo

View File

@@ -30,35 +30,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 Indépendant" icon="rocket">
Importez votre configuration web existant dans Tauri ou démarrez votre
nouveau projet de rêve. Tauri supporte n'importe quel framework frontend,
vous n'avez donc pas besoin de changer votre configuration.
</Card>
<Card title="Multiplatforme" icon="rocket">
Développez vos applications pour Linux, macOS, Windows, Android et iOS -
tout cela à partir du même code.
</Card>
<Card title="Communication Inter-Processus" icon="rocket">
Écrivez votre frontend en Javascript, la logique de l'application en Rust,
puis intégrez le tout profondément dans le système avec Swift et Kotlin.
</Card>
<Card title="Sécurité Maximale" icon="rocket">
C'est la première préoccupation de l'équipe Tauri, qui dirige nos priorités
et nos plus grandes innovations.
</Card>
<Card title="Poids Minimal" icon="rocket">
En utilisant le moteur de rendu web natif du système d'exploitation, le
poids d'une application Tauri peut atteindre 600 Ko.
</Card>
<Card title="Fonctionne avec Rust" icon="rocket">
Avec la performance et la sécurité au cœur de ses priorités, Rust est le
langage de la nouvelle génération d'applications.
</Card>
<Card title="Frontend Indépendant" icon="rocket">
Importez votre configuration web existant dans Tauri ou démarrez votre
nouveau projet de rêve. Tauri supporte n'importe quel framework frontend,
vous n'avez donc pas besoin de changer votre configuration.
</Card>
<Card title="Multiplatforme" icon="rocket">
Développez vos applications pour Linux, macOS, Windows, Android et iOS -
tout cela à partir du même code.
</Card>
<Card title="Communication Inter-Processus" icon="rocket">
Écrivez votre frontend en Javascript, la logique de l'application en Rust,
puis intégrez le tout profondément dans le système avec Swift et Kotlin.
</Card>
<Card title="Sécurité Maximale" icon="rocket">
C'est la première préoccupation de l'équipe Tauri, qui dirige nos priorités
et nos plus grandes innovations.
</Card>
<Card title="Poids Minimal" icon="rocket">
En utilisant le moteur de rendu web natif du système d'exploitation, le
poids d'une application Tauri peut atteindre 600 Ko.
</Card>
<Card title="Fonctionne avec Rust" icon="rocket">
Avec la performance et la sécurité au cœur de ses priorités, Rust est le
langage de la nouvelle génération d'applications.
</Card>
</CardGrid>

View File

@@ -88,9 +88,9 @@ Suivez ces étapes pour envoyer une notification:
```js
import {
isPermissionGranted,
requestPermission,
sendNotification,
isPermissionGranted,
requestPermission,
sendNotification,
} from '@tauri-apps/plugin-notification';
// Avez-vous la permission d'envoyer une notification ?
@@ -98,13 +98,13 @@ let permissionGranted = await isPermissionGranted();
// Le cas échéant on la demande
if (!permissionGranted) {
const permission = await requestPermission();
permissionGranted = permission === 'granted';
const permission = await requestPermission();
permissionGranted = permission === 'granted';
}
// Une fois la permission obtenue, on envoie la notification
if (permissionGranted) {
sendNotification({ title: 'Tauri', body: 'Tauri est incroyable!' });
sendNotification({ title: 'Tauri', body: 'Tauri est incroyable!' });
}
```

View File

@@ -19,21 +19,21 @@ Un framework manque à la liste ? Il peut fonctionner avec Tauri sans configurat
## JavaScript
<CardGrid>
<LinkCard title="Next.js" href="/fr/start/frontend/nextjs" />
<LinkCard title="Nuxt" href="/fr/start/frontend/nuxt" />
<LinkCard title="Qwik" href="/fr/start/frontend/qwik" />
<LinkCard title="Svelte" href="/fr/start/frontend/svelte" />
<LinkCard title="Vite" href="/fr/start/frontend/vite" />
<LinkCard title="Webpack" href="/fr/start/frontend/webpack" />
<LinkCard title="Next.js" href="/fr/start/frontend/nextjs" />
<LinkCard title="Nuxt" href="/fr/start/frontend/nuxt" />
<LinkCard title="Qwik" href="/fr/start/frontend/qwik" />
<LinkCard title="Svelte" href="/fr/start/frontend/svelte" />
<LinkCard title="Vite" href="/fr/start/frontend/vite" />
<LinkCard title="Webpack" href="/fr/start/frontend/webpack" />
</CardGrid>
## Rust
<CardGrid>
<LinkCard title="Leptos" href="/fr/start/frontend/leptos" />
<LinkCard title="Sycamore" href="/fr/start/frontend/sycamore" />
<LinkCard title="Trunk" href="/fr/start/frontend/trunk" />
<LinkCard title="Yew" href="/fr/start/frontend/yew" />
<LinkCard title="Leptos" href="/fr/start/frontend/leptos" />
<LinkCard title="Sycamore" href="/fr/start/frontend/sycamore" />
<LinkCard title="Trunk" href="/fr/start/frontend/trunk" />
<LinkCard title="Yew" href="/fr/start/frontend/yew" />
</CardGrid>
## Instructions de Configuration

View File

@@ -20,9 +20,9 @@ Next.js est un framework React. Apprenez-en plus au sujet de Next.js sur https:/
1. Installez `internal-ip` pour le développement mobile :
<CommandTabs
npm="npm install --save-dev internal-ip"
yarn="yarn add -D internal-ip"
pnpm="pnpm add -D internal-ip"
npm="npm install --save-dev internal-ip"
yarn="yarn add -D internal-ip"
pnpm="pnpm add -D internal-ip"
/>
2. Mettez à jour la configuration de Tauri :
@@ -33,12 +33,12 @@ Next.js est un framework React. Apprenez-en plus au sujet de Next.js sur https:/
```json
// tauri.conf.json
{
"build": {
"beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run build",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
"build": {
"beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run build",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
}
```
@@ -48,12 +48,12 @@ Next.js est un framework React. Apprenez-en plus au sujet de Next.js sur https:/
```json
// tauri.conf.json
{
"build": {
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
"build": {
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
}
```
@@ -63,12 +63,12 @@ Next.js est un framework React. Apprenez-en plus au sujet de Next.js sur https:/
```json
// tauri.conf.json
{
"build": {
"beforeDevCommand": "pnpm dev",
"beforeBuildCommand": "pnpm generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
"build": {
"beforeDevCommand": "pnpm dev",
"beforeBuildCommand": "pnpm generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
}
```
@@ -81,25 +81,25 @@ Next.js est un framework React. Apprenez-en plus au sujet de Next.js sur https:/
// next.conf.ts
const isProd = process.env.NODE_ENV === 'production';
module.exports = async (phase, { defaultConfig }) => {
let internalHost = null;
// En mode développement, on utilise "internal-ip" pour se servir des assets.
let internalHost = null;
// En mode développement, on utilise "internal-ip" pour se servir des assets.
if (!isProd) {
const { internalIpV4 } = await import('internal-ip');
internalHost = await internalIpV4();
}
const nextConfig = {
// Assurez-vous que Next.js utilise SSG au lieu de SSR
// https://nextjs.org/docs/pages/building-your-application/deploying/static-exports
output: 'export',
// Note: Cette fonctionnalité expérimentale est requise pour utiliser NextJS Image en mode SSG.
// Voir https://nextjs.org/docs/messages/export-image-api pour des solutions différentes.
images: {
unoptimized: true,
},
// Configurez assetPrefix sinon le server ne résoudra pas correctement vos assets.
assetPrefix: isProd ? null : `http://${internalHost}:3000`,
};
return nextConfig;
if (!isProd) {
const { internalIpV4 } = await import('internal-ip');
internalHost = await internalIpV4();
}
const nextConfig = {
// Assurez-vous que Next.js utilise SSG au lieu de SSR
// https://nextjs.org/docs/pages/building-your-application/deploying/static-exports
output: 'export',
// Note: Cette fonctionnalité expérimentale est requise pour utiliser NextJS Image en mode SSG.
// Voir https://nextjs.org/docs/messages/export-image-api pour des solutions différentes.
images: {
unoptimized: true,
},
// Configurez assetPrefix sinon le server ne résoudra pas correctement vos assets.
assetPrefix: isProd ? null : `http://${internalHost}:3000`,
};
return nextConfig;
};
```

View File

@@ -25,12 +25,12 @@ Apprenez-en plus au sujet de Nuxt sur https://nuxt.com. Ce guide est valable à
```json
// tauri.conf.json
{
"build": {
"beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
"build": {
"beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
}
```
@@ -40,12 +40,12 @@ Apprenez-en plus au sujet de Nuxt sur https://nuxt.com. Ce guide est valable à
```json
// tauri.conf.json
{
"build": {
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
"build": {
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
}
```
@@ -55,12 +55,12 @@ Apprenez-en plus au sujet de Nuxt sur https://nuxt.com. Ce guide est valable à
```json
// tauri.conf.json
{
"build": {
"beforeDevCommand": "pnpm dev",
"beforeBuildCommand": "pnpm generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
"build": {
"beforeDevCommand": "pnpm dev",
"beforeBuildCommand": "pnpm generate",
"devPath": "http://localhost:3000",
"distDir": "../dist"
}
}
```
@@ -72,32 +72,32 @@ Apprenez-en plus au sujet de Nuxt sur https://nuxt.com. Ce guide est valable à
```ts
export default defineNuxtConfig({
// (optionnel) Activez les outils de développement Nuxt
devtools: { enabled: true },
// Activez SSG
ssr: false,
vite: {
// Meilleur compatibilité pour la sortie "Tauri CLI"
clearScreen: false,
// Activez les variables d'environnement
// Vous pouvez trouver les variables d'environnements additionnelles sur
// https://tauri.app/2/reference/environment-variables/
envPrefix: ['VITE_', 'TAURI_'],
server: {
// Tauri requiert un port constant
strictPort: true,
// Active le serveur de développement pour être visible par les autres appareils pour le développement mobile
host: '0.0.0.0',
hmr: {
// Utilisez le websocket pour le rechargement à chaud
// (optionnel) Activez les outils de développement Nuxt
devtools: { enabled: true },
// Activez SSG
ssr: false,
vite: {
// Meilleur compatibilité pour la sortie "Tauri CLI"
clearScreen: false,
// Activez les variables d'environnement
// Vous pouvez trouver les variables d'environnements additionnelles sur
// https://tauri.app/2/reference/environment-variables/
envPrefix: ['VITE_', 'TAURI_'],
server: {
// Tauri requiert un port constant
strictPort: true,
// Active le serveur de développement pour être visible par les autres appareils pour le développement mobile
host: '0.0.0.0',
hmr: {
// Utilisez le websocket pour le rechargement à chaud
protocol: 'ws',
// Assurez-vous que ce soit disponible sur le réseau
host: '0.0.0.0',
// Utilisez un port spécifique pour hmr
port: 5183,
},
},
},
protocol: 'ws',
// Assurez-vous que ce soit disponible sur le réseau
host: '0.0.0.0',
// Utilisez un port spécifique pour hmr
port: 5183,
},
},
},
});
```

View File

@@ -27,15 +27,15 @@ Vous devrez utiliser `cargo install --git https://github.com/amrbashir/trunk` po
```json
// tauri.conf.json
{
"build": {
"beforeDevCommand": "trunk serve",
"beforeBuildCommand": "trunk build",
"devPath": "http://localhost:8080",
"distDir": "../dist"
},
"app": {
"withGlobalTauri": true
}
"build": {
"beforeDevCommand": "trunk serve",
"beforeBuildCommand": "trunk build",
"devPath": "http://localhost:8080",
"distDir": "../dist"
},
"app": {
"withGlobalTauri": true
}
}
```

View File

@@ -14,13 +14,13 @@ Ce guide vous explique comment mettre à niveau votre application basée sur Tau
Tauri v2 contient la commande `migrate` qui simplifie votre migration:
<CommandTabs
npm="npm install @tauri-apps/cli@latest
npm="npm install @tauri-apps/cli@latest
npm run tauri migrate"
yarn="yarn upgrade @tauri-apps/cli --latest
yarn="yarn upgrade @tauri-apps/cli --latest
yarn tauri migrate"
pnpm="pnpm update @tauri-apps/cli --latest
pnpm="pnpm update @tauri-apps/cli --latest
pnpm tauri migrate"
cargo='cargo install tauri-cli --version "^2.0.0-beta"
cargo='cargo install tauri-cli --version "^2.0.0-beta"
cargo tauri migrate'
/>
@@ -130,9 +130,9 @@ fn main() {
```json
// package.json
{
"dependencies": {
"@tauri-apps/plugin-app": "^2.0.0"
}
"dependencies": {
"@tauri-apps/plugin-app": "^2.0.0"
}
}
```
@@ -190,9 +190,9 @@ fn main() {
```json
// package.json
{
"dependencies": {
"@tauri-apps/plugin-cli": "^2.0.0"
}
"dependencies": {
"@tauri-apps/plugin-cli": "^2.0.0"
}
}
```
@@ -241,9 +241,9 @@ fn main() {
```json
// package.json
{
"dependencies": {
"@tauri-apps/plugin-clipboard-manager": "^2.0.0"
}
"dependencies": {
"@tauri-apps/plugin-clipboard-manager": "^2.0.0"
}
}
```
@@ -299,21 +299,21 @@ fn main() {
```json
// package.json
{
"dependencies": {
"@tauri-apps/plugin-dialog": "^2.0.0"
}
"dependencies": {
"@tauri-apps/plugin-dialog": "^2.0.0"
}
}
```
```js
import { save } from '@tauri-apps/plugin-dialog';
const filePath = await save({
filters: [
{
name: 'Image',
extensions: ['png', 'jpeg'],
},
],
filters: [
{
name: 'Image',
extensions: ['png', 'jpeg'],
},
],
});
```
@@ -365,9 +365,9 @@ fn main() {
```json
// package.json
{
"dependencies": {
"@tauri-apps/plugin-fs": "^2.0.0"
}
"dependencies": {
"@tauri-apps/plugin-fs": "^2.0.0"
}
}
```
@@ -411,16 +411,16 @@ fn main() {
```json
// package.json
{
"dependencies": {
"@tauri-apps/plugin-global-shortcut": "^2.0.0"
}
"dependencies": {
"@tauri-apps/plugin-global-shortcut": "^2.0.0"
}
}
```
```js
import { register } from '@tauri-apps/plugin-global-shortcut';
await register('CommandOrControl+Shift+C', () => {
console.log('Shortcut triggered');
console.log('Shortcut triggered');
});
```
@@ -468,16 +468,16 @@ fn main() {
```json
// package.json
{
"dependencies": {
"@tauri-apps/plugin-http": "^2.0.0"
}
"dependencies": {
"@tauri-apps/plugin-http": "^2.0.0"
}
}
```
```js
import { fetch } from '@tauri-apps/plugin-http';
const response = await fetch(
'https://raw.githubusercontent.com/tauri-apps/tauri/dev/package.json'
'https://raw.githubusercontent.com/tauri-apps/tauri/dev/package.json'
);
```
@@ -534,9 +534,9 @@ fn main() {
```json
// package.json
{
"dependencies": {
"@tauri-apps/plugin-notification": "^2.0.0"
}
"dependencies": {
"@tauri-apps/plugin-notification": "^2.0.0"
}
}
```
@@ -714,9 +714,9 @@ fn main() {
```json
// package.json
{
"dependencies": {
"@tauri-apps/plugin-os": "^2.0.0"
}
"dependencies": {
"@tauri-apps/plugin-os": "^2.0.0"
}
}
```
@@ -769,9 +769,9 @@ fn main() {
```json
// package.json
{
"dependencies": {
"@tauri-apps/plugin-process": "^2.0.0"
}
"dependencies": {
"@tauri-apps/plugin-process": "^2.0.0"
}
}
```
@@ -828,9 +828,9 @@ fn main() {
```json
// package.json
{
"dependencies": {
"@tauri-apps/plugin-shell": "^2.0.0"
}
"dependencies": {
"@tauri-apps/plugin-shell": "^2.0.0"
}
}
```
@@ -1009,9 +1009,9 @@ fn main() {
```json
// package.json
{
"dependencies": {
"@tauri-apps/plugin-updater": "^2.0.0"
}
"dependencies": {
"@tauri-apps/plugin-updater": "^2.0.0"
}
}
```
@@ -1021,13 +1021,13 @@ import { relaunch } from '@tauri-apps/plugin-process';
const update = await check();
if (update.response.available) {
console.log(
`Update to ${update.response.latestVersion} available! Date: ${update.response.date}`
);
console.log(`Release notes: ${update.response.body}`);
await update.downloadAndInstall();
// nécéssite le plugin `process`
await relaunch();
console.log(
`Update to ${update.response.latestVersion} available! Date: ${update.response.date}`
);
console.log(`Release notes: ${update.response.body}`);
await update.downloadAndInstall();
// nécéssite le plugin `process`
await relaunch();
}
```
@@ -1096,9 +1096,9 @@ fn main() {
```json
// package.json
{
"dependencies": {
"@tauri-apps/plugin-window": "^2.0.0"
}
"dependencies": {
"@tauri-apps/plugin-window": "^2.0.0"
}
}
```

View File

@@ -31,41 +31,41 @@ import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components';
import Cta from '@fragments/cta.mdx';
<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>
<div class="lp-cta-card">
<Card title="Create a Project" icon="rocket">
<Cta />
</Card>
<Card title="Create a Project" icon="rocket">
<Cta />
</Card>
</div>
<CardGrid>
<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 that drives 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 little
as 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 that drives 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 little
as 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

@@ -13,36 +13,36 @@ Crea un file `.vscode/launch.json` e incolla il contenuto JSON sottostante in es
```json title=".vscode/launch.json"
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Tauri Development Debug",
"cargo": {
"args": [
"build",
"--manifest-path=./src-tauri/Cargo.toml",
"--no-default-features"
]
},
// task for the `beforeDevCommand` if used, must be configured in `.vscode/tasks.json`
"preLaunchTask": "ui:dev"
},
{
"type": "lldb",
"request": "launch",
"name": "Tauri Production Debug",
"cargo": {
"args": ["build", "--release", "--manifest-path=./src-tauri/Cargo.toml"]
},
// task for the `beforeBuildCommand` if used, must be configured in `.vscode/tasks.json`
"preLaunchTask": "ui:build"
}
]
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Tauri Development Debug",
"cargo": {
"args": [
"build",
"--manifest-path=./src-tauri/Cargo.toml",
"--no-default-features"
]
},
// task for the `beforeDevCommand` if used, must be configured in `.vscode/tasks.json`
"preLaunchTask": "ui:dev"
},
{
"type": "lldb",
"request": "launch",
"name": "Tauri Production Debug",
"cargo": {
"args": ["build", "--release", "--manifest-path=./src-tauri/Cargo.toml"]
},
// task for the `beforeBuildCommand` if used, must be configured in `.vscode/tasks.json`
"preLaunchTask": "ui:build"
}
]
}
```
@@ -52,29 +52,29 @@ Nota che non utilizza il Tauri CLI, quindi le esclusive funzioni CLI non vengono
```json title=".vscode/tasks.json"
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "ui:dev",
"type": "shell",
// `dev` keeps running in the background
// ideally you should also configure a `problemMatcher`
// see https://code.visualstudio.com/docs/editor/tasks#_can-a-background-task-be-used-as-a-prelaunchtask-in-launchjson
"isBackground": true,
// change this to your `beforeDevCommand`:
"command": "yarn",
"args": ["dev"]
},
{
"label": "ui:build",
"type": "shell",
// change this to your `beforeBuildCommand`:
"command": "yarn",
"args": ["build"]
}
]
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "ui:dev",
"type": "shell",
// `dev` keeps running in the background
// ideally you should also configure a `problemMatcher`
// see https://code.visualstudio.com/docs/editor/tasks#_can-a-background-task-be-used-as-a-prelaunchtask-in-launchjson
"isBackground": true,
// change this to your `beforeDevCommand`:
"command": "yarn",
"args": ["dev"]
},
{
"label": "ui:build",
"type": "shell",
// change this to your `beforeBuildCommand`:
"command": "yarn",
"args": ["build"]
}
]
}
```

View File

@@ -93,11 +93,11 @@ See [Access Control List](/reference/acl/) for more information.
```json title="src-tauri/capabilities/mobile.json"
{
"$schema": "../gen/schemas/mobile-schema.json",
"identifier": "mobile-capability",
"windows": ["main"],
"platforms": ["iOS", "android"],
"permissions": ["barcode-scanner:allow-scan", "barcode-scanner:allow-cancel"]
"$schema": "../gen/schemas/mobile-schema.json",
"identifier": "mobile-capability",
"windows": ["main"],
"platforms": ["iOS", "android"],
"permissions": ["barcode-scanner:allow-scan", "barcode-scanner:allow-cancel"]
}
```

View File

@@ -86,33 +86,33 @@ Under `tauri.conf.json`, you have the following structure to configure the inter
```json title="src-tauri/tauri.conf.json"
{
"plugins": {
"cli": {
"description": "Tauri CLI Plugin Example",
"args": [
{
"short": "v",
"name": "verbose",
"description": "Verbosity level"
}
],
"subcommands": {
"run": {
"description": "Run the application",
"args": [
{
"name": "debug",
"description": "Run application in debug mode"
},
{
"name": "release",
"description": "Run application in release mode"
}
]
}
}
}
}
"plugins": {
"cli": {
"description": "Tauri CLI Plugin Example",
"args": [
{
"short": "v",
"name": "verbose",
"description": "Verbosity level"
}
],
"subcommands": {
"run": {
"description": "Run the application",
"args": [
{
"name": "debug",
"description": "Run application in debug mode"
},
{
"name": "release",
"description": "Run application in release mode"
}
]
}
}
}
}
}
```
@@ -134,18 +134,18 @@ A positional argument is identified by its position in the list of arguments. Wi
```json title="src-tauri/tauri.conf.json"
{
"args": [
{
"name": "source",
"index": 1,
"takesValue": true
},
{
"name": "destination",
"index": 2,
"takesValue": true
}
]
"args": [
{
"name": "source",
"index": 1,
"takesValue": true
},
{
"name": "destination",
"index": 2,
"takesValue": true
}
]
}
```
@@ -157,15 +157,15 @@ A named argument is a (key, value) pair where the key identifies the value. With
```json title="tauri-src/tauri.conf.json"
{
"args": [
{
"name": "type",
"short": "t",
"takesValue": true,
"multiple": true,
"possibleValues": ["foo", "bar"]
}
]
"args": [
{
"name": "type",
"short": "t",
"takesValue": true,
"multiple": true,
"possibleValues": ["foo", "bar"]
}
]
}
```
@@ -177,12 +177,12 @@ A flag argument is a standalone key whose presence or absence provides informati
```json title="tauri-src/tauri.conf.json"
{
"args": [
{
"name": "verbose",
"short": "v"
}
]
"args": [
{
"name": "verbose",
"short": "v"
}
]
}
```
@@ -222,14 +222,14 @@ import { getMatches } from '@tauri-apps/plugin-cli';
const matches = await getMatches();
if (matches.subcommand?.name === 'run') {
// `./your-app run $ARGS` was executed
const args = matches.subcommand.matches.args;
if (args.debug?.value === true) {
// `./your-app run --debug` was executed
}
if (args.release?.value === true) {
// `./your-app run --release` was executed
}
// `./your-app run $ARGS` was executed
const args = matches.subcommand.matches.args;
if (args.debug?.value === true) {
// `./your-app run --debug` was executed
}
if (args.release?.value === true) {
// `./your-app run --release` was executed
}
}
```
@@ -272,11 +272,11 @@ See [Access Control List](/reference/acl/) for more information.
```json title="src-tauri/capabilities/main.json" ins={6}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": ["cli:default"]
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": ["cli:default"]
}
```

View File

@@ -102,19 +102,19 @@ For [universal links](https://developer.apple.com/documentation/xcode/allowing-a
```json title=".well-known/apple-app-site-association"
{
"applinks": {
"details": [
{
"appIDs": ["$DEVELOPMENT_TEAM_ID.$APP_BUNDLE_ID"],
"components": [
{
"/": "/open/*",
"comment": "Matches any URL whose path starts with /open/"
}
]
}
]
}
"applinks": {
"details": [
{
"appIDs": ["$DEVELOPMENT_TEAM_ID.$APP_BUNDLE_ID"],
"components": [
{
"/": "/open/*",
"comment": "Matches any URL whose path starts with /open/"
}
]
}
]
}
}
```
@@ -146,14 +146,14 @@ Under `tauri.conf.json > plugins > deep-link`, configure the domains you want to
```json title="tauri.conf.json"
{
"plugins": {
"deep-link": {
"domains": [
{ "host": "your.website.com", "pathPrefix": ["/open"] },
{ "host": "another.site.br" }
]
}
}
"plugins": {
"deep-link": {
"domains": [
{ "host": "your.website.com", "pathPrefix": ["/open"] },
{ "host": "another.site.br" }
]
}
}
}
```
@@ -168,7 +168,7 @@ The deep-link plugin is available in both JavaScript and Rust.
import { onOpenUrl } from '@tauri-apps/plugin-deep-link';
await onOpenUrl((urls) => {
console.log('deep link:', urls);
console.log('deep link:', urls);
});
```
@@ -205,15 +205,15 @@ See [Access Control List](/reference/acl/) for more information.
```json title="src-tauri/capabilities/main.json" ins={9}
{
"$schema": "../gen/schemas/mobile-schema.json",
"identifier": "mobile-capability",
"windows": ["main"],
"platforms": ["iOS", "android"],
"permissions": [
// Usually you will need event:default to listen to the deep-link event
"event:default",
"deep-link:default"
]
"$schema": "../gen/schemas/mobile-schema.json",
"identifier": "mobile-capability",
"windows": ["main"],
"platforms": ["iOS", "android"],
"permissions": [
// Usually you will need event:default to listen to the deep-link event
"event:default",
"deep-link:default"
]
}
```

View File

@@ -96,8 +96,8 @@ import { ask } from '@tauri-apps/plugin-dialog';
// Create a Yes/No dialog
const answer = await ask('This action cannot be reverted. Are you sure?', {
title: 'Tauri',
kind: 'warning',
title: 'Tauri',
kind: 'warning',
});
console.log(answer);
@@ -115,8 +115,8 @@ import { confirm } from '@tauri-apps/plugin-dialog';
// Creates a confirmation Ok/Cancel dialog
const confirmation = await confirm(
'This action cannot be reverted. Are you sure?',
{ title: 'Tauri', kind: 'warning' }
'This action cannot be reverted. Are you sure?',
{ title: 'Tauri', kind: 'warning' }
);
console.log(confirmation);
@@ -149,8 +149,8 @@ import { open } from '@tauri-apps/plugin-dialog';
// Open a dialog
const file = await open({
multiple: false,
directory: false,
multiple: false,
directory: false,
});
console.log(file);
// Prints file path and name to the console
@@ -166,12 +166,12 @@ Open a file/directory save dialog.
import { save } from '@tauri-apps/plugin-dialog';
// Prompt to save a 'My Filter' with extension .png or .jpeg
const path = await save({
filters: [
{
name: 'My Filter',
extensions: ['png', 'jpeg'],
},
],
filters: [
{
name: 'My Filter',
extensions: ['png', 'jpeg'],
},
],
});
console.log(path);
// Prints the chosen path

View File

@@ -133,17 +133,17 @@ See [Access Control List](/reference/acl/) for more information.
```json title="src-tauri/capabilities/main.json" ins={7-11}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"fs:default",
{
"identifier": "fs:allow-exists",
"allow": [{ "path": "$APPDATA/*" }]
}
]
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"fs:default",
{
"identifier": "fs:allow-exists",
"allow": [{ "path": "$APPDATA/*" }]
}
]
}
```
@@ -464,16 +464,16 @@ To allow any `fs` command to access specific scopes:
```json title="src-tauri/capabilities/main.json" {7-10}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
{
"identifier": "fs:scope",
"allow": [{ "path": "$APPDATA" }, { "path": "$APPDATA/**" }]
}
]
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
{
"identifier": "fs:scope",
"allow": [{ "path": "$APPDATA" }, { "path": "$APPDATA/**" }]
}
]
}
```
@@ -481,19 +481,19 @@ To allow specific `fs` command to access specific scopes:
```json title="src-tauri/capabilities/main.json" {7-14}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
{
"identifier": "fs:allow-rename",
"allow": [{ "path": "$HOME/**" }]
},
{
"identifier": "fs:allow-exists",
"allow": [{ "path": "$APPDATA/*" }]
}
]
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
{
"identifier": "fs:allow-rename",
"allow": [{ "path": "$HOME/**" }]
},
{
"identifier": "fs:allow-exists",
"allow": [{ "path": "$APPDATA/*" }]
}
]
}
```

View File

@@ -90,7 +90,7 @@ The global-shortcut plugin is available in both JavaScript and Rust.
import { register } from '@tauri-apps/plugin-global-shortcut';
await register('CommandOrControl+Shift+C', () => {
console.log('Shortcut triggered');
console.log('Shortcut triggered');
});
```
@@ -136,15 +136,15 @@ See [Access Control List](/reference/acl/) for more information.
```json title="src-tauri/capabilities/main.json" ins={7-9}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"global-shortcut:allow-is-registered",
"global-shortcut:allow-register",
"global-shortcut:allow-unregister"
]
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"global-shortcut:allow-is-registered",
"global-shortcut:allow-register",
"global-shortcut:allow-unregister"
]
}
```

View File

@@ -75,13 +75,13 @@ The http plugin is available in both as an JavaScript API and in Rust as a [reqw
```json
//src-tauri/capabilities/base.json
{
"permissions": [
{
"identifier": "http:default",
"allow": [{ "url": "https://*.tauri.app" }],
"deny": [{ "url": "https://private.tauri.app" }]
}
]
"permissions": [
{
"identifier": "http:default",
"allow": [{ "url": "https://*.tauri.app" }],
"deny": [{ "url": "https://private.tauri.app" }]
}
]
}
```
@@ -94,7 +94,7 @@ The http plugin is available in both as an JavaScript API and in Rust as a [reqw
// Send a GET request
const response = await fetch('http://test.tauri.app/data.json', {
method: 'GET',
method: 'GET',
});
console.log(response.status); // e.g. 200
console.log(response.statusText); // e.g. "OK"

View File

@@ -17,16 +17,16 @@ Tauri comes with extensibility in mind. On this page you'll find:
- **[Community Resources](#community-resources)**: More plugins and recipes built by the Tauri community
<Search>
## Features
<FeaturesList />
## Community Resources
<LinkCard
title="Have something to share?"
description="Open a pull request to show us your amazing resource."
href="https://github.com/tauri-apps/awesome-tauri/pulls"
/>
### Plugins
<AwesomeTauri section="plugins-no-official" />
### Integrations
<AwesomeTauri section="integrations" />
## Features
<FeaturesList />
## Community Resources
<LinkCard
title="Have something to share?"
description="Open a pull request to show us your amazing resource."
href="https://github.com/tauri-apps/awesome-tauri/pulls"
/>
### Plugins
<AwesomeTauri section="plugins-no-official" />
### Integrations
<AwesomeTauri section="integrations" />
</Search>

View File

@@ -132,11 +132,11 @@ See [Access Control List](/reference/acl/) for more information.
```json title="src-tauri/capabilities/main.json" ins={6}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": ["log:default"]
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": ["log:default"]
}
```

View File

@@ -92,9 +92,9 @@ Follow these steps to send a notification:
```js
import {
isPermissionGranted,
requestPermission,
sendNotification,
isPermissionGranted,
requestPermission,
sendNotification,
} from '@tauri-apps/plugin-notification';
// Do you have permission to send a notification?
@@ -102,13 +102,13 @@ let permissionGranted = await isPermissionGranted();
// If not we need to request it
if (!permissionGranted) {
const permission = await requestPermission();
permissionGranted = permission === 'granted';
const permission = await requestPermission();
permissionGranted = permission === 'granted';
}
// Once permission has been granted we can send the notification
if (permissionGranted) {
sendNotification({ title: 'Tauri', body: 'Tauri is awesome!' });
sendNotification({ title: 'Tauri', body: 'Tauri is awesome!' });
}
```

View File

@@ -85,8 +85,8 @@ The shell plugin is available in both JavaScript and Rust.
import { Command } from '@tauri-apps/plugin-shell';
let result = await Command.create('exec-sh', [
'-c',
"echo 'Hello World!'",
'-c',
"echo 'Hello World!'",
]).execute();
console.log(result);
```
@@ -125,28 +125,28 @@ See [Access Control List](/reference/acl/) for more information.
```json title="src-tauri/capabilities/main.json" ins={6-23}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
{
"identifier": "shell:allow-execute",
"allow": [
{
"name": "exec-sh",
"cmd": "sh",
"args": [
"-c",
{
"validator": "\\S+"
}
],
"sidecar": false
}
]
}
]
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
{
"identifier": "shell:allow-execute",
"allow": [
{
"name": "exec-sh",
"cmd": "sh",
"args": [
"-c",
{
"validator": "\\S+"
}
],
"sidecar": false
}
]
}
]
}
```

View File

@@ -44,20 +44,20 @@ branch = "v2"
Then, you have to add JavaScript Guest bindings using your preferred JavaScript package manager.
<Tabs>
<TabItem label="npm.io">
<CommandTabs
npm="npm add @tauri-apps/plugin-sql"
yarn="yarn add @tauri-apps/plugin-sql"
pnpm="pnpm add @tauri-apps/plugin-sql"
/>
</TabItem>
<TabItem label="Git">
<CommandTabs
npm="npm add https://github.com/tauri-apps/tauri-plugin-sql#v2"
yarn="yarn add https://github.com/tauri-apps/tauri-plugin-sql#v2"
pnpm="pnpm add https://github.com/tauri-apps/tauri-plugin-sql#v2"
/>
</TabItem>
<TabItem label="npm.io">
<CommandTabs
npm="npm add @tauri-apps/plugin-sql"
yarn="yarn add @tauri-apps/plugin-sql"
pnpm="pnpm add @tauri-apps/plugin-sql"
/>
</TabItem>
<TabItem label="Git">
<CommandTabs
npm="npm add https://github.com/tauri-apps/tauri-plugin-sql#v2"
yarn="yarn add https://github.com/tauri-apps/tauri-plugin-sql#v2"
pnpm="pnpm add https://github.com/tauri-apps/tauri-plugin-sql#v2"
/>
</TabItem>
</Tabs>
## Usage
@@ -230,11 +230,11 @@ See [Access Control List](/reference/acl/) for more information.
```json title="src-tauri/capabilities/main.json" ins={7-8}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": ["sql:allow-load", "sql:allow-execute"]
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": ["sql:allow-load", "sql:allow-execute"]
}
```

View File

@@ -146,16 +146,16 @@ See [Access Control List](/reference/acl/) for more information.
```json title="src-tauri/capabilities/main.json" ins={6-11}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"store:allow-get",
"store:allow-set",
"store:allow-save",
"store:allow-load"
]
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"store:allow-get",
"store:allow-set",
"store:allow-save",
"store:allow-load"
]
}
```

View File

@@ -13,5 +13,5 @@ import PluginLinks from '@components/PluginLinks.astro';
<PluginLinks plugin="updater" />
<Stub>
Based on https://github.com/tauri-apps/plugins-workspace/tree/plugins/updater
Based on https://github.com/tauri-apps/plugins-workspace/tree/plugins/updater
</Stub>

View File

@@ -74,10 +74,10 @@ Here's an example of how you can use the plugin to upload and download files:
import { upload } from '@tauri-apps/plugin-upload';
upload(
'https://example.com/file-upload',
'./path/to/my/file.txt',
(progress, total) => console.log(`Uploaded ${progress} of ${total} bytes`), // a callback that will be called with the upload progress
{ 'Content-Type': 'text/plain' } // optional headers to send with the request
'https://example.com/file-upload',
'./path/to/my/file.txt',
(progress, total) => console.log(`Uploaded ${progress} of ${total} bytes`), // a callback that will be called with the upload progress
{ 'Content-Type': 'text/plain' } // optional headers to send with the request
);
```
@@ -85,9 +85,9 @@ upload(
import { download } from '@tauri-apps/plugin-upload';
download(
'https://example.com/file-download-link',
'./path/to/save/my/file.txt',
(progress, total) => console.log(`Downloaded ${progress} of ${total} bytes`), // a callback that will be called with the download progress
{ 'Content-Type': 'text/plain' } // optional headers to send with the request
'https://example.com/file-download-link',
'./path/to/save/my/file.txt',
(progress, total) => console.log(`Downloaded ${progress} of ${total} bytes`), // a callback that will be called with the download progress
{ 'Content-Type': 'text/plain' } // optional headers to send with the request
);
```

View File

@@ -87,7 +87,7 @@ import WebSocket from '@tauri-apps/plugin-websocket';
const ws = await WebSocket.connect('ws://127.0.0.1:8080');
ws.addListener((msg) => {
console.log('Received Message:', msg);
console.log('Received Message:', msg);
});
await ws.send('Hello World!');
@@ -103,11 +103,11 @@ See [Access Control List](/reference/acl/) for more information.
```json title="src-tauri/capabilities/main.json" ins={6}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": ["websocket:default"]
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": ["websocket:default"]
}
```

View File

@@ -57,11 +57,11 @@ See [Access Control List](/reference/acl/) for more information.
```json title="src-tauri/capabilities/main.json" ins={7-8}
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": ["window:default", "window:allow-start-dragging"]
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": ["window:default", "window:allow-start-dragging"]
}
```
@@ -80,27 +80,27 @@ Add this CSS sample to keep it at the top of the screen and style the buttons:
```css
.titlebar {
height: 30px;
background: #329ea3;
user-select: none;
display: flex;
justify-content: flex-end;
position: fixed;
top: 0;
left: 0;
right: 0;
height: 30px;
background: #329ea3;
user-select: none;
display: flex;
justify-content: flex-end;
position: fixed;
top: 0;
left: 0;
right: 0;
}
.titlebar-button {
display: inline-flex;
justify-content: center;
align-items: center;
width: 30px;
height: 30px;
user-select: none;
-webkit-user-select: none;
display: inline-flex;
justify-content: center;
align-items: center;
width: 30px;
height: 30px;
user-select: none;
-webkit-user-select: none;
}
.titlebar-button:hover {
background: #5bbec3;
background: #5bbec3;
}
```
@@ -110,21 +110,21 @@ Put this at the top of your `<body>` tag:
```html
<div data-tauri-drag-region class="titlebar">
<div class="titlebar-button" id="titlebar-minimize">
<img
src="https://api.iconify.design/mdi:window-minimize.svg"
alt="minimize"
/>
</div>
<div class="titlebar-button" id="titlebar-maximize">
<img
src="https://api.iconify.design/mdi:window-maximize.svg"
alt="maximize"
/>
</div>
<div class="titlebar-button" id="titlebar-close">
<img src="https://api.iconify.design/mdi:close.svg" alt="close" />
</div>
<div class="titlebar-button" id="titlebar-minimize">
<img
src="https://api.iconify.design/mdi:window-minimize.svg"
alt="minimize"
/>
</div>
<div class="titlebar-button" id="titlebar-maximize">
<img
src="https://api.iconify.design/mdi:window-maximize.svg"
alt="maximize"
/>
</div>
<div class="titlebar-button" id="titlebar-close">
<img src="https://api.iconify.design/mdi:close.svg" alt="close" />
</div>
</div>
```
@@ -140,14 +140,14 @@ import { Window } from '@tauri-apps/api/window';
const appWindow = new Window('main');
document
.getElementById('titlebar-minimize')
?.addEventListener('click', () => appWindow.minimize());
.getElementById('titlebar-minimize')
?.addEventListener('click', () => appWindow.minimize());
document
.getElementById('titlebar-maximize')
?.addEventListener('click', () => appWindow.toggleMaximize());
.getElementById('titlebar-maximize')
?.addEventListener('click', () => appWindow.toggleMaximize());
document
.getElementById('titlebar-close')
?.addEventListener('click', () => appWindow.close());
.getElementById('titlebar-close')
?.addEventListener('click', () => appWindow.close());
```
### (macOS) Transparent Titlebar with Custom Window Background Color

View File

@@ -35,10 +35,10 @@ Use your project's package manager to add the dependency:
{' '}
<CommandTabs
npm="npm run tauri add window-state"
yarn="yarn run tauri add window-state"
pnpm="pnpm tauri add window-state"
cargo="cargo tauri add window-state"
npm="npm run tauri add window-state"
yarn="yarn run tauri add window-state"
pnpm="pnpm tauri add window-state"
cargo="cargo tauri add window-state"
/>
</TabItem>
@@ -98,8 +98,8 @@ Similarly you can manually restore a window's state from disk:
```javascript
import {
restoreStateCurrent,
StateFlags,
restoreStateCurrent,
StateFlags,
} from '@tauri-apps/plugin-window-state';
restoreStateCurrent(StateFlags.ALL);

View File

@@ -20,20 +20,20 @@ The following JSON defines a capability that enables default functionality for c
```json title="src-tauri/capabilities/main.json
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"path:default",
"event:default",
"window:default",
"app:default",
"resources:default",
"menu:default",
"tray:default",
"window:allow-set-title"
]
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"path:default",
"event:default",
"window:default",
"app:default",
"resources:default",
"menu:default",
"tray:default",
"window:allow-set-title"
]
}
```
@@ -47,11 +47,11 @@ For example, let's define a capability for desktop. Note it enables permissions
```json title="src-tauri/capabilities/desktop.json
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "desktop-capability",
"windows": ["main"],
"platforms": ["linux", "macOS", "windows"],
"permissions": ["global-shortcut:allow-register"]
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "desktop-capability",
"windows": ["main"],
"platforms": ["linux", "macOS", "windows"],
"permissions": ["global-shortcut:allow-register"]
}
```
@@ -59,15 +59,15 @@ And define a capability for mobile. Note it enables permissions on plugins that
```json title="src-tauri/capabilities/mobile.json
{
"$schema": "../gen/schemas/mobile-schema.json",
"identifier": "mobile-capability",
"windows": ["main"],
"platforms": ["iOS", "android"],
"permissions": [
"nfc:allow-scan",
"biometric:allow-authenticate",
"barcode-scanner:allow-scan"
]
"$schema": "../gen/schemas/mobile-schema.json",
"identifier": "mobile-capability",
"windows": ["main"],
"platforms": ["iOS", "android"],
"permissions": [
"nfc:allow-scan",
"biometric:allow-authenticate",
"barcode-scanner:allow-scan"
]
}
```
@@ -81,11 +81,11 @@ In the example above we defined specific capabilities for desktop and mobile, an
```json title="src-tauri/capabilities/linux.json
{
"$schema": "../gen/schemas/linux-schema.json",
"identifier": "linux-capability",
"windows": ["main"],
"platforms": ["iOS", "android"],
"permissions": ["dbus::call"]
"$schema": "../gen/schemas/linux-schema.json",
"identifier": "linux-capability",
"windows": ["main"],
"platforms": ["iOS", "android"],
"permissions": ["dbus::call"]
}
```
@@ -115,12 +115,12 @@ Let's study some examples for official plugins:
```json title="src-tauri/capabilities/base.json
{
"permissions": [
{
"identifier": "fs:scope",
"allow": [{ "path": "$APPDATA" }, { "path": "$APPDATA/**" }]
}
]
"permissions": [
{
"identifier": "fs:scope",
"allow": [{ "path": "$APPDATA" }, { "path": "$APPDATA/**" }]
}
]
}
```
@@ -132,12 +132,12 @@ Let's study some examples for official plugins:
```json title="src-tauri/capabilities/base.json
{
"permissions": [
{
"identifier": "fs:allow-rename",
"allow": [{ "path": "$HOME/**" }]
}
]
"permissions": [
{
"identifier": "fs:allow-rename",
"allow": [{ "path": "$HOME/**" }]
}
]
}
```
@@ -145,13 +145,13 @@ Let's study some examples for official plugins:
```json title="src-tauri/capabilities/base.json
{
"permissions": [
{
"identifier": "http:default",
"allow": [{ "url": "https://*.tauri.app" }],
"deny": [{ "url": "https://private.tauri.app" }]
}
]
"permissions": [
{
"identifier": "http:default",
"allow": [{ "url": "https://*.tauri.app" }],
"deny": [{ "url": "https://private.tauri.app" }]
}
]
}
```

View File

@@ -12,10 +12,10 @@ The Tauri command line interface (CLI) is the way to interact with Tauri through
You can add the Tauri CLI to your current project using your package manager of choice:
<CommandTabs
npm="npm install --save-dev @tauri-apps/cli@next"
yarn="yarn add -D @tauri-apps/cli@next"
pnpm="pnpm add -D @tauri-apps/cli@next"
cargo='cargo install tauri-cli --version "^2.0.0-beta"'
npm="npm install --save-dev @tauri-apps/cli@next"
yarn="yarn add -D @tauri-apps/cli@next"
pnpm="pnpm add -D @tauri-apps/cli@next"
cargo='cargo install tauri-cli --version "^2.0.0-beta"'
/>
{/* TODO: 2.0 */}
@@ -47,10 +47,10 @@ For CLI commands related to developing plugins visit the [Develop a Tauri Plugin
## `build`
<CommandTabs
npm="npm run tauri build"
yarn="yarn tauri build"
pnpm="pnpm tauri build"
cargo="cargo tauri build"
npm="npm run tauri build"
yarn="yarn tauri build"
pnpm="pnpm tauri build"
cargo="cargo tauri build"
/>
```
@@ -94,10 +94,10 @@ Options:
## `dev`
<CommandTabs
npm="npm run tauri dev"
yarn="yarn tauri dev"
pnpm="pnpm tauri dev"
cargo="cargo tauri dev"
npm="npm run tauri dev"
yarn="yarn tauri dev"
pnpm="pnpm tauri dev"
cargo="cargo tauri dev"
/>
```
@@ -131,10 +131,10 @@ If you have entered a command to the `build.beforeDevCommand` property, this one
## `icon`
<CommandTabs
npm="npm run tauri icon"
yarn="yarn tauri icon"
pnpm="pnpm tauri icon"
cargo="cargo tauri icon"
npm="npm run tauri icon"
yarn="yarn tauri icon"
pnpm="pnpm tauri icon"
cargo="cargo tauri icon"
/>
```
@@ -157,10 +157,10 @@ Options:
## `info`
<CommandTabs
npm="npm run tauri info"
yarn="yarn tauri info"
pnpm="pnpm tauri info"
cargo="cargo tauri info"
npm="npm run tauri info"
yarn="yarn tauri info"
pnpm="pnpm tauri info"
cargo="cargo tauri info"
/>
```
@@ -180,10 +180,10 @@ Options:
## `init`
<CommandTabs
npm="npm run tauri init"
yarn="yarn tauri init"
pnpm="pnpm tauri init"
cargo="cargo tauri init"
npm="npm run tauri init"
yarn="yarn tauri init"
pnpm="pnpm tauri init"
cargo="cargo tauri init"
/>
```
@@ -225,10 +225,10 @@ Options:
## `add`
<CommandTabs
npm="npm run tauri add"
yarn="yarn tauri add"
pnpm="pnpm tauri add"
cargo="cargo tauri add"
npm="npm run tauri add"
yarn="yarn tauri add"
pnpm="pnpm tauri add"
cargo="cargo tauri add"
/>
```
@@ -251,10 +251,10 @@ Options:
## `signer`
<CommandTabs
npm="npm run tauri signer"
yarn="yarn tauri signer"
pnpm="pnpm tauri signer"
cargo="cargo tauri signer"
npm="npm run tauri signer"
yarn="yarn tauri signer"
pnpm="pnpm tauri signer"
cargo="cargo tauri signer"
/>
```
@@ -276,10 +276,10 @@ Options:
## `completions`
<CommandTabs
npm="npm run tauri completions"
yarn="yarn tauri completions"
pnpm="pnpm tauri completions"
cargo="cargo tauri completions"
npm="npm run tauri completions"
yarn="yarn tauri completions"
pnpm="pnpm tauri completions"
cargo="cargo tauri completions"
/>
```
@@ -432,10 +432,10 @@ Add-Content -Path $profile -Value '& "$PSScriptRoot\_tauri.ps1"'
## `android`
<CommandTabs
npm="npm run tauri android"
yarn="yarn tauri android"
pnpm="pnpm tauri android"
cargo="cargo tauri android"
npm="npm run tauri android"
yarn="yarn tauri android"
pnpm="pnpm tauri android"
cargo="cargo tauri android"
/>
```
@@ -459,10 +459,10 @@ Options:
## `ios`
<CommandTabs
npm="npm run tauri ios"
yarn="yarn tauri ios"
pnpm="pnpm tauri ios"
cargo="cargo tauri ios"
npm="npm run tauri ios"
yarn="yarn tauri ios"
pnpm="pnpm tauri ios"
cargo="cargo tauri ios"
/>
```
@@ -486,10 +486,10 @@ Options:
## `migrate`
<CommandTabs
npm="npm run tauri migrate"
yarn="yarn tauri migrate"
pnpm="pnpm tauri migrate"
cargo="cargo tauri migrate"
npm="npm run tauri migrate"
yarn="yarn tauri migrate"
pnpm="pnpm tauri migrate"
cargo="cargo tauri migrate"
/>
```
@@ -506,10 +506,10 @@ Options:
## `permission`
<CommandTabs
npm="npm tauri permission"
yarn="yarn tauri permission"
pnpm="pnpm tauri permission"
cargo="cargo tauri permission"
npm="npm tauri permission"
yarn="yarn tauri permission"
pnpm="pnpm tauri permission"
cargo="cargo tauri permission"
/>
```
@@ -591,10 +591,10 @@ Options:
## `capability`
<CommandTabs
npm="npm tauri capability"
yarn="yarn tauri capability"
pnpm="pnpm tauri capability"
cargo="cargo tauri capability"
npm="npm tauri capability"
yarn="yarn tauri capability"
pnpm="pnpm tauri capability"
cargo="cargo tauri capability"
/>
```
@@ -634,10 +634,10 @@ Options:
## `help`
<CommandTabs
npm="npm run tauri help"
yarn="yarn tauri help"
pnpm="pnpm tauri help"
cargo="cargo tauri help"
npm="npm run tauri help"
yarn="yarn tauri help"
pnpm="pnpm tauri help"
cargo="cargo tauri help"
/>
```

View File

@@ -43,20 +43,20 @@ for core plugins and the `window.setTitle` API.
```json title="src-tauri/capabilities/main.json
{
"$schema": "./schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"path:default",
"event:default",
"window:default",
"app:default",
"resources:default",
"menu:default",
"tray:default",
"window:allow-set-title"
]
"$schema": "./schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"path:default",
"event:default",
"window:default",
"app:default",
"resources:default",
"menu:default",
"tray:default",
"window:allow-set-title"
]
}
```
@@ -72,11 +72,11 @@ capability files in the `capabilities` directory.
```json title=src-tauri/tauri.conf.json
{
"app": {
"security": {
"capabilities": ["my-capability", "main-capability"]
}
}
"app": {
"security": {
"capabilities": ["my-capability", "main-capability"]
}
}
}
```
@@ -84,19 +84,19 @@ Inline capabilities can be mixed with pre-defined capabilities.
```json title=src-tauri/tauri.conf.json
{
"app": {
"security": {
"capabilities": [
{
"identifier": "my-capability",
"description": "My application capability used for all windows",
"windows": ["*"],
"permissions": ["fs:default", "allow-home-read-extended"]
},
"my-second-capability"
]
}
}
"app": {
"security": {
"capabilities": [
{
"identifier": "my-capability",
"description": "My application capability used for all windows",
"windows": ["*"],
"permissions": ["fs:default", "allow-home-read-extended"]
},
"my-second-capability"
]
}
}
}
```
@@ -111,11 +111,11 @@ Note it enables permissions on plugins that are only available on desktop:
```json title="src-tauri/capabilities/desktop.json
{
"$schema": "./schemas/desktop-schema.json",
"identifier": "desktop-capability",
"windows": ["main"],
"platforms": ["linux", "macOS", "windows"],
"permissions": ["global-shortcut:allow-register"]
"$schema": "./schemas/desktop-schema.json",
"identifier": "desktop-capability",
"windows": ["main"],
"platforms": ["linux", "macOS", "windows"],
"permissions": ["global-shortcut:allow-register"]
}
```
@@ -124,15 +124,15 @@ Note it enables permissions on plugins that are only available on mobile:
```json title="src-tauri/capabilities/mobile.json
{
"$schema": "./schemas/mobile-schema.json",
"identifier": "mobile-capability",
"windows": ["main"],
"platforms": ["iOS", "android"],
"permissions": [
"nfc:allow-scan",
"biometric:allow-authenticate",
"barcode-scanner:allow-scan"
]
"$schema": "./schemas/mobile-schema.json",
"identifier": "mobile-capability",
"windows": ["main"],
"platforms": ["iOS", "android"],
"permissions": [
"nfc:allow-scan",
"biometric:allow-authenticate",
"barcode-scanner:allow-scan"
]
}
```

Some files were not shown because too many files have changed in this diff Show More