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, "printWidth": 100,
"semi": true, "semi": true,
"singleQuote": true, "singleQuote": true,
"tabWidth": 2, "tabWidth": 2,
"trailingComma": "es5", "trailingComma": "es5",
"useTabs": true, "useTabs": false,
"plugins": ["prettier-plugin-astro"], "plugins": ["prettier-plugin-astro"],
"overrides": [ "overrides": [
{ {
"files": "*.astro", "files": "*.astro",
"options": { "options": {
"parser": "astro" "parser": "astro"
} }
}, },
{ {
"files": ["*.json", "*.md", "*.toml", "*.yml"], "files": ["*.json", "*.md", "*.toml", "*.yml"],
"options": { "options": {
"useTabs": false "useTabs": false
} }
}, },
{ {
"files": ["*.md", "*.mdx"], "files": ["*.md", "*.mdx"],
"options": { "options": {
"printWidth": 80 "printWidth": 80
} }
} }
] ]
} }

View File

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

View File

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

View File

@@ -2,18 +2,18 @@ import { TranslationStatusBuilder } from './lib/translation-status/builder';
import locales from '../../locales.json'; import locales from '../../locales.json';
const translationStatusBuilder = new TranslationStatusBuilder({ const translationStatusBuilder = new TranslationStatusBuilder({
pageSourceDir: '../../src/content/docs', pageSourceDir: '../../src/content/docs',
htmlOutputFilePath: '../../dist/contribute/translate-status.html', htmlOutputFilePath: '../../dist/contribute/translate-status.html',
sourceLanguage: 'en', sourceLanguage: 'en',
targetLanguages: Object.values(locales) targetLanguages: Object.values(locales)
.reduce((acc, { lang }) => (lang !== 'en' ? [lang, ...acc] : acc), []) .reduce((acc, { lang }) => (lang !== 'en' ? [lang, ...acc] : acc), [])
.sort(), .sort(),
languageLabels: Object.values(locales) languageLabels: Object.values(locales)
.filter((loc) => loc.lang !== 'en') .filter((loc) => loc.lang !== 'en')
.reduce((acc, curr) => ({ [curr.lang]: curr.label, ...acc }), {}), .reduce((acc, curr) => ({ [curr.lang]: curr.label, ...acc }), {}),
githubRepo: process.env.GITHUB_REPOSITORY || 'tauri-apps/tauri-docs', githubRepo: process.env.GITHUB_REPOSITORY || 'tauri-apps/tauri-docs',
gitHubRef: 'v2', gitHubRef: 'v2',
githubToken: process.env.GITHUB_TOKEN, githubToken: process.env.GITHUB_TOKEN,
}); });
await translationStatusBuilder.run(); await translationStatusBuilder.run();

View File

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

View File

@@ -8,7 +8,7 @@ export const isCi = process.env.CI;
* @param {...any} params * @param {...any} params
*/ */
export function debug(message, ...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 * @param {...any} params
*/ */
export function warning(message, ...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 * @param {...any} params
*/ */
export function error(message, ...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. * while leaving new paragraphs intact.
*/ */
export function dedentMd(...markdown) { 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 * @param {string} template
*/ */
export function formatCount(count, template) { export function formatCount(count, template) {
/** @param {string} text */ /** @param {string} text */
const wrapWithCount = (text) => { const wrapWithCount = (text) => {
// If no count was given, we're outputting a single issue in annotations, // 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 // so omit count and capitalize the first letter of the issue type description
if (count === undefined) return text[0].toUpperCase() + text.slice(1); if (count === undefined) return text[0].toUpperCase() + text.slice(1);
// Otherwise, prefix the issue type description with count // Otherwise, prefix the issue type description with count
return `${count} ${text}`; return `${count} ${text}`;
}; };
const usePlural = count !== undefined && count !== 1; const usePlural = count !== undefined && count !== 1;
const templateParts = template.split('|'); const templateParts = template.split('|');
const usedTemplate = templateParts.length === 2 ? templateParts[usePlural ? 1 : 0] : template; const usedTemplate = templateParts.length === 2 ? templateParts[usePlural ? 1 : 0] : template;
return wrapWithCount(usedTemplate.replace(/\(s\)/g, usePlural ? 's' : '')); return wrapWithCount(usedTemplate.replace(/\(s\)/g, usePlural ? 's' : ''));
} }
export default { export default {
debug, debug,
warning, warning,
error, error,
dedentMd, dedentMd,
formatCount, formatCount,
isCi, 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; export const COMMIT_IGNORE = /(en-only|typo|broken link|i18nReady|i18nIgnore)/i;
interface PullRequest { interface PullRequest {
html_url: string; html_url: string;
title: string; title: string;
labels: { labels: {
name: string; name: string;
}[]; }[];
} }
/** /**
@@ -27,458 +27,458 @@ interface PullRequest {
* This code is designed to be run on every push to the `main` branch. * This code is designed to be run on every push to the `main` branch.
*/ */
export class TranslationStatusBuilder { export class TranslationStatusBuilder {
constructor(config: { constructor(config: {
pageSourceDir: string; pageSourceDir: string;
/** /**
* Full path & file name of the HTML file that the translation status should be written to. * 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. * If the parent path does not exist yet, it will be created.
* */ * */
htmlOutputFilePath: string; htmlOutputFilePath: string;
sourceLanguage: string; sourceLanguage: string;
targetLanguages: string[]; targetLanguages: string[];
languageLabels: { [key: string]: string }; languageLabels: { [key: string]: string };
githubRepo: string; githubRepo: string;
gitHubRef: string; gitHubRef: string;
githubToken?: string | undefined; githubToken?: string | undefined;
}) { }) {
this.pageSourceDir = config.pageSourceDir; this.pageSourceDir = config.pageSourceDir;
this.htmlOutputFilePath = path.resolve(config.htmlOutputFilePath); this.htmlOutputFilePath = path.resolve(config.htmlOutputFilePath);
this.sourceLanguage = config.sourceLanguage; this.sourceLanguage = config.sourceLanguage;
this.targetLanguages = config.targetLanguages; this.targetLanguages = config.targetLanguages;
this.languageLabels = config.languageLabels; this.languageLabels = config.languageLabels;
this.githubRepo = config.githubRepo; this.githubRepo = config.githubRepo;
this.githubRef = config.gitHubRef; this.githubRef = config.gitHubRef;
this.githubToken = config.githubToken ?? ''; this.githubToken = config.githubToken ?? '';
this.git = simpleGit({ this.git = simpleGit({
maxConcurrentProcesses: Math.max(2, Math.min(32, os.cpus().length)), maxConcurrentProcesses: Math.max(2, Math.min(32, os.cpus().length)),
}); });
} }
readonly pageSourceDir; readonly pageSourceDir;
readonly htmlOutputFilePath; readonly htmlOutputFilePath;
readonly sourceLanguage; readonly sourceLanguage;
readonly targetLanguages; readonly targetLanguages;
readonly languageLabels; readonly languageLabels;
readonly githubRepo; readonly githubRepo;
readonly githubRef; readonly githubRef;
readonly githubToken; readonly githubToken;
readonly git; readonly git;
async run() { async run() {
// Before we start, validate that this is not a shallow clone of the repo // Before we start, validate that this is not a shallow clone of the repo
const isShallowRepo = await this.git.revparse(['--is-shallow-repository']); const isShallowRepo = await this.git.revparse(['--is-shallow-repository']);
if (isShallowRepo !== 'false') { if (isShallowRepo !== 'false') {
output.error(dedent`This script cannot operate on a shallow clone of the git repository. 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: Please add the checkout setting "fetch-depth: 0" to your GitHub workflow:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
fetch-depth: 0 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 // Ensure that the output directory exists before continuing
output.debug(`- Output file path: ${this.htmlOutputFilePath}`); output.debug(`- Output file path: ${this.htmlOutputFilePath}`);
const outputDir = path.dirname(this.htmlOutputFilePath); const outputDir = path.dirname(this.htmlOutputFilePath);
if (!fs.existsSync(outputDir)) { if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true }); fs.mkdirSync(outputDir, { recursive: true });
} }
// Create an index of all Markdown/MDX pages grouped by language, // Create an index of all Markdown/MDX pages grouped by language,
// with information about the last minor & major commit per page // with information about the last minor & major commit per page
output.debug(`- Generating page index...`); output.debug(`- Generating page index...`);
const pages = await this.createPageIndex(); const pages = await this.createPageIndex();
// Determine translation status by source page // Determine translation status by source page
const statusByPage = this.getTranslationStatusByPage(pages); const statusByPage = this.getTranslationStatusByPage(pages);
// Fetch all pull requests // Fetch all pull requests
const pullRequests = await this.getPullRequests(); const pullRequests = await this.getPullRequests();
// Render a human-friendly summary // Render a human-friendly summary
output.debug(`- Building HTML file...`); output.debug(`- Building HTML file...`);
const html = this.renderHtmlStatusPage(statusByPage, pullRequests); const html = this.renderHtmlStatusPage(statusByPage, pullRequests);
// Write HTML output to file // Write HTML output to file
fs.writeFileSync(this.htmlOutputFilePath, html); fs.writeFileSync(this.htmlOutputFilePath, html);
output.debug(''); output.debug('');
output.debug('*** Success!'); output.debug('*** Success!');
output.debug(''); output.debug('');
} }
/** Get all pull requests with the `i18n` tag */ /** Get all pull requests with the `i18n` tag */
async getPullRequests() { async getPullRequests() {
const pullRequests: PullRequest[] = await githubGet({ const pullRequests: PullRequest[] = await githubGet({
url: `https://api.github.com/repos/${this.githubRepo}/pulls?state=open&per_page=100`, url: `https://api.github.com/repos/${this.githubRepo}/pulls?state=open&per_page=100`,
githubToken: this.githubToken, 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> { async createPageIndex(): Promise<PageIndex> {
// Initialize a new page index with a stable key order // Initialize a new page index with a stable key order
const pages: PageIndex = { const pages: PageIndex = {
[this.sourceLanguage]: {}, [this.sourceLanguage]: {},
}; };
this.targetLanguages.forEach((lang) => (pages[lang.toLowerCase()] = {})); this.targetLanguages.forEach((lang) => (pages[lang.toLowerCase()] = {}));
// Enumerate all markdown pages with supported languages in pageSourceDir, // Enumerate all markdown pages with supported languages in pageSourceDir,
// retrieve their page data and update them // retrieve their page data and update them
const pagePaths = await glob(`**/*.{md,mdx}`, { const pagePaths = await glob(`**/*.{md,mdx}`, {
cwd: this.pageSourceDir, cwd: this.pageSourceDir,
}); });
const updatedPages = await Promise.all( const updatedPages = await Promise.all(
pagePaths.sort().map(async (pagePath) => { pagePaths.sort().map(async (pagePath) => {
const pathParts = pagePath.split('/'); const pathParts = pagePath.split('/');
const isLanguageSubpathIncluded = this.targetLanguages const isLanguageSubpathIncluded = this.targetLanguages
.map((el) => el.toLowerCase()) .map((el) => el.toLowerCase())
.includes(pathParts[0]!); .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. // 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 lang = isLanguageSubpathIncluded ? pathParts[0] : this.sourceLanguage;
const subpath = pathParts.splice(1).join('/'); const subpath = pathParts.splice(1).join('/');
// Create or update page data for the page // Create or update page data for the page
return { return {
lang, lang,
subpath: isLanguageSubpathIncluded ? subpath : pagePath, subpath: isLanguageSubpathIncluded ? subpath : pagePath,
pageData: await this.getSinglePageData(pagePath), pageData: await this.getSinglePageData(pagePath),
}; };
}) })
); );
// Write the updated pages to the index // Write the updated pages to the index
updatedPages.forEach((page) => { updatedPages.forEach((page) => {
if (!page) return; if (!page) return;
const { lang, subpath, pageData } = page; const { lang, subpath, pageData } = page;
if (!pageData) return; if (!pageData) return;
pages[lang!]![subpath] = pageData; pages[lang!]![subpath] = pageData;
}); });
return pages; return pages;
} }
/** /**
* Processes the markdown page located in the pageSourceDir subpath `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. * and creates a new page data object based on its frontmatter and git history.
*/ */
async getSinglePageData(pagePath: string): Promise<PageData | undefined> { async getSinglePageData(pagePath: string): Promise<PageData | undefined> {
const fullFilePath = `${this.pageSourceDir}/${pagePath}`; const fullFilePath = `${this.pageSourceDir}/${pagePath}`;
// Retrieve git history for the current page // Retrieve git history for the current page
const gitHistory = await this.getGitHistory(fullFilePath); const gitHistory = await this.getGitHistory(fullFilePath);
if (!gitHistory) return; if (!gitHistory) return;
// Retrieve i18nReady flag from frontmatter // Retrieve i18nReady flag from frontmatter
const frontMatterBlock = tryGetFrontMatterBlock(fullFilePath); const frontMatterBlock = tryGetFrontMatterBlock(fullFilePath);
const i18nReady = /^\s*i18nReady:\s*true\s*$/m.test(frontMatterBlock); const i18nReady = /^\s*i18nReady:\s*true\s*$/m.test(frontMatterBlock);
return { return {
...(i18nReady ? { i18nReady: true } : {}), ...(i18nReady ? { i18nReady: true } : {}),
lastChange: gitHistory.lastCommitDate, lastChange: gitHistory.lastCommitDate,
lastCommitMsg: gitHistory.lastCommitMessage, lastCommitMsg: gitHistory.lastCommitMessage,
lastMajorChange: gitHistory.lastMajorCommitDate, lastMajorChange: gitHistory.lastMajorCommitDate,
lastMajorCommitMsg: gitHistory.lastMajorCommitMessage, lastMajorCommitMsg: gitHistory.lastMajorCommitMessage,
}; };
} }
async getGitHistory(filePath: string) { async getGitHistory(filePath: string) {
const gitLog = await this.git.log({ const gitLog = await this.git.log({
file: filePath, file: filePath,
strictDate: true, strictDate: true,
}); });
const lastCommit = gitLog.latest; const lastCommit = gitLog.latest;
if (!lastCommit) { if (!lastCommit) {
return; return;
// Disabled since we have generated reference files // Disabled since we have generated reference files
// throw new Error(dedent`Failed to retrieve last commit information for file // throw new Error(dedent`Failed to retrieve last commit information for file
// "${filePath}". Your working copy should not contain uncommitted new pages // "${filePath}". Your working copy should not contain uncommitted new pages
// when running this script.`); // when running this script.`);
} }
// Attempt to find the last "major" commit, ignoring any commits that // Attempt to find the last "major" commit, ignoring any commits that
// usually do not require translations to be updated // usually do not require translations to be updated
const lastMajorCommit = const lastMajorCommit =
gitLog.all.find((logEntry) => { gitLog.all.find((logEntry) => {
return !logEntry.message.match(COMMIT_IGNORE); return !logEntry.message.match(COMMIT_IGNORE);
}) || lastCommit; }) || lastCommit;
return { return {
lastCommitMessage: lastCommit.message, lastCommitMessage: lastCommit.message,
lastCommitDate: toUtcString(lastCommit.date), lastCommitDate: toUtcString(lastCommit.date),
lastMajorCommitMessage: lastMajorCommit.message, lastMajorCommitMessage: lastMajorCommit.message,
lastMajorCommitDate: toUtcString(lastMajorCommit.date), lastMajorCommitDate: toUtcString(lastMajorCommit.date),
}; };
} }
getTranslationStatusByPage(pages: PageIndex): PageTranslationStatus[] { getTranslationStatusByPage(pages: PageIndex): PageTranslationStatus[] {
const sourcePages = pages[this.sourceLanguage]; const sourcePages = pages[this.sourceLanguage];
const arrContent: PageTranslationStatus[] = []; const arrContent: PageTranslationStatus[] = [];
Object.keys(sourcePages!).forEach((subpath) => { Object.keys(sourcePages!).forEach((subpath) => {
const sourcePage = sourcePages![subpath]!; const sourcePage = sourcePages![subpath]!;
if (!sourcePage.i18nReady) return; if (!sourcePage.i18nReady) return;
const content: PageTranslationStatus = { const content: PageTranslationStatus = {
subpath, subpath,
sourcePage, sourcePage,
githubUrl: this.getPageUrl({ lang: this.sourceLanguage, subpath }), githubUrl: this.getPageUrl({ lang: this.sourceLanguage, subpath }),
translations: {}, translations: {},
}; };
this.targetLanguages.forEach((lang) => { this.targetLanguages.forEach((lang) => {
const i18nPage = pages[lang.toLowerCase()]![subpath]!; const i18nPage = pages[lang.toLowerCase()]![subpath]!;
content.translations[lang] = { content.translations[lang] = {
page: i18nPage, page: i18nPage,
isMissing: !i18nPage, isMissing: !i18nPage,
isOutdated: i18nPage && sourcePage.lastMajorChange > i18nPage.lastMajorChange, isOutdated: i18nPage && sourcePage.lastMajorChange > i18nPage.lastMajorChange,
githubUrl: this.getPageUrl({ lang, subpath }), githubUrl: this.getPageUrl({ lang, subpath }),
sourceHistoryUrl: this.getPageUrl({ sourceHistoryUrl: this.getPageUrl({
lang: 'en', lang: 'en',
subpath, subpath,
type: 'commits', type: 'commits',
query: i18nPage ? `?since=${i18nPage.lastMajorChange}` : '', query: i18nPage ? `?since=${i18nPage.lastMajorChange}` : '',
}), }),
}; };
}); });
arrContent.push(content); arrContent.push(content);
}); });
return arrContent; return arrContent;
} }
getPageUrl({ getPageUrl({
type = 'blob', type = 'blob',
lang, lang,
subpath, subpath,
query = '', query = '',
}: { }: {
type?: string; type?: string;
refName?: string; refName?: string;
lang: string; lang: string;
subpath: string; subpath: string;
query?: string; query?: string;
}) { }) {
const noDotSrcDir = this.pageSourceDir.replaceAll(/\.+\//g, ''); const noDotSrcDir = this.pageSourceDir.replaceAll(/\.+\//g, '');
const isSrcLang = lang === this.sourceLanguage; const isSrcLang = lang === this.sourceLanguage;
return `https://github.com/${this.githubRepo}/${type}/${this.githubRef}/${noDotSrcDir}${ return `https://github.com/${this.githubRepo}/${type}/${this.githubRef}/${noDotSrcDir}${
isSrcLang ? '' : `/${lang}` isSrcLang ? '' : `/${lang}`
}/${subpath}${query}`; }/${subpath}${query}`;
} }
/** /**
* Renders the primary HTML output of this script by loading a template from disk, * 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. * rendering the individual views to HTML, and inserting them into the template.
*/ */
renderHtmlStatusPage(statusByPage: PageTranslationStatus[], prs: PullRequest[]) { renderHtmlStatusPage(statusByPage: PageTranslationStatus[], prs: PullRequest[]) {
// Load HTML template // Load HTML template
const templateFilePath = path.join( const templateFilePath = path.join(
path.dirname(fileURLToPath(import.meta.url)), path.dirname(fileURLToPath(import.meta.url)),
'template.html' 'template.html'
); );
const html = fs.readFileSync(templateFilePath, { encoding: 'utf8' }); const html = fs.readFileSync(templateFilePath, { encoding: 'utf8' });
// Replace placeholders in the template with the rendered views // Replace placeholders in the template with the rendered views
// and return the resulting HTML page // and return the resulting HTML page
return html return html
.replace( .replace(
'<!-- TranslationStatusByLanguage -->', '<!-- TranslationStatusByLanguage -->',
this.renderTranslationStatusByLanguage(statusByPage) this.renderTranslationStatusByLanguage(statusByPage)
) )
.replace('<!-- TranslationNeedsReview -->', this.renderTranslationNeedsReview(prs)) .replace('<!-- TranslationNeedsReview -->', this.renderTranslationNeedsReview(prs))
.replace( .replace(
'<!-- TranslationStatusByPage -->', '<!-- TranslationStatusByPage -->',
this.renderTranslationStatusByPage(statusByPage) this.renderTranslationStatusByPage(statusByPage)
); );
} }
renderTranslationStatusByLanguage(statusByPage: PageTranslationStatus[]) { renderTranslationStatusByLanguage(statusByPage: PageTranslationStatus[]) {
const lines: string[] = []; const lines: string[] = [];
this.targetLanguages.forEach((lang) => { this.targetLanguages.forEach((lang) => {
const missing = statusByPage.filter((content) => content.translations[lang]!.isMissing); const missing = statusByPage.filter((content) => content.translations[lang]!.isMissing);
const outdated = statusByPage.filter((content) => content.translations[lang]!.isOutdated); const outdated = statusByPage.filter((content) => content.translations[lang]!.isOutdated);
lines.push('<details>'); lines.push('<details>');
lines.push( lines.push(
`<summary><strong>` + `<summary><strong>` +
`${this.languageLabels[lang]} (${lang})` + `${this.languageLabels[lang]} (${lang})` +
`</strong><br>` + `</strong><br>` +
`<span class="progress-summary">` + `<span class="progress-summary">` +
`${statusByPage.length - outdated.length - missing.length} done, ` + `${statusByPage.length - outdated.length - missing.length} done, ` +
`${outdated.length} need${outdated.length === 1 ? 's' : ''} updating, ` + `${outdated.length} need${outdated.length === 1 ? 's' : ''} updating, ` +
`${missing.length} missing` + `${missing.length} missing` +
`</span>` + `</span>` +
'<br>' + '<br>' +
this.renderProgressBar(statusByPage.length, outdated.length, missing.length) + this.renderProgressBar(statusByPage.length, outdated.length, missing.length) +
`</summary>` `</summary>`
); );
lines.push(``); lines.push(``);
if (outdated.length > 0) { if (outdated.length > 0) {
lines.push(`<h5>🔄&nbsp; Needs updating</h5>`); lines.push(`<h5>🔄&nbsp; Needs updating</h5>`);
lines.push(`<ul>`); lines.push(`<ul>`);
lines.push( lines.push(
...outdated.map( ...outdated.map(
(content) => (content) =>
`<li>` + `<li>` +
`${this.renderLink(content.githubUrl, content.subpath)} ` + `${this.renderLink(content.githubUrl, content.subpath)} ` +
`(${this.renderLink( `(${this.renderLink(
content.translations[lang]!.githubUrl, content.translations[lang]!.githubUrl,
'outdated translation' 'outdated translation'
)}, ${this.renderLink( )}, ${this.renderLink(
content.translations[lang]!.sourceHistoryUrl, content.translations[lang]!.sourceHistoryUrl,
'source change history' 'source change history'
)})` + )})` +
`</li>` `</li>`
) )
); );
lines.push(`</ul>`); lines.push(`</ul>`);
} }
if (missing.length > 0) { if (missing.length > 0) {
lines.push(`<h5>❌&nbsp; Missing</h5>`); lines.push(`<h5>❌&nbsp; Missing</h5>`);
lines.push(`<ul>`); lines.push(`<ul>`);
lines.push( lines.push(
...missing.map( ...missing.map(
(content) => (content) =>
`<li>` + `<li>` +
`${this.renderLink( `${this.renderLink(
content.githubUrl, content.githubUrl,
content.subpath content.subpath
)} &nbsp; ${this.renderCreatePageButton(lang, content.subpath)}` + )} &nbsp; ${this.renderCreatePageButton(lang, content.subpath)}` +
`</li>` `</li>`
) )
); );
lines.push(`</ul>`); lines.push(`</ul>`);
} }
lines.push(`</details>`); lines.push(`</details>`);
lines.push(``); lines.push(``);
}); });
return lines.join('\n'); return lines.join('\n');
} }
renderTranslationNeedsReview(prs: PullRequest[]) { renderTranslationNeedsReview(prs: PullRequest[]) {
const lines: string[] = []; const lines: string[] = [];
if (prs.length > 0) { if (prs.length > 0) {
lines.push(`<ul>`); lines.push(`<ul>`);
lines.push( lines.push(
...prs.map((pr) => { ...prs.map((pr) => {
const title = pr.title.replaceAll('`', ''); const title = pr.title.replaceAll('`', '');
return `<li>` + this.renderLink(pr.html_url, title) + `</li>`; return `<li>` + this.renderLink(pr.html_url, title) + `</li>`;
}) })
); );
lines.push(`</ul>`); lines.push(`</ul>`);
} }
lines.push(``); lines.push(``);
return lines.join('\n'); return lines.join('\n');
} }
renderTranslationStatusByPage(statusByPage: PageTranslationStatus[]) { renderTranslationStatusByPage(statusByPage: PageTranslationStatus[]) {
const lines: string[] = []; const lines: string[] = [];
lines.push('<div class="table-container"/>'); lines.push('<div class="table-container"/>');
lines.push('<table role="table" class="status-by-page">'); lines.push('<table role="table" class="status-by-page">');
lines.push('<thead><tr>'); lines.push('<thead><tr>');
lines.push(['Page', ...this.targetLanguages].map((col) => `<th>${col}</th>`).join('')); lines.push(['Page', ...this.targetLanguages].map((col) => `<th>${col}</th>`).join(''));
lines.push('</tr></thead>'); lines.push('</tr></thead>');
lines.push('<tbody>'); lines.push('<tbody>');
const spacer = `<tr class="spacer">\n${this.targetLanguages const spacer = `<tr class="spacer">\n${this.targetLanguages
.map(() => `<td></td>`) .map(() => `<td></td>`)
.join('\n')}\n</tr>`; .join('\n')}\n</tr>`;
lines.push(spacer); lines.push(spacer);
statusByPage.forEach((content) => { statusByPage.forEach((content) => {
const cols = []; const cols = [];
cols.push(this.renderLink(content.githubUrl, content.subpath)); cols.push(this.renderLink(content.githubUrl, content.subpath));
cols.push( cols.push(
...this.targetLanguages.map((lang) => { ...this.targetLanguages.map((lang) => {
const translation = content.translations[lang]!; const translation = content.translations[lang]!;
if (translation.isMissing) if (translation.isMissing)
return `<span title="${lang}: Missing"><span aria-hidden="true">❌</span></span>`; return `<span title="${lang}: Missing"><span aria-hidden="true">❌</span></span>`;
if (translation.isOutdated) 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}: Needs updating"><span aria-hidden="true">🔄</span></a>`;
return `<a href="${translation.githubUrl}" title="${lang}: Completed"><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(`<tr>\n${cols.map((col) => `<td>${col}</td>`).join('\n')}\n</tr>`);
}); });
lines.push(spacer); lines.push(spacer);
lines.push('</tbody>'); lines.push('</tbody>');
lines.push('</table>'); lines.push('</table>');
lines.push(`\n<sup>❌ Missing &nbsp; 🔄 Needs updating &nbsp; ✔ Completed</sup>`); lines.push(`\n<sup>❌ Missing &nbsp; 🔄 Needs updating &nbsp; ✔ Completed</sup>`);
lines.push('</div>'); 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. * Render a link to a pre-filled GitHub UI for creating a new file.
* *
* @param lang Language tag to create page for * @param lang Language tag to create page for
* @param filename Subpath of page to create * @param filename Subpath of page to create
*/ */
renderCreatePageButton(lang: string, filename: string): string { renderCreatePageButton(lang: string, filename: string): string {
// We include `lang` twice because GitHub eats the last path segment when setting filename. // We include `lang` twice because GitHub eats the last path segment when setting filename.
const createUrl = new URL( const createUrl = new URL(
`https://github.com/${this.githubRepo}/new/${this.githubRef}/src/content/docs` `https://github.com/${this.githubRepo}/new/${this.githubRef}/src/content/docs`
); );
createUrl.searchParams.set('filename', lang + '/' + filename); createUrl.searchParams.set('filename', lang + '/' + filename);
createUrl.searchParams.set('value', '---\ntitle:\ndescription:\n---\n'); createUrl.searchParams.set('value', '---\ntitle:\ndescription:\n---\n');
return this.renderLink(createUrl.href, `Create\xa0page\xa0+`, 'create-button'); return this.renderLink(createUrl.href, `Create\xa0page\xa0+`, 'create-button');
} }
/** /**
* Render a progress bar with emoji. * Render a progress bar with emoji.
*/ */
renderProgressBar( renderProgressBar(
total: number, total: number,
outdated: number, outdated: number,
missing: number, missing: number,
{ size = 20 }: { size?: number } = {} { size = 20 }: { size?: number } = {}
) { ) {
const outdatedLength = Math.round((outdated / total) * size); const outdatedLength = Math.round((outdated / total) * size);
const missingLength = Math.round((missing / total) * size); const missingLength = Math.round((missing / total) * size);
const doneLength = size - outdatedLength - missingLength; const doneLength = size - outdatedLength - missingLength;
return ( return (
'<span class="progress-bar" aria-hidden="true">' + '<span class="progress-bar" aria-hidden="true">' +
[ [
[doneLength, '🟦'], [doneLength, '🟦'],
[outdatedLength, '🟧'], [outdatedLength, '🟧'],
[missingLength, '⬜'], [missingLength, '⬜'],
] ]
.map(([length, icon]) => Array(length).fill(icon)) .map(([length, icon]) => Array(length).fill(icon))
.flat() .flat()
.join('') + .join('') +
'</span>' '</span>'
); );
} }
renderLink(href: string, text: string, className = ''): string { renderLink(href: string, text: string, className = ''): string {
return `<a href="${escape(href)}" class="${escape( return `<a href="${escape(href)}" class="${escape(
className className
)}" target="_blank" rel="noopener noreferrer">${escape(text)}</a>`; )}" target="_blank" rel="noopener noreferrer">${escape(text)}</a>`;
} }
} }
function toUtcString(date: string) { function toUtcString(date: string) {
return new Date(date).toISOString(); return new Date(date).toISOString();
} }
function tryGetFrontMatterBlock(filePath: string): string { function tryGetFrontMatterBlock(filePath: string): string {
const contents = fs.readFileSync(filePath, 'utf8'); const contents = fs.readFileSync(filePath, 'utf8');
const matches = contents.match(/^\s*---([\S\s]*?)\n---/); const matches = contents.match(/^\s*---([\S\s]*?)\n---/);
if (!matches) return ''; if (!matches) return '';
return matches[1]; return matches[1];
} }

View File

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

View File

@@ -1,28 +1,28 @@
export type PageIndex = { export type PageIndex = {
[language: string]: { [language: string]: {
[pagePath: string]: PageData; [pagePath: string]: PageData;
}; };
}; };
export type PageData = { export type PageData = {
lastChange: string; lastChange: string;
lastCommitMsg: string; lastCommitMsg: string;
lastMajorChange: string; lastMajorChange: string;
lastMajorCommitMsg: string; lastMajorCommitMsg: string;
i18nReady?: boolean; i18nReady?: boolean;
}; };
export type PageTranslationStatus = { export type PageTranslationStatus = {
subpath: string; subpath: string;
sourcePage: PageData; sourcePage: PageData;
githubUrl: string; githubUrl: string;
translations: { translations: {
[language: string]: { [language: string]: {
page: PageData; page: PageData;
isMissing: boolean; isMissing: boolean;
isOutdated: boolean; isOutdated: boolean;
githubUrl: string; githubUrl: string;
sourceHistoryUrl: string; sourceHistoryUrl: string;
}; };
}; };
}; };

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,7 @@
*/ */
interface Props { interface Props {
placeholder?: string; placeholder?: string;
} }
const { placeholder = 'Search features and community resources...' } = Astro.props; const { placeholder = 'Search features and community resources...' } = Astro.props;
@@ -14,49 +14,49 @@ const { placeholder = 'Search features and community resources...' } = Astro.pro
--- ---
<card-search> <card-search>
<div id="search-input" class="pagefind-ui pagefind-ui--reset"> <div id="search-input" class="pagefind-ui pagefind-ui--reset">
<form <form
class="pagefind-ui__form" class="pagefind-ui__form"
role="search" role="search"
aria-label="search_label" aria-label="search_label"
action="javascript:void(0);" action="javascript:void(0);"
> >
<input <input
data-wrapper="search-feature-list" data-wrapper="search-feature-list"
class="pagefind-ui__search-input" class="pagefind-ui__search-input"
id="search" id="search"
aria-label="search" aria-label="search"
placeholder={placeholder} placeholder={placeholder}
title={placeholder} title={placeholder}
autocapitalize="none" autocapitalize="none"
enterkeyhint="search" enterkeyhint="search"
autocomplete="off" autocomplete="off"
/> />
<button class="pagefind-ui__search-clear pagefind-ui__suppressed">clear</button> <button class="pagefind-ui__search-clear pagefind-ui__suppressed">clear</button>
</form> </form>
</div> </div>
<slot /> <slot />
</card-search> </card-search>
<script> <script>
class CardSearch extends HTMLElement { class CardSearch extends HTMLElement {
constructor() { constructor() {
super(); super();
window.addEventListener('DOMContentLoaded', () => { window.addEventListener('DOMContentLoaded', () => {
const form = this.querySelector<HTMLFormElement>('form.pagefind-ui__form')!; const form = this.querySelector<HTMLFormElement>('form.pagefind-ui__form')!;
const searchBar = this.querySelector<HTMLInputElement>('input.pagefind-ui__search-input')!; const searchBar = this.querySelector<HTMLInputElement>('input.pagefind-ui__search-input')!;
const clear = this.querySelector<HTMLButtonElement>('button.pagefind-ui__search-clear')!; const clear = this.querySelector<HTMLButtonElement>('button.pagefind-ui__search-clear')!;
const inputs = Array.from(this.children).filter((el) => el.classList.contains('card-grid')); const inputs = Array.from(this.children).filter((el) => el.classList.contains('card-grid'));
const headings = Array.from(this.children).filter( const headings = Array.from(this.children).filter(
(el) => el instanceof HTMLHeadingElement || el instanceof HTMLParagraphElement (el) => el instanceof HTMLHeadingElement || el instanceof HTMLParagraphElement
); );
// Create "nothing found" for each heading // Create "nothing found" for each heading
headings.forEach((heading, i) => { headings.forEach((heading, i) => {
const wrn = document.createElement('p'); const wrn = document.createElement('p');
wrn.classList.add('hidden', 'warning'); wrn.classList.add('hidden', 'warning');
wrn.id = `warn-${i}`; wrn.id = `warn-${i}`;
wrn.innerHTML = ` wrn.innerHTML = `
<style> <style>
.warning { .warning {
font-style: italic; font-style: italic;
@@ -69,170 +69,170 @@ const { placeholder = 'Search features and community resources...' } = Astro.pro
</style> </style>
<span>No results found in ${heading.textContent}</span> <span>No results found in ${heading.textContent}</span>
`; `;
heading.insertAdjacentElement('afterend', wrn); heading.insertAdjacentElement('afterend', wrn);
}); });
// Select all "nothing found" created before // Select all "nothing found" created before
const warnings = document.querySelectorAll('[id^="warn-"]'); const warnings = document.querySelectorAll('[id^="warn-"]');
// Prevent page reload // Prevent page reload
form.addEventListener('submit', (e) => { form.addEventListener('submit', (e) => {
e.preventDefault(); e.preventDefault();
}); });
// Prevent reload on 'Enter', and enable clean input on 'Escape' // Prevent reload on 'Enter', and enable clean input on 'Escape'
form.addEventListener('keydown', (e) => { form.addEventListener('keydown', (e) => {
clear.classList.remove('pagefind-ui__suppressed'); clear.classList.remove('pagefind-ui__suppressed');
if (e.key === 'Escape') { if (e.key === 'Escape') {
reset(); reset();
} else if (e.key === 'Enter') e.preventDefault(); } else if (e.key === 'Enter') e.preventDefault();
}); });
searchBar.addEventListener('keydown', (e) => { searchBar.addEventListener('keydown', (e) => {
if (e.key === 'Enter') e.preventDefault(); if (e.key === 'Enter') e.preventDefault();
}); });
// Search // Search
searchBar.addEventListener('keyup', () => { searchBar.addEventListener('keyup', () => {
searchFilter(); searchFilter();
}); });
// Clear search button 'X' // Clear search button 'X'
clear.addEventListener('click', () => { clear.addEventListener('click', () => {
clear.classList.add('pagefind-ui__suppressed'); clear.classList.add('pagefind-ui__suppressed');
reset(); reset();
}); });
function reset() { function reset() {
searchBar.value = ''; searchBar.value = '';
searchFilter(); searchFilter();
} }
/** /**
* Function to search and filter based on input * Function to search and filter based on input
*/ */
function searchFilter() { function searchFilter() {
const query = searchBar.value.toUpperCase(); const query = searchBar.value.toUpperCase();
inputs.forEach((elements, i) => { inputs.forEach((elements, i) => {
const cards = Array.from(elements.children); const cards = Array.from(elements.children);
let total = cards.length; let total = cards.length;
cards.forEach((el) => { cards.forEach((el) => {
const htmlElement = el as HTMLElement; const htmlElement = el as HTMLElement;
const span = el.querySelector('span'); const span = el.querySelector('span');
const txtValue = span?.textContent || span?.innerText; const txtValue = span?.textContent || span?.innerText;
if (!txtValue) return; if (!txtValue) return;
// Filter list // Filter list
if (txtValue.toUpperCase().indexOf(query) > -1) { if (txtValue.toUpperCase().indexOf(query) > -1) {
total++; total++;
htmlElement.style.display = ''; htmlElement.style.display = '';
} else { } else {
total--; total--;
htmlElement.style.display = 'none'; htmlElement.style.display = 'none';
} }
// Show warning if none found // Show warning if none found
if (total === 0) { if (total === 0) {
warnings[i].classList.remove('hidden'); warnings[i].classList.remove('hidden');
} else { } else {
warnings[i].classList.add('hidden'); warnings[i].classList.add('hidden');
} }
}); });
}); });
} }
}); });
} }
} }
customElements.define('card-search', CardSearch); customElements.define('card-search', CardSearch);
</script> </script>
<style> <style>
:root { :root {
--pagefind-ui-border-width: 1px; --pagefind-ui-border-width: 1px;
--pagefind-ui-scale: 0.8; --pagefind-ui-scale: 0.8;
--pagefind-ui-tag: #eeeeee; --pagefind-ui-tag: #eeeeee;
--pagefind-ui-border-radius: 8px; --pagefind-ui-border-radius: 8px;
--pagefind-ui-image-border-radius: 8px; --pagefind-ui-image-border-radius: 8px;
--pagefind-ui-image-box-ratio: 3 / 2; --pagefind-ui-image-box-ratio: 3 / 2;
} }
.pagefind-ui { .pagefind-ui {
width: 100%; width: 100%;
color: var(--sl-color-gray-2); color: var(--sl-color-gray-2);
font-family: var(--__sl-font); font-family: var(--__sl-font);
margin-block: 2rem 1rem; margin-block: 2rem 1rem;
} }
.pagefind-ui__form { .pagefind-ui__form {
position: relative; position: relative;
} }
.pagefind-ui__form::before { .pagefind-ui__form::before {
background-color: var(--sl-color-gray-2); background-color: var(--sl-color-gray-2);
width: calc(18px * var(--pagefind-ui-scale)); width: calc(18px * var(--pagefind-ui-scale));
height: calc(18px * var(--pagefind-ui-scale)); height: calc(18px * var(--pagefind-ui-scale));
top: calc(23px * var(--pagefind-ui-scale)); top: calc(23px * var(--pagefind-ui-scale));
left: calc(20px * var(--pagefind-ui-scale)); left: calc(20px * var(--pagefind-ui-scale));
content: ''; content: '';
position: absolute; position: absolute;
display: block; display: block;
opacity: 0.7; 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"); -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"); 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%; -webkit-mask-size: 100%;
mask-size: 100%; mask-size: 100%;
z-index: 9; z-index: 9;
pointer-events: none; pointer-events: none;
} }
.pagefind-ui__search-input { .pagefind-ui__search-input {
box-sizing: border-box; box-sizing: border-box;
position: relative; position: relative;
display: flex; display: flex;
font-weight: 400; font-weight: 400;
height: calc(64px * var(--pagefind-ui-scale)); height: calc(64px * var(--pagefind-ui-scale));
padding: 0 calc(70px * var(--pagefind-ui-scale)) 0 calc(54px * 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); background-color: var(--sl-color-black);
border: var(--pagefind-ui-border-width) solid var(--sl-color-gray-5); border: var(--pagefind-ui-border-width) solid var(--sl-color-gray-5);
border-radius: var(--pagefind-ui-border-radius); border-radius: var(--pagefind-ui-border-radius);
font-size: calc(21px * var(--pagefind-ui-scale)); font-size: calc(21px * var(--pagefind-ui-scale));
appearance: none; appearance: none;
-webkit-appearance: none; -webkit-appearance: none;
color: var(--sl-color-white); color: var(--sl-color-white);
width: 100%; width: 100%;
} }
.pagefind-ui__search-input::placeholder { .pagefind-ui__search-input::placeholder {
color: var(--sl-color-gray-3); color: var(--sl-color-gray-3);
opacity: 1; /* Firefox */ opacity: 1; /* Firefox */
} }
.pagefind-ui__search-clear { .pagefind-ui__search-clear {
position: absolute; position: absolute;
top: calc(3px * var(--pagefind-ui-scale)); top: calc(3px * var(--pagefind-ui-scale));
right: calc(3px * var(--pagefind-ui-scale)); right: calc(3px * var(--pagefind-ui-scale));
height: calc(58px * var(--pagefind-ui-scale)); height: calc(58px * var(--pagefind-ui-scale));
color: var(--sl-color-gray-2); color: var(--sl-color-gray-2);
font-size: calc(14px * var(--pagefind-ui-scale)); font-size: calc(14px * var(--pagefind-ui-scale));
cursor: pointer; cursor: pointer;
border-radius: var(--pagefind-ui-border-radius); border-radius: var(--pagefind-ui-border-radius);
width: calc(60px * var(--pagefind-ui-scale)); width: calc(60px * var(--pagefind-ui-scale));
padding: 0; padding: 0;
background-color: transparent; background-color: transparent;
overflow: hidden; overflow: hidden;
} }
.pagefind-ui__search-clear:focus { .pagefind-ui__search-clear:focus {
outline: 1px solid var(--sl-color-accent); outline: 1px solid var(--sl-color-accent);
} }
.pagefind-ui__search-clear::before { .pagefind-ui__search-clear::before {
content: ''; 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") -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; 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") 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; center / 50% no-repeat;
background-color: var(--sl-color-text-accent); background-color: var(--sl-color-text-accent);
display: block; display: block;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.pagefind-ui__suppressed { .pagefind-ui__suppressed {
opacity: 0; opacity: 0;
pointer-events: none; pointer-events: none;
} }
</style> </style>

View File

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

View File

@@ -4,5 +4,5 @@ const enabled = process.env.TAURI_DEBUG && Astro.slots.has('default');
--- ---
<Card title="Demo"> <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> </Card>

View File

@@ -7,8 +7,8 @@ const CONTENT_SET_ID = '54faa8e2-cd0d-4997-9a3c-d9fed28f358a';
--- ---
<FeelbackReaction <FeelbackReaction
contentSetId={CONTENT_SET_ID} contentSetId={CONTENT_SET_ID}
key={Astro.url.pathname} key={Astro.url.pathname}
layout="list" layout="list"
preset="feeling" 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"> <Card title="Contribute" icon="pencil">
<p> <p>
This is a stub and is waiting for contributions. Get involved by 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" <a href={baseUrl}>visiting us on GitHub</a> or <a href="https://discord.com/invite/tauri"
>joining us on Discord</a >joining us on Discord</a
>. >.
</p> </p>
{ {
Astro.slots.has('default') && ( Astro.slots.has('default') && (
<> <>
<div>Here are some ideas to get you started:</div> <div>Here are some ideas to get you started:</div>
<slot /> <slot />
</> </>
) )
} }
</Card> </Card>

View File

@@ -7,30 +7,30 @@ interface Props extends HTMLAttributes<'a'> {}
--- ---
<div> <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> </div>
<style> <style>
div { div {
display: inline-block; display: inline-block;
} }
a { a {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: 0.5rem;
border: 1px solid var(--sl-color-orange-low); border: 1px solid var(--sl-color-orange-low);
border-radius: 0.5rem; border-radius: 0.5rem;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
text-decoration: none; text-decoration: none;
box-shadow: var(--sl-shadow-sm); box-shadow: var(--sl-shadow-sm);
color: var(--sl-color-gray-1) !important; color: var(--sl-color-gray-1) !important;
font-weight: 600; font-weight: 600;
line-height: var(--sl-line-height-headings); line-height: var(--sl-line-height-headings);
} }
a:hover { a:hover {
background: var(--sl-color-orange-low); background: var(--sl-color-orange-low);
border-color: var(--sl-color-orange-high); border-color: var(--sl-color-orange-high);
} }
</style> </style>

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,96 +6,96 @@ const { labels } = Astro.props;
--- ---
<div class="page sl-flex"> <div class="page sl-flex">
<header class="header"><slot name="header" /></header> <header class="header"><slot name="header" /></header>
<nav class="sidebar" aria-label={labels['sidebarNav.accessibleLabel']}> <nav class="sidebar" aria-label={labels['sidebarNav.accessibleLabel']}>
<MobileMenuToggle {...Astro.props} /> <MobileMenuToggle {...Astro.props} />
<div <div
id="starlight__sidebar" id="starlight__sidebar"
class={Astro.props.entry.slug === '' || Astro.props.locale === Astro.props.entry.slug class={Astro.props.entry.slug === '' || Astro.props.locale === Astro.props.entry.slug
? 'sidebar-pane lp-hide' ? 'sidebar-pane lp-hide'
: 'sidebar-pane'} : 'sidebar-pane'}
> >
<div class="sidebar-content sl-flex"> <div class="sidebar-content sl-flex">
<Sidebar {...Astro.props} /> <Sidebar {...Astro.props} />
</div> </div>
</div> </div>
</nav> </nav>
<div class="main-frame"><slot /></div> <div class="main-frame"><slot /></div>
</div> </div>
<style> <style>
.page { .page {
flex-direction: column; flex-direction: column;
min-height: 100vh; min-height: 100vh;
} }
@media (min-width: 50rem) { @media (min-width: 50rem) {
.lp-hide { .lp-hide {
display: none; display: none;
} }
} }
.header { .header {
z-index: var(--sl-z-index-navbar); z-index: var(--sl-z-index-navbar);
position: fixed; position: fixed;
inset-inline-start: 0; inset-inline-start: 0;
inset-block-start: 0; inset-block-start: 0;
width: 100%; width: 100%;
height: var(--sl-nav-height); height: var(--sl-nav-height);
border-bottom: 1px solid var(--sl-color-hairline-shade); border-bottom: 1px solid var(--sl-color-hairline-shade);
padding: var(--sl-nav-pad-y) var(--sl-nav-pad-x); padding: var(--sl-nav-pad-y) var(--sl-nav-pad-x);
padding-inline-end: var(--sl-nav-pad-x); padding-inline-end: var(--sl-nav-pad-x);
background-color: var(--sl-color-bg-nav); background-color: var(--sl-color-bg-nav);
} }
:global([data-has-sidebar]) .header { :global([data-has-sidebar]) .header {
padding-inline-end: calc(var(--sl-nav-gap) + var(--sl-nav-pad-x) + var(--sl-menu-button-size)); padding-inline-end: calc(var(--sl-nav-gap) + var(--sl-nav-pad-x) + var(--sl-menu-button-size));
} }
.sidebar-pane { .sidebar-pane {
visibility: var(--sl-sidebar-visibility, hidden); visibility: var(--sl-sidebar-visibility, hidden);
position: fixed; position: fixed;
z-index: var(--sl-z-index-menu); z-index: var(--sl-z-index-menu);
inset-block: var(--sl-nav-height) 0; inset-block: var(--sl-nav-height) 0;
inset-inline-start: 0; inset-inline-start: 0;
width: 100%; width: 100%;
background-color: var(--sl-color-black); background-color: var(--sl-color-black);
overflow-y: auto; overflow-y: auto;
} }
:global([aria-expanded='true']) ~ .sidebar-pane { :global([aria-expanded='true']) ~ .sidebar-pane {
--sl-sidebar-visibility: visible; --sl-sidebar-visibility: visible;
} }
.sidebar-content { .sidebar-content {
height: 100%; height: 100%;
min-height: max-content; min-height: max-content;
padding: 1rem var(--sl-sidebar-pad-x) 0; padding: 1rem var(--sl-sidebar-pad-x) 0;
flex-direction: column; flex-direction: column;
gap: 1rem; gap: 1rem;
} }
@media (min-width: 50rem) { @media (min-width: 50rem) {
.sidebar-content::after { .sidebar-content::after {
content: ''; content: '';
padding-bottom: 1px; padding-bottom: 1px;
} }
} }
.main-frame { .main-frame {
padding-top: calc(var(--sl-nav-height) + var(--sl-mobile-toc-height)); padding-top: calc(var(--sl-nav-height) + var(--sl-mobile-toc-height));
padding-inline-start: var(--sl-content-inline-start); padding-inline-start: var(--sl-content-inline-start);
} }
@media (min-width: 50rem) { @media (min-width: 50rem) {
:global([data-has-sidebar]) .header { :global([data-has-sidebar]) .header {
padding-inline-end: var(--sl-nav-pad-x); padding-inline-end: var(--sl-nav-pad-x);
} }
.sidebar-pane { .sidebar-pane {
--sl-sidebar-visibility: visible; --sl-sidebar-visibility: visible;
width: var(--sl-sidebar-width); width: var(--sl-sidebar-width);
background-color: var(--sl-color-bg-sidebar); background-color: var(--sl-color-bg-sidebar);
border-inline-end: 1px solid var(--sl-color-hairline-shade); border-inline-end: 1px solid var(--sl-color-hairline-shade);
} }
} }
</style> </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 // 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 // 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) => { const multiSidebarConfig: [string, boolean, Props][] = Astro.props.sidebar.map((entry) => {
if (entry.type !== 'group') { if (entry.type !== 'group') {
throw new AstroError( throw new AstroError(
`\`${entry.label}\` cannot be used with multiple Starlight sidebars. `\`${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. 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` 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 // Recursively check if a group of sidebar entries contains the current page
const findIfIsCurrent = (entry: (typeof Astro.props.sidebar)[number]): boolean => { const findIfIsCurrent = (entry: (typeof Astro.props.sidebar)[number]): boolean => {
if (entry.type === 'link') { if (entry.type === 'link') {
return entry.isCurrent; return entry.isCurrent;
} }
return entry.entries.some((item) => findIfIsCurrent(item)); 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; let foundCurrentPage = false;
for (const page of multiSidebarConfig) { for (const page of multiSidebarConfig) {
if (page[1]) { if (page[1]) {
foundCurrentPage = true; foundCurrentPage = true;
break; break;
} }
} }
if (!foundCurrentPage && multiSidebarConfig.length > 0) { if (!foundCurrentPage && multiSidebarConfig.length > 0) {
multiSidebarConfig[0][1] = true; multiSidebarConfig[0][1] = true;
} }
if (!multiSidebarConfig.some(([_label, isCurrentPage, _config]) => isCurrentPage)) { if (!multiSidebarConfig.some(([_label, isCurrentPage, _config]) => isCurrentPage)) {
multiSidebarConfig[0][1] = true; multiSidebarConfig[0][1] = true;
} }
--- ---
<div class="__collapse"> <div class="__collapse">
{ {
multiSidebarConfig.map(([label, isCurrentPage, config]) => ( multiSidebarConfig.map(([label, isCurrentPage, config]) => (
<> <>
<input type="radio" name="sidebar" role="tab" aria-label={label} checked={isCurrentPage} /> <input type="radio" name="sidebar" role="tab" aria-label={label} checked={isCurrentPage} />
<div class="__collapse-content"> <div class="__collapse-content">
<Default {...config}> <Default {...config}>
<slot /> <slot />
</Default> </Default>
</div> </div>
</> </>
)) ))
} }
</div> </div>
<style> <style>
.__collapse { .__collapse {
display: grid; display: grid;
} }
.__collapse > input { .__collapse > input {
/* Layout */ /* Layout */
position: relative; position: relative;
display: inline-flex; display: inline-flex;
grid-row-start: 1; grid-row-start: 1;
appearance: none; appearance: none;
width: 100%; width: 100%;
min-height: fit-content; min-height: fit-content;
/* Styles */ /* Styles */
border-radius: 0.25rem; border-radius: 0.25rem;
padding: 0.2em 0.5rem; padding: 0.2em 0.5rem;
line-height: 1.4; line-height: 1.4;
font-size: var(--sl-text-lg); font-size: var(--sl-text-lg);
font-weight: 600; font-weight: 600;
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
margin-bottom: var(--sl-nav-pad-y); margin-bottom: var(--sl-nav-pad-y);
} }
.__collapse > input::after { .__collapse > input::after {
content: attr(aria-label); content: attr(aria-label);
} }
.__collapse > input:checked { .__collapse > input:checked {
color: var(--sl-color-text-invert); color: var(--sl-color-text-invert);
background-color: var(--sl-color-text-accent); background-color: var(--sl-color-text-accent);
} }
.__collapse > .__collapse-content { .__collapse > .__collapse-content {
display: none; display: none;
grid-column-start: 1; grid-column-start: 1;
grid-column-end: span 999; grid-column-end: span 999;
grid-row-start: 2; grid-row-start: 2;
border-top: 1px solid var(--sl-color-gray-5); border-top: 1px solid var(--sl-color-gray-5);
padding-top: 1rem; padding-top: 1rem;
} }
.__collapse > input:checked + .__collapse-content { .__collapse > input:checked + .__collapse-content {
display: block; display: block;
} }
</style> </style>
<style is:global> <style is:global>
.top-level { .top-level {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.sidebar-pane { .sidebar-pane {
overflow-y: scroll; overflow-y: scroll;
} }
</style> </style>

View File

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

View File

@@ -3,6 +3,6 @@ import { docsSchema, i18nSchema } from '@astrojs/starlight/schema';
import { blogSchema } from 'starlight-blog/schema'; import { blogSchema } from 'starlight-blog/schema';
export const collections = { export const collections = {
docs: defineCollection({ schema: docsSchema({ extend: blogSchema() }) }), docs: defineCollection({ schema: docsSchema({ extend: blogSchema() }) }),
i18n: defineCollection({ type: 'data', schema: i18nSchema() }), 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'; import { LinkCard, CardGrid } from '@astrojs/starlight/components';
<CardGrid> <CardGrid>
<LinkCard <LinkCard
title="Tauri Philosophy" title="Tauri Philosophy"
href="/about/philosophy" href="/about/philosophy"
description="Learn more about the approach behind Tauri" description="Learn more about the approach behind Tauri"
/> />
<LinkCard <LinkCard
title="Governance" title="Governance"
href="/about/governance" href="/about/governance"
description="Understand how the Tauri governance structure is setup" description="Understand how the Tauri governance structure is setup"
/> />
<LinkCard <LinkCard
title="Trademark" title="Trademark"
href="/about/trademark" href="/about/trademark"
description="Guidelines for using the Tauri trademark" description="Guidelines for using the Tauri trademark"
/> />
</CardGrid> </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: You can update the dependencies with:
<CommandTabs <CommandTabs
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest" npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest" yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest" pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update" cargo="cargo update"
/> />
## What's New in 1.1.0 ## 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 ```json title=tauri.conf.json
{ {
"build": { "build": {
"devPath": "http://localhost:8000", "devPath": "http://localhost:8000",
"distDir": "../dist" "distDir": "../dist"
} }
} }
``` ```
```json5 ```json5
{ {
build: { build: {
// devServer URL (comments are allowed!) // devServer URL (comments are allowed!)
devPath: 'http://localhost:8000', devPath: 'http://localhost:8000',
distDir: '../dist', 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: Make sure to update both NPM and Cargo dependencies to the 1.2.0 release. You can update the dependencies with:
<CommandTabs <CommandTabs
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest" npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest" yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest" pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update" cargo="cargo update"
/> />
## What's in 1.2.0 ## 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: Make sure to update both NPM and Cargo dependencies to the 1.3.0 release. You can update the dependencies with:
<CommandTabs <CommandTabs
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest" npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest" yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest" pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update" cargo="cargo update"
/> />
## What's in 1.3.0 ## 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: Make sure to update both NPM and Cargo dependencies to the 1.4.0 release. You can update the dependencies with:
<CommandTabs <CommandTabs
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest" npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest" yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest" pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update" cargo="cargo update"
/> />
## What's in 1.4.0 ## 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: Make sure to update both NPM and Cargo dependencies to the 1.5.0 release. You can update the dependencies with:
<CommandTabs <CommandTabs
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest" npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest" yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest" pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update" cargo="cargo update"
/> />
## What's in 1.5.0 ## 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: Make sure to update both NPM and Cargo dependencies to the 1.6.0 release. You can update the dependencies with:
<CommandTabs <CommandTabs
npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest" npm="npm install @tauri-apps/cli@latest @tauri-apps/api@latest"
yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest" yarn="yarn upgrade @tauri-apps/cli @tauri-apps/api --latest"
pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest" pnpm="pnpm update @tauri-apps/cli @tauri-apps/api --latest"
cargo="cargo update" cargo="cargo update"
/> />
## What's in 1.6.0 ## 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: Make sure to update both NPM and Cargo dependencies to the latest alpha release. You can update the NPM dependencies with:
<CommandTabs <CommandTabs
npm="npm install @tauri-apps/cli@next @tauri-apps/api@next" npm="npm install @tauri-apps/cli@next @tauri-apps/api@next"
yarn="yarn upgrade @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" pnpm="pnpm update @tauri-apps/cli@next @tauri-apps/api@next"
cargo='cargo add tauri@2.0.0-alpha.4 cargo='cargo add tauri@2.0.0-alpha.4
cargo add tauri-build@2.0.0-alpha.2 --build cargo add tauri-build@2.0.0-alpha.2 --build
cargo install tauri-cli --version "^2.0.0-alpha"' cargo install tauri-cli --version "^2.0.0-alpha"'
/> />
@@ -120,7 +120,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
```js ```js
import { invoke } from '@tauri-apps/api/tauri'; import { invoke } from '@tauri-apps/api/tauri';
invoke('plugin:example|ping', { value: 'Tauri' }).then(({ value }) => 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: Make sure to update both NPM and Cargo dependencies to the 2.0.0-alpha.0 release. You can update the dependencies with:
<CommandTabs <CommandTabs
npm="npm install @tauri-apps/cli@next @tauri-apps/api@next" npm="npm install @tauri-apps/cli@next @tauri-apps/api@next"
yarn="yarn upgrade @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" pnpm="pnpm update @tauri-apps/cli@next @tauri-apps/api@next"
cargo='cargo add tauri@2.0.0-alpha.0 cargo='cargo add tauri@2.0.0-alpha.0
cargo add tauri-build@2.0.0-alpha.0 --build cargo add tauri-build@2.0.0-alpha.0 --build
cargo install tauri-cli --version "^2.0.0-alpha"' 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: Learn more above the specific IPC patterns in the following guides:
<CardGrid> <CardGrid>
<LinkCard <LinkCard
title="Brownfield" title="Brownfield"
href="/concept/inter-process-communication/brownfield/" href="/concept/inter-process-communication/brownfield/"
/> />
<LinkCard <LinkCard
title="Isolation" title="Isolation"
href="/concept/inter-process-communication/isolation/" href="/concept/inter-process-communication/isolation/"
/> />
</CardGrid> </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. 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'; import useBaseUrl from '@docusaurus/useBaseUrl';
<div className="row"> <div className="row">
<div className="col col--4"> <div className="col col--4">
<table> <table>
<tr> <tr>
<td>Ease of Use</td> <td>Ease of Use</td>
<td> <td>
<Rater value="3" /> <Rater value="3" />
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Extensibility</td> <td>Extensibility</td>
<td> <td>
<Rater value="5" /> <Rater value="5" />
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Performance</td> <td>Performance</td>
<td> <td>
<Rater value="4" /> <Rater value="4" />
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Security</td> <td>Security</td>
<td> <td>
<Rater value="4" /> <Rater value="4" />
</td> </td>
</tr> </tr>
</table> </table>
</div> </div>
<div className="col col--4 pattern-logo"> <div className="col col--4 pattern-logo">
<img src={useBaseUrl('img/recipes/Bridge.svg')} alt="Bridge" /> <img src={useBaseUrl('img/recipes/Bridge.svg')} alt="Bridge" />
</div> </div>
<div className="col col--4"> <div className="col col--4">
Pros: Pros:
<ul> <ul>
<li>Highly configurable</li> <li>Highly configurable</li>
<li>No Rust skills required</li> <li>No Rust skills required</li>
</ul> </ul>
Cons: Cons:
<ul> <ul>
<li>Some WebAPIs unavailable</li> <li>Some WebAPIs unavailable</li>
<li>Challenge to implement</li> <li>Challenge to implement</li>
</ul> </ul>
</div> </div>
</div> </div>
## Description ## Description
@@ -93,97 +93,97 @@ Here's what you need to add to your tauri.conf.json file:
```json ```json
{ {
"tauri": { "tauri": {
"allowlist": { "allowlist": {
"all": false, "all": false,
"clipboard": { "clipboard": {
"all": false, "all": false,
"readText": false, "readText": false,
"writeText": false "writeText": false
}, },
"dialog": { "dialog": {
"all": false, "all": false,
"ask": false, "ask": false,
"confirm": false, "confirm": false,
"message": false, "message": false,
"open": false, "open": false,
"save": false "save": false
}, },
"fs": { "fs": {
"all": false, "all": false,
"copyFile": false, "copyFile": false,
"createDir": false, "createDir": false,
"readDir": false, "readDir": false,
"readFile": false, "readFile": false,
"removeDir": false, "removeDir": false,
"removeFile": false, "removeFile": false,
"renameFile": false, "renameFile": false,
"scope": [], "scope": [],
"writeFile": false "writeFile": false
}, },
"globalShortcut": { "globalShortcut": {
"all": false "all": false
}, },
"http": { "http": {
"all": false, "all": false,
"request": false, "request": false,
"scope": [] "scope": []
}, },
"notification": { "notification": {
"all": false "all": false
}, },
"os": { "os": {
"all": false "all": false
}, },
"path": { "path": {
"all": false "all": false
}, },
"process": { "process": {
"all": false, "all": false,
"exit": false, "exit": false,
"relaunch": false, "relaunch": false,
"relaunchDangerousAllowSymlinkMacos": false "relaunchDangerousAllowSymlinkMacos": false
}, },
"protocol": { "protocol": {
"all": false, "all": false,
"asset": false, "asset": false,
"assetScope": [] "assetScope": []
}, },
"shell": { "shell": {
"all": false, "all": false,
"execute": false, "execute": false,
"open": false, "open": false,
"scope": [], "scope": [],
"sidecar": false "sidecar": false
}, },
"window": { "window": {
"all": false, "all": false,
"center": false, "center": false,
"close": false, "close": false,
"create": false, "create": false,
"hide": false, "hide": false,
"maximize": false, "maximize": false,
"minimize": false, "minimize": false,
"print": false, "print": false,
"requestUserAttention": false, "requestUserAttention": false,
"setAlwaysOnTop": false, "setAlwaysOnTop": false,
"setDecorations": false, "setDecorations": false,
"setFocus": false, "setFocus": false,
"setFullscreen": false, "setFullscreen": false,
"setIcon": false, "setIcon": false,
"setMaxSize": false, "setMaxSize": false,
"setMinSize": false, "setMinSize": false,
"setPosition": false, "setPosition": false,
"setResizable": false, "setResizable": false,
"setSize": false, "setSize": false,
"setSkipTaskbar": false, "setSkipTaskbar": false,
"setTitle": false, "setTitle": false,
"show": false, "show": false,
"startDragging": false, "startDragging": false,
"unmaximize": false, "unmaximize": false,
"unminimize": false "unminimize": false
} }
} }
} }
} }
``` ```

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,48 +7,48 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
import Rater from '@theme/Rater'; import Rater from '@theme/Rater';
<div className="row"> <div className="row">
<div className="col col--4"> <div className="col col--4">
<table> <table>
<tr> <tr>
<td>Ease of Use</td> <td>Ease of Use</td>
<td> <td>
<Rater value="4" /> <Rater value="4" />
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Extensibility</td> <td>Extensibility</td>
<td> <td>
<Rater value="4" /> <Rater value="4" />
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Performance</td> <td>Performance</td>
<td> <td>
<Rater value="3" /> <Rater value="3" />
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Security</td> <td>Security</td>
<td> <td>
<Rater value="5" /> <Rater value="5" />
</td> </td>
</tr> </tr>
</table> </table>
</div> </div>
<div className="col col--4 pattern-logo"> <div className="col col--4 pattern-logo">
<img src={useBaseUrl('img/recipes/Multiwin.svg')} alt="Multiwin" /> <img src={useBaseUrl('img/recipes/Multiwin.svg')} alt="Multiwin" />
</div> </div>
<div className="col col--4"> <div className="col col--4">
Pros: Pros:
<ul> <ul>
<li>Windows can be spawned or destroyed at runtime</li> <li>Windows can be spawned or destroyed at runtime</li>
<li>Separation of concerns</li> <li>Separation of concerns</li>
</ul> </ul>
Cons: Cons:
<ul> <ul>
<li>Somewhat complex</li> <li>Somewhat complex</li>
</ul> </ul>
</div> </div>
</div> </div>
## Description ## 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. 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> <CardGrid>
<LinkCard <LinkCard
title="Tauri Architecture" title="Tauri Architecture"
href="/concept/architecture/" href="/concept/architecture/"
description="Architecture and ecosystem." description="Architecture and ecosystem."
/> />
<LinkCard <LinkCard
title="Inter-Process Communication (IPC)" title="Inter-Process Communication (IPC)"
href="/concept/inter-process-communication/" href="/concept/inter-process-communication/"
description="The inner workings on the IPC." description="The inner workings on the IPC."
/> />
<LinkCard <LinkCard
title="Security" title="Security"
href="/security" href="/security"
description="How Tauri enforces security practices." description="How Tauri enforces security practices."
/> />
<LinkCard <LinkCard
title="Process Model" title="Process Model"
href="/concept/process-model/" href="/concept/process-model/"
description="Which processes Tauri manages and why." description="Which processes Tauri manages and why."
/> />
<LinkCard <LinkCard
title="App Size" title="App Size"
href="/concept/size/" href="/concept/size/"
description="How to make your app as small as possible." description="How to make your app as small as possible."
/> />
<LinkCard <LinkCard
title="Rendering" title="Rendering"
href="/concept/rendering/" href="/concept/rendering/"
description="What a renderer is in Tauri." description="What a renderer is in Tauri."
/> />
</CardGrid> </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. To create a debug build, run the `tauri build --debug` command.
<CommandTabs <CommandTabs
npm="npm run tauri build -- --debug" npm="npm run tauri build -- --debug"
yarn="yarn tauri build --debug" yarn="yarn tauri build --debug"
pnpm="pnpm tauri build --debug" pnpm="pnpm tauri build --debug"
cargo="cargo 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. 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" ```json title=".vscode/launch.json"
{ {
// Use IntelliSense to learn about possible attributes. // Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes. // Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"type": "lldb", "type": "lldb",
"request": "launch", "request": "launch",
"name": "Tauri Development Debug", "name": "Tauri Development Debug",
"cargo": { "cargo": {
"args": [ "args": [
"build", "build",
"--manifest-path=./src-tauri/Cargo.toml", "--manifest-path=./src-tauri/Cargo.toml",
"--no-default-features" "--no-default-features"
] ]
}, },
// task for the `beforeDevCommand` if used, must be configured in `.vscode/tasks.json` // task for the `beforeDevCommand` if used, must be configured in `.vscode/tasks.json`
"preLaunchTask": "ui:dev" "preLaunchTask": "ui:dev"
}, },
{ {
"type": "lldb", "type": "lldb",
"request": "launch", "request": "launch",
"name": "Tauri Production Debug", "name": "Tauri Production Debug",
"cargo": { "cargo": {
"args": ["build", "--release", "--manifest-path=./src-tauri/Cargo.toml"] "args": ["build", "--release", "--manifest-path=./src-tauri/Cargo.toml"]
}, },
// task for the `beforeBuildCommand` if used, must be configured in `.vscode/tasks.json` // task for the `beforeBuildCommand` if used, must be configured in `.vscode/tasks.json`
"preLaunchTask": "ui:build" "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" ```json title=".vscode/tasks.json"
{ {
// See https://go.microsoft.com/fwlink/?LinkId=733558 // See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format // for the documentation about the tasks.json format
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
{ {
"label": "ui:dev", "label": "ui:dev",
"type": "shell", "type": "shell",
// `dev` keeps running in the background // `dev` keeps running in the background
// ideally you should also configure a `problemMatcher` // 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 // see https://code.visualstudio.com/docs/editor/tasks#_can-a-background-task-be-used-as-a-prelaunchtask-in-launchjson
"isBackground": true, "isBackground": true,
// change this to your `beforeDevCommand`: // change this to your `beforeDevCommand`:
"command": "yarn", "command": "yarn",
"args": ["dev"] "args": ["dev"]
}, },
{ {
"label": "ui:build", "label": "ui:build",
"type": "shell", "type": "shell",
// change this to your `beforeBuildCommand`: // change this to your `beforeBuildCommand`:
"command": "yarn", "command": "yarn",
"args": ["build"] "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: After installing, you can run the following to create a plugin project:
<CommandTabs <CommandTabs
npm="npm run tauri plugin new [name]" npm="npm run tauri plugin new [name]"
yarn="yarn tauri plugin new [name]" yarn="yarn tauri plugin new [name]"
pnpm="pnpm tauri plugin new [name]" pnpm="pnpm tauri plugin new [name]"
cargo="cargo 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: 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 ```html
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>Hello Tauri!</title> <title>Hello Tauri!</title>
<style> <style>
body { body {
/* Add a nice colorscheme */ /* Add a nice colorscheme */
background-color: #222831; background-color: #222831;
color: #ececec; color: #ececec;
/* Make the body the exact size of the window */ /* Make the body the exact size of the window */
margin: 0; margin: 0;
height: 100vh; height: 100vh;
width: 100vw; width: 100vw;
/* Vertically and horizontally center children of the body tag */ /* Vertically and horizontally center children of the body tag */
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
</style> </style>
</head> </head>
<body> <body>
<h1>Hello, Tauri!</h1> <h1>Hello, Tauri!</h1>
</body> </body>
</html> </html>
``` ```
@@ -150,26 +150,26 @@ here.
```json ```json
{ {
"build": { "build": {
"distDir": "dist" "distDir": "dist"
}, },
"tauri": { "tauri": {
"bundle": { "bundle": {
"identifier": "studio.tauri.hello_tauri_webdriver", "identifier": "studio.tauri.hello_tauri_webdriver",
"icon": ["icon.png"] "icon": ["icon.png"]
}, },
"allowlist": { "allowlist": {
"all": false "all": false
}, },
"windows": [ "windows": [
{ {
"width": 800, "width": 800,
"height": 600, "height": 600,
"resizable": true, "resizable": true,
"fullscreen": false "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. see the following application pop up.
<div style={{ textAlign: 'center' }}> <div style={{ textAlign: 'center' }}>
<Image src={HelloTauriWebdriver} alt="Hello Tauri Webdriver" /> <Image src={HelloTauriWebdriver} alt="Hello Tauri Webdriver" />
</div> </div>
_Note: If you are modifying the application and want to use the Devtools, then run it without `--release` and "Inspect _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 ```json
{ {
"name": "selenium", "name": "selenium",
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"scripts": { "scripts": {
"test": "mocha" "test": "mocha"
}, },
"dependencies": { "dependencies": {
"chai": "^4.3.4", "chai": "^4.3.4",
"mocha": "^9.0.3", "mocha": "^9.0.3",
"selenium-webdriver": "^4.0.0-beta.4" "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. If you want to install the dependencies from scratch, just run the following command.
<CommandTabs <CommandTabs
npm="npm install mocha chai selenium-webdriver" npm="npm install mocha chai selenium-webdriver"
yarn="yarn add 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 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 // create the path to the expected application binary
const application = path.resolve( const application = path.resolve(
__dirname, __dirname,
'..', '..',
'..', '..',
'..', '..',
'target', 'target',
'release', 'release',
'hello-tauri-webdriver' 'hello-tauri-webdriver'
); );
// keep track of the webdriver instance we create // keep track of the webdriver instance we create
@@ -106,61 +106,61 @@ let driver;
let tauriDriver; let tauriDriver;
before(async function () { before(async function () {
// set timeout to 2 minutes to allow the program to build if it needs to // set timeout to 2 minutes to allow the program to build if it needs to
this.timeout(120000); this.timeout(120000);
// ensure the program has been built // ensure the program has been built
spawnSync('cargo', ['build', '--release']); spawnSync('cargo', ['build', '--release']);
// start tauri-driver // start tauri-driver
tauriDriver = spawn( tauriDriver = spawn(
path.resolve(os.homedir(), '.cargo', 'bin', 'tauri-driver'), path.resolve(os.homedir(), '.cargo', 'bin', 'tauri-driver'),
[], [],
{ stdio: [null, process.stdout, process.stderr] } { stdio: [null, process.stdout, process.stderr] }
); );
const capabilities = new Capabilities(); const capabilities = new Capabilities();
capabilities.set('tauri:options', { application }); capabilities.set('tauri:options', { application });
capabilities.setBrowserName('wry'); capabilities.setBrowserName('wry');
// start the webdriver client // start the webdriver client
driver = await new Builder() driver = await new Builder()
.withCapabilities(capabilities) .withCapabilities(capabilities)
.usingServer('http://127.0.0.1:4444/') .usingServer('http://127.0.0.1:4444/')
.build(); .build();
}); });
after(async function () { after(async function () {
// stop the webdriver session // stop the webdriver session
await driver.quit(); await driver.quit();
// kill the tauri-driver process // kill the tauri-driver process
tauriDriver.kill(); tauriDriver.kill();
}); });
describe('Hello Tauri', () => { describe('Hello Tauri', () => {
it('should be cordial', async () => { it('should be cordial', async () => {
const text = await driver.findElement(By.css('body > h1')).getText(); const text = await driver.findElement(By.css('body > h1')).getText();
expect(text).to.match(/^[hH]ello/); expect(text).to.match(/^[hH]ello/);
}); });
it('should be excited', async () => { it('should be excited', async () => {
const text = await driver.findElement(By.css('body > h1')).getText(); const text = await driver.findElement(By.css('body > h1')).getText();
expect(text).to.match(/!$/); expect(text).to.match(/!$/);
}); });
it('should be easy on the eyes', async () => { it('should be easy on the eyes', async () => {
// selenium returns color css values as rgb(r, g, b) // selenium returns color css values as rgb(r, g, b)
const text = await driver const text = await driver
.findElement(By.css('body')) .findElement(By.css('body'))
.getCssValue('background-color'); .getCssValue('background-color');
const rgb = text.match(/^rgb\((?<r>\d+), (?<g>\d+), (?<b>\d+)\)$/).groups; const rgb = text.match(/^rgb\((?<r>\d+), (?<g>\d+), (?<b>\d+)\)$/).groups;
expect(rgb).to.have.all.keys('r', 'g', 'b'); expect(rgb).to.have.all.keys('r', 'g', 'b');
const luma = 0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b; const luma = 0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b;
expect(luma).to.be.lessThan(100); expect(luma).to.be.lessThan(100);
}); });
}); });
``` ```

View File

@@ -38,20 +38,20 @@ guide on setting it up from scratch.
```json ```json
{ {
"name": "webdriverio", "name": "webdriverio",
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"scripts": { "scripts": {
"test": "wdio run wdio.conf.js" "test": "wdio run wdio.conf.js"
}, },
"dependencies": { "dependencies": {
"@wdio/cli": "^7.9.1" "@wdio/cli": "^7.9.1"
}, },
"devDependencies": { "devDependencies": {
"@wdio/local-runner": "^7.9.1", "@wdio/local-runner": "^7.9.1",
"@wdio/mocha-framework": "^7.9.1", "@wdio/mocha-framework": "^7.9.1",
"@wdio/spec-reporter": "^7.9.0" "@wdio/spec-reporter": "^7.9.0"
} }
} }
``` ```
@@ -91,36 +91,36 @@ const { spawn, spawnSync } = require('child_process');
let tauriDriver; let tauriDriver;
exports.config = { exports.config = {
specs: ['./develop/tests/specs/**/*.js'], specs: ['./develop/tests/specs/**/*.js'],
maxInstances: 1, maxInstances: 1,
capabilities: [ capabilities: [
{ {
maxInstances: 1, maxInstances: 1,
'tauri:options': { 'tauri:options': {
application: '../../target/release/hello-tauri-webdriver', application: '../../target/release/hello-tauri-webdriver',
}, },
}, },
], ],
reporters: ['spec'], reporters: ['spec'],
framework: 'mocha', framework: 'mocha',
mochaOpts: { mochaOpts: {
ui: 'bdd', ui: 'bdd',
timeout: 60000, timeout: 60000,
}, },
// ensure the rust project is built since we expect this binary to exist for the webdriver sessions // ensure the rust project is built since we expect this binary to exist for the webdriver sessions
onPrepare: () => spawnSync('cargo', ['build', '--release']), onPrepare: () => spawnSync('cargo', ['build', '--release']),
// ensure we are running `tauri-driver` before the session starts so that we can proxy the webdriver requests // ensure we are running `tauri-driver` before the session starts so that we can proxy the webdriver requests
beforeSession: () => beforeSession: () =>
(tauriDriver = spawn( (tauriDriver = spawn(
path.resolve(os.homedir(), '.cargo', 'bin', 'tauri-driver'), path.resolve(os.homedir(), '.cargo', 'bin', 'tauri-driver'),
[], [],
{ stdio: [null, process.stdout, process.stderr] } { stdio: [null, process.stdout, process.stderr] }
)), )),
// clean up the `tauri-driver` process we spawned at the start of the session // clean up the `tauri-driver` process we spawned at the start of the session
afterSession: () => tauriDriver.kill(), 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 ```js
// calculates the luma from a hex color `#abcdef` // calculates the luma from a hex color `#abcdef`
function luma(hex) { function luma(hex) {
if (hex.startsWith('#')) { if (hex.startsWith('#')) {
hex = hex.substring(1); hex = hex.substring(1);
} }
const rgb = parseInt(hex, 16); const rgb = parseInt(hex, 16);
const r = (rgb >> 16) & 0xff; const r = (rgb >> 16) & 0xff;
const g = (rgb >> 8) & 0xff; const g = (rgb >> 8) & 0xff;
const b = (rgb >> 0) & 0xff; const b = (rgb >> 0) & 0xff;
return 0.2126 * r + 0.7152 * g + 0.0722 * b; return 0.2126 * r + 0.7152 * g + 0.0722 * b;
} }
describe('Hello Tauri', () => { describe('Hello Tauri', () => {
it('should be cordial', async () => { it('should be cordial', async () => {
const header = await $('body > h1'); const header = await $('body > h1');
const text = await header.getText(); const text = await header.getText();
expect(text).toMatch(/^[hH]ello/); expect(text).toMatch(/^[hH]ello/);
}); });
it('should be excited', async () => { it('should be excited', async () => {
const header = await $('body > h1'); const header = await $('body > h1');
const text = await header.getText(); const text = await header.getText();
expect(text).toMatch(/!$/); expect(text).toMatch(/!$/);
}); });
it('should be easy on the eyes', async () => { it('should be easy on the eyes', async () => {
const body = await $('body'); const body = await $('body');
const backgroundColor = await body.getCSSProperty('background-color'); const backgroundColor = await body.getCSSProperty('background-color');
expect(luma(backgroundColor.parsed.hex)).toBeLessThan(100); 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'; import { LinkCard, CardGrid } from '@astrojs/starlight/components';
<CardGrid> <CardGrid>
<LinkCard title="Setup" href="/start/develop/tests/webdriver/example/" /> <LinkCard title="Setup" href="/start/develop/tests/webdriver/example/" />
<LinkCard <LinkCard
title="Selenium" title="Selenium"
href="/start/develop/tests/webdriver/example/selenium" href="/start/develop/tests/webdriver/example/selenium"
/> />
<LinkCard <LinkCard
title="WebdriverIO" title="WebdriverIO"
href="/start/develop/tests/webdriver/example/webdriverio" href="/start/develop/tests/webdriver/example/webdriverio"
/> />
</CardGrid> </CardGrid>
## Continuous Integration (CI) ## 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. 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 <LinkCard
title="Continuous Integration (CI)" title="Continuous Integration (CI)"
href="/start/develop/tests/webdriver/ci" href="/start/develop/tests/webdriver/ci"
/> />
[webdriver]: https://www.w3.org/TR/webdriver/ [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 ```js
invoke('my_custom_command') invoke('my_custom_command')
.then((message) => console.log(message)) .then((message) => console.log(message))
.catch((error) => console.error(error)); .catch((error) => console.error(error));
``` ```
As mentioned above, everything returned from commands must implement [`serde::Serialize`], including errors. 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 ```js
invoke('my_custom_command', { value: 'Hello, Async!' }).then(() => 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 // Invocation from JavaScript
invoke('my_custom_command', { invoke('my_custom_command', {
number: 42, number: 42,
}) })
.then((res) => .then((res) =>
console.log(`Message: ${res.message}, Other Val: ${res.other_val}`) console.log(`Message: ${res.message}, Other Val: ${res.other_val}`)
) )
.catch((e) => console.error(e)); .catch((e) => console.error(e));
``` ```
[`async_runtime::spawn`]: https://docs.rs/tauri/2.0.0-beta/tauri/async_runtime/fn.spawn.html [`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 ### 2. Start Tauri Development Window
<CommandTabs <CommandTabs
npm="npm run tauri dev" npm="npm run tauri dev"
yarn="yarn tauri dev" yarn="yarn tauri dev"
pnpm="pnpm tauri dev" pnpm="pnpm tauri dev"
cargo="cargo 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. 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" ```json title="src-tauri/tauri.conf.json"
{ {
"tauri": { "tauri": {
"bundle": { "bundle": {
"externalBin": [ "externalBin": [
"/absolute/path/to/sidecar", "/absolute/path/to/sidecar",
"relative/path/to/binary", "relative/path/to/binary",
"binaries/my-sidecar" "binaries/my-sidecar"
] ]
} }
} }
} }
``` ```
@@ -59,23 +59,23 @@ const fs = require('fs');
let extension = ''; let extension = '';
if (process.platform === 'win32') { if (process.platform === 'win32') {
extension = '.exe'; extension = '.exe';
} }
async function main() { async function main() {
const rustInfo = (await execa('rustc', ['-vV'])).stdout; const rustInfo = (await execa('rustc', ['-vV'])).stdout;
const targetTriple = /host: (\S+)/g.exec(rustInfo)[1]; const targetTriple = /host: (\S+)/g.exec(rustInfo)[1];
if (!targetTriple) { if (!targetTriple) {
console.error('Failed to determine platform target triple'); console.error('Failed to determine platform target triple');
} }
fs.renameSync( fs.renameSync(
`src-tauri/binaries/sidecar${extension}`, `src-tauri/binaries/sidecar${extension}`,
`src-tauri/binaries/sidecar-${targetTriple}${extension}` `src-tauri/binaries/sidecar-${targetTriple}${extension}`
); );
} }
main().catch((e) => { 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} ```json title="src-tauri/capabilities/main.json" ins={14-31}
{ {
"$schema": "../gen/schemas/desktop-schema.json", "$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default", "identifier": "default",
"description": "Capability for the main window", "description": "Capability for the main window",
"windows": ["main"], "windows": ["main"],
"permissions": [ "permissions": [
"path:default", "path:default",
"event:default", "event:default",
"window:default", "window:default",
"app:default", "app:default",
"resources:default", "resources:default",
"menu:default", "menu:default",
"tray:default", "tray:default",
{ {
"identifier": "shell:allow-execute", "identifier": "shell:allow-execute",
"allow": [ "allow": [
{ {
"args": [ "args": [
"arg1", "arg1",
"-a", "-a",
"--arg2", "--arg2",
{ {
"validator": "\\S+" "validator": "\\S+"
} }
], ],
"cmd": "", "cmd": "",
"name": "binaries/my-sidecar", "name": "binaries/my-sidecar",
"sidecar": true "sidecar": true
} }
] ]
}, },
"shell:allow-open" "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` // `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`. // notice that the args array matches EXACTLY what is specified on `tauri.conf.json`.
const command = Command.sidecar('binaries/my-sidecar', [ const command = Command.sidecar('binaries/my-sidecar', [
'arg1', 'arg1',
'-a', '-a',
'--arg2', '--arg2',
'any-string-that-matches-the-validator', 'any-string-that-matches-the-validator',
]); ]);
const output = await command.execute(); const output = await command.execute();
``` ```

View File

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

View File

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

View File

@@ -93,8 +93,8 @@ import { ask } from '@tauri-apps/plugin-dialog';
// Crea un diálogo de Sí/No // Crea un diálogo de Sí/No
const answer = await ask('This action cannot be reverted. Are you sure?', { const answer = await ask('This action cannot be reverted. Are you sure?', {
title: 'Tauri', title: 'Tauri',
type: 'warning', type: 'warning',
}); });
console.log(answer); console.log(answer);
@@ -112,8 +112,8 @@ import { confirm } from '@tauri-apps/plugin-dialog';
// Crea un diálogo de confirmación Ok/Cancelar // Crea un diálogo de confirmación Ok/Cancelar
const confirmation = await confirm( const confirmation = await confirm(
'This action cannot be reverted. Are you sure?', 'This action cannot be reverted. Are you sure?',
{ title: 'Tauri', type: 'warning' } { title: 'Tauri', type: 'warning' }
); );
console.log(confirmation); console.log(confirmation);
@@ -146,8 +146,8 @@ import { open } from '@tauri-apps/plugin-dialog';
// Abre un diálogo // Abre un diálogo
const file = await open({ const file = await open({
multiple: false, multiple: false,
directory: false, directory: false,
}); });
console.log(file); console.log(file);
// Imprime la ruta y el nombre del archivo en la consola // 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'; import { save } from '@tauri-apps/plugin-dialog';
// Indica para guardar un 'My Filter' con extensión .png o .jpeg // Indica para guardar un 'My Filter' con extensión .png o .jpeg
const path = await save({ const path = await save({
filters: [ filters: [
{ {
name: 'My Filter', name: 'My Filter',
extensions: ['png', 'jpeg'], extensions: ['png', 'jpeg'],
}, },
], ],
}); });
console.log(path); console.log(path);
// Imprime la ruta escogida // 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 - **[Recursos de la Comunidad](#recursos-de-la-comunidad)**: Más plugins y fórmulas creadas por la comunidad de Tauri
<Search> <Search>
## Características ## Características
<FeaturesList /> <FeaturesList />
## Recursos de la comunidad ## Recursos de la comunidad
<CommunityList /> <CommunityList />
</Search> </Search>
:::tip[¿Tienes algo genial que compartir?] :::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 ## JavaScript
<CardGrid> <CardGrid>
<LinkCard title="Next.js" href="/start/frontend/nextjs" /> <LinkCard title="Next.js" href="/start/frontend/nextjs" />
<LinkCard title="Nuxt" href="/start/frontend/nuxt" /> <LinkCard title="Nuxt" href="/start/frontend/nuxt" />
<LinkCard title="Qwik" href="/start/frontend/qwik" /> <LinkCard title="Qwik" href="/start/frontend/qwik" />
<LinkCard title="Svelte" href="/start/frontend/svelte" /> <LinkCard title="Svelte" href="/start/frontend/svelte" />
<LinkCard title="Vite" href="/start/frontend/vite" /> <LinkCard title="Vite" href="/start/frontend/vite" />
<LinkCard title="Webpack" href="/start/frontend/webpack" /> <LinkCard title="Webpack" href="/start/frontend/webpack" />
</CardGrid> </CardGrid>
## Rust ## Rust
<CardGrid> <CardGrid>
<LinkCard title="Leptos" href="/start/frontend/leptos" /> <LinkCard title="Leptos" href="/start/frontend/leptos" />
<LinkCard title="Sycamore" href="/start/frontend/sycamore" /> <LinkCard title="Sycamore" href="/start/frontend/sycamore" />
<LinkCard title="Trunk" href="/start/frontend/trunk" /> <LinkCard title="Trunk" href="/start/frontend/trunk" />
<LinkCard title="Yew" href="/start/frontend/yew" /> <LinkCard title="Yew" href="/start/frontend/yew" />
</CardGrid> </CardGrid>
## Checklist de Configuración ## 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: 1. Instala `internal-ip` para el desarrollo móvil:
<CommandTabs <CommandTabs
npm="npm install --save-dev internal-ip" npm="npm install --save-dev internal-ip"
yarn="yarn add -D internal-ip" yarn="yarn add -D internal-ip"
pnpm="pnpm add -D internal-ip" pnpm="pnpm add -D internal-ip"
/> />
2. Actualiza la configuración de Tauri: 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 ```json
// tauri.conf.json // tauri.conf.json
{ {
"build": { "build": {
"beforeDevCommand": "npm run dev", "beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run build", "beforeBuildCommand": "npm run build",
"devPath": "http://localhost:3000", "devPath": "http://localhost:3000",
"distDir": "../dist" "distDir": "../dist"
} }
} }
``` ```
@@ -47,12 +47,12 @@ Next.js es un framework para React. Aprende más sobre Next.js en https://nextjs
```json ```json
// tauri.conf.json // tauri.conf.json
{ {
"build": { "build": {
"beforeDevCommand": "yarn dev", "beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn generate", "beforeBuildCommand": "yarn generate",
"devPath": "http://localhost:3000", "devPath": "http://localhost:3000",
"distDir": "../dist" "distDir": "../dist"
} }
} }
``` ```
@@ -62,12 +62,12 @@ Next.js es un framework para React. Aprende más sobre Next.js en https://nextjs
```json ```json
// tauri.conf.json // tauri.conf.json
{ {
"build": { "build": {
"beforeDevCommand": "pnpm dev", "beforeDevCommand": "pnpm dev",
"beforeBuildCommand": "pnpm generate", "beforeBuildCommand": "pnpm generate",
"devPath": "http://localhost:3000", "devPath": "http://localhost:3000",
"distDir": "../dist" "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 // next.conf.ts
const isProd = process.env.NODE_ENV === 'production'; const isProd = process.env.NODE_ENV === 'production';
module.exports = async (phase, { defaultConfig }) => { module.exports = async (phase, { defaultConfig }) => {
let internalHost = null; let internalHost = null;
// En modo de desarrollo usamos internal-ip para servir los archivos // En modo de desarrollo usamos internal-ip para servir los archivos
if (!isProd) { if (!isProd) {
const { internalIpV4 } = await import('internal-ip'); const { internalIpV4 } = await import('internal-ip');
internalHost = await internalIpV4(); internalHost = await internalIpV4();
} }
const nextConfig = { const nextConfig = {
// Aségurate de que Next.js use SSG en lugar de SSR // Aségurate de que Next.js use SSG en lugar de SSR
// https://nextjs.org/docs/pages/building-your-application/deploying/static-exports // https://nextjs.org/docs/pages/building-your-application/deploying/static-exports
output: 'export', output: 'export',
// Nota: Esta función experimental es necesaria para usar NextJS Image en modo SSG. // 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. // Consulta https://nextjs.org/docs/messages/export-image-api para ver diferentes soluciones.
images: { images: {
unoptimized: true, unoptimized: true,
}, },
// Configura assetPrefix o el servidor no resolverá correctamente tus archivos. // Configura assetPrefix o el servidor no resolverá correctamente tus archivos.
assetPrefix: isProd ? null : `http://${internalHost}:3000`, assetPrefix: isProd ? null : `http://${internalHost}:3000`,
}; };
return nextConfig; 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 ```json
// tauri.conf.json // tauri.conf.json
{ {
"build": { "build": {
"beforeDevCommand": "npm run dev", "beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run generate", "beforeBuildCommand": "npm run generate",
"devPath": "http://localhost:3000", "devPath": "http://localhost:3000",
"distDir": "../dist" "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 ```json
// tauri.conf.json // tauri.conf.json
{ {
"build": { "build": {
"beforeDevCommand": "yarn dev", "beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn generate", "beforeBuildCommand": "yarn generate",
"devPath": "http://localhost:3000", "devPath": "http://localhost:3000",
"distDir": "../dist" "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 ```json
// tauri.conf.json // tauri.conf.json
{ {
"build": { "build": {
"beforeDevCommand": "pnpm dev", "beforeDevCommand": "pnpm dev",
"beforeBuildCommand": "pnpm generate", "beforeBuildCommand": "pnpm generate",
"devPath": "http://localhost:3000", "devPath": "http://localhost:3000",
"distDir": "../dist" "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 ```ts
export default defineNuxtConfig({ export default defineNuxtConfig({
// (opcional) Habilita las herramientas de desarrollo de Nuxt // (opcional) Habilita las herramientas de desarrollo de Nuxt
devtools: { enabled: true }, devtools: { enabled: true },
// Habilita SSG // Habilita SSG
ssr: false, ssr: false,
vite: { vite: {
// Mejor soporte para la salida de Tauri CLI // Mejor soporte para la salida de Tauri CLI
clearScreen: false, clearScreen: false,
// Habilita las variables de entorno // Habilita las variables de entorno
// Las variables de entorno adicionales se pueden encontrar en // Las variables de entorno adicionales se pueden encontrar en
// https://tauri.app/2/reference/environment-variables/ // https://tauri.app/2/reference/environment-variables/
envPrefix: ['VITE_', 'TAURI_'], envPrefix: ['VITE_', 'TAURI_'],
server: { server: {
// Tauri requiere un puerto consistente // Tauri requiere un puerto consistente
strictPort: true, strictPort: true,
// Habilita el servidor de desarrollo para pueda ser accedido por otros dispositivos para el desarrollo móvil // Habilita el servidor de desarrollo para pueda ser accedido por otros dispositivos para el desarrollo móvil
host: '0.0.0.0', host: '0.0.0.0',
hmr: { hmr: {
// Usa un websocket para la recarga rápida en móviles // Usa un websocket para la recarga rápida en móviles
protocol: 'ws', protocol: 'ws',
// Asegúrate de que esté disponible en la red // Asegúrate de que esté disponible en la red
host: '0.0.0.0', host: '0.0.0.0',
// Usa un puerto específico para hmr // Usa un puerto específico para hmr
port: 5183, port: 5183,
}, },
}, },
}, },
}); });
``` ```

View File

@@ -19,15 +19,15 @@ Trunk es una herramienta de empaquetado de aplicaciones web WASM para Rust. Obt
```json ```json
// tauri.conf.json // tauri.conf.json
{ {
"build": { "build": {
"beforeDevCommand": "trunk serve", "beforeDevCommand": "trunk serve",
"beforeBuildCommand": "trunk build", "beforeBuildCommand": "trunk build",
"devPath": "http://localhost:8080", "devPath": "http://localhost:8080",
"distDir": "../dist" "distDir": "../dist"
}, },
"app": { "app": {
"withGlobalTauri": true "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`. Pour créer une version de débogage, exécutez la commande `tauri build --debug`.
<CommandTabs <CommandTabs
npm="npm run tauri build -- --debug" npm="npm run tauri build -- --debug"
yarn="yarn tauri build --debug" yarn="yarn tauri build --debug"
pnpm="pnpm tauri build --debug" pnpm="pnpm tauri build --debug"
cargo="cargo 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`. 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'; import { LinkCard, CardGrid } from '@astrojs/starlight/components';
<CardGrid> <CardGrid>
<LinkCard <LinkCard
title="Débogage de l'application" title="Débogage de l'application"
href="/fr/develop/Debug/application//" href="/fr/develop/Debug/application//"
/> />
<LinkCard title="Débogage dans VS Code" href="/fr/develop/Debug/vscode" /> <LinkCard title="Débogage dans VS Code" href="/fr/develop/Debug/vscode" />
<LinkCard <LinkCard
title="Débogage dans RustRover" title="Débogage dans RustRover"
href="/fr/develop/Debug/rustrover" href="/fr/develop/Debug/rustrover"
/> />
</CardGrid> </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" ```json title=".vscode/launch.json"
{ {
// Utilisez IntelliSense pour en savoir plus sur les attributs possibles. // Utilisez IntelliSense pour en savoir plus sur les attributs possibles.
// Survolez pour afficher les descriptions des attributs existants. // Survolez pour afficher les descriptions des attributs existants.
// Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387 // Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"type": "lldb", "type": "lldb",
"request": "launch", "request": "launch",
"name": "Débogage de la production de Tauri", "name": "Débogage de la production de Tauri",
"cargo": { "cargo": {
"args": [ "args": [
"build", "build",
"--manifest-path=./src-tauri/Cargo.toml", "--manifest-path=./src-tauri/Cargo.toml",
"--no-default-features" "--no-default-features"
] ]
}, },
// La tâche pour `beforeDevCommand` si elle est utilisée, doit être configurée dans `.vscode/tasks.json` // La tâche pour `beforeDevCommand` si elle est utilisée, doit être configurée dans `.vscode/tasks.json`
"preLaunchTask": "ui:dev" "preLaunchTask": "ui:dev"
}, },
{ {
"type": "lldb", "type": "lldb",
"request": "launch", "request": "launch",
"name": "Debug de production de Tauri", "name": "Debug de production de Tauri",
"cargo": { "cargo": {
"args": ["build", "--release", "--manifest-path=./src-tauri/Cargo.toml"] "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` // La tâche pour `beforeBuildCommand` si elle est utilisée, doit être configurée dans `.vscode/tasks.json`
"preLaunchTask": "ui:build" "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" ```json title=".vscode/tasks.json"
{ {
// See https://go.microsoft.com/fwlink/?LinkId=733558 // See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format // for the documentation about the tasks.json format
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
{ {
"label": "ui:dev", "label": "ui:dev",
"type": "shell", "type": "shell",
// `dev` keeps running in the background // `dev` keeps running in the background
// ideally you should also configure a `problemMatcher` // 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 // see https://code.visualstudio.com/docs/editor/tasks#_can-a-background-task-be-used-as-a-prelaunchtask-in-launchjson
"isBackground": true, "isBackground": true,
// change this to your `beforeDevCommand`: // change this to your `beforeDevCommand`:
"command": "yarn", "command": "yarn",
"args": ["dev"] "args": ["dev"]
}, },
{ {
"label": "ui:build", "label": "ui:build",
"type": "shell", "type": "shell",
// change this to your `beforeBuildCommand`: // change this to your `beforeBuildCommand`:
"command": "yarn", "command": "yarn",
"args": ["build"] "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 ### 2. Démarrer la fenêtre de développement de Tauri
<CommandTabs <CommandTabs
npm="npm run tauri dev" npm="npm run tauri dev"
yarn="yarn tauri dev" yarn="yarn tauri dev"
pnpm="pnpm tauri dev" pnpm="pnpm tauri dev"
cargo="cargo 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. 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'; import { LinkCard, CardGrid } from '@astrojs/starlight/components';
<CardGrid> <CardGrid>
<LinkCard <LinkCard
title="Development Cycle" title="Development Cycle"
href="/fr/guides/develop/development-cycle/" href="/fr/guides/develop/development-cycle/"
/> />
<LinkCard <LinkCard
title="Mise à jour des dépendances" title="Mise à jour des dépendances"
href="/fr/guides/develop/updating-dependencies" href="/fr/guides/develop/updating-dependencies"
/> />
</CardGrid> </CardGrid>

View File

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

View File

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

View File

@@ -88,9 +88,9 @@ Suivez ces étapes pour envoyer une notification:
```js ```js
import { import {
isPermissionGranted, isPermissionGranted,
requestPermission, requestPermission,
sendNotification, sendNotification,
} from '@tauri-apps/plugin-notification'; } from '@tauri-apps/plugin-notification';
// Avez-vous la permission d'envoyer une notification ? // Avez-vous la permission d'envoyer une notification ?
@@ -98,13 +98,13 @@ let permissionGranted = await isPermissionGranted();
// Le cas échéant on la demande // Le cas échéant on la demande
if (!permissionGranted) { if (!permissionGranted) {
const permission = await requestPermission(); const permission = await requestPermission();
permissionGranted = permission === 'granted'; permissionGranted = permission === 'granted';
} }
// Une fois la permission obtenue, on envoie la notification // Une fois la permission obtenue, on envoie la notification
if (permissionGranted) { 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 ## JavaScript
<CardGrid> <CardGrid>
<LinkCard title="Next.js" href="/fr/start/frontend/nextjs" /> <LinkCard title="Next.js" href="/fr/start/frontend/nextjs" />
<LinkCard title="Nuxt" href="/fr/start/frontend/nuxt" /> <LinkCard title="Nuxt" href="/fr/start/frontend/nuxt" />
<LinkCard title="Qwik" href="/fr/start/frontend/qwik" /> <LinkCard title="Qwik" href="/fr/start/frontend/qwik" />
<LinkCard title="Svelte" href="/fr/start/frontend/svelte" /> <LinkCard title="Svelte" href="/fr/start/frontend/svelte" />
<LinkCard title="Vite" href="/fr/start/frontend/vite" /> <LinkCard title="Vite" href="/fr/start/frontend/vite" />
<LinkCard title="Webpack" href="/fr/start/frontend/webpack" /> <LinkCard title="Webpack" href="/fr/start/frontend/webpack" />
</CardGrid> </CardGrid>
## Rust ## Rust
<CardGrid> <CardGrid>
<LinkCard title="Leptos" href="/fr/start/frontend/leptos" /> <LinkCard title="Leptos" href="/fr/start/frontend/leptos" />
<LinkCard title="Sycamore" href="/fr/start/frontend/sycamore" /> <LinkCard title="Sycamore" href="/fr/start/frontend/sycamore" />
<LinkCard title="Trunk" href="/fr/start/frontend/trunk" /> <LinkCard title="Trunk" href="/fr/start/frontend/trunk" />
<LinkCard title="Yew" href="/fr/start/frontend/yew" /> <LinkCard title="Yew" href="/fr/start/frontend/yew" />
</CardGrid> </CardGrid>
## Instructions de Configuration ## 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 : 1. Installez `internal-ip` pour le développement mobile :
<CommandTabs <CommandTabs
npm="npm install --save-dev internal-ip" npm="npm install --save-dev internal-ip"
yarn="yarn add -D internal-ip" yarn="yarn add -D internal-ip"
pnpm="pnpm add -D internal-ip" pnpm="pnpm add -D internal-ip"
/> />
2. Mettez à jour la configuration de Tauri : 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 ```json
// tauri.conf.json // tauri.conf.json
{ {
"build": { "build": {
"beforeDevCommand": "npm run dev", "beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run build", "beforeBuildCommand": "npm run build",
"devPath": "http://localhost:3000", "devPath": "http://localhost:3000",
"distDir": "../dist" "distDir": "../dist"
} }
} }
``` ```
@@ -48,12 +48,12 @@ Next.js est un framework React. Apprenez-en plus au sujet de Next.js sur https:/
```json ```json
// tauri.conf.json // tauri.conf.json
{ {
"build": { "build": {
"beforeDevCommand": "yarn dev", "beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn generate", "beforeBuildCommand": "yarn generate",
"devPath": "http://localhost:3000", "devPath": "http://localhost:3000",
"distDir": "../dist" "distDir": "../dist"
} }
} }
``` ```
@@ -63,12 +63,12 @@ Next.js est un framework React. Apprenez-en plus au sujet de Next.js sur https:/
```json ```json
// tauri.conf.json // tauri.conf.json
{ {
"build": { "build": {
"beforeDevCommand": "pnpm dev", "beforeDevCommand": "pnpm dev",
"beforeBuildCommand": "pnpm generate", "beforeBuildCommand": "pnpm generate",
"devPath": "http://localhost:3000", "devPath": "http://localhost:3000",
"distDir": "../dist" "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 // next.conf.ts
const isProd = process.env.NODE_ENV === 'production'; const isProd = process.env.NODE_ENV === 'production';
module.exports = async (phase, { defaultConfig }) => { module.exports = async (phase, { defaultConfig }) => {
let internalHost = null; let internalHost = null;
// En mode développement, on utilise "internal-ip" pour se servir des assets. // En mode développement, on utilise "internal-ip" pour se servir des assets.
if (!isProd) { if (!isProd) {
const { internalIpV4 } = await import('internal-ip'); const { internalIpV4 } = await import('internal-ip');
internalHost = await internalIpV4(); internalHost = await internalIpV4();
} }
const nextConfig = { const nextConfig = {
// Assurez-vous que Next.js utilise SSG au lieu de SSR // Assurez-vous que Next.js utilise SSG au lieu de SSR
// https://nextjs.org/docs/pages/building-your-application/deploying/static-exports // https://nextjs.org/docs/pages/building-your-application/deploying/static-exports
output: 'export', output: 'export',
// Note: Cette fonctionnalité expérimentale est requise pour utiliser NextJS Image en mode SSG. // 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. // Voir https://nextjs.org/docs/messages/export-image-api pour des solutions différentes.
images: { images: {
unoptimized: true, unoptimized: true,
}, },
// Configurez assetPrefix sinon le server ne résoudra pas correctement vos assets. // Configurez assetPrefix sinon le server ne résoudra pas correctement vos assets.
assetPrefix: isProd ? null : `http://${internalHost}:3000`, assetPrefix: isProd ? null : `http://${internalHost}:3000`,
}; };
return nextConfig; return nextConfig;
}; };
``` ```

View File

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

View File

@@ -27,15 +27,15 @@ Vous devrez utiliser `cargo install --git https://github.com/amrbashir/trunk` po
```json ```json
// tauri.conf.json // tauri.conf.json
{ {
"build": { "build": {
"beforeDevCommand": "trunk serve", "beforeDevCommand": "trunk serve",
"beforeBuildCommand": "trunk build", "beforeBuildCommand": "trunk build",
"devPath": "http://localhost:8080", "devPath": "http://localhost:8080",
"distDir": "../dist" "distDir": "../dist"
}, },
"app": { "app": {
"withGlobalTauri": true "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: Tauri v2 contient la commande `migrate` qui simplifie votre migration:
<CommandTabs <CommandTabs
npm="npm install @tauri-apps/cli@latest npm="npm install @tauri-apps/cli@latest
npm run tauri migrate" npm run tauri migrate"
yarn="yarn upgrade @tauri-apps/cli --latest yarn="yarn upgrade @tauri-apps/cli --latest
yarn tauri migrate" yarn tauri migrate"
pnpm="pnpm update @tauri-apps/cli --latest pnpm="pnpm update @tauri-apps/cli --latest
pnpm tauri migrate" 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' cargo tauri migrate'
/> />
@@ -130,9 +130,9 @@ fn main() {
```json ```json
// package.json // package.json
{ {
"dependencies": { "dependencies": {
"@tauri-apps/plugin-app": "^2.0.0" "@tauri-apps/plugin-app": "^2.0.0"
} }
} }
``` ```
@@ -190,9 +190,9 @@ fn main() {
```json ```json
// package.json // package.json
{ {
"dependencies": { "dependencies": {
"@tauri-apps/plugin-cli": "^2.0.0" "@tauri-apps/plugin-cli": "^2.0.0"
} }
} }
``` ```
@@ -241,9 +241,9 @@ fn main() {
```json ```json
// package.json // package.json
{ {
"dependencies": { "dependencies": {
"@tauri-apps/plugin-clipboard-manager": "^2.0.0" "@tauri-apps/plugin-clipboard-manager": "^2.0.0"
} }
} }
``` ```
@@ -299,21 +299,21 @@ fn main() {
```json ```json
// package.json // package.json
{ {
"dependencies": { "dependencies": {
"@tauri-apps/plugin-dialog": "^2.0.0" "@tauri-apps/plugin-dialog": "^2.0.0"
} }
} }
``` ```
```js ```js
import { save } from '@tauri-apps/plugin-dialog'; import { save } from '@tauri-apps/plugin-dialog';
const filePath = await save({ const filePath = await save({
filters: [ filters: [
{ {
name: 'Image', name: 'Image',
extensions: ['png', 'jpeg'], extensions: ['png', 'jpeg'],
}, },
], ],
}); });
``` ```
@@ -365,9 +365,9 @@ fn main() {
```json ```json
// package.json // package.json
{ {
"dependencies": { "dependencies": {
"@tauri-apps/plugin-fs": "^2.0.0" "@tauri-apps/plugin-fs": "^2.0.0"
} }
} }
``` ```
@@ -411,16 +411,16 @@ fn main() {
```json ```json
// package.json // package.json
{ {
"dependencies": { "dependencies": {
"@tauri-apps/plugin-global-shortcut": "^2.0.0" "@tauri-apps/plugin-global-shortcut": "^2.0.0"
} }
} }
``` ```
```js ```js
import { register } from '@tauri-apps/plugin-global-shortcut'; import { register } from '@tauri-apps/plugin-global-shortcut';
await register('CommandOrControl+Shift+C', () => { await register('CommandOrControl+Shift+C', () => {
console.log('Shortcut triggered'); console.log('Shortcut triggered');
}); });
``` ```
@@ -468,16 +468,16 @@ fn main() {
```json ```json
// package.json // package.json
{ {
"dependencies": { "dependencies": {
"@tauri-apps/plugin-http": "^2.0.0" "@tauri-apps/plugin-http": "^2.0.0"
} }
} }
``` ```
```js ```js
import { fetch } from '@tauri-apps/plugin-http'; import { fetch } from '@tauri-apps/plugin-http';
const response = await fetch( 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 ```json
// package.json // package.json
{ {
"dependencies": { "dependencies": {
"@tauri-apps/plugin-notification": "^2.0.0" "@tauri-apps/plugin-notification": "^2.0.0"
} }
} }
``` ```
@@ -714,9 +714,9 @@ fn main() {
```json ```json
// package.json // package.json
{ {
"dependencies": { "dependencies": {
"@tauri-apps/plugin-os": "^2.0.0" "@tauri-apps/plugin-os": "^2.0.0"
} }
} }
``` ```
@@ -769,9 +769,9 @@ fn main() {
```json ```json
// package.json // package.json
{ {
"dependencies": { "dependencies": {
"@tauri-apps/plugin-process": "^2.0.0" "@tauri-apps/plugin-process": "^2.0.0"
} }
} }
``` ```
@@ -828,9 +828,9 @@ fn main() {
```json ```json
// package.json // package.json
{ {
"dependencies": { "dependencies": {
"@tauri-apps/plugin-shell": "^2.0.0" "@tauri-apps/plugin-shell": "^2.0.0"
} }
} }
``` ```
@@ -1009,9 +1009,9 @@ fn main() {
```json ```json
// package.json // package.json
{ {
"dependencies": { "dependencies": {
"@tauri-apps/plugin-updater": "^2.0.0" "@tauri-apps/plugin-updater": "^2.0.0"
} }
} }
``` ```
@@ -1021,13 +1021,13 @@ import { relaunch } from '@tauri-apps/plugin-process';
const update = await check(); const update = await check();
if (update.response.available) { if (update.response.available) {
console.log( console.log(
`Update to ${update.response.latestVersion} available! Date: ${update.response.date}` `Update to ${update.response.latestVersion} available! Date: ${update.response.date}`
); );
console.log(`Release notes: ${update.response.body}`); console.log(`Release notes: ${update.response.body}`);
await update.downloadAndInstall(); await update.downloadAndInstall();
// nécéssite le plugin `process` // nécéssite le plugin `process`
await relaunch(); await relaunch();
} }
``` ```
@@ -1096,9 +1096,9 @@ fn main() {
```json ```json
// package.json // package.json
{ {
"dependencies": { "dependencies": {
"@tauri-apps/plugin-window": "^2.0.0" "@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'; import Cta from '@fragments/cta.mdx';
<div class="hero-bg"> <div class="hero-bg">
<div class="bg-logo"></div> <div class="bg-logo"></div>
<div class="bg-grad"></div> <div class="bg-grad"></div>
<div class="bg-grad-red"></div> <div class="bg-grad-red"></div>
</div> </div>
<div class="lp-cta-card"> <div class="lp-cta-card">
<Card title="Create a Project" icon="rocket"> <Card title="Create a Project" icon="rocket">
<Cta /> <Cta />
</Card> </Card>
</div> </div>
<CardGrid> <CardGrid>
<Card title="Frontend Independent" icon="rocket"> <Card title="Frontend Independent" icon="rocket">
Bring your existing web stack to Tauri or start that new dream project. 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 Tauri supports any frontend framework so you don't need to change your
stack. stack.
</Card> </Card>
<Card title="Cross Platform" icon="rocket"> <Card title="Cross Platform" icon="rocket">
Build your app for Linux, macOS, Windows, Android and iOS - all from a Build your app for Linux, macOS, Windows, Android and iOS - all from a
single codebase. single codebase.
</Card> </Card>
<Card title="Inter-Process Communication" icon="rocket"> <Card title="Inter-Process Communication" icon="rocket">
Write your frontend in JavaScript, application logic in Rust, and integrate Write your frontend in JavaScript, application logic in Rust, and integrate
deep into the system with Swift and Kotlin. deep into the system with Swift and Kotlin.
</Card> </Card>
<Card title="Maximum Security" icon="rocket"> <Card title="Maximum Security" icon="rocket">
Front-of-mind for the Tauri Team that drives our highest priorities and Front-of-mind for the Tauri Team that drives our highest priorities and
biggest innovations. biggest innovations.
</Card> </Card>
<Card title="Minimal Size" icon="rocket"> <Card title="Minimal Size" icon="rocket">
By using the OS's native web renderer, the size of a Tauri app can be little By using the OS's native web renderer, the size of a Tauri app can be little
as 600KB. as 600KB.
</Card> </Card>
<Card title="Powered by Rust" icon="rocket"> <Card title="Powered by Rust" icon="rocket">
With performance and security at the center, Rust is the language for the With performance and security at the center, Rust is the language for the
next generation of apps. next generation of apps.
</Card> </Card>
</CardGrid> </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" ```json title=".vscode/launch.json"
{ {
// Use IntelliSense to learn about possible attributes. // Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes. // Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"type": "lldb", "type": "lldb",
"request": "launch", "request": "launch",
"name": "Tauri Development Debug", "name": "Tauri Development Debug",
"cargo": { "cargo": {
"args": [ "args": [
"build", "build",
"--manifest-path=./src-tauri/Cargo.toml", "--manifest-path=./src-tauri/Cargo.toml",
"--no-default-features" "--no-default-features"
] ]
}, },
// task for the `beforeDevCommand` if used, must be configured in `.vscode/tasks.json` // task for the `beforeDevCommand` if used, must be configured in `.vscode/tasks.json`
"preLaunchTask": "ui:dev" "preLaunchTask": "ui:dev"
}, },
{ {
"type": "lldb", "type": "lldb",
"request": "launch", "request": "launch",
"name": "Tauri Production Debug", "name": "Tauri Production Debug",
"cargo": { "cargo": {
"args": ["build", "--release", "--manifest-path=./src-tauri/Cargo.toml"] "args": ["build", "--release", "--manifest-path=./src-tauri/Cargo.toml"]
}, },
// task for the `beforeBuildCommand` if used, must be configured in `.vscode/tasks.json` // task for the `beforeBuildCommand` if used, must be configured in `.vscode/tasks.json`
"preLaunchTask": "ui:build" "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" ```json title=".vscode/tasks.json"
{ {
// See https://go.microsoft.com/fwlink/?LinkId=733558 // See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format // for the documentation about the tasks.json format
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
{ {
"label": "ui:dev", "label": "ui:dev",
"type": "shell", "type": "shell",
// `dev` keeps running in the background // `dev` keeps running in the background
// ideally you should also configure a `problemMatcher` // 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 // see https://code.visualstudio.com/docs/editor/tasks#_can-a-background-task-be-used-as-a-prelaunchtask-in-launchjson
"isBackground": true, "isBackground": true,
// change this to your `beforeDevCommand`: // change this to your `beforeDevCommand`:
"command": "yarn", "command": "yarn",
"args": ["dev"] "args": ["dev"]
}, },
{ {
"label": "ui:build", "label": "ui:build",
"type": "shell", "type": "shell",
// change this to your `beforeBuildCommand`: // change this to your `beforeBuildCommand`:
"command": "yarn", "command": "yarn",
"args": ["build"] "args": ["build"]
} }
] ]
} }
``` ```

View File

@@ -93,11 +93,11 @@ See [Access Control List](/reference/acl/) for more information.
```json title="src-tauri/capabilities/mobile.json" ```json title="src-tauri/capabilities/mobile.json"
{ {
"$schema": "../gen/schemas/mobile-schema.json", "$schema": "../gen/schemas/mobile-schema.json",
"identifier": "mobile-capability", "identifier": "mobile-capability",
"windows": ["main"], "windows": ["main"],
"platforms": ["iOS", "android"], "platforms": ["iOS", "android"],
"permissions": ["barcode-scanner:allow-scan", "barcode-scanner:allow-cancel"] "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" ```json title="src-tauri/tauri.conf.json"
{ {
"plugins": { "plugins": {
"cli": { "cli": {
"description": "Tauri CLI Plugin Example", "description": "Tauri CLI Plugin Example",
"args": [ "args": [
{ {
"short": "v", "short": "v",
"name": "verbose", "name": "verbose",
"description": "Verbosity level" "description": "Verbosity level"
} }
], ],
"subcommands": { "subcommands": {
"run": { "run": {
"description": "Run the application", "description": "Run the application",
"args": [ "args": [
{ {
"name": "debug", "name": "debug",
"description": "Run application in debug mode" "description": "Run application in debug mode"
}, },
{ {
"name": "release", "name": "release",
"description": "Run application in release mode" "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" ```json title="src-tauri/tauri.conf.json"
{ {
"args": [ "args": [
{ {
"name": "source", "name": "source",
"index": 1, "index": 1,
"takesValue": true "takesValue": true
}, },
{ {
"name": "destination", "name": "destination",
"index": 2, "index": 2,
"takesValue": true "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" ```json title="tauri-src/tauri.conf.json"
{ {
"args": [ "args": [
{ {
"name": "type", "name": "type",
"short": "t", "short": "t",
"takesValue": true, "takesValue": true,
"multiple": true, "multiple": true,
"possibleValues": ["foo", "bar"] "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" ```json title="tauri-src/tauri.conf.json"
{ {
"args": [ "args": [
{ {
"name": "verbose", "name": "verbose",
"short": "v" "short": "v"
} }
] ]
} }
``` ```
@@ -222,14 +222,14 @@ import { getMatches } from '@tauri-apps/plugin-cli';
const matches = await getMatches(); const matches = await getMatches();
if (matches.subcommand?.name === 'run') { if (matches.subcommand?.name === 'run') {
// `./your-app run $ARGS` was executed // `./your-app run $ARGS` was executed
const args = matches.subcommand.matches.args; const args = matches.subcommand.matches.args;
if (args.debug?.value === true) { if (args.debug?.value === true) {
// `./your-app run --debug` was executed // `./your-app run --debug` was executed
} }
if (args.release?.value === true) { if (args.release?.value === true) {
// `./your-app run --release` was executed // `./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} ```json title="src-tauri/capabilities/main.json" ins={6}
{ {
"$schema": "../gen/schemas/desktop-schema.json", "$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability", "identifier": "main-capability",
"description": "Capability for the main window", "description": "Capability for the main window",
"windows": ["main"], "windows": ["main"],
"permissions": ["cli:default"] "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" ```json title=".well-known/apple-app-site-association"
{ {
"applinks": { "applinks": {
"details": [ "details": [
{ {
"appIDs": ["$DEVELOPMENT_TEAM_ID.$APP_BUNDLE_ID"], "appIDs": ["$DEVELOPMENT_TEAM_ID.$APP_BUNDLE_ID"],
"components": [ "components": [
{ {
"/": "/open/*", "/": "/open/*",
"comment": "Matches any URL whose path starts with /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" ```json title="tauri.conf.json"
{ {
"plugins": { "plugins": {
"deep-link": { "deep-link": {
"domains": [ "domains": [
{ "host": "your.website.com", "pathPrefix": ["/open"] }, { "host": "your.website.com", "pathPrefix": ["/open"] },
{ "host": "another.site.br" } { "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'; import { onOpenUrl } from '@tauri-apps/plugin-deep-link';
await onOpenUrl((urls) => { 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} ```json title="src-tauri/capabilities/main.json" ins={9}
{ {
"$schema": "../gen/schemas/mobile-schema.json", "$schema": "../gen/schemas/mobile-schema.json",
"identifier": "mobile-capability", "identifier": "mobile-capability",
"windows": ["main"], "windows": ["main"],
"platforms": ["iOS", "android"], "platforms": ["iOS", "android"],
"permissions": [ "permissions": [
// Usually you will need event:default to listen to the deep-link event // Usually you will need event:default to listen to the deep-link event
"event:default", "event:default",
"deep-link:default" "deep-link:default"
] ]
} }
``` ```

View File

@@ -96,8 +96,8 @@ import { ask } from '@tauri-apps/plugin-dialog';
// Create a Yes/No dialog // Create a Yes/No dialog
const answer = await ask('This action cannot be reverted. Are you sure?', { const answer = await ask('This action cannot be reverted. Are you sure?', {
title: 'Tauri', title: 'Tauri',
kind: 'warning', kind: 'warning',
}); });
console.log(answer); console.log(answer);
@@ -115,8 +115,8 @@ import { confirm } from '@tauri-apps/plugin-dialog';
// Creates a confirmation Ok/Cancel dialog // Creates a confirmation Ok/Cancel dialog
const confirmation = await confirm( const confirmation = await confirm(
'This action cannot be reverted. Are you sure?', 'This action cannot be reverted. Are you sure?',
{ title: 'Tauri', kind: 'warning' } { title: 'Tauri', kind: 'warning' }
); );
console.log(confirmation); console.log(confirmation);
@@ -149,8 +149,8 @@ import { open } from '@tauri-apps/plugin-dialog';
// Open a dialog // Open a dialog
const file = await open({ const file = await open({
multiple: false, multiple: false,
directory: false, directory: false,
}); });
console.log(file); console.log(file);
// Prints file path and name to the console // 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'; import { save } from '@tauri-apps/plugin-dialog';
// Prompt to save a 'My Filter' with extension .png or .jpeg // Prompt to save a 'My Filter' with extension .png or .jpeg
const path = await save({ const path = await save({
filters: [ filters: [
{ {
name: 'My Filter', name: 'My Filter',
extensions: ['png', 'jpeg'], extensions: ['png', 'jpeg'],
}, },
], ],
}); });
console.log(path); console.log(path);
// Prints the chosen 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} ```json title="src-tauri/capabilities/main.json" ins={7-11}
{ {
"$schema": "../gen/schemas/desktop-schema.json", "$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability", "identifier": "main-capability",
"description": "Capability for the main window", "description": "Capability for the main window",
"windows": ["main"], "windows": ["main"],
"permissions": [ "permissions": [
"fs:default", "fs:default",
{ {
"identifier": "fs:allow-exists", "identifier": "fs:allow-exists",
"allow": [{ "path": "$APPDATA/*" }] "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} ```json title="src-tauri/capabilities/main.json" {7-10}
{ {
"$schema": "../gen/schemas/desktop-schema.json", "$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability", "identifier": "main-capability",
"description": "Capability for the main window", "description": "Capability for the main window",
"windows": ["main"], "windows": ["main"],
"permissions": [ "permissions": [
{ {
"identifier": "fs:scope", "identifier": "fs:scope",
"allow": [{ "path": "$APPDATA" }, { "path": "$APPDATA/**" }] "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} ```json title="src-tauri/capabilities/main.json" {7-14}
{ {
"$schema": "../gen/schemas/desktop-schema.json", "$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability", "identifier": "main-capability",
"description": "Capability for the main window", "description": "Capability for the main window",
"windows": ["main"], "windows": ["main"],
"permissions": [ "permissions": [
{ {
"identifier": "fs:allow-rename", "identifier": "fs:allow-rename",
"allow": [{ "path": "$HOME/**" }] "allow": [{ "path": "$HOME/**" }]
}, },
{ {
"identifier": "fs:allow-exists", "identifier": "fs:allow-exists",
"allow": [{ "path": "$APPDATA/*" }] "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'; import { register } from '@tauri-apps/plugin-global-shortcut';
await register('CommandOrControl+Shift+C', () => { 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} ```json title="src-tauri/capabilities/main.json" ins={7-9}
{ {
"$schema": "../gen/schemas/desktop-schema.json", "$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability", "identifier": "main-capability",
"description": "Capability for the main window", "description": "Capability for the main window",
"windows": ["main"], "windows": ["main"],
"permissions": [ "permissions": [
"global-shortcut:allow-is-registered", "global-shortcut:allow-is-registered",
"global-shortcut:allow-register", "global-shortcut:allow-register",
"global-shortcut:allow-unregister" "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 ```json
//src-tauri/capabilities/base.json //src-tauri/capabilities/base.json
{ {
"permissions": [ "permissions": [
{ {
"identifier": "http:default", "identifier": "http:default",
"allow": [{ "url": "https://*.tauri.app" }], "allow": [{ "url": "https://*.tauri.app" }],
"deny": [{ "url": "https://private.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 // Send a GET request
const response = await fetch('http://test.tauri.app/data.json', { const response = await fetch('http://test.tauri.app/data.json', {
method: 'GET', method: 'GET',
}); });
console.log(response.status); // e.g. 200 console.log(response.status); // e.g. 200
console.log(response.statusText); // e.g. "OK" 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 - **[Community Resources](#community-resources)**: More plugins and recipes built by the Tauri community
<Search> <Search>
## Features ## Features
<FeaturesList /> <FeaturesList />
## Community Resources ## Community Resources
<LinkCard <LinkCard
title="Have something to share?" title="Have something to share?"
description="Open a pull request to show us your amazing resource." description="Open a pull request to show us your amazing resource."
href="https://github.com/tauri-apps/awesome-tauri/pulls" href="https://github.com/tauri-apps/awesome-tauri/pulls"
/> />
### Plugins ### Plugins
<AwesomeTauri section="plugins-no-official" /> <AwesomeTauri section="plugins-no-official" />
### Integrations ### Integrations
<AwesomeTauri section="integrations" /> <AwesomeTauri section="integrations" />
</Search> </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} ```json title="src-tauri/capabilities/main.json" ins={6}
{ {
"$schema": "../gen/schemas/desktop-schema.json", "$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability", "identifier": "main-capability",
"description": "Capability for the main window", "description": "Capability for the main window",
"windows": ["main"], "windows": ["main"],
"permissions": ["log:default"] "permissions": ["log:default"]
} }
``` ```

View File

@@ -92,9 +92,9 @@ Follow these steps to send a notification:
```js ```js
import { import {
isPermissionGranted, isPermissionGranted,
requestPermission, requestPermission,
sendNotification, sendNotification,
} from '@tauri-apps/plugin-notification'; } from '@tauri-apps/plugin-notification';
// Do you have permission to send a 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 not we need to request it
if (!permissionGranted) { if (!permissionGranted) {
const permission = await requestPermission(); const permission = await requestPermission();
permissionGranted = permission === 'granted'; permissionGranted = permission === 'granted';
} }
// Once permission has been granted we can send the notification // Once permission has been granted we can send the notification
if (permissionGranted) { 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'; import { Command } from '@tauri-apps/plugin-shell';
let result = await Command.create('exec-sh', [ let result = await Command.create('exec-sh', [
'-c', '-c',
"echo 'Hello World!'", "echo 'Hello World!'",
]).execute(); ]).execute();
console.log(result); 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} ```json title="src-tauri/capabilities/main.json" ins={6-23}
{ {
"$schema": "../gen/schemas/desktop-schema.json", "$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability", "identifier": "main-capability",
"description": "Capability for the main window", "description": "Capability for the main window",
"windows": ["main"], "windows": ["main"],
"permissions": [ "permissions": [
{ {
"identifier": "shell:allow-execute", "identifier": "shell:allow-execute",
"allow": [ "allow": [
{ {
"name": "exec-sh", "name": "exec-sh",
"cmd": "sh", "cmd": "sh",
"args": [ "args": [
"-c", "-c",
{ {
"validator": "\\S+" "validator": "\\S+"
} }
], ],
"sidecar": false "sidecar": false
} }
] ]
} }
] ]
} }
``` ```

View File

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

View File

@@ -13,5 +13,5 @@ import PluginLinks from '@components/PluginLinks.astro';
<PluginLinks plugin="updater" /> <PluginLinks plugin="updater" />
<Stub> <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> </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'; import { upload } from '@tauri-apps/plugin-upload';
upload( upload(
'https://example.com/file-upload', 'https://example.com/file-upload',
'./path/to/my/file.txt', './path/to/my/file.txt',
(progress, total) => console.log(`Uploaded ${progress} of ${total} bytes`), // a callback that will be called with the upload progress (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 { 'Content-Type': 'text/plain' } // optional headers to send with the request
); );
``` ```
@@ -85,9 +85,9 @@ upload(
import { download } from '@tauri-apps/plugin-upload'; import { download } from '@tauri-apps/plugin-upload';
download( download(
'https://example.com/file-download-link', 'https://example.com/file-download-link',
'./path/to/save/my/file.txt', './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 (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 { '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'); const ws = await WebSocket.connect('ws://127.0.0.1:8080');
ws.addListener((msg) => { ws.addListener((msg) => {
console.log('Received Message:', msg); console.log('Received Message:', msg);
}); });
await ws.send('Hello World!'); 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} ```json title="src-tauri/capabilities/main.json" ins={6}
{ {
"$schema": "../gen/schemas/desktop-schema.json", "$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability", "identifier": "main-capability",
"description": "Capability for the main window", "description": "Capability for the main window",
"windows": ["main"], "windows": ["main"],
"permissions": ["websocket:default"] "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} ```json title="src-tauri/capabilities/main.json" ins={7-8}
{ {
"$schema": "../gen/schemas/desktop-schema.json", "$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability", "identifier": "main-capability",
"description": "Capability for the main window", "description": "Capability for the main window",
"windows": ["main"], "windows": ["main"],
"permissions": ["window:default", "window:allow-start-dragging"] "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 ```css
.titlebar { .titlebar {
height: 30px; height: 30px;
background: #329ea3; background: #329ea3;
user-select: none; user-select: none;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
} }
.titlebar-button { .titlebar-button {
display: inline-flex; display: inline-flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 30px; width: 30px;
height: 30px; height: 30px;
user-select: none; user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
} }
.titlebar-button:hover { .titlebar-button:hover {
background: #5bbec3; background: #5bbec3;
} }
``` ```
@@ -110,21 +110,21 @@ Put this at the top of your `<body>` tag:
```html ```html
<div data-tauri-drag-region class="titlebar"> <div data-tauri-drag-region class="titlebar">
<div class="titlebar-button" id="titlebar-minimize"> <div class="titlebar-button" id="titlebar-minimize">
<img <img
src="https://api.iconify.design/mdi:window-minimize.svg" src="https://api.iconify.design/mdi:window-minimize.svg"
alt="minimize" alt="minimize"
/> />
</div> </div>
<div class="titlebar-button" id="titlebar-maximize"> <div class="titlebar-button" id="titlebar-maximize">
<img <img
src="https://api.iconify.design/mdi:window-maximize.svg" src="https://api.iconify.design/mdi:window-maximize.svg"
alt="maximize" alt="maximize"
/> />
</div> </div>
<div class="titlebar-button" id="titlebar-close"> <div class="titlebar-button" id="titlebar-close">
<img src="https://api.iconify.design/mdi:close.svg" alt="close" /> <img src="https://api.iconify.design/mdi:close.svg" alt="close" />
</div> </div>
</div> </div>
``` ```
@@ -140,14 +140,14 @@ import { Window } from '@tauri-apps/api/window';
const appWindow = new Window('main'); const appWindow = new Window('main');
document document
.getElementById('titlebar-minimize') .getElementById('titlebar-minimize')
?.addEventListener('click', () => appWindow.minimize()); ?.addEventListener('click', () => appWindow.minimize());
document document
.getElementById('titlebar-maximize') .getElementById('titlebar-maximize')
?.addEventListener('click', () => appWindow.toggleMaximize()); ?.addEventListener('click', () => appWindow.toggleMaximize());
document document
.getElementById('titlebar-close') .getElementById('titlebar-close')
?.addEventListener('click', () => appWindow.close()); ?.addEventListener('click', () => appWindow.close());
``` ```
### (macOS) Transparent Titlebar with Custom Window Background Color ### (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 <CommandTabs
npm="npm run tauri add window-state" npm="npm run tauri add window-state"
yarn="yarn run tauri add window-state" yarn="yarn run tauri add window-state"
pnpm="pnpm tauri add window-state" pnpm="pnpm tauri add window-state"
cargo="cargo tauri add window-state" cargo="cargo tauri add window-state"
/> />
</TabItem> </TabItem>
@@ -98,8 +98,8 @@ Similarly you can manually restore a window's state from disk:
```javascript ```javascript
import { import {
restoreStateCurrent, restoreStateCurrent,
StateFlags, StateFlags,
} from '@tauri-apps/plugin-window-state'; } from '@tauri-apps/plugin-window-state';
restoreStateCurrent(StateFlags.ALL); 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 ```json title="src-tauri/capabilities/main.json
{ {
"$schema": "../gen/schemas/desktop-schema.json", "$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability", "identifier": "main-capability",
"description": "Capability for the main window", "description": "Capability for the main window",
"windows": ["main"], "windows": ["main"],
"permissions": [ "permissions": [
"path:default", "path:default",
"event:default", "event:default",
"window:default", "window:default",
"app:default", "app:default",
"resources:default", "resources:default",
"menu:default", "menu:default",
"tray:default", "tray:default",
"window:allow-set-title" "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 ```json title="src-tauri/capabilities/desktop.json
{ {
"$schema": "../gen/schemas/desktop-schema.json", "$schema": "../gen/schemas/desktop-schema.json",
"identifier": "desktop-capability", "identifier": "desktop-capability",
"windows": ["main"], "windows": ["main"],
"platforms": ["linux", "macOS", "windows"], "platforms": ["linux", "macOS", "windows"],
"permissions": ["global-shortcut:allow-register"] "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 ```json title="src-tauri/capabilities/mobile.json
{ {
"$schema": "../gen/schemas/mobile-schema.json", "$schema": "../gen/schemas/mobile-schema.json",
"identifier": "mobile-capability", "identifier": "mobile-capability",
"windows": ["main"], "windows": ["main"],
"platforms": ["iOS", "android"], "platforms": ["iOS", "android"],
"permissions": [ "permissions": [
"nfc:allow-scan", "nfc:allow-scan",
"biometric:allow-authenticate", "biometric:allow-authenticate",
"barcode-scanner:allow-scan" "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 ```json title="src-tauri/capabilities/linux.json
{ {
"$schema": "../gen/schemas/linux-schema.json", "$schema": "../gen/schemas/linux-schema.json",
"identifier": "linux-capability", "identifier": "linux-capability",
"windows": ["main"], "windows": ["main"],
"platforms": ["iOS", "android"], "platforms": ["iOS", "android"],
"permissions": ["dbus::call"] "permissions": ["dbus::call"]
} }
``` ```
@@ -115,12 +115,12 @@ Let's study some examples for official plugins:
```json title="src-tauri/capabilities/base.json ```json title="src-tauri/capabilities/base.json
{ {
"permissions": [ "permissions": [
{ {
"identifier": "fs:scope", "identifier": "fs:scope",
"allow": [{ "path": "$APPDATA" }, { "path": "$APPDATA/**" }] "allow": [{ "path": "$APPDATA" }, { "path": "$APPDATA/**" }]
} }
] ]
} }
``` ```
@@ -132,12 +132,12 @@ Let's study some examples for official plugins:
```json title="src-tauri/capabilities/base.json ```json title="src-tauri/capabilities/base.json
{ {
"permissions": [ "permissions": [
{ {
"identifier": "fs:allow-rename", "identifier": "fs:allow-rename",
"allow": [{ "path": "$HOME/**" }] "allow": [{ "path": "$HOME/**" }]
} }
] ]
} }
``` ```
@@ -145,13 +145,13 @@ Let's study some examples for official plugins:
```json title="src-tauri/capabilities/base.json ```json title="src-tauri/capabilities/base.json
{ {
"permissions": [ "permissions": [
{ {
"identifier": "http:default", "identifier": "http:default",
"allow": [{ "url": "https://*.tauri.app" }], "allow": [{ "url": "https://*.tauri.app" }],
"deny": [{ "url": "https://private.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: You can add the Tauri CLI to your current project using your package manager of choice:
<CommandTabs <CommandTabs
npm="npm install --save-dev @tauri-apps/cli@next" npm="npm install --save-dev @tauri-apps/cli@next"
yarn="yarn add -D @tauri-apps/cli@next" yarn="yarn add -D @tauri-apps/cli@next"
pnpm="pnpm add -D @tauri-apps/cli@next" pnpm="pnpm add -D @tauri-apps/cli@next"
cargo='cargo install tauri-cli --version "^2.0.0-beta"' cargo='cargo install tauri-cli --version "^2.0.0-beta"'
/> />
{/* TODO: 2.0 */} {/* TODO: 2.0 */}
@@ -47,10 +47,10 @@ For CLI commands related to developing plugins visit the [Develop a Tauri Plugin
## `build` ## `build`
<CommandTabs <CommandTabs
npm="npm run tauri build" npm="npm run tauri build"
yarn="yarn tauri build" yarn="yarn tauri build"
pnpm="pnpm tauri build" pnpm="pnpm tauri build"
cargo="cargo tauri build" cargo="cargo tauri build"
/> />
``` ```
@@ -94,10 +94,10 @@ Options:
## `dev` ## `dev`
<CommandTabs <CommandTabs
npm="npm run tauri dev" npm="npm run tauri dev"
yarn="yarn tauri dev" yarn="yarn tauri dev"
pnpm="pnpm tauri dev" pnpm="pnpm tauri dev"
cargo="cargo tauri dev" cargo="cargo tauri dev"
/> />
``` ```
@@ -131,10 +131,10 @@ If you have entered a command to the `build.beforeDevCommand` property, this one
## `icon` ## `icon`
<CommandTabs <CommandTabs
npm="npm run tauri icon" npm="npm run tauri icon"
yarn="yarn tauri icon" yarn="yarn tauri icon"
pnpm="pnpm tauri icon" pnpm="pnpm tauri icon"
cargo="cargo tauri icon" cargo="cargo tauri icon"
/> />
``` ```
@@ -157,10 +157,10 @@ Options:
## `info` ## `info`
<CommandTabs <CommandTabs
npm="npm run tauri info" npm="npm run tauri info"
yarn="yarn tauri info" yarn="yarn tauri info"
pnpm="pnpm tauri info" pnpm="pnpm tauri info"
cargo="cargo tauri info" cargo="cargo tauri info"
/> />
``` ```
@@ -180,10 +180,10 @@ Options:
## `init` ## `init`
<CommandTabs <CommandTabs
npm="npm run tauri init" npm="npm run tauri init"
yarn="yarn tauri init" yarn="yarn tauri init"
pnpm="pnpm tauri init" pnpm="pnpm tauri init"
cargo="cargo tauri init" cargo="cargo tauri init"
/> />
``` ```
@@ -225,10 +225,10 @@ Options:
## `add` ## `add`
<CommandTabs <CommandTabs
npm="npm run tauri add" npm="npm run tauri add"
yarn="yarn tauri add" yarn="yarn tauri add"
pnpm="pnpm tauri add" pnpm="pnpm tauri add"
cargo="cargo tauri add" cargo="cargo tauri add"
/> />
``` ```
@@ -251,10 +251,10 @@ Options:
## `signer` ## `signer`
<CommandTabs <CommandTabs
npm="npm run tauri signer" npm="npm run tauri signer"
yarn="yarn tauri signer" yarn="yarn tauri signer"
pnpm="pnpm tauri signer" pnpm="pnpm tauri signer"
cargo="cargo tauri signer" cargo="cargo tauri signer"
/> />
``` ```
@@ -276,10 +276,10 @@ Options:
## `completions` ## `completions`
<CommandTabs <CommandTabs
npm="npm run tauri completions" npm="npm run tauri completions"
yarn="yarn tauri completions" yarn="yarn tauri completions"
pnpm="pnpm tauri completions" pnpm="pnpm tauri completions"
cargo="cargo tauri completions" cargo="cargo tauri completions"
/> />
``` ```
@@ -432,10 +432,10 @@ Add-Content -Path $profile -Value '& "$PSScriptRoot\_tauri.ps1"'
## `android` ## `android`
<CommandTabs <CommandTabs
npm="npm run tauri android" npm="npm run tauri android"
yarn="yarn tauri android" yarn="yarn tauri android"
pnpm="pnpm tauri android" pnpm="pnpm tauri android"
cargo="cargo tauri android" cargo="cargo tauri android"
/> />
``` ```
@@ -459,10 +459,10 @@ Options:
## `ios` ## `ios`
<CommandTabs <CommandTabs
npm="npm run tauri ios" npm="npm run tauri ios"
yarn="yarn tauri ios" yarn="yarn tauri ios"
pnpm="pnpm tauri ios" pnpm="pnpm tauri ios"
cargo="cargo tauri ios" cargo="cargo tauri ios"
/> />
``` ```
@@ -486,10 +486,10 @@ Options:
## `migrate` ## `migrate`
<CommandTabs <CommandTabs
npm="npm run tauri migrate" npm="npm run tauri migrate"
yarn="yarn tauri migrate" yarn="yarn tauri migrate"
pnpm="pnpm tauri migrate" pnpm="pnpm tauri migrate"
cargo="cargo tauri migrate" cargo="cargo tauri migrate"
/> />
``` ```
@@ -506,10 +506,10 @@ Options:
## `permission` ## `permission`
<CommandTabs <CommandTabs
npm="npm tauri permission" npm="npm tauri permission"
yarn="yarn tauri permission" yarn="yarn tauri permission"
pnpm="pnpm tauri permission" pnpm="pnpm tauri permission"
cargo="cargo tauri permission" cargo="cargo tauri permission"
/> />
``` ```
@@ -591,10 +591,10 @@ Options:
## `capability` ## `capability`
<CommandTabs <CommandTabs
npm="npm tauri capability" npm="npm tauri capability"
yarn="yarn tauri capability" yarn="yarn tauri capability"
pnpm="pnpm tauri capability" pnpm="pnpm tauri capability"
cargo="cargo tauri capability" cargo="cargo tauri capability"
/> />
``` ```
@@ -634,10 +634,10 @@ Options:
## `help` ## `help`
<CommandTabs <CommandTabs
npm="npm run tauri help" npm="npm run tauri help"
yarn="yarn tauri help" yarn="yarn tauri help"
pnpm="pnpm tauri help" pnpm="pnpm tauri help"
cargo="cargo 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 ```json title="src-tauri/capabilities/main.json
{ {
"$schema": "./schemas/desktop-schema.json", "$schema": "./schemas/desktop-schema.json",
"identifier": "main-capability", "identifier": "main-capability",
"description": "Capability for the main window", "description": "Capability for the main window",
"windows": ["main"], "windows": ["main"],
"permissions": [ "permissions": [
"path:default", "path:default",
"event:default", "event:default",
"window:default", "window:default",
"app:default", "app:default",
"resources:default", "resources:default",
"menu:default", "menu:default",
"tray:default", "tray:default",
"window:allow-set-title" "window:allow-set-title"
] ]
} }
``` ```
@@ -72,11 +72,11 @@ capability files in the `capabilities` directory.
```json title=src-tauri/tauri.conf.json ```json title=src-tauri/tauri.conf.json
{ {
"app": { "app": {
"security": { "security": {
"capabilities": ["my-capability", "main-capability"] "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 ```json title=src-tauri/tauri.conf.json
{ {
"app": { "app": {
"security": { "security": {
"capabilities": [ "capabilities": [
{ {
"identifier": "my-capability", "identifier": "my-capability",
"description": "My application capability used for all windows", "description": "My application capability used for all windows",
"windows": ["*"], "windows": ["*"],
"permissions": ["fs:default", "allow-home-read-extended"] "permissions": ["fs:default", "allow-home-read-extended"]
}, },
"my-second-capability" "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 ```json title="src-tauri/capabilities/desktop.json
{ {
"$schema": "./schemas/desktop-schema.json", "$schema": "./schemas/desktop-schema.json",
"identifier": "desktop-capability", "identifier": "desktop-capability",
"windows": ["main"], "windows": ["main"],
"platforms": ["linux", "macOS", "windows"], "platforms": ["linux", "macOS", "windows"],
"permissions": ["global-shortcut:allow-register"] "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 ```json title="src-tauri/capabilities/mobile.json
{ {
"$schema": "./schemas/mobile-schema.json", "$schema": "./schemas/mobile-schema.json",
"identifier": "mobile-capability", "identifier": "mobile-capability",
"windows": ["main"], "windows": ["main"],
"platforms": ["iOS", "android"], "platforms": ["iOS", "android"],
"permissions": [ "permissions": [
"nfc:allow-scan", "nfc:allow-scan",
"biometric:allow-authenticate", "biometric:allow-authenticate",
"barcode-scanner:allow-scan" "barcode-scanner:allow-scan"
] ]
} }
``` ```

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