mirror of
https://github.com/tachiyomiorg/website.git
synced 2024-10-31 23:15:05 +01:00
Fix remaining eslint errors
This commit is contained in:
parent
e0da86ca0c
commit
bbac199151
@ -2,12 +2,8 @@ root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
@ -9,7 +9,7 @@ module.exports = {
|
||||
'stylus/selector-type-no-unknown': null,
|
||||
'stylus/selector-list-comma': 'always',
|
||||
'stylus/indentation': [
|
||||
'tab',
|
||||
2,
|
||||
{
|
||||
indentInsideParens: 'twice',
|
||||
},
|
||||
|
@ -19,6 +19,9 @@ export default antfu({
|
||||
'docs/.vitepress/cache/**',
|
||||
],
|
||||
|
||||
typescript: true,
|
||||
vue: true,
|
||||
|
||||
...compat.config({
|
||||
rules: {
|
||||
'comma-dangle': ['error', 'only-multiline'],
|
||||
|
@ -1,30 +1,34 @@
|
||||
import process from "node:process"
|
||||
import { URL, fileURLToPath } from "node:url"
|
||||
import { defineConfig, loadEnv } from "vitepress"
|
||||
import ElementPlus from "unplugin-element-plus/vite"
|
||||
import process from 'node:process'
|
||||
import { URL, fileURLToPath } from 'node:url'
|
||||
import { defineConfig, loadEnv } from 'vitepress'
|
||||
import ElementPlus from 'unplugin-element-plus/vite'
|
||||
|
||||
import markdownConfig from "./config/markdownConfig"
|
||||
import markdownConfig from './config/markdownConfig'
|
||||
|
||||
// For use with loading Markdown plugins
|
||||
import themeConfig from "./config/themeConfig"
|
||||
import themeConfig from './config/themeConfig'
|
||||
|
||||
// Theme related config
|
||||
import headConfig from "./config/headConfig" // Provides how to generate Meta head tags
|
||||
import headConfig from './config/headConfig'
|
||||
|
||||
import generateMeta from "./config/hooks/generateMeta"
|
||||
// Provides how to generate Meta head tags
|
||||
|
||||
import generateMeta from './config/hooks/generateMeta'
|
||||
|
||||
// Enhanced meta generation
|
||||
import generateFeed from "./config/hooks/generateFeed" // Allows generation of RSS feed
|
||||
import generateOgImages from "./config/hooks/generateOgImages"
|
||||
import generateFeed from './config/hooks/generateFeed'
|
||||
|
||||
const title = "Tachiyomi"
|
||||
const description = "Discover and read manga, webtoons, comics, and more – easier than ever on your Android device."
|
||||
// Allows generation of RSS feed
|
||||
import generateOgImages from './config/hooks/generateOgImages'
|
||||
|
||||
const env = loadEnv("", process.cwd())
|
||||
const hostname: string = env.VITE_HOSTNAME || "http://localhost:4173"
|
||||
const title = 'Tachiyomi'
|
||||
const description = 'Discover and read manga, webtoons, comics, and more – easier than ever on your Android device.'
|
||||
|
||||
const env = loadEnv('', process.cwd())
|
||||
const hostname: string = env.VITE_HOSTNAME || 'http://localhost:4173'
|
||||
|
||||
export default defineConfig({
|
||||
outDir: "../dist",
|
||||
outDir: '../dist',
|
||||
lastUpdated: true,
|
||||
cleanUrls: true,
|
||||
title,
|
||||
@ -35,7 +39,7 @@ export default defineConfig({
|
||||
head: headConfig,
|
||||
markdown: markdownConfig,
|
||||
themeConfig,
|
||||
transformHead: async (context) => generateMeta(context, hostname),
|
||||
transformHead: async context => generateMeta(context, hostname),
|
||||
buildEnd: async (context) => {
|
||||
generateFeed(context, hostname)
|
||||
generateOgImages(context)
|
||||
@ -47,26 +51,26 @@ export default defineConfig({
|
||||
// Used to show the release version on navbar.
|
||||
find: /^.*\/VPNavBarMenu\.vue$/,
|
||||
replacement: fileURLToPath(
|
||||
new URL("./theme/components/CustomNavBarMenu.vue", import.meta.url),
|
||||
new URL('./theme/components/CustomNavBarMenu.vue', import.meta.url),
|
||||
),
|
||||
},
|
||||
{
|
||||
find: /^.*VPNavScreenMenu\.vue$/,
|
||||
replacement: fileURLToPath(
|
||||
new URL("./theme/components/CustomNavScreenMenu.vue", import.meta.url),
|
||||
new URL('./theme/components/CustomNavScreenMenu.vue', import.meta.url),
|
||||
),
|
||||
},
|
||||
{
|
||||
find: /^.*VPSwitchAppearance\.vue$/,
|
||||
replacement: fileURLToPath(
|
||||
new URL("./theme/components/CustomSwitchAppearance.vue", import.meta.url),
|
||||
new URL('./theme/components/CustomSwitchAppearance.vue', import.meta.url),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [ElementPlus({})],
|
||||
ssr: {
|
||||
noExternal: ["element-plus"],
|
||||
noExternal: ['element-plus'],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -1,6 +1,6 @@
|
||||
export const GITHUB_EXTENSION_JSON
|
||||
= "https://raw.githubusercontent.com/tachiyomiorg/tachiyomi-extensions/repo/index.json"
|
||||
export const GITHUB_STABLE_API = "https://api.github.com/repos/tachiyomiorg/tachiyomi/releases/latest"
|
||||
export const GITHUB_STABLE_RELEASE = "https://github.com/tachiyomiorg/tachiyomi/releases/latest"
|
||||
export const GITHUB_PREVIEW_API = "https://api.github.com/repos/tachiyomiorg/android-app-preview/releases/latest"
|
||||
export const GITHUB_PREVIEW_RELEASE = "https://github.com/tachiyomiorg/android-app-preview/releases/latest"
|
||||
= 'https://raw.githubusercontent.com/tachiyomiorg/tachiyomi-extensions/repo/index.json'
|
||||
export const GITHUB_STABLE_API = 'https://api.github.com/repos/tachiyomiorg/tachiyomi/releases/latest'
|
||||
export const GITHUB_STABLE_RELEASE = 'https://github.com/tachiyomiorg/tachiyomi/releases/latest'
|
||||
export const GITHUB_PREVIEW_API = 'https://api.github.com/repos/tachiyomiorg/android-app-preview/releases/latest'
|
||||
export const GITHUB_PREVIEW_RELEASE = 'https://github.com/tachiyomiorg/android-app-preview/releases/latest'
|
||||
|
@ -1,59 +1,59 @@
|
||||
import type { HeadConfig } from "vitepress"
|
||||
import type { HeadConfig } from 'vitepress'
|
||||
|
||||
const headConfig: HeadConfig[] = [
|
||||
["meta", { name: "darkreader-lock" }],
|
||||
['meta', { name: 'darkreader-lock' }],
|
||||
|
||||
["meta", { name: "theme-color", content: "#818CF8" }],
|
||||
["meta", { name: "msapplication-TileColor", content: "#818CF8" }],
|
||||
['meta', { name: 'theme-color', content: '#818CF8' }],
|
||||
['meta', { name: 'msapplication-TileColor', content: '#818CF8' }],
|
||||
|
||||
["meta", { name: "viewport", content: "width=device-width, initial-scale=1.0" }],
|
||||
["meta", { name: "referrer", content: "no-referrer-when-downgrade" }],
|
||||
['meta', { name: 'viewport', content: 'width=device-width, initial-scale=1.0' }],
|
||||
['meta', { name: 'referrer', content: 'no-referrer-when-downgrade' }],
|
||||
|
||||
["link", { rel: "icon", type: "image/x-icon", href: "/favicon.ico" }],
|
||||
['link', { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
|
||||
[
|
||||
"link",
|
||||
'link',
|
||||
{
|
||||
rel: "icon",
|
||||
type: "image/png",
|
||||
sizes: "32x32",
|
||||
href: "/favicon-32x32.png",
|
||||
rel: 'icon',
|
||||
type: 'image/png',
|
||||
sizes: '32x32',
|
||||
href: '/favicon-32x32.png',
|
||||
},
|
||||
],
|
||||
[
|
||||
"link",
|
||||
'link',
|
||||
{
|
||||
rel: "icon",
|
||||
type: "image/png",
|
||||
sizes: "16x16",
|
||||
href: "/favicon-16x16.png",
|
||||
rel: 'icon',
|
||||
type: 'image/png',
|
||||
sizes: '16x16',
|
||||
href: '/favicon-16x16.png',
|
||||
},
|
||||
],
|
||||
["link", { rel: "manifest", href: "/site.webmanifest" }],
|
||||
["link", { rel: "mask-icon", href: "/safari-pinned-tab.svg", color: "#818CF8" }],
|
||||
['link', { rel: 'manifest', href: '/site.webmanifest' }],
|
||||
['link', { rel: 'mask-icon', href: '/safari-pinned-tab.svg', color: '#818CF8' }],
|
||||
[
|
||||
"link",
|
||||
'link',
|
||||
{
|
||||
rel: "apple-touch-icon",
|
||||
type: "image/x-icon",
|
||||
sizes: "180x180",
|
||||
href: "/favicon.ico",
|
||||
rel: 'apple-touch-icon',
|
||||
type: 'image/x-icon',
|
||||
sizes: '180x180',
|
||||
href: '/favicon.ico',
|
||||
},
|
||||
],
|
||||
|
||||
["meta", { name: "twitter:card", content: "summary" }],
|
||||
["meta", { name: "twitter:site", content: "@tachiyomiorg" }],
|
||||
["meta", { name: "twitter:creator", content: "@tachiyomiorg" }],
|
||||
['meta', { name: 'twitter:card', content: 'summary' }],
|
||||
['meta', { name: 'twitter:site', content: '@tachiyomiorg' }],
|
||||
['meta', { name: 'twitter:creator', content: '@tachiyomiorg' }],
|
||||
|
||||
["meta", { property: "og:site_name", content: "Tachiyomi" }],
|
||||
['meta', { property: 'og:site_name', content: 'Tachiyomi' }],
|
||||
[
|
||||
"meta",
|
||||
'meta',
|
||||
{
|
||||
property: "og:description",
|
||||
content: "Discover and read manga, webtoons, comics, and more – easier than ever on your Android device.",
|
||||
property: 'og:description',
|
||||
content: 'Discover and read manga, webtoons, comics, and more – easier than ever on your Android device.',
|
||||
},
|
||||
],
|
||||
["meta", { property: "og:locale", content: "en_US" }],
|
||||
["meta", { property: "og:type", content: "website" }],
|
||||
['meta', { property: 'og:locale', content: 'en_US' }],
|
||||
['meta', { property: 'og:type', content: 'website' }],
|
||||
]
|
||||
|
||||
export default headConfig
|
||||
|
@ -1,7 +1,7 @@
|
||||
import path from "node:path"
|
||||
import { writeFileSync } from "node:fs"
|
||||
import { Feed, type Item } from "feed"
|
||||
import { type SiteConfig, createContentLoader } from "vitepress"
|
||||
import path from 'node:path'
|
||||
import { writeFileSync } from 'node:fs'
|
||||
import { Feed, type Item } from 'feed'
|
||||
import { type SiteConfig, createContentLoader } from 'vitepress'
|
||||
|
||||
async function generateFeed(config: SiteConfig, hostname: string) {
|
||||
const feed = new Feed({
|
||||
@ -9,21 +9,21 @@ async function generateFeed(config: SiteConfig, hostname: string) {
|
||||
description: config.site.description,
|
||||
id: hostname,
|
||||
link: hostname,
|
||||
language: "en",
|
||||
language: 'en',
|
||||
image: `${hostname}/img/logo.png`,
|
||||
favicon: `${hostname}/favicon.ico`,
|
||||
copyright: `Copyright © 2015 - ${new Date().getFullYear()} Javier Tomás`,
|
||||
})
|
||||
const json: Item[] = []
|
||||
|
||||
const posts = await createContentLoader("news/*.md", {
|
||||
const posts = await createContentLoader('news/*.md', {
|
||||
excerpt: true,
|
||||
render: true,
|
||||
includeSrc: true,
|
||||
}).load()
|
||||
|
||||
// Filter everything that"s not of type `article` (e.g. index.md)
|
||||
const filteredPosts = posts.filter((post) => post.frontmatter.type === "article")
|
||||
const filteredPosts = posts.filter(post => post.frontmatter.type === 'article')
|
||||
|
||||
filteredPosts.sort((a, b) => +new Date(b.frontmatter.date as string) - +new Date(a.frontmatter.date as string))
|
||||
|
||||
@ -31,14 +31,14 @@ async function generateFeed(config: SiteConfig, hostname: string) {
|
||||
const fullUrl = `${hostname}${url}`
|
||||
|
||||
// Strip `​` from `html` string
|
||||
const content = (html ?? "")
|
||||
.replace(/​/g, "")
|
||||
const content = (html ?? '')
|
||||
.replace(/​/g, '')
|
||||
.replace(/<a href="(\/.*?)">/g, `<a href="${hostname}$1">`)
|
||||
|
||||
const markdown = (src ?? "")
|
||||
.replace(/^---.*---/s, "")
|
||||
const markdown = (src ?? '')
|
||||
.replace(/^---.*---/s, '')
|
||||
.replace(/]\((\/.*?)\)/g, `](${hostname}$1)`)
|
||||
.replace(/^# .*$/m, "")
|
||||
.replace(/^# .*$/m, '')
|
||||
.trim()
|
||||
|
||||
const post = {
|
||||
@ -54,8 +54,8 @@ async function generateFeed(config: SiteConfig, hostname: string) {
|
||||
json.push({ ...post, content: markdown })
|
||||
}
|
||||
|
||||
writeFileSync(path.join(config.outDir, "feed.rss"), feed.rss2())
|
||||
writeFileSync(path.join(config.outDir, "news.json"), JSON.stringify(json))
|
||||
writeFileSync(path.join(config.outDir, 'feed.rss'), feed.rss2())
|
||||
writeFileSync(path.join(config.outDir, 'news.json'), JSON.stringify(json))
|
||||
}
|
||||
|
||||
export default generateFeed
|
||||
|
@ -1,125 +1,127 @@
|
||||
import type { HeadConfig, TransformContext } from "vitepress"
|
||||
import type { HeadConfig, TransformContext } from 'vitepress'
|
||||
|
||||
function generateMeta(context: TransformContext, hostname: string) {
|
||||
const head: HeadConfig[] = []
|
||||
const { pageData } = context
|
||||
|
||||
const url = `${hostname}/${pageData.relativePath.replace(/((^|\/)index)?\.md$/, "$2")}`
|
||||
const url = `${hostname}/${pageData.relativePath.replace(/((^|\/)index)?\.md$/, '$2')}`
|
||||
|
||||
head.push(["link", { rel: "canonical", href: url }])
|
||||
head.push(["meta", { property: "og:url", content: url }])
|
||||
head.push(["meta", { name: "twitter:url", content: url }])
|
||||
head.push(["meta", { name: "twitter:card", content: "summary_large_image" }])
|
||||
head.push(['link', { rel: 'canonical', href: url }])
|
||||
head.push(['meta', { property: 'og:url', content: url }])
|
||||
head.push(['meta', { name: 'twitter:url', content: url }])
|
||||
head.push(['meta', { name: 'twitter:card', content: 'summary_large_image' }])
|
||||
|
||||
if (pageData.frontmatter.theme)
|
||||
head.push(['meta', { name: 'theme-color', content: pageData.frontmatter.theme }])
|
||||
|
||||
if (pageData.frontmatter.type)
|
||||
head.push(['meta', { property: 'og:type', content: pageData.frontmatter.type }])
|
||||
|
||||
if (pageData.frontmatter.theme) {
|
||||
head.push(["meta", { name: "theme-color", content: pageData.frontmatter.theme }])
|
||||
}
|
||||
if (pageData.frontmatter.type) {
|
||||
head.push(["meta", { property: "og:type", content: pageData.frontmatter.type }])
|
||||
}
|
||||
if (pageData.frontmatter.customMetaTitle) {
|
||||
head.push([
|
||||
"meta",
|
||||
'meta',
|
||||
{
|
||||
property: "og:title",
|
||||
property: 'og:title',
|
||||
content: pageData.frontmatter.customMetaTitle,
|
||||
},
|
||||
])
|
||||
head.push([
|
||||
"meta",
|
||||
'meta',
|
||||
{
|
||||
name: "twitter:title",
|
||||
name: 'twitter:title',
|
||||
content: pageData.frontmatter.customMetaTitle,
|
||||
},
|
||||
])
|
||||
head.push(["meta", { property: "og:site_name", content: "" }])
|
||||
} else {
|
||||
head.push(["meta", { property: "og:title", content: pageData.frontmatter.title }])
|
||||
head.push(["meta", { name: "twitter:title", content: pageData.frontmatter.title }])
|
||||
head.push(['meta', { property: 'og:site_name', content: '' }])
|
||||
}
|
||||
else {
|
||||
head.push(['meta', { property: 'og:title', content: pageData.frontmatter.title }])
|
||||
head.push(['meta', { name: 'twitter:title', content: pageData.frontmatter.title }])
|
||||
}
|
||||
if (pageData.frontmatter.description) {
|
||||
head.push([
|
||||
"meta",
|
||||
'meta',
|
||||
{
|
||||
property: "og:description",
|
||||
property: 'og:description',
|
||||
content: pageData.frontmatter.description,
|
||||
},
|
||||
])
|
||||
head.push([
|
||||
"meta",
|
||||
'meta',
|
||||
{
|
||||
name: "twitter:description",
|
||||
name: 'twitter:description',
|
||||
content: pageData.frontmatter.description,
|
||||
},
|
||||
])
|
||||
}
|
||||
if (pageData.frontmatter.image) {
|
||||
head.push([
|
||||
"meta",
|
||||
'meta',
|
||||
{
|
||||
property: "og:image",
|
||||
content: `${hostname}/${pageData.frontmatter.image.replace(/^\//, "")}`,
|
||||
property: 'og:image',
|
||||
content: `${hostname}/${pageData.frontmatter.image.replace(/^\//, '')}`,
|
||||
},
|
||||
])
|
||||
head.push([
|
||||
"meta",
|
||||
'meta',
|
||||
{
|
||||
name: "twitter:image",
|
||||
content: `${hostname}/${pageData.frontmatter.image.replace(/^\//, "")}`,
|
||||
name: 'twitter:image',
|
||||
content: `${hostname}/${pageData.frontmatter.image.replace(/^\//, '')}`,
|
||||
},
|
||||
])
|
||||
} else {
|
||||
const url = pageData.filePath.replace("index.md", "").replace(".md", "")
|
||||
const imageUrl = `${url}/__og_image__/og.png`.replace(/\/\//g, "/").replace(/^\//, "")
|
||||
}
|
||||
else {
|
||||
const url = pageData.filePath.replace('index.md', '').replace('.md', '')
|
||||
const imageUrl = `${url}/__og_image__/og.png`.replace(/\/\//g, '/').replace(/^\//, '')
|
||||
|
||||
head.push(["meta", { property: "og:image", content: `${hostname}/${imageUrl}` }])
|
||||
head.push(["meta", { property: "og:image:width", content: "1200" }])
|
||||
head.push(["meta", { property: "og:image:height", content: "628" }])
|
||||
head.push(["meta", { property: "og:image:type", content: "image/png" }])
|
||||
head.push(["meta", { property: "og:image:alt", content: pageData.frontmatter.title }])
|
||||
head.push(["meta", { name: "twitter:image", content: `${hostname}/${imageUrl}` }])
|
||||
head.push(["meta", { name: "twitter:image:width", content: "1200" }])
|
||||
head.push(["meta", { name: "twitter:image:height", content: "628" }])
|
||||
head.push(["meta", { name: "twitter:image:alt", content: pageData.frontmatter.title }])
|
||||
}
|
||||
if (pageData.frontmatter.tag) {
|
||||
head.push(["meta", { property: "article:tag", content: pageData.frontmatter.tag }])
|
||||
head.push(['meta', { property: 'og:image', content: `${hostname}/${imageUrl}` }])
|
||||
head.push(['meta', { property: 'og:image:width', content: '1200' }])
|
||||
head.push(['meta', { property: 'og:image:height', content: '628' }])
|
||||
head.push(['meta', { property: 'og:image:type', content: 'image/png' }])
|
||||
head.push(['meta', { property: 'og:image:alt', content: pageData.frontmatter.title }])
|
||||
head.push(['meta', { name: 'twitter:image', content: `${hostname}/${imageUrl}` }])
|
||||
head.push(['meta', { name: 'twitter:image:width', content: '1200' }])
|
||||
head.push(['meta', { name: 'twitter:image:height', content: '628' }])
|
||||
head.push(['meta', { name: 'twitter:image:alt', content: pageData.frontmatter.title }])
|
||||
}
|
||||
if (pageData.frontmatter.tag)
|
||||
head.push(['meta', { property: 'article:tag', content: pageData.frontmatter.tag }])
|
||||
|
||||
if (pageData.frontmatter.date) {
|
||||
head.push([
|
||||
"meta",
|
||||
'meta',
|
||||
{
|
||||
property: "article:published_time",
|
||||
property: 'article:published_time',
|
||||
content: pageData.frontmatter.date,
|
||||
},
|
||||
])
|
||||
}
|
||||
if (pageData.lastUpdated && pageData.frontmatter.lastUpdated !== false) {
|
||||
head.push([
|
||||
"meta",
|
||||
'meta',
|
||||
{
|
||||
property: "article:modified_time",
|
||||
property: 'article:modified_time',
|
||||
content: new Date(pageData.lastUpdated).toISOString(),
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
if (pageData.filePath === "news/index.md") {
|
||||
if (pageData.filePath === 'news/index.md') {
|
||||
head.push([
|
||||
"link",
|
||||
'link',
|
||||
{
|
||||
rel: "alternate",
|
||||
type: "application/rss+xml",
|
||||
title: "RSS feed for the news archive",
|
||||
rel: 'alternate',
|
||||
type: 'application/rss+xml',
|
||||
title: 'RSS feed for the news archive',
|
||||
href: `${hostname}/feed.rss`,
|
||||
},
|
||||
])
|
||||
head.push([
|
||||
"link",
|
||||
'link',
|
||||
{
|
||||
rel: "alternate",
|
||||
type: "application/json",
|
||||
title: "JSON of the news archive",
|
||||
rel: 'alternate',
|
||||
type: 'application/json',
|
||||
title: 'JSON of the news archive',
|
||||
href: `${hostname}/news.json`,
|
||||
},
|
||||
])
|
||||
|
@ -1,46 +1,46 @@
|
||||
import { mkdir, readFile, writeFile } from "node:fs/promises"
|
||||
import { dirname, resolve } from "node:path"
|
||||
import { fileURLToPath } from "node:url"
|
||||
import { createContentLoader } from "vitepress"
|
||||
import type { ContentData, SiteConfig } from "vitepress"
|
||||
import { type SatoriOptions, satoriVue } from "x-satori/vue"
|
||||
import { renderAsync } from "@resvg/resvg-js"
|
||||
import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
||||
import { dirname, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { createContentLoader } from 'vitepress'
|
||||
import type { ContentData, SiteConfig } from 'vitepress'
|
||||
import { type SatoriOptions, satoriVue } from 'x-satori/vue'
|
||||
import { renderAsync } from '@resvg/resvg-js'
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
const __fonts = resolve(__dirname, "../../fonts")
|
||||
const __fonts = resolve(__dirname, '../../fonts')
|
||||
|
||||
async function generateOgImages(config: SiteConfig) {
|
||||
const pages = await createContentLoader("**/*.md", { excerpt: true }).load()
|
||||
const template = await readFile(resolve(__dirname, "../../theme/components/OgImageTemplate.vue"), "utf-8")
|
||||
const pages = await createContentLoader('**/*.md', { excerpt: true }).load()
|
||||
const template = await readFile(resolve(__dirname, '../../theme/components/OgImageTemplate.vue'), 'utf-8')
|
||||
|
||||
const fonts: SatoriOptions["fonts"] = [
|
||||
const fonts: SatoriOptions['fonts'] = [
|
||||
{
|
||||
name: "Inter",
|
||||
data: await readFile(resolve(__fonts, "Inter-Regular.otf")),
|
||||
name: 'Inter',
|
||||
data: await readFile(resolve(__fonts, 'Inter-Regular.otf')),
|
||||
weight: 400,
|
||||
style: "normal",
|
||||
style: 'normal',
|
||||
},
|
||||
{
|
||||
name: "Inter",
|
||||
data: await readFile(resolve(__fonts, "Inter-Medium.otf")),
|
||||
name: 'Inter',
|
||||
data: await readFile(resolve(__fonts, 'Inter-Medium.otf')),
|
||||
weight: 500,
|
||||
style: "normal",
|
||||
style: 'normal',
|
||||
},
|
||||
{
|
||||
name: "Inter",
|
||||
data: await readFile(resolve(__fonts, "Inter-SemiBold.otf")),
|
||||
name: 'Inter',
|
||||
data: await readFile(resolve(__fonts, 'Inter-SemiBold.otf')),
|
||||
weight: 600,
|
||||
style: "normal",
|
||||
style: 'normal',
|
||||
},
|
||||
{
|
||||
name: "Inter",
|
||||
data: await readFile(resolve(__fonts, "Inter-Bold.otf")),
|
||||
name: 'Inter',
|
||||
data: await readFile(resolve(__fonts, 'Inter-Bold.otf')),
|
||||
weight: 700,
|
||||
style: "normal",
|
||||
style: 'normal',
|
||||
},
|
||||
]
|
||||
|
||||
const filteredPages = pages.filter((p) => p.frontmatter.image === undefined)
|
||||
const filteredPages = pages.filter(p => p.frontmatter.image === undefined)
|
||||
|
||||
for (const page of filteredPages) {
|
||||
await generateImage({
|
||||
@ -58,17 +58,16 @@ interface GenerateImagesOptions {
|
||||
page: ContentData
|
||||
template: string
|
||||
outDir: string
|
||||
fonts: SatoriOptions["fonts"]
|
||||
fonts: SatoriOptions['fonts']
|
||||
}
|
||||
|
||||
function getDir(url: string) {
|
||||
if (url.startsWith("/docs/faq/")) {
|
||||
return "FAQ"
|
||||
} else if (url.startsWith("/docs/guides/")) {
|
||||
return "Guide"
|
||||
} else if (url.startsWith("/news/") && url !== "/news/") {
|
||||
return "News"
|
||||
}
|
||||
if (url.startsWith('/docs/faq/'))
|
||||
return 'FAQ'
|
||||
else if (url.startsWith('/docs/guides/'))
|
||||
return 'Guide'
|
||||
else if (url.startsWith('/news/') && url !== '/news/')
|
||||
return 'News'
|
||||
|
||||
return undefined
|
||||
}
|
||||
@ -82,11 +81,11 @@ async function generateImage({ page, template, outDir, fonts }: GenerateImagesOp
|
||||
fonts,
|
||||
props: {
|
||||
title:
|
||||
frontmatter.layout === "home"
|
||||
frontmatter.layout === 'home'
|
||||
? frontmatter.hero.name ?? frontmatter.title
|
||||
: frontmatter.customMetaTitle ?? frontmatter.title,
|
||||
description:
|
||||
frontmatter.layout === "home"
|
||||
frontmatter.layout === 'home'
|
||||
? frontmatter.hero.tagline ?? frontmatter.description
|
||||
: frontmatter.description,
|
||||
dir: getDir(url),
|
||||
@ -97,13 +96,13 @@ async function generateImage({ page, template, outDir, fonts }: GenerateImagesOp
|
||||
|
||||
const render = await renderAsync(svg, {
|
||||
fitTo: {
|
||||
mode: "width",
|
||||
mode: 'width',
|
||||
value: 1200,
|
||||
},
|
||||
})
|
||||
|
||||
const outputFolder = resolve(outDir, url.substring(1), "__og_image__")
|
||||
const outputFile = resolve(outputFolder, "og.png")
|
||||
const outputFolder = resolve(outDir, url.substring(1), '__og_image__')
|
||||
const outputFile = resolve(outputFolder, 'og.png')
|
||||
|
||||
await mkdir(outputFolder, { recursive: true })
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
import type { MarkdownOptions } from "vitepress"
|
||||
import type { MarkdownOptions } from 'vitepress'
|
||||
|
||||
import { attrs } from "@mdit/plugin-attrs"
|
||||
import { figure } from "@mdit/plugin-figure"
|
||||
import { imgLazyload } from "@mdit/plugin-img-lazyload"
|
||||
import { imgMark } from "@mdit/plugin-img-mark"
|
||||
import { imgSize } from "@mdit/plugin-img-size"
|
||||
import { include } from "@mdit/plugin-include"
|
||||
import { tabsMarkdownPlugin } from "vitepress-plugin-tabs"
|
||||
import shortcode_plugin from "markdown-it-shortcode-tag"
|
||||
import shortcodes from "./shortcodes"
|
||||
import { attrs } from '@mdit/plugin-attrs'
|
||||
import { figure } from '@mdit/plugin-figure'
|
||||
import { imgLazyload } from '@mdit/plugin-img-lazyload'
|
||||
import { imgMark } from '@mdit/plugin-img-mark'
|
||||
import { imgSize } from '@mdit/plugin-img-size'
|
||||
import { include } from '@mdit/plugin-include'
|
||||
import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'
|
||||
import shortcode_plugin from 'markdown-it-shortcode-tag'
|
||||
import shortcodes from './shortcodes'
|
||||
|
||||
const markdownConfig: MarkdownOptions = {
|
||||
config: (md) => {
|
||||
@ -19,7 +19,7 @@ const markdownConfig: MarkdownOptions = {
|
||||
.use(imgMark)
|
||||
.use(imgSize)
|
||||
.use(include, {
|
||||
currentPath: (env) => env.filePath,
|
||||
currentPath: env => env.filePath,
|
||||
})
|
||||
.use(tabsMarkdownPlugin)
|
||||
.use(shortcode_plugin, shortcodes)
|
||||
|
@ -1,29 +1,29 @@
|
||||
import type { DefaultTheme } from "vitepress"
|
||||
import type { DefaultTheme } from 'vitepress'
|
||||
|
||||
const nav: DefaultTheme.NavItem[] = [
|
||||
{
|
||||
text: "Get v{app_version}",
|
||||
activeMatch: "^/*?(download|changelogs)/*?$",
|
||||
text: 'Get v{app_version}',
|
||||
activeMatch: '^/*?(download|changelogs)/*?$',
|
||||
items: [
|
||||
{
|
||||
text: "Download",
|
||||
link: "/download/",
|
||||
text: 'Download',
|
||||
link: '/download/',
|
||||
},
|
||||
{
|
||||
text: "Changelogs",
|
||||
link: "/changelogs/",
|
||||
text: 'Changelogs',
|
||||
link: '/changelogs/',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Docs",
|
||||
link: "/docs/guides/getting-started",
|
||||
activeMatch: "/docs/",
|
||||
text: 'Docs',
|
||||
link: '/docs/guides/getting-started',
|
||||
activeMatch: '/docs/',
|
||||
},
|
||||
{
|
||||
text: "News",
|
||||
link: "/news/",
|
||||
activeMatch: "/news/",
|
||||
text: 'News',
|
||||
link: '/news/',
|
||||
activeMatch: '/news/',
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
import type { DefaultTheme } from "vitepress"
|
||||
import type { DefaultTheme } from 'vitepress'
|
||||
|
||||
const sidebar: DefaultTheme.SidebarMulti = {
|
||||
"/download/": defaultSidebar(),
|
||||
"/extensions/": defaultSidebar(),
|
||||
"/docs/": defaultSidebar(),
|
||||
"/forks/": defaultSidebar(),
|
||||
"/changelogs/": defaultSidebar(),
|
||||
"/news/": defaultSidebar(),
|
||||
"/sandbox/": defaultSidebar(),
|
||||
'/download/': defaultSidebar(),
|
||||
'/extensions/': defaultSidebar(),
|
||||
'/docs/': defaultSidebar(),
|
||||
'/forks/': defaultSidebar(),
|
||||
'/changelogs/': defaultSidebar(),
|
||||
'/news/': defaultSidebar(),
|
||||
'/sandbox/': defaultSidebar(),
|
||||
}
|
||||
|
||||
function defaultSidebar(): DefaultTheme.SidebarItem[] {
|
||||
@ -15,112 +15,112 @@ function defaultSidebar(): DefaultTheme.SidebarItem[] {
|
||||
{
|
||||
items: [
|
||||
{
|
||||
text: "Download",
|
||||
link: "/download/",
|
||||
text: 'Download',
|
||||
link: '/download/',
|
||||
},
|
||||
{
|
||||
text: "Extensions",
|
||||
link: "/extensions/",
|
||||
text: 'Extensions',
|
||||
link: '/extensions/',
|
||||
},
|
||||
{
|
||||
text: "Changelogs",
|
||||
link: "/changelogs/",
|
||||
text: 'Changelogs',
|
||||
link: '/changelogs/',
|
||||
},
|
||||
{
|
||||
text: "Forks",
|
||||
link: "/forks/",
|
||||
text: 'Forks',
|
||||
link: '/forks/',
|
||||
},
|
||||
{
|
||||
text: "Contribute",
|
||||
link: "/docs/contribute",
|
||||
text: 'Contribute',
|
||||
link: '/docs/contribute',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Frequently Asked Questions",
|
||||
text: 'Frequently Asked Questions',
|
||||
items: [
|
||||
{ text: "General", link: "/docs/faq/general" },
|
||||
{ text: 'General', link: '/docs/faq/general' },
|
||||
{
|
||||
text: "Library",
|
||||
link: "/docs/faq/library",
|
||||
text: 'Library',
|
||||
link: '/docs/faq/library',
|
||||
},
|
||||
{
|
||||
text: "Browse",
|
||||
link: "/docs/faq/browse/",
|
||||
text: 'Browse',
|
||||
link: '/docs/faq/browse/',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: "Extensions", link: "/docs/faq/browse/extensions" },
|
||||
{ text: 'Extensions', link: '/docs/faq/browse/extensions' },
|
||||
{
|
||||
text: "Local source",
|
||||
link: "/docs/faq/browse/local-source",
|
||||
text: 'Local source',
|
||||
link: '/docs/faq/browse/local-source',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Downloads",
|
||||
link: "/docs/faq/downloads",
|
||||
text: 'Downloads',
|
||||
link: '/docs/faq/downloads',
|
||||
},
|
||||
{
|
||||
text: "Reader",
|
||||
link: "/docs/faq/reader",
|
||||
text: 'Reader',
|
||||
link: '/docs/faq/reader',
|
||||
},
|
||||
{
|
||||
text: "Settings",
|
||||
link: "/docs/faq/settings",
|
||||
text: 'Settings',
|
||||
link: '/docs/faq/settings',
|
||||
},
|
||||
{
|
||||
text: "Android 11+",
|
||||
link: "/docs/faq/android-11+",
|
||||
text: 'Android 11+',
|
||||
link: '/docs/faq/android-11+',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Guides",
|
||||
text: 'Guides',
|
||||
items: [
|
||||
{
|
||||
text: "Getting started",
|
||||
link: "/docs/guides/getting-started",
|
||||
text: 'Getting started',
|
||||
link: '/docs/guides/getting-started',
|
||||
},
|
||||
{
|
||||
text: "Troubleshooting",
|
||||
link: "/docs/guides/troubleshooting/",
|
||||
text: 'Troubleshooting',
|
||||
link: '/docs/guides/troubleshooting/',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Common issues",
|
||||
link: "/docs/guides/troubleshooting/common-issues",
|
||||
text: 'Common issues',
|
||||
link: '/docs/guides/troubleshooting/common-issues',
|
||||
},
|
||||
{
|
||||
text: "Diagnosis",
|
||||
link: "/docs/guides/troubleshooting/diagnosis",
|
||||
text: 'Diagnosis',
|
||||
link: '/docs/guides/troubleshooting/diagnosis',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Source migration",
|
||||
link: "/docs/guides/source-migration",
|
||||
text: 'Source migration',
|
||||
link: '/docs/guides/source-migration',
|
||||
},
|
||||
{ text: "Backups", link: "/docs/guides/backups" },
|
||||
{ text: "Tracking", link: "/docs/guides/tracking" },
|
||||
{ text: "Categories", link: "/docs/guides/categories" },
|
||||
{ text: 'Backups', link: '/docs/guides/backups' },
|
||||
{ text: 'Tracking', link: '/docs/guides/tracking' },
|
||||
{ text: 'Categories', link: '/docs/guides/categories' },
|
||||
{
|
||||
text: "Local source",
|
||||
link: "/docs/guides/local-source/",
|
||||
text: 'Local source',
|
||||
link: '/docs/guides/local-source/',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{
|
||||
text: "Advanced editing",
|
||||
link: "/docs/guides/local-source/advanced",
|
||||
text: 'Advanced editing',
|
||||
link: '/docs/guides/local-source/advanced',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Reader settings",
|
||||
link: "/docs/guides/reader-settings",
|
||||
text: 'Reader settings',
|
||||
link: '/docs/guides/reader-settings',
|
||||
},
|
||||
{
|
||||
text: "Shizuku",
|
||||
link: "/docs/guides/shizuku",
|
||||
text: 'Shizuku',
|
||||
link: '/docs/guides/shizuku',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -1,19 +1,18 @@
|
||||
export function simpleLangName(code: string) {
|
||||
if (code === "all") {
|
||||
return "All"
|
||||
}
|
||||
const namesInEnglish = new Intl.DisplayNames(["en"], { type: "language" })
|
||||
return capitalize(namesInEnglish.of(code)!, "en")
|
||||
if (code === 'all')
|
||||
return 'All'
|
||||
|
||||
const namesInEnglish = new Intl.DisplayNames(['en'], { type: 'language' })
|
||||
return capitalize(namesInEnglish.of(code)!, 'en')
|
||||
}
|
||||
|
||||
export function langName(code: string) {
|
||||
if (code === "all") {
|
||||
return "All"
|
||||
}
|
||||
if (code === 'all')
|
||||
return 'All'
|
||||
|
||||
const namesInEnglish = new Intl.DisplayNames(["en"], { type: "language" })
|
||||
const namesInNative = new Intl.DisplayNames([code], { type: "language" })
|
||||
return `${capitalize(namesInEnglish.of(code)!, "en")} - ${capitalize(namesInNative.of(code)!, code)}`
|
||||
const namesInEnglish = new Intl.DisplayNames(['en'], { type: 'language' })
|
||||
const namesInNative = new Intl.DisplayNames([code], { type: 'language' })
|
||||
return `${capitalize(namesInEnglish.of(code)!, 'en')} - ${capitalize(namesInNative.of(code)!, code)}`
|
||||
}
|
||||
|
||||
function capitalize(string: string, locale: string) {
|
||||
|
@ -28,54 +28,52 @@ interface Navigation {
|
||||
|
||||
const navigationMappings: Record<string, Navigation> = {
|
||||
// Main menus
|
||||
"main_library": { name: "Library", icon: iconMappings.bookmarkBoxOutline },
|
||||
"main_updates": { name: "Updates", icon: iconMappings.alertDecagramOutline },
|
||||
"main_history": { name: "History", icon: iconMappings.history },
|
||||
"main_browse": { name: "Browse", icon: iconMappings.compassOutline },
|
||||
"main_more": { name: "More", icon: iconMappings.dotsHorizontal },
|
||||
'main_library': { name: 'Library', icon: iconMappings.bookmarkBoxOutline },
|
||||
'main_updates': { name: 'Updates', icon: iconMappings.alertDecagramOutline },
|
||||
'main_history': { name: 'History', icon: iconMappings.history },
|
||||
'main_browse': { name: 'Browse', icon: iconMappings.compassOutline },
|
||||
'main_more': { name: 'More', icon: iconMappings.dotsHorizontal },
|
||||
|
||||
// Browse menu
|
||||
"sources": { name: "Sources", dependsOn: "main_browse" },
|
||||
"extensions": { name: "Extensions", dependsOn: "main_browse" },
|
||||
"migrate": { name: "Migrate", dependsOn: "main_browse" },
|
||||
'sources': { name: 'Sources', dependsOn: 'main_browse' },
|
||||
'extensions': { name: 'Extensions', dependsOn: 'main_browse' },
|
||||
'migrate': { name: 'Migrate', dependsOn: 'main_browse' },
|
||||
|
||||
// More menu
|
||||
"downloaded-only": { name: "Downloaded only", icon: iconMappings.cloudOffOutline, dependsOn: "main_more" },
|
||||
"incognito-mode": { name: "Incognito mode", icon: iconMappings.glasses, dependsOn: "main_more" },
|
||||
"download-queue": { name: "Download queue", icon: iconMappings.downloadOutline, dependsOn: "main_more" },
|
||||
"categories": { name: "Categories", icon: iconMappings.labelOutline, dependsOn: "main_more" },
|
||||
"statistics": { name: "Statistics", icon: iconMappings.queryStats, dependsOn: "main_more" },
|
||||
"backup-and-restore": { name: "Backup and restore", icon: iconMappings.backupRestore, dependsOn: "main_more" },
|
||||
"settings": { name: "Settings", icon: iconMappings.cog, dependsOn: "main_more" },
|
||||
"about": { name: "About", icon: iconMappings.informationOutline, dependsOn: "main_more" },
|
||||
"help": { name: "Help", icon: iconMappings.helpCircleOutline, dependsOn: "main_more" },
|
||||
'downloaded-only': { name: 'Downloaded only', icon: iconMappings.cloudOffOutline, dependsOn: 'main_more' },
|
||||
'incognito-mode': { name: 'Incognito mode', icon: iconMappings.glasses, dependsOn: 'main_more' },
|
||||
'download-queue': { name: 'Download queue', icon: iconMappings.downloadOutline, dependsOn: 'main_more' },
|
||||
'categories': { name: 'Categories', icon: iconMappings.labelOutline, dependsOn: 'main_more' },
|
||||
'statistics': { name: 'Statistics', icon: iconMappings.queryStats, dependsOn: 'main_more' },
|
||||
'backup-and-restore': { name: 'Backup and restore', icon: iconMappings.backupRestore, dependsOn: 'main_more' },
|
||||
'settings': { name: 'Settings', icon: iconMappings.cog, dependsOn: 'main_more' },
|
||||
'about': { name: 'About', icon: iconMappings.informationOutline, dependsOn: 'main_more' },
|
||||
'help': { name: 'Help', icon: iconMappings.helpCircleOutline, dependsOn: 'main_more' },
|
||||
|
||||
// Settings submenu
|
||||
"appearance": { name: "Appearance", icon: iconMappings.paletteOutline, dependsOn: "settings" },
|
||||
"library": { name: "Library", icon: iconMappings.bookmarkBoxOutline, dependsOn: "settings" },
|
||||
"downloads": { name: "Downloads", icon: iconMappings.downloadOutline, dependsOn: "settings" },
|
||||
"tracking": { name: "Tracking", icon: iconMappings.sync, dependsOn: "settings" },
|
||||
"browse": { name: "Browse", icon: iconMappings.compassOutline, dependsOn: "settings" },
|
||||
"security-and-privacy": { name: "Security and privacy", icon: iconMappings.security, dependsOn: "settings" },
|
||||
"reader": { name: "Reader", icon: iconMappings.bookOpenOutline, dependsOn: "settings" },
|
||||
"advanced": { name: "Advanced", icon: iconMappings.codeTags, dependsOn: "settings" },
|
||||
'appearance': { name: 'Appearance', icon: iconMappings.paletteOutline, dependsOn: 'settings' },
|
||||
'library': { name: 'Library', icon: iconMappings.bookmarkBoxOutline, dependsOn: 'settings' },
|
||||
'downloads': { name: 'Downloads', icon: iconMappings.downloadOutline, dependsOn: 'settings' },
|
||||
'tracking': { name: 'Tracking', icon: iconMappings.sync, dependsOn: 'settings' },
|
||||
'browse': { name: 'Browse', icon: iconMappings.compassOutline, dependsOn: 'settings' },
|
||||
'security-and-privacy': { name: 'Security and privacy', icon: iconMappings.security, dependsOn: 'settings' },
|
||||
'reader': { name: 'Reader', icon: iconMappings.bookOpenOutline, dependsOn: 'settings' },
|
||||
'advanced': { name: 'Advanced', icon: iconMappings.codeTags, dependsOn: 'settings' },
|
||||
}
|
||||
|
||||
function generateNavigationHtml(navKey: string) {
|
||||
const navData = navigationMappings[navKey]
|
||||
|
||||
if (!navData) {
|
||||
return "<strong style='color:var(--vp-c-danger-1)'>Unsupported Navigation!</strong>"
|
||||
}
|
||||
if (!navData)
|
||||
return '<strong style=\'color:var(--vp-c-danger-1)\'>Unsupported Navigation!</strong>'
|
||||
|
||||
const { name, icon, dependsOn } = navData
|
||||
|
||||
const iconHtml = icon ?? ""
|
||||
const iconHtml = icon ?? ''
|
||||
let html = `<span class='shortcode navigation ${navKey}'>${iconHtml}<span class="name">${name}</span></span>`
|
||||
|
||||
if (dependsOn) {
|
||||
if (dependsOn)
|
||||
html = `${generateNavigationHtml(dependsOn)} -> ${html}`
|
||||
}
|
||||
|
||||
return html
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import type { DefaultTheme } from "vitepress"
|
||||
import type { DefaultTheme } from 'vitepress'
|
||||
|
||||
import nav from "./navigation/navbar"
|
||||
import sidebar from "./navigation/sidebar"
|
||||
import nav from './navigation/navbar'
|
||||
import sidebar from './navigation/sidebar'
|
||||
|
||||
const themeConfig: DefaultTheme.Config = {
|
||||
logo: {
|
||||
src: "/img/logo-128px.png",
|
||||
src: '/img/logo-128px.png',
|
||||
width: 24,
|
||||
height: 24,
|
||||
},
|
||||
@ -17,60 +17,60 @@ const themeConfig: DefaultTheme.Config = {
|
||||
|
||||
socialLinks: [
|
||||
{
|
||||
icon: "github",
|
||||
link: "https://github.com/tachiyomiorg/tachiyomi",
|
||||
ariaLabel: "Project GitHub",
|
||||
icon: 'github',
|
||||
link: 'https://github.com/tachiyomiorg/tachiyomi',
|
||||
ariaLabel: 'Project GitHub',
|
||||
},
|
||||
{
|
||||
icon: "discord",
|
||||
link: "https://discord.gg/tachiyomi",
|
||||
ariaLabel: "Discord Server",
|
||||
icon: 'discord',
|
||||
link: 'https://discord.gg/tachiyomi',
|
||||
ariaLabel: 'Discord Server',
|
||||
},
|
||||
{
|
||||
icon: "x",
|
||||
link: "https://twitter.com/tachiyomiorg",
|
||||
ariaLabel: "X Page",
|
||||
icon: 'x',
|
||||
link: 'https://twitter.com/tachiyomiorg',
|
||||
ariaLabel: 'X Page',
|
||||
},
|
||||
{
|
||||
icon: "facebook",
|
||||
link: "https://facebook.com/tachiyomiorg",
|
||||
ariaLabel: "Facebook Page",
|
||||
icon: 'facebook',
|
||||
link: 'https://facebook.com/tachiyomiorg',
|
||||
ariaLabel: 'Facebook Page',
|
||||
},
|
||||
{
|
||||
icon: {
|
||||
svg: '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 0 1-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 0 1 .042.52c0 2.694-3.13 4.87-7.004 4.87-3.874 0-7.004-2.176-7.004-4.87 0-.183.015-.366.043-.534A1.748 1.748 0 0 1 4.028 12c0-.968.786-1.754 1.754-1.754.463 0 .898.196 1.207.49 1.207-.883 2.878-1.43 4.744-1.487l.885-4.182a.342.342 0 0 1 .14-.197.35.35 0 0 1 .238-.042l2.906.617a1.214 1.214 0 0 1 1.108-.701zM9.25 12C8.561 12 8 12.562 8 13.25c0 .687.561 1.248 1.25 1.248.687 0 1.248-.561 1.248-1.249 0-.688-.561-1.249-1.249-1.249zm5.5 0c-.687 0-1.248.561-1.248 1.25 0 .687.561 1.248 1.249 1.248.688 0 1.249-.561 1.249-1.249 0-.687-.562-1.249-1.25-1.249zm-5.466 3.99a.327.327 0 0 0-.231.094.33.33 0 0 0 0 .463c.842.842 2.484.913 2.961.913.477 0 2.105-.056 2.961-.913a.361.361 0 0 0 .029-.463.33.33 0 0 0-.464 0c-.547.533-1.684.73-2.512.73-.828 0-1.979-.196-2.512-.73a.326.326 0 0 0-.232-.095z"/></svg>',
|
||||
},
|
||||
link: "https://reddit.com/r/Tachiyomi",
|
||||
ariaLabel: "Support subreddit",
|
||||
link: 'https://reddit.com/r/Tachiyomi',
|
||||
ariaLabel: 'Support subreddit',
|
||||
},
|
||||
// { icon: "instagram", link: "https://instagram.com/tachiyomiorg", ariaLabel: "Instagram Page" },
|
||||
],
|
||||
|
||||
footer: {
|
||||
message: "<a href=\"https://www.apache.org/licenses/LICENSE-2.0\" target=\"_blank\">Open-source Apache Licensed</a> <span class=\"divider\">|</span> <a href=\"/privacy/\">Privacy policy</a> <span class=\"divider\">|</span> Powered by <a target=\"_blank\" href=\"https://www.netlify.com/\">Netlify <img src=\"/img/logo-netlify.svg\" alt=\"Netlify Logo\" height=\"12px\" width=\"12px\" style=\"display: inline-block\"></a>",
|
||||
message: '<a href="https://www.apache.org/licenses/LICENSE-2.0" target="_blank">Open-source Apache Licensed</a> <span class="divider">|</span> <a href="/privacy/">Privacy policy</a> <span class="divider">|</span> Powered by <a target="_blank" href="https://www.netlify.com/">Netlify <img src="/img/logo-netlify.svg" alt="Netlify Logo" height="12px" width="12px" style="display: inline-block"></a>',
|
||||
copyright: `Copyright © 2015 - ${new Date().getFullYear()} Javier Tomás`,
|
||||
},
|
||||
|
||||
editLink: {
|
||||
pattern: "https://github.com/tachiyomiorg/website/edit/main/website/src/:path",
|
||||
text: "Help us improve this page",
|
||||
pattern: 'https://github.com/tachiyomiorg/website/edit/main/website/src/:path',
|
||||
text: 'Help us improve this page',
|
||||
},
|
||||
|
||||
lastUpdated: {
|
||||
text: "Last updated",
|
||||
text: 'Last updated',
|
||||
formatOptions: {
|
||||
forceLocale: true,
|
||||
dateStyle: "long",
|
||||
timeStyle: "short",
|
||||
dateStyle: 'long',
|
||||
timeStyle: 'short',
|
||||
},
|
||||
},
|
||||
|
||||
search: {
|
||||
provider: "algolia",
|
||||
provider: 'algolia',
|
||||
options: {
|
||||
appId: "2C8EHFTRW7",
|
||||
apiKey: "ee38c6e04295e4d206399ab59a58ea9a",
|
||||
indexName: "tachiyomi",
|
||||
appId: '2C8EHFTRW7',
|
||||
apiKey: 'ee38c6e04295e4d206399ab59a58ea9a',
|
||||
indexName: 'tachiyomi',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { useData } from "vitepress"
|
||||
import DefaultTheme from "vitepress/theme"
|
||||
import { nextTick, provide } from "vue"
|
||||
import { useData } from 'vitepress'
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import { nextTick, provide } from 'vue'
|
||||
|
||||
const { isDark } = useData()
|
||||
|
||||
function shouldEnableTransitions() {
|
||||
return "startViewTransition" in document
|
||||
&& window.matchMedia("(prefers-reduced-motion: no-preference)").matches
|
||||
return 'startViewTransition' in document
|
||||
&& window.matchMedia('(prefers-reduced-motion: no-preference)').matches
|
||||
}
|
||||
|
||||
provide("toggle-appearance", async ({ clientX: x, clientY: y }: MouseEvent) => {
|
||||
provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => {
|
||||
if (!shouldEnableTransitions()) {
|
||||
isDark.value = !isDark.value
|
||||
return
|
||||
@ -34,8 +34,8 @@ provide("toggle-appearance", async ({ clientX: x, clientY: y }: MouseEvent) => {
|
||||
{ clipPath: isDark.value ? clipPath.reverse() : clipPath },
|
||||
{
|
||||
duration: 300,
|
||||
easing: "ease-in",
|
||||
pseudoElement: `::view-transition-${isDark.value ? "old" : "new"}(root)`,
|
||||
easing: 'ease-in',
|
||||
pseudoElement: `::view-transition-${isDark.value ? 'old' : 'new'}(root)`,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, toRefs } from "vue"
|
||||
import MarkdownIt from "markdown-it"
|
||||
import { type AppRelease, data as release } from "../data/release.data"
|
||||
import Contributors from "./Contributors.vue"
|
||||
import { computed, toRefs } from 'vue'
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import { type AppRelease, data as release } from '../data/release.data'
|
||||
import Contributors from './Contributors.vue'
|
||||
|
||||
const props = defineProps<{ type: keyof AppRelease }>()
|
||||
const { type } = toRefs(props)
|
||||
@ -10,9 +10,9 @@ const { type } = toRefs(props)
|
||||
const md = new MarkdownIt()
|
||||
|
||||
const changelog = computed(() => {
|
||||
const flavoredString = (release[type.value].body ?? "")
|
||||
.replace(/(?<=\(|(, ))@(.*?)(?=\)|(, ))/g, "[@$2](https://github.com/$2)")
|
||||
.replace("https://github.com/tachiyomiorg/tachiyomi/releases", "/changelogs/")
|
||||
const flavoredString = (release[type.value].body ?? '')
|
||||
.replace(/(?<=\(|(, ))@(.*?)(?=\)|(, ))/g, '[@$2](https://github.com/$2)')
|
||||
.replace('https://github.com/tachiyomiorg/tachiyomi/releases', '/changelogs/')
|
||||
|
||||
return md.render(flavoredString)
|
||||
})
|
||||
|
@ -1,25 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
import MarkdownIt from "markdown-it"
|
||||
import { data as changelogs } from "../data/changelogs.data"
|
||||
import Contributors from "./Contributors.vue"
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import { data as changelogs } from '../data/changelogs.data'
|
||||
import Contributors from './Contributors.vue'
|
||||
|
||||
const md = new MarkdownIt()
|
||||
|
||||
function renderMarkdown(string: string | null | undefined) {
|
||||
const body = string ?? "No changelog provided."
|
||||
const body = string ?? 'No changelog provided.'
|
||||
const flavoredString = body
|
||||
.split(/---\r\n\r\n### Checksums|---\r\n\r\nMD5/)[0]
|
||||
.replace(/(?<=\(|(, ))@(.*?)(?=\)|(, ))/g, "[@$2](https://github.com/$2)")
|
||||
.replace(/#(\d+)/g, "[#$1](https://github.com/tachiyomiorg/tachiyomi/issues/$1)")
|
||||
.replace(/^Check out the .*past release notes.* if you're.*$/m, "")
|
||||
.replace(/https\:\/\/github.com\/tachiyomiorg\/tachiyomi\/releases\/tag\/(.*?)/g, "#$1")
|
||||
.replace(/(?<=\(|(, ))@(.*?)(?=\)|(, ))/g, '[@$2](https://github.com/$2)')
|
||||
.replace(/#(\d+)/g, '[#$1](https://github.com/tachiyomiorg/tachiyomi/issues/$1)')
|
||||
.replace(/^Check out the .*past release notes.* if you're.*$/m, '')
|
||||
.replace(/https\:\/\/github.com\/tachiyomiorg\/tachiyomi\/releases\/tag\/(.*?)/g, '#$1')
|
||||
.trim()
|
||||
|
||||
return md.render(flavoredString)
|
||||
}
|
||||
|
||||
const dateFormatter = new Intl.DateTimeFormat("en", {
|
||||
dateStyle: "medium",
|
||||
const dateFormatter = new Intl.DateTimeFormat('en', {
|
||||
dateStyle: 'medium',
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -1,38 +1,37 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, toRefs } from "vue"
|
||||
import { computed, ref, toRefs } from 'vue'
|
||||
|
||||
const props = defineProps<{ body: string; author: string; tag: string }>()
|
||||
const { body, author, tag } = toRefs(props)
|
||||
|
||||
function isHigherThan(tagName: string, reference: string) {
|
||||
return reference.localeCompare(tagName, undefined, { numeric: true, sensitivity: "base" }) >= 0
|
||||
return reference.localeCompare(tagName, undefined, { numeric: true, sensitivity: 'base' }) >= 0
|
||||
}
|
||||
|
||||
const notMentioned = computed(() => {
|
||||
return isHigherThan("v0.8.5", tag.value) ? ["arkon"] : []
|
||||
return isHigherThan('v0.8.5', tag.value) ? ['arkon'] : []
|
||||
})
|
||||
|
||||
const nonExistent = ref<string[]>([])
|
||||
|
||||
const contributors = computed(() => {
|
||||
const list = [...body.value.matchAll(/(?<=\(|(, ))@(.*?)(?=\)|(, ))/g)]
|
||||
.map((match) => match[2])
|
||||
const uncredited = author.value.includes("[bot]")
|
||||
.map(match => match[2])
|
||||
const uncredited = author.value.includes('[bot]')
|
||||
? notMentioned.value
|
||||
: [author.value, ...notMentioned.value]
|
||||
|
||||
return [...new Set([...uncredited, ...list])].filter((user) => !nonExistent.value.includes(user))
|
||||
return [...new Set([...uncredited, ...list])].filter(user => !nonExistent.value.includes(user))
|
||||
})
|
||||
|
||||
const listFormatter = new Intl.ListFormat("en", {
|
||||
style: "long",
|
||||
type: "conjunction",
|
||||
const listFormatter = new Intl.ListFormat('en', {
|
||||
style: 'long',
|
||||
type: 'conjunction',
|
||||
})
|
||||
|
||||
const contributorsText = computed(() => {
|
||||
if (contributors.value.length <= 3) {
|
||||
if (contributors.value.length <= 3)
|
||||
return listFormatter.format(contributors.value)
|
||||
}
|
||||
|
||||
return listFormatter.format([
|
||||
...contributors.value.slice(0, 2),
|
||||
@ -41,9 +40,8 @@ const contributorsText = computed(() => {
|
||||
})
|
||||
|
||||
function addToNonExistent(user: string) {
|
||||
if (!nonExistent.value.includes(user)) {
|
||||
if (!nonExistent.value.includes(user))
|
||||
nonExistent.value.push(user)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from "vue"
|
||||
import { useData } from "vitepress"
|
||||
import type { DefaultTheme } from "vitepress/theme"
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useData } from 'vitepress'
|
||||
import type { DefaultTheme } from 'vitepress/theme'
|
||||
|
||||
import VPNavBarMenuLink from "vitepress/dist/client/theme-default/components/VPNavBarMenuLink.vue"
|
||||
import VPNavBarMenuGroup from "vitepress/dist/client/theme-default/components/VPNavBarMenuGroup.vue"
|
||||
import VPNavBarMenuLink from 'vitepress/dist/client/theme-default/components/VPNavBarMenuLink.vue'
|
||||
import VPNavBarMenuGroup from 'vitepress/dist/client/theme-default/components/VPNavBarMenuGroup.vue'
|
||||
|
||||
import { data as release } from "../data/release.data"
|
||||
import { data as release } from '../data/release.data'
|
||||
|
||||
const { theme } = useData<DefaultTheme.Config>()
|
||||
|
||||
@ -22,20 +22,18 @@ onMounted(() => {
|
||||
* and navbar doesn't support using the VitePress data loading.
|
||||
*/
|
||||
const nav = computed(() => {
|
||||
if (!replace.value) {
|
||||
if (!replace.value)
|
||||
return theme.value.nav
|
||||
}
|
||||
|
||||
return theme.value.nav?.map((item) => {
|
||||
if (!item.text.includes("{app_version}")) {
|
||||
if (!item.text.includes('{app_version}'))
|
||||
return item
|
||||
}
|
||||
|
||||
const appVersion = release.stable.tag_name.substring(1)
|
||||
|
||||
return {
|
||||
...item,
|
||||
text: item.text.replace("{app_version}", appVersion),
|
||||
text: item.text.replace('{app_version}', appVersion),
|
||||
} satisfies DefaultTheme.NavItem
|
||||
})
|
||||
})
|
||||
|
@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from "vue"
|
||||
import { type DefaultTheme, useData } from "vitepress"
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { type DefaultTheme, useData } from 'vitepress'
|
||||
|
||||
import VPNavScreenMenuLink from "vitepress/dist/client/theme-default/components/VPNavScreenMenuLink.vue"
|
||||
import VPNavScreenMenuGroup from "vitepress/dist/client/theme-default/components/VPNavScreenMenuGroup.vue"
|
||||
import VPNavScreenMenuLink from 'vitepress/dist/client/theme-default/components/VPNavScreenMenuLink.vue'
|
||||
import VPNavScreenMenuGroup from 'vitepress/dist/client/theme-default/components/VPNavScreenMenuGroup.vue'
|
||||
|
||||
import { data as release } from "../data/release.data"
|
||||
import { data as release } from '../data/release.data'
|
||||
|
||||
const { theme } = useData<DefaultTheme.Config>()
|
||||
|
||||
@ -21,20 +21,18 @@ onMounted(() => {
|
||||
* and navbar doesn't support using the VitePress data loading.
|
||||
*/
|
||||
const nav = computed(() => {
|
||||
if (!replace.value) {
|
||||
if (!replace.value)
|
||||
return theme.value.nav
|
||||
}
|
||||
|
||||
return theme.value.nav?.map((item) => {
|
||||
if (!item.text.includes("{app_version}")) {
|
||||
if (!item.text.includes('{app_version}'))
|
||||
return item
|
||||
}
|
||||
|
||||
const appVersion = release.stable.tag_name.substring(1)
|
||||
|
||||
return {
|
||||
...item,
|
||||
text: item.text.replace("{app_version}", appVersion),
|
||||
text: item.text.replace('{app_version}', appVersion),
|
||||
} satisfies DefaultTheme.NavItem
|
||||
})
|
||||
})
|
||||
|
@ -1,19 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
/// <reference types="@types/gtag.js" />
|
||||
|
||||
import { computed, onMounted, ref } from "vue"
|
||||
import { data as release } from "../data/release.data"
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { data as release } from '../data/release.data'
|
||||
|
||||
const downloadInformation = computed(() => ({
|
||||
preview: {
|
||||
tagName: release.preview.tag_name ?? "r0000",
|
||||
tagName: release.preview.tag_name ?? 'r0000',
|
||||
asset: (release.preview.assets ?? [])
|
||||
.find((a) => /^tachiyomi-r\d{4,}.apk/.test(a.name)),
|
||||
.find(a => /^tachiyomi-r\d{4,}.apk/.test(a.name)),
|
||||
},
|
||||
stable: {
|
||||
tagName: release.stable.tag_name ?? "v0.00.0",
|
||||
tagName: release.stable.tag_name ?? 'v0.00.0',
|
||||
asset: (release.stable.assets ?? [])
|
||||
.find((a) => /^tachiyomi-v\d+\.\d+\.\d+.apk/.test(a.name)),
|
||||
.find(a => /^tachiyomi-v\d+\.\d+\.\d+.apk/.test(a.name)),
|
||||
},
|
||||
}))
|
||||
|
||||
@ -23,11 +23,11 @@ onMounted(() => {
|
||||
isAndroid.value = !!navigator.userAgent.match(/android/i)
|
||||
})
|
||||
|
||||
function handleAnalytics(type: "preview" | "stable") {
|
||||
window.gtag?.("event", "Download", {
|
||||
event_category: "App",
|
||||
event_label: type === "stable" ? "Stable" : "Preview",
|
||||
version: type === "stable"
|
||||
function handleAnalytics(type: 'preview' | 'stable') {
|
||||
window.gtag?.('event', 'Download', {
|
||||
event_category: 'App',
|
||||
event_label: type === 'stable' ? 'Stable' : 'Preview',
|
||||
version: type === 'stable'
|
||||
? release.stable.tag_name
|
||||
: release.preview.tag_name,
|
||||
})
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, toRefs } from "vue"
|
||||
import { useMediaQuery } from "@vueuse/core"
|
||||
import { computed, toRefs } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
|
||||
import {
|
||||
ElForm,
|
||||
@ -10,13 +10,13 @@ import {
|
||||
ElRadio,
|
||||
ElRadioGroup,
|
||||
ElSelect,
|
||||
} from "element-plus"
|
||||
} from 'element-plus'
|
||||
|
||||
import { langName, simpleLangName } from "../../../config/scripts/languages"
|
||||
import type { Extension } from "../../queries/useExtensionsRepositoryQuery"
|
||||
import { langName, simpleLangName } from '../../../config/scripts/languages'
|
||||
import type { Extension } from '../../queries/useExtensionsRepositoryQuery'
|
||||
|
||||
export type Nsfw = "Show all" | "NSFW" | "SFW"
|
||||
export type Sort = "Ascending" | "Descending"
|
||||
export type Nsfw = 'Show all' | 'NSFW' | 'SFW'
|
||||
export type Sort = 'Ascending' | 'Descending'
|
||||
|
||||
const props = defineProps<{
|
||||
extensions: Extension[][]
|
||||
@ -27,16 +27,16 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e: "update:search", search: string): void
|
||||
(e: "update:lang", lang: string[]): void
|
||||
(e: "update:nsfw", nsfw: Nsfw): void
|
||||
(e: "update:sort", sort: Sort): void
|
||||
(e: 'update:search', search: string): void
|
||||
(e: 'update:lang', lang: string[]): void
|
||||
(e: 'update:nsfw', nsfw: Nsfw): void
|
||||
(e: 'update:sort', sort: Sort): void
|
||||
}>()
|
||||
|
||||
const { extensions } = toRefs(props)
|
||||
|
||||
const isSmallScreen = useMediaQuery("(max-width: 767px)")
|
||||
const labelPosition = computed(() => isSmallScreen.value ? "top" : "right")
|
||||
const isSmallScreen = useMediaQuery('(max-width: 767px)')
|
||||
const labelPosition = computed(() => isSmallScreen.value ? 'top' : 'right')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, toRefs } from "vue"
|
||||
import { langName, simpleLangName } from "../../../config/scripts/languages"
|
||||
import type { Extension } from "../../queries/useExtensionsRepositoryQuery"
|
||||
import ExtensionItem from "./ExtensionItem.vue"
|
||||
import { computed, toRefs } from 'vue'
|
||||
import { langName, simpleLangName } from '../../../config/scripts/languages'
|
||||
import type { Extension } from '../../queries/useExtensionsRepositoryQuery'
|
||||
import ExtensionItem from './ExtensionItem.vue'
|
||||
|
||||
const props = defineProps<{ list: Extension[]; totalCount: number }>()
|
||||
const { list } = toRefs(props)
|
||||
@ -10,7 +10,7 @@ const { list } = toRefs(props)
|
||||
const groupName = computed(() => {
|
||||
const firstItem = list.value[0]
|
||||
|
||||
return firstItem.lang === "en"
|
||||
return firstItem.lang === 'en'
|
||||
? simpleLangName(firstItem.lang)
|
||||
: langName(firstItem.lang)
|
||||
})
|
||||
|
@ -1,17 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
/// <reference types="@types/gtag.js" />
|
||||
|
||||
import { computed, toRefs } from "vue"
|
||||
import type { Extension } from "../../queries/useExtensionsRepositoryQuery"
|
||||
import { computed, toRefs } from 'vue'
|
||||
import type { Extension } from '../../queries/useExtensionsRepositoryQuery'
|
||||
|
||||
const props = defineProps<{ item: Extension }>()
|
||||
const { item } = toRefs(props)
|
||||
|
||||
const pkgId = computed(() => {
|
||||
return item.value.pkg.replace("eu.kanade.tachiyomi.extension.", "")
|
||||
return item.value.pkg.replace('eu.kanade.tachiyomi.extension.', '')
|
||||
})
|
||||
|
||||
const pkgName = computed(() => item.value.name.split(": ")[1])
|
||||
const pkgName = computed(() => item.value.name.split(': ')[1])
|
||||
const pkgIsNsfw = computed(() => item.value.nsfw === 1)
|
||||
|
||||
const iconUrl = computed(() => {
|
||||
@ -23,8 +23,8 @@ const apkUrl = computed(() => {
|
||||
})
|
||||
|
||||
function handleAnalytics() {
|
||||
window.gtag?.("event", "Download", {
|
||||
event_category: "Extension",
|
||||
window.gtag?.('event', 'Download', {
|
||||
event_category: 'Extension',
|
||||
event_label: pkgName.value,
|
||||
version: item.value.version,
|
||||
})
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, toRefs } from "vue"
|
||||
import type { Extension } from "../../queries/useExtensionsRepositoryQuery"
|
||||
import ExtensionGroup from "./ExtensionGroup.vue"
|
||||
import { computed, toRefs } from 'vue'
|
||||
import type { Extension } from '../../queries/useExtensionsRepositoryQuery'
|
||||
import ExtensionGroup from './ExtensionGroup.vue'
|
||||
|
||||
const props = defineProps<{ extensions: Extension[][] }>()
|
||||
const { extensions } = toRefs(props)
|
||||
|
@ -1,18 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import groupBy from "lodash.groupby"
|
||||
import { ElLoading } from "element-plus"
|
||||
import groupBy from 'lodash.groupby'
|
||||
import { ElLoading } from 'element-plus'
|
||||
|
||||
import { computed, nextTick, onMounted, reactive, ref, watch } from "vue"
|
||||
import { simpleLangName } from "../../../config/scripts/languages"
|
||||
import useExtensionsRepositoryQuery from "../../queries/useExtensionsRepositoryQuery"
|
||||
import type { Extension } from "../../queries/useExtensionsRepositoryQuery"
|
||||
import ExtensionFilters from "./ExtensionFilters.vue"
|
||||
import ExtensionList from "./ExtensionList.vue"
|
||||
import type { Nsfw, Sort } from "./ExtensionFilters.vue"
|
||||
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { simpleLangName } from '../../../config/scripts/languages'
|
||||
import useExtensionsRepositoryQuery from '../../queries/useExtensionsRepositoryQuery'
|
||||
import type { Extension } from '../../queries/useExtensionsRepositoryQuery'
|
||||
import ExtensionFilters from './ExtensionFilters.vue'
|
||||
import ExtensionList from './ExtensionList.vue'
|
||||
import type { Nsfw, Sort } from './ExtensionFilters.vue'
|
||||
|
||||
const { data: extensions, isLoading } = useExtensionsRepositoryQuery({
|
||||
select: (response) => {
|
||||
const values: Extension[][] = Object.values(groupBy(response, "lang"))
|
||||
const values: Extension[][] = Object.values(groupBy(response, 'lang'))
|
||||
values.sort(languageComparator)
|
||||
|
||||
return values
|
||||
@ -20,33 +20,33 @@ const { data: extensions, isLoading } = useExtensionsRepositoryQuery({
|
||||
})
|
||||
|
||||
const filters = reactive({
|
||||
search: "",
|
||||
search: '',
|
||||
lang: [] as string[],
|
||||
nsfw: "Show all" as Nsfw,
|
||||
sort: "Ascending" as Sort,
|
||||
nsfw: 'Show all' as Nsfw,
|
||||
sort: 'Ascending' as Sort,
|
||||
})
|
||||
|
||||
function languageComparator(a: Extension[], b: Extension[]) {
|
||||
const langA = simpleLangName(a[0].lang)
|
||||
const langB = simpleLangName(b[0].lang)
|
||||
if (langA === "All" && langB === "English") {
|
||||
if (langA === 'All' && langB === 'English')
|
||||
return -1
|
||||
}
|
||||
if (langA === "English" && langB === "All") {
|
||||
|
||||
if (langA === 'English' && langB === 'All')
|
||||
return 1
|
||||
}
|
||||
if (langA === "English") {
|
||||
|
||||
if (langA === 'English')
|
||||
return -1
|
||||
}
|
||||
if (langB === "English") {
|
||||
|
||||
if (langB === 'English')
|
||||
return 1
|
||||
}
|
||||
if (langA < langB) {
|
||||
|
||||
if (langA < langB)
|
||||
return -1
|
||||
}
|
||||
if (langA > langB) {
|
||||
|
||||
if (langA > langB)
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -60,33 +60,32 @@ const filteredExtensions = computed(() => {
|
||||
|
||||
if (filters.search) {
|
||||
filteredGroup = filteredGroup.filter(
|
||||
(ext) =>
|
||||
ext =>
|
||||
ext.name.toLowerCase().includes(filters.search.toLowerCase())
|
||||
|| ext.sources.some((source) => source.id.includes(filters.search)),
|
||||
|| ext.sources.some(source => source.id.includes(filters.search)),
|
||||
)
|
||||
}
|
||||
filteredGroup = filteredGroup.filter((ext) =>
|
||||
filters.nsfw === "Show all" ? true : ext.nsfw === (filters.nsfw === "NSFW" ? 1 : 0),
|
||||
filteredGroup = filteredGroup.filter(ext =>
|
||||
filters.nsfw === 'Show all' ? true : ext.nsfw === (filters.nsfw === 'NSFW' ? 1 : 0),
|
||||
)
|
||||
|
||||
if (filters.sort && filters.sort === "Descending") {
|
||||
if (filters.sort && filters.sort === 'Descending')
|
||||
filteredGroup = filteredGroup.reverse()
|
||||
}
|
||||
if (filteredGroup.length) {
|
||||
|
||||
if (filteredGroup.length)
|
||||
filtered.push(filteredGroup)
|
||||
}
|
||||
}
|
||||
|
||||
return filtered
|
||||
})
|
||||
|
||||
const loadingInstance = ref<ReturnType<typeof ElLoading["service"]>>()
|
||||
const loadingInstance = ref<ReturnType<typeof ElLoading['service']>>()
|
||||
|
||||
onMounted(() => {
|
||||
loadingInstance.value = ElLoading.service({
|
||||
target: ".extensions",
|
||||
target: '.extensions',
|
||||
fullscreen: false,
|
||||
background: "transparent",
|
||||
background: 'transparent',
|
||||
})
|
||||
})
|
||||
|
||||
@ -95,15 +94,14 @@ watch(extensions, async () => {
|
||||
await nextTick()
|
||||
const extElement = document.getElementById(window.location.hash.substring(1))
|
||||
|
||||
extElement?.scrollIntoView({ behavior: "smooth" })
|
||||
extElement?.scrollIntoView({ behavior: 'smooth' })
|
||||
extElement?.focus({ preventScroll: true })
|
||||
}
|
||||
})
|
||||
|
||||
watch([isLoading, loadingInstance], async ([newIsLoading]) => {
|
||||
if (!newIsLoading) {
|
||||
if (!newIsLoading)
|
||||
loadingInstance.value?.close()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -1,17 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { IconChevronRight } from "@iconify-prerendered/vue-mdi"
|
||||
import { data as newsList } from "../data/news.data"
|
||||
import { IconChevronRight } from '@iconify-prerendered/vue-mdi'
|
||||
import { data as newsList } from '../data/news.data'
|
||||
|
||||
const dateFormatter = new Intl.DateTimeFormat("en", {
|
||||
dateStyle: "medium",
|
||||
timeZone: "UTC",
|
||||
const dateFormatter = new Intl.DateTimeFormat('en', {
|
||||
dateStyle: 'medium',
|
||||
timeZone: 'UTC',
|
||||
})
|
||||
|
||||
function formatDate(date: string) {
|
||||
const [year, month, day] = date
|
||||
.substring(0, 10)
|
||||
.split("-")
|
||||
.map((number) => Number.parseInt(number, 10))
|
||||
.split('-')
|
||||
.map(number => Number.parseInt(number, 10))
|
||||
const utcDate = Date.UTC(year, month - 1, day)
|
||||
return dateFormatter.format(utcDate)
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, toRefs } from "vue"
|
||||
import moment from "moment"
|
||||
import { type AppRelease, data as release } from "../data/release.data"
|
||||
import { computed, onMounted, ref, toRefs } from 'vue'
|
||||
import moment from 'moment'
|
||||
import { type AppRelease, data as release } from '../data/release.data'
|
||||
|
||||
const props = defineProps<{ type: keyof AppRelease }>()
|
||||
const { type } = toRefs(props)
|
||||
|
||||
const momentInfo = computed(() => ({
|
||||
relative: moment(release[type.value].published_at).fromNow(),
|
||||
exact: moment(release[type.value].published_at).format("dddd, MMMM Do YYYY [at] HH:mm"),
|
||||
exact: moment(release[type.value].published_at).format('dddd, MMMM Do YYYY [at] HH:mm'),
|
||||
iso: release[type.value].published_at ?? undefined,
|
||||
}))
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { defineLoader } from "vitepress"
|
||||
import { Octokit } from "@octokit/rest"
|
||||
import type { GetResponseDataTypeFromEndpointMethod } from "@octokit/types"
|
||||
import { defineLoader } from 'vitepress'
|
||||
import { Octokit } from '@octokit/rest'
|
||||
import type { GetResponseDataTypeFromEndpointMethod } from '@octokit/types'
|
||||
|
||||
const octokit = new Octokit()
|
||||
|
||||
@ -12,8 +12,8 @@ export { data }
|
||||
export default defineLoader({
|
||||
async load(): Promise<GitHubReleaseList> {
|
||||
const releases = await octokit.paginate(octokit.repos.listReleases, {
|
||||
owner: "tachiyomiorg",
|
||||
repo: "tachiyomi",
|
||||
owner: 'tachiyomiorg',
|
||||
repo: 'tachiyomi',
|
||||
per_page: 100,
|
||||
})
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { createContentLoader } from "vitepress"
|
||||
import { createContentLoader } from 'vitepress'
|
||||
|
||||
export interface News {
|
||||
title: string
|
||||
@ -10,11 +10,11 @@ export interface News {
|
||||
declare const data: News[]
|
||||
export { data }
|
||||
|
||||
export default createContentLoader("news/*.md", {
|
||||
export default createContentLoader('news/*.md', {
|
||||
excerpt: true,
|
||||
transform(articles) {
|
||||
return articles
|
||||
.filter(({ url }) => url !== "/news/")
|
||||
.filter(({ url }) => url !== '/news/')
|
||||
.map(
|
||||
({ frontmatter, url }) =>
|
||||
<News>{
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { defineLoader } from "vitepress"
|
||||
import { Octokit } from "@octokit/rest"
|
||||
import type { GetResponseDataTypeFromEndpointMethod } from "@octokit/types"
|
||||
import { defineLoader } from 'vitepress'
|
||||
import { Octokit } from '@octokit/rest'
|
||||
import type { GetResponseDataTypeFromEndpointMethod } from '@octokit/types'
|
||||
|
||||
const octokit = new Octokit()
|
||||
|
||||
@ -17,13 +17,13 @@ export { data }
|
||||
export default defineLoader({
|
||||
async load(): Promise<AppRelease> {
|
||||
const { data: stable } = await octokit.repos.getLatestRelease({
|
||||
owner: "tachiyomiorg",
|
||||
repo: "tachiyomi",
|
||||
owner: 'tachiyomiorg',
|
||||
repo: 'tachiyomi',
|
||||
})
|
||||
|
||||
const { data: preview } = await octokit.repos.getLatestRelease({
|
||||
owner: "tachiyomiorg",
|
||||
repo: "tachiyomi-preview",
|
||||
owner: 'tachiyomiorg',
|
||||
repo: 'tachiyomi-preview',
|
||||
})
|
||||
|
||||
return { stable, preview }
|
||||
|
@ -1,31 +1,31 @@
|
||||
// https://vitepress.dev/guide/custom-theme
|
||||
import DefaultTheme from "vitepress/theme"
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
|
||||
// Import Stylus files
|
||||
import "./styles/base.styl"
|
||||
import './styles/base.styl'
|
||||
|
||||
// Import Global plugins
|
||||
import "element-plus/theme-chalk/dark/css-vars.css"
|
||||
import 'element-plus/theme-chalk/dark/css-vars.css'
|
||||
|
||||
import { VueQueryPlugin } from "@tanstack/vue-query"
|
||||
import { VueQueryPlugin } from '@tanstack/vue-query'
|
||||
|
||||
import { enhanceAppWithTabs } from "vitepress-plugin-tabs/client"
|
||||
import { enhanceAppWithTabs } from 'vitepress-plugin-tabs/client'
|
||||
|
||||
// Import icon components
|
||||
import { IconBugReport, IconDownload, IconNewspaperVariant } from "@iconify-prerendered/vue-mdi"
|
||||
import { IconBugReport, IconDownload, IconNewspaperVariant } from '@iconify-prerendered/vue-mdi'
|
||||
|
||||
import analytics from "./plugin/analytics"
|
||||
import Layout from "./Layout.vue"
|
||||
import analytics from './plugin/analytics'
|
||||
import Layout from './Layout.vue'
|
||||
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
enhanceApp({ app }) {
|
||||
app.use(VueQueryPlugin)
|
||||
enhanceAppWithTabs(app)
|
||||
app.component("IconDownload", IconDownload)
|
||||
app.component("IconNewspaperVariant", IconNewspaperVariant)
|
||||
app.component("IconBugReport", IconBugReport)
|
||||
analytics({ id: "G-2CBXXM1Y86" })
|
||||
app.component('IconDownload', IconDownload)
|
||||
app.component('IconNewspaperVariant', IconNewspaperVariant)
|
||||
app.component('IconBugReport', IconBugReport)
|
||||
analytics({ id: 'G-2CBXXM1Y86' })
|
||||
},
|
||||
Layout,
|
||||
}
|
||||
|
@ -3,13 +3,12 @@
|
||||
// https://github.com/ZhongxuYang/vitepress-plugin-google-analytics
|
||||
|
||||
function mountGoogleAnalytics(id: string) {
|
||||
if (("dataLayer" in window && window.gtag) || window.location.hostname === "localhost") {
|
||||
if (('dataLayer' in window && window.gtag) || window.location.hostname === 'localhost')
|
||||
return
|
||||
}
|
||||
|
||||
const analyticsScript = document.createElement("script")
|
||||
const analyticsScript = document.createElement('script')
|
||||
|
||||
analyticsScript.addEventListener("load", () => {
|
||||
analyticsScript.addEventListener('load', () => {
|
||||
// @ts-expect-error Missing types
|
||||
window.dataLayer = window.dataLayer || []
|
||||
function gtag(..._args: any[]) {
|
||||
@ -18,8 +17,8 @@ function mountGoogleAnalytics(id: string) {
|
||||
window.dataLayer.push(arguments)
|
||||
}
|
||||
|
||||
gtag("js", new Date())
|
||||
gtag("config", id)
|
||||
gtag('js', new Date())
|
||||
gtag('config', id)
|
||||
|
||||
window.gtag = gtag
|
||||
})
|
||||
@ -30,8 +29,7 @@ function mountGoogleAnalytics(id: string) {
|
||||
}
|
||||
|
||||
export default function ({ id }: { id: string }) {
|
||||
// eslint-disable-next-line n/prefer-global/process
|
||||
if (process.env.NODE_ENV === "production" && id && typeof window !== "undefined") {
|
||||
// eslint-disable-next-line node/prefer-global/process
|
||||
if (process.env.NODE_ENV === 'production' && id && typeof window !== 'undefined')
|
||||
mountGoogleAnalytics(id)
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import type { UseQueryOptions } from "@tanstack/vue-query"
|
||||
import { useQuery } from "@tanstack/vue-query"
|
||||
import axios from "axios"
|
||||
import { GITHUB_EXTENSION_JSON } from "../../config/constants"
|
||||
import type { UseQueryOptions } from '@tanstack/vue-query'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
import axios from 'axios'
|
||||
import { GITHUB_EXTENSION_JSON } from '../../config/constants'
|
||||
|
||||
export type ReleaseType = "stable" | "preview"
|
||||
export type ReleaseType = 'stable' | 'preview'
|
||||
|
||||
export interface Extension {
|
||||
name: string
|
||||
@ -31,7 +31,7 @@ type UseExtensionsRepositoryQueryOptions<S = Extension[]> =
|
||||
|
||||
export default function useExtensionsRepositoryQuery<S = Extension[]>(options: UseExtensionsRepositoryQueryOptions<S> = {}) {
|
||||
return useQuery<Extension[], Error, S>({
|
||||
queryKey: ["extensions"],
|
||||
queryKey: ['extensions'],
|
||||
queryFn: async () => {
|
||||
const { data } = await axios.get<Extension[]>(GITHUB_EXTENSION_JSON)
|
||||
|
||||
|
4
website/src/.vitepress/vue-shim.d.ts
vendored
4
website/src/.vitepress/vue-shim.d.ts
vendored
@ -1,5 +1,5 @@
|
||||
declare module "*.vue" {
|
||||
import type { Component } from "vue"
|
||||
declare module '*.vue' {
|
||||
import type { Component } from 'vue'
|
||||
|
||||
const _default: Component
|
||||
export default _default
|
||||
|
Loading…
Reference in New Issue
Block a user