Add a changelogs page and other minor fixes (#15)

* Add a changelogs page.

* Fix local search highlight colors.

* Make changelog headers a link.

* Replace GitHub links with local ones.

* Fix missing `Badge` component.

* Show release contributors.

* Add missing flex-wrap to mobile devices.

* Add missing `alt` attribute to the avatar.

* Use `@theme` alias while importing components from the theme.

* Minor design tweaks to the extensions items.

* Show contributors on WhatsNew component as well.

* Fix extensions page not centered.

* Show sidebar on the changelogs page.

* Fix missing link replacements.

* Add missing unmentioned credits.
This commit is contained in:
Alessandro Jean 2023-09-04 04:03:19 -03:00 committed by GitHub
parent 489801174e
commit 31169bd527
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 447 additions and 62 deletions

View File

@ -73,6 +73,8 @@
}, },
"dependencies": { "dependencies": {
"@iconify-prerendered/vue-mdi": "^0.23.1689058119", "@iconify-prerendered/vue-mdi": "^0.23.1689058119",
"@octokit/rest": "^20.0.1",
"@octokit/types": "^11.1.0",
"@tanstack/vue-query": "^4.34.0", "@tanstack/vue-query": "^4.34.0",
"axios": "^1.5.0", "axios": "^1.5.0",
"element-plus": "^2.3.12", "element-plus": "^2.3.12",

View File

@ -4,6 +4,12 @@ dependencies:
'@iconify-prerendered/vue-mdi': '@iconify-prerendered/vue-mdi':
specifier: ^0.23.1689058119 specifier: ^0.23.1689058119
version: 0.23.1689058119(vue@3.3.4) version: 0.23.1689058119(vue@3.3.4)
'@octokit/rest':
specifier: ^20.0.1
version: 20.0.1
'@octokit/types':
specifier: ^11.1.0
version: 11.1.0
'@tanstack/vue-query': '@tanstack/vue-query':
specifier: ^4.34.0 specifier: ^4.34.0
version: 4.34.0(vue@3.3.4) version: 4.34.0(vue@3.3.4)
@ -757,6 +763,111 @@ packages:
fastq: 1.15.0 fastq: 1.15.0
dev: true dev: true
/@octokit/auth-token@4.0.0:
resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==}
engines: {node: '>= 18'}
dev: false
/@octokit/core@5.0.0:
resolution: {integrity: sha512-YbAtMWIrbZ9FCXbLwT9wWB8TyLjq9mxpKdgB3dUNxQcIVTf9hJ70gRPwAcqGZdY6WdJPZ0I7jLaaNDCiloGN2A==}
engines: {node: '>= 18'}
dependencies:
'@octokit/auth-token': 4.0.0
'@octokit/graphql': 7.0.1
'@octokit/request': 8.1.1
'@octokit/request-error': 5.0.0
'@octokit/types': 11.1.0
before-after-hook: 2.2.3
universal-user-agent: 6.0.0
dev: false
/@octokit/endpoint@9.0.0:
resolution: {integrity: sha512-szrQhiqJ88gghWY2Htt8MqUDO6++E/EIXqJ2ZEp5ma3uGS46o7LZAzSLt49myB7rT+Hfw5Y6gO3LmOxGzHijAQ==}
engines: {node: '>= 18'}
dependencies:
'@octokit/types': 11.1.0
is-plain-object: 5.0.0
universal-user-agent: 6.0.0
dev: false
/@octokit/graphql@7.0.1:
resolution: {integrity: sha512-T5S3oZ1JOE58gom6MIcrgwZXzTaxRnxBso58xhozxHpOqSTgDS6YNeEUvZ/kRvXgPrRz/KHnZhtb7jUMRi9E6w==}
engines: {node: '>= 18'}
dependencies:
'@octokit/request': 8.1.1
'@octokit/types': 11.1.0
universal-user-agent: 6.0.0
dev: false
/@octokit/openapi-types@18.0.0:
resolution: {integrity: sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw==}
dev: false
/@octokit/plugin-paginate-rest@8.0.0(@octokit/core@5.0.0):
resolution: {integrity: sha512-2xZ+baZWUg+qudVXnnvXz7qfrTmDeYPCzangBVq/1gXxii/OiS//4shJp9dnCCvj1x+JAm9ji1Egwm1BA47lPQ==}
engines: {node: '>= 18'}
peerDependencies:
'@octokit/core': '>=5'
dependencies:
'@octokit/core': 5.0.0
'@octokit/types': 11.1.0
dev: false
/@octokit/plugin-request-log@4.0.0(@octokit/core@5.0.0):
resolution: {integrity: sha512-2uJI1COtYCq8Z4yNSnM231TgH50bRkheQ9+aH8TnZanB6QilOnx8RMD2qsnamSOXtDj0ilxvevf5fGsBhBBzKA==}
engines: {node: '>= 18'}
peerDependencies:
'@octokit/core': '>=5'
dependencies:
'@octokit/core': 5.0.0
dev: false
/@octokit/plugin-rest-endpoint-methods@9.0.0(@octokit/core@5.0.0):
resolution: {integrity: sha512-KquMF/VB1IkKNiVnzJKspY5mFgGyLd7HzdJfVEGTJFzqu9BRFNWt+nwTCMuUiWc72gLQhRWYubTwOkQj+w/1PA==}
engines: {node: '>= 18'}
peerDependencies:
'@octokit/core': '>=5'
dependencies:
'@octokit/core': 5.0.0
'@octokit/types': 11.1.0
dev: false
/@octokit/request-error@5.0.0:
resolution: {integrity: sha512-1ue0DH0Lif5iEqT52+Rf/hf0RmGO9NWFjrzmrkArpG9trFfDM/efx00BJHdLGuro4BR/gECxCU2Twf5OKrRFsQ==}
engines: {node: '>= 18'}
dependencies:
'@octokit/types': 11.1.0
deprecation: 2.3.1
once: 1.4.0
dev: false
/@octokit/request@8.1.1:
resolution: {integrity: sha512-8N+tdUz4aCqQmXl8FpHYfKG9GelDFd7XGVzyN8rc6WxVlYcfpHECnuRkgquzz+WzvHTK62co5di8gSXnzASZPQ==}
engines: {node: '>= 18'}
dependencies:
'@octokit/endpoint': 9.0.0
'@octokit/request-error': 5.0.0
'@octokit/types': 11.1.0
is-plain-object: 5.0.0
universal-user-agent: 6.0.0
dev: false
/@octokit/rest@20.0.1:
resolution: {integrity: sha512-wROV21RwHQIMNb2Dgd4+pY+dVy1Dwmp85pBrgr6YRRDYRBu9Gb+D73f4Bl2EukZSj5hInq2Tui9o7gAQpc2k2Q==}
engines: {node: '>= 18'}
dependencies:
'@octokit/core': 5.0.0
'@octokit/plugin-paginate-rest': 8.0.0(@octokit/core@5.0.0)
'@octokit/plugin-request-log': 4.0.0(@octokit/core@5.0.0)
'@octokit/plugin-rest-endpoint-methods': 9.0.0(@octokit/core@5.0.0)
dev: false
/@octokit/types@11.1.0:
resolution: {integrity: sha512-Fz0+7GyLm/bHt8fwEqgvRBWwIV1S6wRRyq+V6exRKLVWaKGsuy6H9QFYeBVDV7rK6fO3XwHgQOPxv+cLj2zpXQ==}
dependencies:
'@octokit/openapi-types': 18.0.0
dev: false
/@pkgjs/parseargs@0.11.0: /@pkgjs/parseargs@0.11.0:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'} engines: {node: '>=14'}
@ -1554,6 +1665,10 @@ packages:
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dev: true dev: true
/before-after-hook@2.2.3:
resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==}
dev: false
/body-parser@1.20.1: /body-parser@1.20.1:
resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
@ -1899,6 +2014,10 @@ packages:
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
dev: true dev: true
/deprecation@2.3.1:
resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==}
dev: false
/destroy@1.2.0: /destroy@1.2.0:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
@ -2987,7 +3106,6 @@ packages:
/is-plain-object@5.0.0: /is-plain-object@5.0.0:
resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dev: true
/is-regex@1.1.4: /is-regex@1.1.4:
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
@ -3589,7 +3707,6 @@ packages:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
dependencies: dependencies:
wrappy: 1.0.2 wrappy: 1.0.2
dev: true
/onetime@5.1.2: /onetime@5.1.2:
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
@ -4576,6 +4693,10 @@ packages:
tiny-inflate: 1.0.3 tiny-inflate: 1.0.3
dev: true dev: true
/universal-user-agent@6.0.0:
resolution: {integrity: sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==}
dev: false
/unpipe@1.0.0: /unpipe@1.0.0:
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
@ -4840,7 +4961,6 @@ packages:
/wrappy@1.0.2: /wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
dev: true
/write-file-atomic@5.0.1: /write-file-atomic@5.0.1:
resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==}

View File

@ -1,3 +1,5 @@
const APP_VERSION = "0.14.6";
const nav = [ const nav = [
{ {
text: "Documentation", text: "Documentation",
@ -5,7 +7,7 @@ const nav = [
activeMatch: "/docs/", activeMatch: "/docs/",
}, },
{ {
text: "0.14.6", text: APP_VERSION,
items: [ items: [
{ {
text: "Download", text: "Download",
@ -13,7 +15,7 @@ const nav = [
}, },
{ {
text: "Changelog", text: "Changelog",
link: "https://github.com/tachiyomiorg/tachiyomi/releases/latest", link: `/changelogs/#v${APP_VERSION}`,
}, },
{ {
text: "Contributing", text: "Contributing",

View File

@ -3,6 +3,7 @@ import { getSidebar } from "vitepress-plugin-auto-sidebar";
const sidebar = { const sidebar = {
"/docs/": defaultSidebar(), "/docs/": defaultSidebar(),
"/forks/": defaultSidebar(), "/forks/": defaultSidebar(),
"/changelogs/": defaultSidebar(),
"/news/": getSidebar({ "/news/": getSidebar({
contentRoot: "/src/", contentRoot: "/src/",
contentDirs: ["news"], contentDirs: ["news"],
@ -120,6 +121,10 @@ function defaultSidebar() {
text: "Extensions", text: "Extensions",
link: "/extensions/", link: "/extensions/",
}, },
{
text: "Changelogs",
link: "/changelogs/",
},
{ {
text: "Forks", text: "Forks",
link: "/forks/", link: "/forks/",

View File

@ -59,7 +59,7 @@ const themeConfig: DefaultTheme.Config = {
lastUpdated: { lastUpdated: {
text: "Last updated", text: "Last updated",
formatOptions: { formatOptions: {
dateStyle: "long", dateStyle: "short",
timeStyle: "short", timeStyle: "short",
}, },
}, },

View File

@ -0,0 +1,69 @@
<script setup lang="ts">
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 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")
.trim();
return md.render(flavoredString);
}
const dateFormatter = new Intl.DateTimeFormat("en", {
dateStyle: "medium",
});
</script>
<template>
<div
v-for="(release, index) of changelogs"
:key="release.tag_name"
>
<h2 :id="release.tag_name">
<a
:href="release.html_url"
target="_blank"
>
{{ release.tag_name.substring(1) }}
</a>
<Badge v-if="index === 0" type="tip" text="Latest" />
<a
class="header-anchor"
:href="`#${release.tag_name}`"
:aria-label="`Permalink to &quot;${release.tag_name}&quot;`"
/>
</h2>
<time :datetime="release.published_at!">
{{ dateFormatter.format(new Date(release.published_at!)) }}
</time>
<div v-html="renderMarkdown(release.body)"></div>
<Contributors
:body="release.body!"
:author="release.author.login"
:tag="release.tag_name"
/>
</div>
</template>
<style lang="stylus" scoped>
h2 {
margin-bottom: 0;
display: flex;
align-items: center;
gap: 0.5rem;
}
time {
font-size: 0.875rem;
color: var(--vp-c-text-2);
}
</style>

View File

@ -0,0 +1,109 @@
<script setup lang="ts">
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;
}
const notMentioned = computed(() => {
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]')
? notMentioned.value
: [author.value, ...notMentioned.value];
return [...new Set([...uncredited, ...list])].filter((user) => !nonExistent.value.includes(user));
});
// @ts-expect-error
const listFormatter = new Intl.ListFormat("en", {
style: "long",
type: "conjunction",
});
const contributorsText = computed(() => {
if (contributors.value.length <= 3) {
return listFormatter.format(contributors.value);
}
return listFormatter.format([
...contributors.value.slice(0, 2),
`${contributors.value.length - 2} other contributors`
]);
});
function addToNonExistent(user: string) {
if (!nonExistent.value.includes(user)) {
nonExistent.value.push(user);
}
}
</script>
<template>
<div class="contributors" v-if="contributors.length > 0">
<h3>Contributors</h3>
<ul>
<li
v-for="contributor of contributors"
:key="contributor"
>
<a
:href="`https://github.com/${contributor}`"
target="_blank"
:title="`${contributor} profile on GitHub`"
:aria-label="`${contributor} profile on GitHub`"
>
<img
:src="`https://github.com/${contributor}.png?size=32`"
:alt="`@${contributor} profile picture`"
@error="addToNonExistent(contributor)"
loading="lazy"
class="avatar"
>
</a>
</li>
</ul>
<div class="names">
{{ contributorsText }}
</div>
</div>
</template>
<style lang="stylus" scoped>
.contributors {
ul {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 0.5rem;
list-style-type: none;
padding-left: 0;
li + li {
margin-top: 0;
}
}
.avatar {
width: 32px;
height: 32px;
border-radius: 50%;
box-shadow: var(--vp-shadow-1);
border: 1px solid var(--vp-c-divider);
}
.names {
font-size: 0.875rem;
color: var(--vp-c-text-2);
}
}
</style>

View File

@ -30,14 +30,12 @@ export default {
</span> </span>
</span> </span>
</h2> </h2>
<div <ExtensionItem
v-for="extension in list" v-for="extension in list"
:id="extension.pkg.replace('eu.kanade.tachiyomi.extension.', '')" :id="extension.pkg.replace('eu.kanade.tachiyomi.extension.', '')"
:key="extension.apk" :key="extension.apk"
class="extension" :item="extension"
> />
<ExtensionItem :item="extension" />
</div>
</div> </div>
</template> </template>

View File

@ -30,14 +30,12 @@ export default {
</span> </span>
</span> </span>
</h2> </h2>
<div <ExtensionItem
v-for="extension in list" v-for="extension in list"
:id="extension.pkg.replace('eu.kanade.tachiyomi.extension.', '')" :id="extension.pkg.replace('eu.kanade.tachiyomi.extension.', '')"
:key="extension.apk" :key="extension.apk"
class="extension" :item="extension"
> />
<ExtensionItem :item="extension" />
</div>
</div> </div>
</template> </template>

View File

@ -30,7 +30,8 @@ export default {
}; };
</script> </script>
<template v-if="item"> <template>
<div class="extension">
<a :href="`#${pkgId}`" class="anchor" aria-hidden="true" @click.stop>#</a> <a :href="`#${pkgId}`" class="anchor" aria-hidden="true" @click.stop>#</a>
<img class="extension-icon" :src="iconUrl" loading="lazy" width="42" height="42" /> <img class="extension-icon" :src="iconUrl" loading="lazy" width="42" height="42" />
<div class="extension-text"> <div class="extension-text">
@ -44,23 +45,28 @@ export default {
<Badge v-if="pkgIsNsfw" type="danger" :text="pkgVersion" title="This extension contains NSFW entries." /> <Badge v-if="pkgIsNsfw" type="danger" :text="pkgVersion" title="This extension contains NSFW entries." />
<Badge v-else type="info" :text="pkgVersion" title="This extension is free from NSFW entries." /> <Badge v-else type="info" :text="pkgVersion" title="This extension is free from NSFW entries." />
<a :href="apkUrl" class="extension-download" title="Download APK" download></a> <a :href="apkUrl" class="extension-download" title="Download APK" download></a>
</div>
</template> </template>
<style lang="stylus"> <style lang="stylus">
.extension { .extension {
position: relative
align-items: center align-items: center
display: flex display: flex
padding: 0.5em 1.5em width: 100%
margin: 0.8em 0.2em padding: 0.5em 0
margin: 0.8em 0
border-radius: 8px border-radius: 8px
gap: 0.675rem
&:hover { &:hover {
background-color: var(--vp-c-bg-soft-mute) background-color: var(--vp-c-bg-soft-mute)
} }
.anchor { .anchor {
margin-left: 0.2em position: absolute
margin-right: 0.2em left: 0
margin-left: -1em
font-size: 1.4em font-size: 1.4em
opacity: 0 opacity: 0
} }
@ -70,25 +76,34 @@ export default {
} }
.extension-icon { .extension-icon {
margin: 0.2em 0.5em flex-shrink: 0
margin-left: -4px;
} }
.extension-text { .extension-text {
flex: 1 flex-grow: 1
min-width: 0
.upper { .upper {
font-weight: 600 font-weight: 600
overflow: hidden
text-overflow: ellipsis
white-space: nowrap
.badge { .badge {
font-weight: 400 font-weight: 400
margin-left: 8px
} }
} }
.lower { .lower {
margin-top: 0.25rem
color: #6c757d color: #6c757d
font-family: monospace font-family: var(--vp-font-family-mono)
font-size: 0.9rem font-size: 0.9rem
overflow: hidden
text-overflow: ellipsis
white-space: nowrap
line-height: 16px
} }
} }
@ -97,6 +112,8 @@ export default {
font-weight: 700 font-weight: 700
font-size: 1.4em font-size: 1.4em
border-radius: 4px border-radius: 4px
flex-shrink: 0
margin-right: -0.4em
.material-icons { .material-icons {
color: white color: white
@ -124,6 +141,11 @@ export default {
.extension { .extension {
border: 1px solid var(--vp-c-divider) border: 1px solid var(--vp-c-divider)
border-radius: 8px border-radius: 8px
padding: 0.5em
.extension-download {
margin-right: 0
}
} }
} }

View File

@ -2,6 +2,7 @@
import { computed, toRefs } from "vue"; import { computed, toRefs } from "vue";
import MarkdownIt from "markdown-it"; import MarkdownIt from "markdown-it";
import { data as release, type AppRelease } from "../data/release.data"; import { data as release, type AppRelease } from "../data/release.data";
import Contributors from "./Contributors.vue";
const props = defineProps<{ type: keyof AppRelease }>(); const props = defineProps<{ type: keyof AppRelease }>();
const { type } = toRefs(props); const { type } = toRefs(props);
@ -11,6 +12,7 @@ const md = new MarkdownIt();
const whatsNew = computed(() => { const whatsNew = computed(() => {
const flavoredString = (release[type.value].body ?? "") const flavoredString = (release[type.value].body ?? "")
.replace(/(?<=\(|(, ))@(.*?)(?=\)|(, ))/g, "[@$2](https://github.com/$2)") .replace(/(?<=\(|(, ))@(.*?)(?=\)|(, ))/g, "[@$2](https://github.com/$2)")
.replace('https://github.com/tachiyomiorg/tachiyomi/releases', '/changelogs/');
return md.render(flavoredString); return md.render(flavoredString);
}) })
@ -22,8 +24,12 @@ const whatsNew = computed(() => {
<IconNewReleases /> <IconNewReleases />
<h2>What's new</h2> <h2>What's new</h2>
</header> </header>
<div v-html="whatsNew"> <div v-html="whatsNew" />
</div> <Contributors
:body="release[type].body!"
:author="release[type].author.login"
:tag="release[type].tag_name"
/>
</div> </div>
<div class="fullChangelog"> <div class="fullChangelog">
<p> <p>

View File

@ -0,0 +1,22 @@
import { defineLoader } from "vitepress"
import { Octokit } from "@octokit/rest";
import type { GetResponseDataTypeFromEndpointMethod } from "@octokit/types";
const octokit = new Octokit();
type GitHubReleaseList = GetResponseDataTypeFromEndpointMethod<typeof octokit.repos.listReleases>;
declare const data: GitHubReleaseList;
export { data };
export default defineLoader({
async load(): Promise<GitHubReleaseList> {
const releases = await octokit.paginate(octokit.repos.listReleases, {
owner: "tachiyomiorg",
repo: "tachiyomi",
per_page: 100,
});
return releases;
}
});

View File

@ -1,33 +1,30 @@
import { defineLoader } from "vitepress" import { defineLoader } from "vitepress"
import axios from "axios"; import { Octokit } from "@octokit/rest";
import { GITHUB_PREVIEW_API, GITHUB_STABLE_API } from "../../config/constants"; import type { GetResponseDataTypeFromEndpointMethod } from "@octokit/types";
const octokit = new Octokit();
type GitHubRelease = GetResponseDataTypeFromEndpointMethod<typeof octokit.repos.getLatestRelease>;
export interface AppRelease { export interface AppRelease {
stable: GitHubRelease; stable: GitHubRelease;
preview: GitHubRelease; preview: GitHubRelease;
} }
export interface GitHubRelease {
body: string;
tag_name: string;
name: string;
assets: GitHubAsset[];
published_at: string;
}
export interface GitHubAsset {
name: string;
content_type: string;
browser_download_url: string;
}
declare const data: AppRelease; declare const data: AppRelease;
export { data }; export { data };
export default defineLoader({ export default defineLoader({
async load(): Promise<AppRelease> { async load(): Promise<AppRelease> {
const { data: stable } = await axios.get<GitHubRelease>(GITHUB_STABLE_API); const { data: stable } = await octokit.repos.getLatestRelease({
const { data: preview } = await axios.get<GitHubRelease>(GITHUB_PREVIEW_API); owner: "tachiyomiorg",
repo: "tachiyomi",
});
const { data: preview } = await octokit.repos.getLatestRelease({
owner: "tachiyomiorg",
repo: "tachiyomi-preview",
});
return { stable, preview }; return { stable, preview };
} }

View File

@ -17,7 +17,7 @@ import { enhanceAppWithTabs } from "vitepress-plugin-tabs/client";
import { IconDownload, IconNewReleases, IconBugReport } from "@iconify-prerendered/vue-mdi"; import { IconDownload, IconNewReleases, IconBugReport } from "@iconify-prerendered/vue-mdi";
export default { export default {
...DefaultTheme, extends: DefaultTheme,
enhanceApp({ app }) { enhanceApp({ app }) {
app.use(ElementPlus); app.use(ElementPlus);
app.use(VueQueryPlugin); app.use(VueQueryPlugin);

View File

@ -114,6 +114,18 @@ html:not(.dark) {
border-radius: 8px border-radius: 8px
} }
/**
* Component: LocalSearch
*/
.VPLocalSearchBox {
--vp-local-search-highlight-bg: var(--vp-c-brand-soft);
--vp-local-search-highlight-text: var(--vp-c-brand-dark);
}
.dark .VPLocalSearchBox {
--vp-local-search-highlight-text: var(--vp-c-brand-lightest);
}
/** /**
* Component: Image Figure * Component: Image Figure
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */

View File

@ -4,7 +4,7 @@
padding: 16px padding: 16px
color: var(--vp-c-text-1) color: var(--vp-c-text-1)
// background-color: var(--vp-code-block-bg) // background-color: var(--vp-code-block-bg)
font-family: 'Roboto Mono', monospace font-family: var(--vp-font-family-mono)
font-size: 0.85rem font-size: 0.85rem
line-height: 1.5 line-height: 1.5

View File

@ -0,0 +1,18 @@
---
title: Changelogs
description: Changelogs of all Tachiyomi stable releases.
lastUpdated: false
editLink: false
prev: false
next: false
---
<script setup>
import Changelogs from "@theme/components/Changelogs.vue";
</script>
# Changelogs
Changelogs of all Tachiyomi stable releases, obtained from the GitHub repository.
<Changelogs />

View File

@ -9,9 +9,9 @@ next: false
--- ---
<script setup> <script setup>
import DownloadButtons from "../.vitepress/theme/components/DownloadButtons.vue" import DownloadButtons from "@theme/components/DownloadButtons.vue";
import ReleaseDate from "../.vitepress/theme/components/ReleaseDate.vue"; import ReleaseDate from "@theme/components/ReleaseDate.vue";
import WhatsNew from "../.vitepress/theme/components/WhatsNew.vue" import WhatsNew from "@theme/components/WhatsNew.vue";
</script> </script>
# Download # Download

View File

@ -1,6 +1,11 @@
--- ---
title: Extensions title: Extensions
description: Browse and install the full list of sources for Tachiyomi. description: Browse and install the full list of sources for Tachiyomi.
aside: false
lastUpdated: false
editLink: false
prev: false
next: false
--- ---
# Extensions # Extensions
@ -10,5 +15,5 @@ Web-based extensions page that enables users to browse and install additional so
<ExtensionsWrapper/> <ExtensionsWrapper/>
<script setup> <script setup>
import ExtensionsWrapper from '../.vitepress/theme/components/Extensions/ExtensionsWrapper.vue' import ExtensionsWrapper from '@theme/components/Extensions/ExtensionsWrapper.vue'
</script> </script>