mirror of
https://github.com/ReVanced/revanced-website.git
synced 2026-01-18 16:53:56 +00:00
Merge branch 'main' of https://github.com/revanced/revanced-website
This commit is contained in:
82
src/_vercel-moment.js
Normal file
82
src/_vercel-moment.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import fs from 'fs';
|
||||
|
||||
// This code was stolen from https://github.com/sveltejs/kit/blob/master/packages/adapter-static/platforms.js
|
||||
|
||||
// sveltekit does things like this instead of actually just using ts for some reason.
|
||||
/** @param {import('@sveltejs/kit').Builder} builder */
|
||||
function vercel_routes(builder) {
|
||||
/** @type {any[]} */
|
||||
const routes = [
|
||||
{
|
||||
src: `/${builder.config.kit.appDir}/immutable/.+`,
|
||||
headers: {
|
||||
'cache-control': 'public, immutable, max-age=31536000'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// explicit redirects
|
||||
for (const [src, redirect] of builder.prerendered.redirects) {
|
||||
routes.push({
|
||||
src,
|
||||
headers: {
|
||||
Location: redirect.location
|
||||
},
|
||||
status: redirect.status
|
||||
});
|
||||
}
|
||||
|
||||
// prerendered pages
|
||||
for (const [src, page] of builder.prerendered.pages) {
|
||||
routes.push({
|
||||
src,
|
||||
dest: `${builder.config.kit.appDir}/prerendered/${page.file}`
|
||||
});
|
||||
}
|
||||
|
||||
// implicit redirects (trailing slashes)
|
||||
for (const [src] of builder.prerendered.pages) {
|
||||
if (src !== '/') {
|
||||
routes.push({
|
||||
src: src.endsWith('/') ? src.slice(0, -1) : src + '/',
|
||||
headers: {
|
||||
location: src
|
||||
},
|
||||
status: 308
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
routes.push({
|
||||
handle: 'filesystem'
|
||||
});
|
||||
|
||||
return routes;
|
||||
}
|
||||
|
||||
export function wrap(adapter, opts) {
|
||||
if (!process.env.VERCEL) {
|
||||
// we don't have to bother :)
|
||||
return adapter(opts);
|
||||
}
|
||||
|
||||
// Not exactly what adapter-static does, but it works.
|
||||
opts.pages = '.vercel/output/static';
|
||||
|
||||
adapter = adapter(opts);
|
||||
|
||||
const adapt_fn = adapter.adapt;
|
||||
adapter.adapt = async (builder) => {
|
||||
const result = await adapt_fn(builder);
|
||||
fs.writeFileSync(
|
||||
'.vercel/output/config.json',
|
||||
JSON.stringify({
|
||||
version: 3,
|
||||
routes: vercel_routes(builder)
|
||||
})
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
return adapter;
|
||||
}
|
||||
29
src/lib/components/atoms/DocsNavNode.svelte
Normal file
29
src/lib/components/atoms/DocsNavNode.svelte
Normal file
@@ -0,0 +1,29 @@
|
||||
<script lang="ts">
|
||||
import type { DocumentInfo } from '$lib/documentation.shared';
|
||||
export let info: DocumentInfo;
|
||||
</script>
|
||||
|
||||
<!-- Always part of a list -->
|
||||
<li>
|
||||
<div class="doc-section">
|
||||
<a href="/docs/{info.slug}">{info.title}</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<style>
|
||||
a {
|
||||
text-decoration: none;
|
||||
background-color: inherit;
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.doc-section {
|
||||
background-color: var(--grey-one);
|
||||
border-radius: 12px;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
li {
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
39
src/lib/components/molecules/DocsNavTree.svelte
Normal file
39
src/lib/components/molecules/DocsNavTree.svelte
Normal file
@@ -0,0 +1,39 @@
|
||||
<script lang="ts">
|
||||
import { is_tree } from '$lib/documentation.shared';
|
||||
import type { DocsTree } from '$lib/documentation.shared';
|
||||
|
||||
import DocsNavNode from '$lib/components/atoms/DocsNavNode.svelte';
|
||||
|
||||
export let tree: DocsTree;
|
||||
// How deeply nested this is.
|
||||
export let nested = 0;
|
||||
</script>
|
||||
|
||||
{#if nested}
|
||||
<!-- The index should be part of the `ul` above us. -->
|
||||
<DocsNavNode info={tree.index} />
|
||||
{/if}
|
||||
|
||||
<ul>
|
||||
{#if !nested}
|
||||
<!-- There is no `ul` above us, so index should go here instead. -->
|
||||
<DocsNavNode info={tree.index} />
|
||||
{/if}
|
||||
|
||||
{#each tree.nodes as node}
|
||||
{#if is_tree(node)}
|
||||
<!-- Recursion here is fine. We are not dealing with a tree the size of a linux root file system. -->
|
||||
<svelte:self tree={node} nested={nested + 1} />
|
||||
{:else}
|
||||
<DocsNavNode info={node} />
|
||||
{/if}
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<style>
|
||||
ul {
|
||||
padding-left: 2rem;
|
||||
list-style-type: "• ";
|
||||
color: var(--white);
|
||||
}
|
||||
</style>
|
||||
@@ -28,17 +28,17 @@
|
||||
</a>
|
||||
<ul>
|
||||
<Navigation href="/">Home</Navigation>
|
||||
<Navigation href="/download/">Download</Navigation>
|
||||
<Navigation href="/docs/">Docs</Navigation>
|
||||
<Navigation href="/patches/">Patches</Navigation>
|
||||
<Navigation href="/download">Download</Navigation>
|
||||
<Navigation is_selected={target => target.startsWith("/docs")} href="/docs">Docs</Navigation>
|
||||
<Navigation href="/patches">Patches</Navigation>
|
||||
</ul>
|
||||
</div>
|
||||
<ul>
|
||||
<Navigation href="/contributors/">
|
||||
<img src="../icons/contrib.svg" alt="Contributors"/>
|
||||
<img src="/icons/contrib.svg" alt="Contributors"/>
|
||||
</Navigation>
|
||||
<Navigation href="/api-settings/">
|
||||
<img src="../icons/settings.svg" alt="Settings"/>
|
||||
<img src="/icons/settings.svg" alt="Settings"/>
|
||||
</Navigation>
|
||||
</ul>
|
||||
<div class="menu-btn" class:open={menuOpen} bind:this={menuBtn}>
|
||||
|
||||
45
src/lib/documentation.scss
Normal file
45
src/lib/documentation.scss
Normal file
@@ -0,0 +1,45 @@
|
||||
#markup-content {
|
||||
/* Defaults for text */
|
||||
color: var(--white);
|
||||
font-weight: 300;
|
||||
font-size: 1rem;
|
||||
letter-spacing: 0.02rem;
|
||||
padding: 100px 30px 0px 30px;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--white);
|
||||
border-bottom: 1.5px solid var(--accent-color);
|
||||
padding: 0px 5px;
|
||||
transition: all 0.4s var(--bezier-one);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-color: var(--accent-color);
|
||||
border-radius: 6px;
|
||||
color: var(--grey-four);
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: var(--grey-six);
|
||||
border-radius: 4px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
/* Markup processors output this for bold text, but css spec is goofy aah */
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
}
|
||||
202
src/lib/documentation.server.ts
Normal file
202
src/lib/documentation.server.ts
Normal file
@@ -0,0 +1,202 @@
|
||||
import { is_tree } from './documentation.shared';
|
||||
import type { Document, DocsTree, DocsTreeNode, DocumentInfo } from './documentation.shared';
|
||||
|
||||
import { browser, prerendering } from '$app/environment';
|
||||
|
||||
import fs, { existsSync as exists } from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
|
||||
import { parse as parse_md } from 'marked';
|
||||
import AsciiDocProcessor from 'asciidoctor'
|
||||
|
||||
// This file does not work in a browser.
|
||||
if (browser) {
|
||||
throw Error('SvelteKit has skill issues');
|
||||
}
|
||||
|
||||
/// Constants
|
||||
|
||||
const supported_formats: Map<string, (markup: string) => Document> = new Map();
|
||||
|
||||
supported_formats.set("md", markup => {
|
||||
let lines = markup.split('\n');
|
||||
|
||||
// Get and remove the first line.
|
||||
const first_line = lines.splice(0, 1)[0];
|
||||
// Remove `# `.
|
||||
const title = first_line.substring(2);
|
||||
|
||||
// Convert the rest to html
|
||||
const content = parse_md(lines.join('\n'));
|
||||
|
||||
return { title, content };
|
||||
});
|
||||
|
||||
const asciidoctor = AsciiDocProcessor();
|
||||
const adoc_fn = markup => {
|
||||
// Get first line.
|
||||
const first_line = markup.split('\n')[0];
|
||||
// Remove `= `.
|
||||
const title = first_line.substring(2);
|
||||
|
||||
// Convert it to html. Unlike markdown, we do not need to remove the first title heading.
|
||||
// NOTE: Maybe consider change the safe mode value.
|
||||
const content = asciidoctor.convert(markup, { doctype: "book" })
|
||||
|
||||
return { title, content };
|
||||
}
|
||||
|
||||
supported_formats.set("adoc", adoc_fn)
|
||||
supported_formats.set("asciidoc", adoc_fn)
|
||||
|
||||
const supported_filetypes = [...supported_formats.keys()];
|
||||
|
||||
let docs_folder = process.env.REVANCED_DOCS_FOLDER;
|
||||
if (docs_folder === undefined) {
|
||||
if (prerendering) { console.warn("Using testing docs in production build") }
|
||||
docs_folder = "testing-docs";
|
||||
}
|
||||
|
||||
const ignored_items = ["assets", "README.md"];
|
||||
|
||||
/// Utility functions
|
||||
|
||||
function is_directory(item: string) {
|
||||
return fs.lstatSync(item).isDirectory();
|
||||
}
|
||||
|
||||
function get_ext(fname: string) {
|
||||
// Get extname and remove the first dot.
|
||||
return path.extname(fname).substring(1);
|
||||
}
|
||||
|
||||
function get_slug_of_node(node: DocsTreeNode): string {
|
||||
if (is_tree(node)) {
|
||||
return node.index.slug;
|
||||
}
|
||||
|
||||
return node.slug;
|
||||
}
|
||||
|
||||
/// Important functions
|
||||
|
||||
// Get a document. Returns null if it does not exist.
|
||||
export function get(slug: string): Document|null {
|
||||
let target = path.join(docs_folder, slug);
|
||||
// Handle index file for folder.
|
||||
if (exists(target) && is_directory(target)) {
|
||||
target += "/index";
|
||||
}
|
||||
|
||||
const dir = path.dirname(target);
|
||||
if (!exists(dir)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let full_path, ext, found = false;
|
||||
// We are looking for the file `${target}.(any_supported_extension)`. Try to find it.
|
||||
for (const item of fs.readdirSync(dir)) {
|
||||
full_path = path.join(dir, item);
|
||||
// Get file extension
|
||||
ext = get_ext(item);
|
||||
|
||||
// Unsupported/unrelated file.
|
||||
if (!supported_formats.has(ext)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const desired_path = `${target}.${ext}`; // Don't grab some other random supported file.
|
||||
if (!is_directory(full_path) && desired_path == full_path) {
|
||||
// We found it.
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Process the file and return.
|
||||
return supported_formats.get(ext)(fs.readFileSync(full_path, 'utf-8'));
|
||||
}
|
||||
|
||||
// Get file information
|
||||
function process_file(fname: string): DocumentInfo {
|
||||
// Remove docs folder prefix and file extension suffix, then split it.
|
||||
const parts = fname
|
||||
.substring(`${docs_folder}/`.length, fname.length - (get_ext(fname).length + 1))
|
||||
.split("/");
|
||||
|
||||
// Remove `index` suffix if present.
|
||||
const last_index = parts.length - 1;
|
||||
if (parts[last_index] == "index") {
|
||||
parts.pop();
|
||||
}
|
||||
|
||||
const slug = parts.join("/");
|
||||
const title = get(slug).title;
|
||||
|
||||
return { slug, title };
|
||||
}
|
||||
|
||||
// Returns a document tree.
|
||||
function process_folder(dir: string): DocsTree {
|
||||
let tree: DocsTree = {
|
||||
index: null,
|
||||
nodes: []
|
||||
};
|
||||
|
||||
// List everything in the directory.
|
||||
const items = fs.readdirSync(dir);
|
||||
|
||||
for (const item of items) {
|
||||
if (ignored_items.includes(item) || [".", "_"].includes(item[0])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const itemPath = path.join(dir, item);
|
||||
|
||||
const is_dir = is_directory(itemPath);
|
||||
let is_index_file = false;
|
||||
|
||||
if (!is_dir) {
|
||||
// Ignore files we cannot process.
|
||||
if (!supported_formats.has(get_ext(item))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const ext of supported_filetypes) {
|
||||
if (item == `index.${ext}`) {
|
||||
is_index_file = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const node = is_dir ? process_folder(itemPath) : process_file(itemPath);
|
||||
|
||||
if (is_index_file) {
|
||||
tree.index = node;
|
||||
} else {
|
||||
tree.nodes.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
if (tree.index === null) {
|
||||
throw Error(`${dir} has no index file.`);
|
||||
}
|
||||
|
||||
// `numeric: true` because we want to be able to specify
|
||||
// the order if necessary by prepending a number to the file name.
|
||||
tree.nodes.sort(
|
||||
(a, b) => get_slug_of_node(a).localeCompare(get_slug_of_node(b), "en", { numeric: true })
|
||||
);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
// Returns the document tree.
|
||||
export function index_content(): DocsTree {
|
||||
return process_folder(docs_folder);
|
||||
}
|
||||
28
src/lib/documentation.shared.ts
Normal file
28
src/lib/documentation.shared.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/// Types
|
||||
|
||||
export interface Document {
|
||||
title: string;
|
||||
// HTML
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface DocumentInfo {
|
||||
title: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
// A tree representing the `docs` folder.
|
||||
export interface DocsTree {
|
||||
// index.whatever
|
||||
index: DocumentInfo;
|
||||
// Everything except index.whatever
|
||||
nodes: DocsTreeNode[];
|
||||
}
|
||||
|
||||
export type DocsTreeNode = DocsTree | DocumentInfo;
|
||||
|
||||
/// Functions
|
||||
|
||||
export function is_tree(node: DocsTreeNode) {
|
||||
return node.hasOwnProperty('nodes');
|
||||
}
|
||||
10
src/routes/docs/+layout.server.ts
Normal file
10
src/routes/docs/+layout.server.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
|
||||
import { index_content } from '$lib/documentation.server';
|
||||
|
||||
// The load function here used to get data from a json file created by a (prerendered) server route.
|
||||
// This was because we could not prerender the documentation route before.
|
||||
// If you can no longer prerender the docs, then you are going to have to move the load functions here to a prerendered server route like before and fetch them here.
|
||||
export const prerender = true;
|
||||
|
||||
export const load: PageServerLoad = () => ({ tree: index_content() });
|
||||
34
src/routes/docs/+layout.svelte
Normal file
34
src/routes/docs/+layout.svelte
Normal file
@@ -0,0 +1,34 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
|
||||
import DocsNavTree from '$lib/components/molecules/DocsNavTree.svelte';
|
||||
|
||||
import { fly } from 'svelte/transition';
|
||||
import { quintOut } from 'svelte/easing';
|
||||
|
||||
export let data: PageData;
|
||||
</script>
|
||||
|
||||
<section id="doc-section-main" in:fly={{ y: 10, easing: quintOut, duration: 700 }}>
|
||||
<div class="menu">
|
||||
<DocsNavTree tree={data.tree} />
|
||||
</div>
|
||||
<slot></slot>
|
||||
</section>
|
||||
|
||||
<style lang="scss">
|
||||
.menu {
|
||||
padding: 90px 15px 0px 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
#doc-section-main {
|
||||
display: grid;
|
||||
grid-template-columns: 300px 3fr;
|
||||
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,126 +0,0 @@
|
||||
<script>
|
||||
import { fly } from 'svelte/transition';
|
||||
import { quintOut } from 'svelte/easing';
|
||||
|
||||
import Button from '$lib/components/atoms/Button.svelte';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>ReVanced | Docs</title>
|
||||
<meta content="ReVanced | Docs" name="og:title" />
|
||||
<meta content="ReVanced | Docs" name="twitter:title" />
|
||||
</svelte:head>
|
||||
|
||||
<main in:fly={{ y: 10, easing: quintOut, duration: 700 }}>
|
||||
<div class="menu">
|
||||
<div class="doc-section-selected">
|
||||
<h3>Prerequisites</h3>
|
||||
</div>
|
||||
<div class="doc-section">
|
||||
<h3>Using ReVanced CLI and installiing ReVanced</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h6>docs/prerequisites</h6>
|
||||
<br />
|
||||
<h4>Requirements</h4>
|
||||
<h5>
|
||||
<ul>
|
||||
<li>ADB</li>
|
||||
<li>x86/x86_64 host architecture</li>
|
||||
<li>Zulu JDK 17</li>
|
||||
<li>Latest Android SDK if you plan to build the integrations from the source</li>
|
||||
<li>
|
||||
The APK file you want to patch (e.g. YouTube v17.36.37 or YouTube Music v5.23.50). If you
|
||||
want to mount patched applications as root, make sure the same version is installed on
|
||||
your device.
|
||||
</li>
|
||||
<li>
|
||||
You can continue by either building everything from source or downloading the prebuilt
|
||||
packages.
|
||||
</li>
|
||||
</ul>
|
||||
<br />
|
||||
You can continue by either
|
||||
<a href="https://github.com/revanced/revanced-documentation/wiki/Building-from-source"
|
||||
>building everything from source</a
|
||||
>
|
||||
or
|
||||
<a
|
||||
href="https://github.com/revanced/revanced-documentation/wiki/Downloading-prebuilt-packages"
|
||||
>downloading the prebuilt packages</a
|
||||
>.
|
||||
</h5>
|
||||
<br />
|
||||
<br />
|
||||
<div class="button-wrapper">
|
||||
Using ReVanced CLI and installing ReVanced (Docs will be overhauled anyways)
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
main {
|
||||
display: grid;
|
||||
grid-template-columns: 300px 3fr;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--white);
|
||||
border-bottom: 1.5px solid var(--accent-color);
|
||||
padding: 0px 5px;
|
||||
transition: all 0.4s var(--bezier-one);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-color: var(--accent-color);
|
||||
border-radius: 6px;
|
||||
color: var(--grey-four);
|
||||
}
|
||||
|
||||
.menu,
|
||||
.content {
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.menu {
|
||||
padding: 90px 15px 0px 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 100px 30px 0px 30px;
|
||||
}
|
||||
|
||||
.doc-section {
|
||||
background-color: var(--grey-one);
|
||||
border-radius: 12px;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.doc-section-selected {
|
||||
background-color: var(--grey-three);
|
||||
border-radius: 12px;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.doc-section-selected > h3 {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.button-wrapper {
|
||||
width: 30rem;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
17
src/routes/docs/[...slug]/+page.server.ts
Normal file
17
src/routes/docs/[...slug]/+page.server.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
import { get } from '$lib/documentation.server';
|
||||
|
||||
// See also: ../+layout.server.ts
|
||||
export const prerender = true;
|
||||
|
||||
export const load: PageServerLoad = ({ params }) => {
|
||||
const document = get(params.slug);
|
||||
if (document === null) {
|
||||
error
|
||||
throw error(404);
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
22
src/routes/docs/[...slug]/+page.svelte
Normal file
22
src/routes/docs/[...slug]/+page.svelte
Normal file
@@ -0,0 +1,22 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
|
||||
import '$lib/documentation.scss';
|
||||
|
||||
|
||||
// Data here comes from a trusted source.
|
||||
// CSS comes from the layout.
|
||||
export let data: PageData;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>ReVanced | Docs</title>
|
||||
<meta content="ReVanced | Docs" name="og:title" />
|
||||
<meta content="ReVanced | Docs" name="twitter:title" />
|
||||
</svelte:head>
|
||||
|
||||
<div id="markup-content">
|
||||
<h1 class="title">{data.title}</h1>
|
||||
|
||||
{@html data.content}
|
||||
</div>
|
||||
Reference in New Issue
Block a user