LOADING

加载过慢请开启缓存 浏览器默认开启

文章效果与字体编码测试

Markdown 基本语法

一级标题

二级标题

三级标题

四级标题

五级标题
六级标题

粗体 斜体 粗斜体 正常 删除线

单行块引用

多个段落的块引用

多个段落的块引用

嵌套块引用

嵌套块引用

带有其它元素的块引用

带有其它元素的块引用

带有其它元素的块引用

带有其它元素的块引用

  1. 有序列表
  2. 有序列表
  3. 有序列表
    1. 有序列表
    2. 有序列表
  4. 有序列表
  • 无序列表
  • 无序列表
  • 无序列表
    • 无序列表
    • 无序列表
  • 无序列表

单行代码块 with Both Chines and English Text

分割线


链接

链接(带有标题)

https://www.lynx3.top/

email@example.com

带格式化的链接

图片

内容类 HTML 嵌入

Markdown 扩展语法

表格

左对齐 居中对齐 右对齐
/ 叫做 斜线
| 叫做 管道符

代码框

编译语言

C++

来源:https://raw.githubusercontent.com/argvchs/fastio/master/fastio.hpp

#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <string>
#include <string_view>
#include <type_traits>
namespace fastio {
namespace symbols {
enum symbol {
    endl,
    ends,
    flush,
    bin,
    oct,
    dec,
    hex,
    left,
    right,
    boolalpha,
    noboolalpha,
    showbase,
    noshowbase,
    showpoint,
    noshowpoint,
    showpos,
    noshowpos,
    ws,
    uppercase,
    lowercase,
    fixed,
    defaultfloat,
    reset
};
struct setbase {
    int base;
    setbase(int n) : base(n) {}
};
struct setfill {
    char fill;
    setfill(char c) : fill(c) {}
};
struct setprecision {
    int precision;
    setprecision(int n) : precision(n) {}
};
struct setw {
    int width;
    setw(int n) : width(n) {}
};
} // namespace symbols
namespace interface {
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
template <typename T>
constexpr bool is_signed_v =
    (std::is_integral_v<T> && std::is_signed_v<T>) || std::is_same_v<T, i128>;
template <typename T>
constexpr bool is_unsigned_v =
    (std::is_integral_v<T> && std::is_unsigned_v<T>) || std::is_same_v<T, u128>;
template <typename T> constexpr bool is_integral_v = is_signed_v<T> || is_unsigned_v<T>;
template <typename T> constexpr bool is_floating_v = std::is_floating_point_v<T>;
template <typename T> struct make_unsigned : public std::make_unsigned<T> {};
template <> struct make_unsigned<i128> : public std::type_identity<u128> {};
template <> struct make_unsigned<u128> : public std::type_identity<u128> {};
template <typename T> using make_unsigned_t = typename make_unsigned<T>::type;
struct noncopyable {
    noncopyable() = default;
    virtual ~noncopyable() = default;
    noncopyable(const noncopyable &) = delete;
    noncopyable &operator=(const noncopyable &) = delete;
};
class istream : public noncopyable {
  private:
    int base = 10;
    bool unget = false, eof = false, fail = false;
    char chr = '\0';
    static bool isssign(char c) { return isspace(c) || c == '+' || c == '-'; }
    int todigit(char c) {
        if (::isdigit(c)) return c - '0';
        if (isupper(c)) return c - 'A' + 10;
        if (islower(c)) return c - 'a' + 10;
        return base;
    }
    bool isdigit(char c) { return todigit(c) < base; }
    virtual char vget() = 0;
  public:
    char get() {
        if (!unget)
            if ((chr = vget()) == EOF) eof = true;
        unget = false;
        return chr;
    }
    istream &get(char &c) {
        c = get();
        return *this;
    }
    explicit operator bool() { return !fail; }
    bool operator!() { return fail; }
    template <typename T, std::enable_if_t<is_integral_v<T>, int> = 0>
    istream &operator>>(T &n) {
        n = 0;
        bool f = false;
        while (isssign(get()) && !eof)
            if (chr == '-' && is_integral_v<T>) f = !f;
        if (eof) return fail = true, *this;
        unget = true;
        while (isdigit(get())) n = n * base + todigit(chr);
        if (f) n = -n;
        unget = true;
        return *this;
    }
    template <typename T, std::enable_if_t<is_floating_v<T>, int> = 0>
    istream &operator>>(T &n) {
        n = 0;
        bool f = false;
        while (isssign(get()) && !eof)
            if (chr == '-') f = !f;
        if (eof) return fail = true, *this;
        unget = true;
        while (isdigit(get())) n = n * base + todigit(chr);
        if (chr == '.') {
            i64 pow = 1;
            while (isdigit(get())) n += todigit(chr) / (T)(pow *= base);
        }
        if (f) n = -n;
        unget = true;
        return *this;
    }
    istream &operator>>(char &c) {
        c = '\0';
        while (isspace(get()) && !eof);
        if (eof) return fail = true, *this;
        c = chr;
        return *this;
    }
    istream &operator>>(bool &f) {
        i64 n;
        *this >> n;
        f = (bool)n;
        return *this;
    }
    istream &operator>>(char *s) {
        s[0] = '\0';
        int len = 0;
        while (isspace(get()) && !eof);
        if (eof) return fail = true, *this;
        unget = true;
        while (isgraph(get())) s[len++] = chr;
        unget = true, s[len] = '\0';
        return *this;
    }
    istream &operator>>(std::string &s) {
        s.clear();
        while (isspace(get()) && !eof);
        if (eof) return fail = true, *this;
        unget = true;
        while (isgraph(get())) s.push_back(chr);
        unget = true;
        return *this;
    }
    istream &operator>>(symbols::symbol a) {
        switch (a) {
        case symbols::bin: base = 2; break;
        case symbols::oct: base = 8; break;
        case symbols::dec: base = 10; break;
        case symbols::hex: base = 16; break;
        case symbols::ws:
            while (isspace(get()) && !eof);
            unget = true;
            break;
        default: base = 10;
        }
        return *this;
    }
    istream &operator>>(symbols::setbase a) {
        base = std::max(std::min(a.base, 36), 2);
        return *this;
    }
    istream &ignore(char end = '\n') {
        while (get() != end && !eof);
        if (eof) return fail = true, *this;
        return *this;
    }
    istream &getline(char *s, char end = '\n') {
        s[0] = '\0';
        int len = 0;
        if (eof) return fail = true, *this;
        while (get() != end && !eof) s[len++] = chr;
        if (s[len - 1] == '\r' && end == '\n') --len;
        s[len] = '\0';
        return *this;
    }
    istream &getline(std::string &s, char end = '\n') {
        s.clear();
        if (eof) return fail = true, *this;
        while (get() != end && !eof) s.push_back(chr);
        if (s.back() == '\r' && end == '\n') s.pop_back();
        return *this;
    }
    istream &get(char *s, char end = '\n') {
        getline(s, end);
        unget = true;
        return *this;
    }
    istream &get(std::string &s, char end = '\n') {
        getline(s, end);
        unget = true;
        return *this;
    }
};
class ostream : public noncopyable {
  private:
    int base = 10, precision = 6, width = 0;
    i64 eps = 1e6;
    bool adjust = true, boolalpha = false, showbase = false, showpoint = false,
         showpos = false, kase = false, fixed = false;
    char setfill = ' ';
    static i64 qpow(i64 n, int m) {
        i64 ret = 1;
        for (int i = m; i; i >>= 1, n *= n)
            if (i & 1) ret *= n;
        return ret;
    }
    void fill(int n) {
        if (width > n) vfill(setfill, width - n);
        width = 0;
    }
    char toalpha(int n) {
        if (n < 10) return n + '0';
        return n - 10 + (kase ? 'A' : 'a');
    }
    virtual void vput(char) = 0;
    virtual void vputs(const char *, int) = 0;
    virtual void vfill(char, int) = 0;
    virtual void vflush() = 0;
  public:
    void put(char c) { vput(c); }
    template <typename T, std::enable_if_t<is_integral_v<T>, int> = 0>
    ostream &operator<<(T n) {
        static char buf[105];
        char *p = buf + 100, *q = buf + 100;
        bool f = n < 0;
        if (f) n = -n;
        make_unsigned_t<T> m = n;
        if (!m) *p-- = '0';
        while (m) *p-- = toalpha(m % base), m /= base;
        if (showbase) switch (base) {
            case 2: *p-- = kase ? 'B' : 'b', *p-- = '0'; break;
            case 8: *p-- = '0'; break;
            case 16: *p-- = kase ? 'X' : 'x', *p-- = '0'; break;
            }
        if (!f) {
            if (showpos && is_signed_v<T>) *p-- = '+';
        } else *p-- = '-';
        if (adjust) fill(q - p);
        vputs(p + 1, q - p);
        if (!adjust) fill(q - p);
        return *this;
    }
    template <typename T, std::enable_if_t<is_floating_v<T>, int> = 0>
    ostream &operator<<(T n) {
        static char buf1[105], buf2[105];
        if (std::isinf(n)) {
            if (n > 0) {
                if (showpos) *this << (kase ? "+INF" : "+inf");
                else *this << (kase ? "INF" : "inf");
            } else *this << (kase ? "-INF" : "-inf");
            return *this;
        }
        if (std::isnan(n)) return *this << (kase ? "NAN" : "nan");
        char *p1 = buf1 + 100, *q1 = buf1 + 100, *p2 = buf2 + 100, *q2 = buf2 + 100;
        bool f = n < 0;
        if (f) n = -n;
        i64 m1 = std::floor(n), m2 = std::round((n - m1) * eps);
        int len = precision;
        if (m2 >= eps) ++m1, m2 = 0;
        if (!m1) *p1-- = '0';
        while (m1) *p1-- = toalpha(m1 % base), m1 /= base;
        while (len--) *p2-- = toalpha(m2 % base), m2 /= base;
        if (showbase) switch (base) {
            case 2: *p1-- = kase ? 'B' : 'b', *p1-- = '0'; break;
            case 8: *p1-- = '0'; break;
            case 16: *p1-- = kase ? 'X' : 'x', *p1-- = '0'; break;
            }
        if (!f) {
            if (showpos) *p1-- = '+';
        } else *p1-- = '-';
        if (!fixed)
            while (*q2 == '0' && p2 != q2) --q2;
        if (showpoint || p2 != q2) *p2-- = '.';
        if (adjust) fill((q1 - p1) + (q2 - p2));
        vputs(p1 + 1, q1 - p1);
        vputs(p2 + 1, q2 - p2);
        if (!adjust) fill((q1 - p1) + (q2 - p2));
        return *this;
    }
    ostream &operator<<(char c) {
        if (adjust) fill(1);
        vput(c);
        if (!adjust) fill(1);
        return *this;
    }
    ostream &operator<<(const char *s) {
        int n = strlen(s);
        if (adjust) fill(n);
        vputs(s, n);
        if (!adjust) fill(n);
        return *this;
    }
    ostream &operator<<(const std::string &s) {
        int n = s.size();
        if (adjust) fill(n);
        vputs(s.data(), n);
        if (!adjust) fill(n);
        return *this;
    }
    ostream &operator<<(std::string_view sv) {
        int n = sv.size();
        if (adjust) fill(n);
        vputs(sv.data(), n);
        if (!adjust) fill(n);
        return *this;
    }
    ostream &operator<<(bool f) {
        if (f) {
            if (boolalpha) *this << (kase ? "TRUE" : "true");
            else *this << '1';
        } else {
            if (boolalpha) *this << (kase ? "FALSE" : "false");
            else *this << '0';
        }
        return *this;
    }
    ostream &operator<<(const void *p) {
        int n = base;
        bool f = showbase;
        base = 16, showbase = true;
        *this << (u64)p;
        base = n, showbase = f;
        return *this;
    }
    ostream &operator<<(std::nullptr_t) {
        return *this << (kase ? "NULLPTR" : "nullptr");
    }
    ostream &operator<<(symbols::symbol a) {
        switch (a) {
        case symbols::endl: vput('\n'); break;
        case symbols::ends: vput(' '); break;
        case symbols::flush: vflush(); break;
        case symbols::bin: eps = qpow(base = 2, precision); break;
        case symbols::oct: eps = qpow(base = 8, precision); break;
        case symbols::dec: eps = qpow(base = 10, precision); break;
        case symbols::hex: eps = qpow(base = 16, precision); break;
        case symbols::left: adjust = false; break;
        case symbols::right: adjust = true; break;
        case symbols::boolalpha: boolalpha = true; break;
        case symbols::noboolalpha: boolalpha = false; break;
        case symbols::showbase: showbase = true; break;
        case symbols::noshowbase: showbase = false; break;
        case symbols::showpoint: showpoint = true; break;
        case symbols::noshowpoint: showpoint = false; break;
        case symbols::showpos: showpos = true; break;
        case symbols::noshowpos: showpos = false; break;
        case symbols::uppercase: kase = true; break;
        case symbols::lowercase: kase = false; break;
        case symbols::fixed: fixed = true; break;
        case symbols::defaultfloat: fixed = false; break;
        default:
            base = 10, precision = 6, width = 0, eps = 1e6;
            adjust = true;
            boolalpha = showbase = showpoint = showpos = kase = fixed = false;
            setfill = ' ';
        }
        return *this;
    }
    ostream &operator<<(symbols::setbase a) {
        base = std::max(std::min(a.base, 36), 2);
        eps = qpow(base, precision);
        return *this;
    }
    ostream &operator<<(symbols::setfill a) {
        setfill = a.fill;
        return *this;
    }
    ostream &operator<<(symbols::setprecision a) {
        precision = std::max(a.precision, 0);
        eps = qpow(base, precision);
        return *this;
    }
    ostream &operator<<(symbols::setw a) {
        width = std::max(a.width, 0);
        return *this;
    }
};
} // namespace interface
const int SIZ = 0xfffff;
class istream : public interface::istream {
  private:
    char buf[SIZ], *p = buf, *q = buf;
    virtual char vget() final {
        if (p == q) {
            int len = fread(buf, 1, SIZ, stream);
            if (!len) return EOF;
            p = buf, q = buf + len;
        }
        return *p++;
    }
  protected:
    FILE *stream = stdin;
  public:
    virtual ~istream() { fclose(stream); }
};
class ifstream : public istream {
  public:
    explicit ifstream(FILE *p) { istream::stream = p; }
    explicit ifstream(const char *s) { istream::stream = fopen(s, "r"); }
};
class ostream : public interface::ostream {
  private:
    char buf[SIZ], *p = buf;
    virtual void vput(char c) final {
        if (p - buf >= SIZ) vflush();
        *p++ = c;
    }
    virtual void vputs(const char *s, int n) final {
        int used = p - buf, len = 0;
        while (n - len + used >= SIZ) {
            memcpy(buf + used, s + len, SIZ - used);
            p = buf + SIZ;
            vflush();
            len += SIZ - used, used = 0;
        }
        memcpy(buf + used, s + len, n - len);
        p = buf + used + n - len;
    }
    virtual void vfill(char c, int n) final {
        int used = p - buf, len = 0;
        while (n - len + used >= SIZ) {
            memset(buf + used, c, SIZ - used);
            p = buf + SIZ;
            vflush();
            len += SIZ - used, used = 0;
        }
        memset(buf + used, c, n - len);
        p = buf + used + n - len;
    }
    virtual void vflush() final {
        fwrite(buf, 1, p - buf, stream);
        p = buf;
        fflush(stream);
    }
  protected:
    FILE *stream = stdout;
  public:
    virtual ~ostream() {
        vflush();
        fclose(stream);
    }
};
class ofstream : public ostream {
  public:
    explicit ofstream(FILE *p) { ostream::stream = p; }
    explicit ofstream(const char *s) { ostream::stream = fopen(s, "w"); }
};
static istream is;
static ostream os;
}; // namespace fastio

Java

来源:https://raw.githubusercontent.com/tiann/KernelSU/master/manager/app/src/main/java/me/weishu/kernelsu/ui/KsuService.java

package me.weishu.kernelsu.ui;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import androidx.annotation.NonNull;
import com.topjohnwu.superuser.ipc.RootService;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import me.weishu.kernelsu.IKsuInterface;
import rikka.parcelablelist.ParcelableListSlice;
/**
 * @author weishu
 * @date 2023/4/18.
 */
public class KsuService extends RootService {
    private static final String TAG = "KsuService";
    class Stub extends IKsuInterface.Stub {
        @Override
        public ParcelableListSlice<PackageInfo> getPackages(int flags) {
            List<PackageInfo> list = getInstalledPackagesAll(flags);
            Log.i(TAG, "getPackages: " + list.size());
            return new ParcelableListSlice<>(list);
        }
    }
    @Override
    public IBinder onBind(@NonNull Intent intent) {
        return new Stub();
    }
    List<Integer> getUserIds() {
        List<Integer> result = new ArrayList<>();
        UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
        List<UserHandle> userProfiles = um.getUserProfiles();
        for (UserHandle userProfile : userProfiles) {
            int userId = userProfile.hashCode();
            result.add(userProfile.hashCode());
        }
        return result;
    }
    ArrayList<PackageInfo> getInstalledPackagesAll(int flags) {
        ArrayList<PackageInfo> packages = new ArrayList<>();
        for (Integer userId : getUserIds()) {
            Log.i(TAG, "getInstalledPackagesAll: " + userId);
            packages.addAll(getInstalledPackagesAsUser(flags, userId));
        }
        return packages;
    }
    List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
        try {
            PackageManager pm = getPackageManager();
            Method getInstalledPackagesAsUser = pm.getClass().getDeclaredMethod("getInstalledPackagesAsUser", int.class, int.class);
            return (List<PackageInfo>) getInstalledPackagesAsUser.invoke(pm, flags, userId);
        } catch (Throwable e) {
            Log.e(TAG, "err", e);
        }
        return new ArrayList<>();
    }
}

Kotlin

来源:https://raw.githubusercontent.com/tiann/KernelSU/master/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt

package me.weishu.kernelsu.ui.screen
import android.net.Uri
import android.os.Environment
import android.os.Parcelable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Save
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.key
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.component.KeyEventBlocker
import me.weishu.kernelsu.ui.util.LkmSelection
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
import me.weishu.kernelsu.ui.util.installBoot
import me.weishu.kernelsu.ui.util.flashModule
import me.weishu.kernelsu.ui.util.reboot
import me.weishu.kernelsu.ui.util.restoreBoot
import me.weishu.kernelsu.ui.util.uninstallPermanently
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
enum class FlashingStatus {
    FLASHING,
    SUCCESS,
    FAILED
}
/**
 * @author weishu
 * @date 2023/1/1.
 */
@OptIn(ExperimentalComposeUiApi::class)
@Composable
@Destination
fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
    var text by rememberSaveable { mutableStateOf("") }
    val logContent = rememberSaveable { StringBuilder() }
    var showFloatAction by rememberSaveable { mutableStateOf(false) }
    val snackBarHost = LocalSnackbarHost.current
    val scope = rememberCoroutineScope()
    val scrollState = rememberScrollState()
    var flashing by rememberSaveable {
        mutableStateOf(FlashingStatus.FLASHING)
    }
    LaunchedEffect(Unit) {
        if (text.isNotEmpty()) {
            return@LaunchedEffect
        }
        withContext(Dispatchers.IO) {
            flashIt(flashIt, onFinish = { showReboot, code ->
                if (code != 0) {
                    text += "Error: exit code = $code.\nPlease save and check the log.\n"
                }
                if (showReboot) {
                    text += "\n\n\n"
                    showFloatAction = true
                }
                flashing = if (code == 0) FlashingStatus.SUCCESS else FlashingStatus.FAILED
            }, onStdout = {
                text += "$it\n"
                logContent.append(it).append("\n")
            }, onStderr = {
                logContent.append(it).append("\n")
            });
        }
    }
    Scaffold(
        topBar = {
            TopBar(
                flashing,
                onBack = {
                    navigator.popBackStack()
                },
                onSave = {
                    scope.launch {
                        val format = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault())
                        val date = format.format(Date())
                        val file = File(
                            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
                            "KernelSU_install_log_${date}.log"
                        )
                        file.writeText(logContent.toString())
                        snackBarHost.showSnackbar("Log saved to ${file.absolutePath}")
                    }
                }
            )
        },
        floatingActionButton = {
            if (showFloatAction) {
                val reboot = stringResource(id = R.string.reboot)
                ExtendedFloatingActionButton(
                    onClick = {
                        scope.launch {
                            withContext(Dispatchers.IO) {
                                reboot()
                            }
                        }
                    },
                    icon = { Icon(Icons.Filled.Refresh, reboot) },
                    text = { Text(text = reboot) },
                )
            }
        }
    ) { innerPadding ->
        KeyEventBlocker {
            it.key == Key.VolumeDown || it.key == Key.VolumeUp
        }
        Column(
            modifier = Modifier
                .fillMaxSize(1f)
                .padding(innerPadding)
                .verticalScroll(scrollState),
        ) {
            LaunchedEffect(text) {
                scrollState.animateScrollTo(scrollState.maxValue)
            }
            Text(
                modifier = Modifier.padding(8.dp),
                text = text,
                fontSize = MaterialTheme.typography.bodySmall.fontSize,
                fontFamily = FontFamily.Monospace,
                lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
            )
        }
    }
}
@Parcelize
sealed class FlashIt : Parcelable {
    data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean) :
        FlashIt()
    data class FlashModule(val uri: Uri) : FlashIt()
    data object FlashRestore : FlashIt()
    data object FlashUninstall : FlashIt()
}
fun flashIt(
    flashIt: FlashIt, onFinish: (Boolean, Int) -> Unit,
    onStdout: (String) -> Unit,
    onStderr: (String) -> Unit
) {
    when (flashIt) {
        is FlashIt.FlashBoot -> installBoot(
            flashIt.boot,
            flashIt.lkm,
            flashIt.ota,
            onFinish,
            onStdout,
            onStderr
        )
        is FlashIt.FlashModule -> flashModule(flashIt.uri, onFinish, onStdout, onStderr)
        FlashIt.FlashRestore -> restoreBoot(onFinish, onStdout, onStderr)
        FlashIt.FlashUninstall -> uninstallPermanently(onFinish, onStdout, onStderr)
    }
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TopBar(status: FlashingStatus, onBack: () -> Unit = {}, onSave: () -> Unit = {}) {
    TopAppBar(
        title = {
            Text(
                stringResource(
                    when (status) {
                        FlashingStatus.FLASHING -> R.string.flashing
                        FlashingStatus.SUCCESS -> R.string.flash_success
                        FlashingStatus.FAILED -> R.string.flash_failed
                    }
                )
            )
        },
        navigationIcon = {
            IconButton(
                onClick = onBack
            ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
        },
        actions = {
            IconButton(onClick = onSave) {
                Icon(
                    imageVector = Icons.Filled.Save,
                    contentDescription = "Localized description"
                )
            }
        }
    )
}
@Preview
@Composable
fun InstallPreview() {
    InstallScreen(EmptyDestinationsNavigator)
}

解释语言

Python

来源:https://raw.githubusercontent.com/yt-dlp/yt-dlp/master/yt_dlp/downloader/dash.py

import time
import urllib.parse
from . import get_suitable_downloader
from .fragment import FragmentFD
from ..utils import update_url_query, urljoin

class DashSegmentsFD(FragmentFD):
    """
    Download segments in a DASH manifest. External downloaders can take over
    the fragment downloads by supporting the 'dash_frag_urls' protocol
    """
    FD_NAME = 'dashsegments'
    def real_download(self, filename, info_dict):
        if 'http_dash_segments_generator' in info_dict['protocol'].split('+'):
            real_downloader = None  # No external FD can support --live-from-start
        else:
            if info_dict.get('is_live'):
                self.report_error('Live DASH videos are not supported')
            real_downloader = get_suitable_downloader(
                info_dict, self.params, None, protocol='dash_frag_urls', to_stdout=(filename == '-'))
        real_start = time.time()
        requested_formats = [{**info_dict, **fmt} for fmt in info_dict.get('requested_formats', [])]
        args = []
        for fmt in requested_formats or [info_dict]:
            try:
                fragment_count = 1 if self.params.get('test') else len(fmt['fragments'])
            except TypeError:
                fragment_count = None
            ctx = {
                'filename': fmt.get('filepath') or filename,
                'live': 'is_from_start' if fmt.get('is_from_start') else fmt.get('is_live'),
                'total_frags': fragment_count,
            }
            if real_downloader:
                self._prepare_external_frag_download(ctx)
            else:
                self._prepare_and_start_frag_download(ctx, fmt)
            ctx['start'] = real_start
            extra_query = None
            extra_param_to_segment_url = info_dict.get('extra_param_to_segment_url')
            if extra_param_to_segment_url:
                extra_query = urllib.parse.parse_qs(extra_param_to_segment_url)
            fragments_to_download = self._get_fragments(fmt, ctx, extra_query)
            if real_downloader:
                self.to_screen(
                    f'[{self.FD_NAME}] Fragment downloads will be delegated to {real_downloader.get_basename()}')
                info_dict['fragments'] = list(fragments_to_download)
                fd = real_downloader(self.ydl, self.params)
                return fd.real_download(filename, info_dict)
            args.append([ctx, fragments_to_download, fmt])
        return self.download_and_append_fragments_multiple(*args, is_fatal=lambda idx: idx == 0)
    def _resolve_fragments(self, fragments, ctx):
        fragments = fragments(ctx) if callable(fragments) else fragments
        return [next(iter(fragments))] if self.params.get('test') else fragments
    def _get_fragments(self, fmt, ctx, extra_query):
        fragment_base_url = fmt.get('fragment_base_url')
        fragments = self._resolve_fragments(fmt['fragments'], ctx)
        frag_index = 0
        for i, fragment in enumerate(fragments):
            frag_index += 1
            if frag_index <= ctx['fragment_index']:
                continue
            fragment_url = fragment.get('url')
            if not fragment_url:
                assert fragment_base_url
                fragment_url = urljoin(fragment_base_url, fragment['path'])
            if extra_query:
                fragment_url = update_url_query(fragment_url, extra_query)
            yield {
                'frag_index': frag_index,
                'fragment_count': fragment.get('fragment_count'),
                'index': i,
                'url': fragment_url,
            }

Shell

来源:https://raw.githubusercontent.com/f-droid/fdroidclient/master/create_ota.sh

#!/bin/bash
#
# Script to prepare an update.zip containing F-Droid

set -e

PROG_DIR=$(dirname $(realpath $0))

TMP_DIR=$(mktemp -d -t fdroidclient.tmp.XXXXXXXX)
trap "rm -rf $TMP_DIR" EXIT

function error() {
    echo "*** ERROR: " $@
    usage
}

function usage() {
    cat << EOFU
Usage: $0 variant
where:
 - variant is one of: debug, release, or binary
EOFU
    exit 1
}

# Parse input
VARIANT="$1"
[[ -z "$VARIANT" ]] && error "Missing variant"

VERSIONCODE=$2

GPG="gpg --keyring $PROG_DIR/f-droid.org-signing-key.gpg --no-default-keyring --trust-model always"

GITVERSION=$(git describe --tags --always)

FDROID_APK=F-Droid.apk

# Collect files
mkdir -p $TMP_DIR/META-INF/com/google/android/
cp app/src/main/scripts/update-binary $TMP_DIR/META-INF/com/google/android/

if [ $VARIANT == "binary" ] ; then
    if [ -z $VERSIONCODE ]; then
        curl -L https://f-droid.org/$FDROID_APK > $TMP_DIR/$FDROID_APK
        curl -L https://f-droid.org/${FDROID_APK}.asc > $TMP_DIR/${FDROID_APK}.asc
    else
        GITVERSION=$VERSIONCODE
        DL_APK=org.fdroid.fdroid_${VERSIONCODE}.apk
        curl -L https://f-droid.org/repo/$DL_APK > $TMP_DIR/$FDROID_APK
        curl -L https://f-droid.org/repo/${DL_APK}.asc > $TMP_DIR/${FDROID_APK}.asc
    fi
    $GPG --verify $TMP_DIR/${FDROID_APK}.asc
    rm $TMP_DIR/${FDROID_APK}.asc
else
    cd $PROG_DIR
    ./gradlew assemble$(echo $VARIANT | tr 'dr' 'DR')
    OUT_DIR=$PROG_DIR/app/build/outputs/apk
    if [ $VARIANT == "debug" ]; then
        cp $OUT_DIR/app-${VARIANT}.apk \
           $TMP_DIR/$FDROID_APK
    elif [ -f $OUT_DIR/app-${VARIANT}-signed.apk ]; then
        cp $OUT_DIR/app-${VARIANT}-signed.apk \
           $TMP_DIR/$FDROID_APK
    else
        cp $OUT_DIR/app-${VARIANT}-unsigned.apk \
           $TMP_DIR/$FDROID_APK
    fi
fi

# Make zip
if [ $VARIANT == "binary" ] ; then
    ZIPBASE=F-DroidFromBinaries-${GITVERSION}
else
    ZIPBASE=F-Droid-${GITVERSION}
fi
if [ $VARIANT == "debug" ]; then
    ZIP=${ZIPBASE}-debug.zip
else
    ZIP=${ZIPBASE}.zip
fi
OUT_DIR=$PROG_DIR/app/build/distributions
mkdir -p $OUT_DIR
[ -f $OUT_DIR/$ZIP ] && rm -f $OUT_DIR/$ZIP
pushd $TMP_DIR
zip -r $OUT_DIR/$ZIP .
popd

JavaScript

来源:https://raw.githubusercontent.com/jerryc127/hexo-theme-butterfly/master/source/js/search/local-search.js

/**
 * Refer to hexo-generator-searchdb
 * https://github.com/next-theme/hexo-generator-searchdb/blob/main/dist/search.js
 * Modified by hexo-theme-butterfly
 */
class LocalSearch {
  constructor ({
    path = '',
    unescape = false,
    top_n_per_article = 1
  }) {
    this.path = path
    this.unescape = unescape
    this.top_n_per_article = top_n_per_article
    this.isfetched = false
    this.datas = null
  }
  getIndexByWord (words, text, caseSensitive = false) {
    const index = []
    const included = new Set()
    if (!caseSensitive) {
      text = text.toLowerCase()
    }
    words.forEach(word => {
      if (this.unescape) {
        const div = document.createElement('div')
        div.innerText = word
        word = div.innerHTML
      }
      const wordLen = word.length
      if (wordLen === 0) return
      let startPosition = 0
      let position = -1
      if (!caseSensitive) {
        word = word.toLowerCase()
      }
      while ((position = text.indexOf(word, startPosition)) > -1) {
        index.push({ position, word })
        included.add(word)
        startPosition = position + wordLen
      }
    })
    // Sort index by position of keyword
    index.sort((left, right) => {
      if (left.position !== right.position) {
        return left.position - right.position
      }
      return right.word.length - left.word.length
    })
    return [index, included]
  }
  // Merge hits into slices
  mergeIntoSlice (start, end, index) {
    let item = index[0]
    let { position, word } = item
    const hits = []
    const count = new Set()
    while (position + word.length <= end && index.length !== 0) {
      count.add(word)
      hits.push({
        position,
        length: word.length
      })
      const wordEnd = position + word.length
      // Move to next position of hit
      index.shift()
      while (index.length !== 0) {
        item = index[0]
        position = item.position
        word = item.word
        if (wordEnd > position) {
          index.shift()
        } else {
          break
        }
      }
    }
    return {
      hits,
      start,
      end,
      count: count.size
    }
  }
  // Highlight title and content
  highlightKeyword (val, slice) {
    let result = ''
    let index = slice.start
    for (const { position, length } of slice.hits) {
      result += val.substring(index, position)
      index = position + length
      result += `<mark class="search-keyword">${val.substr(position, length)}</mark>`
    }
    result += val.substring(index, slice.end)
    return result
  }
  getResultItems (keywords) {
    const resultItems = []
    this.datas.forEach(({ title, content, url }) => {
      // The number of different keywords included in the article.
      const [indexOfTitle, keysOfTitle] = this.getIndexByWord(keywords, title)
      const [indexOfContent, keysOfContent] = this.getIndexByWord(keywords, content)
      const includedCount = new Set([...keysOfTitle, ...keysOfContent]).size
      // Show search results
      const hitCount = indexOfTitle.length + indexOfContent.length
      if (hitCount === 0) return
      const slicesOfTitle = []
      if (indexOfTitle.length !== 0) {
        slicesOfTitle.push(this.mergeIntoSlice(0, title.length, indexOfTitle))
      }
      let slicesOfContent = []
      while (indexOfContent.length !== 0) {
        const item = indexOfContent[0]
        const { position } = item
        // Cut out 120 characters. The maxlength of .search-input is 80.
        const start = Math.max(0, position - 20)
        const end = Math.min(content.length, position + 100)
        slicesOfContent.push(this.mergeIntoSlice(start, end, indexOfContent))
      }
      // Sort slices in content by included keywords' count and hits' count
      slicesOfContent.sort((left, right) => {
        if (left.count !== right.count) {
          return right.count - left.count
        } else if (left.hits.length !== right.hits.length) {
          return right.hits.length - left.hits.length
        }
        return left.start - right.start
      })
      // Select top N slices in content
      const upperBound = parseInt(this.top_n_per_article, 10)
      if (upperBound >= 0) {
        slicesOfContent = slicesOfContent.slice(0, upperBound)
      }
      let resultItem = ''
      url = new URL(url, location.origin)
      url.searchParams.append('highlight', keywords.join(' '))
      if (slicesOfTitle.length !== 0) {
        resultItem += `<div class="local-search-hit-item"><a href="${url.href}"><span class="search-result-title">${this.highlightKeyword(title, slicesOfTitle[0])}</span>`
      } else {
        resultItem += `<div class="local-search-hit-item"><a href="${url.href}"><span class="search-result-title">${title}</span>`
      }
      slicesOfContent.forEach(slice => {
        resultItem += `<p class="search-result">${this.highlightKeyword(content, slice)}...</p></a>`
      })
      resultItem += '</div>'
      resultItems.push({
        item: resultItem,
        id: resultItems.length,
        hitCount,
        includedCount
      })
    })
    return resultItems
  }
  fetchData () {
    const isXml = !this.path.endsWith('json')
    fetch(this.path)
      .then(response => response.text())
      .then(res => {
        // Get the contents from search data
        this.isfetched = true
        this.datas = isXml
          ? [...new DOMParser().parseFromString(res, 'text/xml').querySelectorAll('entry')].map(element => ({
              title: element.querySelector('title').textContent,
              content: element.querySelector('content').textContent,
              url: element.querySelector('url').textContent
            }))
          : JSON.parse(res)
        // Only match articles with non-empty titles
        this.datas = this.datas.filter(data => data.title).map(data => {
          data.title = data.title.trim()
          data.content = data.content ? data.content.trim().replace(/<[^>]+>/g, '') : ''
          data.url = decodeURIComponent(data.url).replace(/\/{2,}/g, '/')
          return data
        })
        // Remove loading animation
        window.dispatchEvent(new Event('search:loaded'))
      })
  }
  // Highlight by wrapping node in mark elements with the given class name
  highlightText (node, slice, className) {
    const val = node.nodeValue
    let index = slice.start
    const children = []
    for (const { position, length } of slice.hits) {
      const text = document.createTextNode(val.substring(index, position))
      index = position + length
      const mark = document.createElement('mark')
      mark.className = className
      mark.appendChild(document.createTextNode(val.substr(position, length)))
      children.push(text, mark)
    }
    node.nodeValue = val.substring(index, slice.end)
    children.forEach(element => {
      node.parentNode.insertBefore(element, node)
    })
  }
  // Highlight the search words provided in the url in the text
  highlightSearchWords (body) {
    const params = new URL(location.href).searchParams.get('highlight')
    const keywords = params ? params.split(' ') : []
    if (!keywords.length || !body) return
    const walk = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, null)
    const allNodes = []
    while (walk.nextNode()) {
      if (!walk.currentNode.parentNode.matches('button, select, textarea, .mermaid')) allNodes.push(walk.currentNode)
    }
    allNodes.forEach(node => {
      const [indexOfNode] = this.getIndexByWord(keywords, node.nodeValue)
      if (!indexOfNode.length) return
      const slice = this.mergeIntoSlice(0, node.nodeValue.length, indexOfNode)
      this.highlightText(node, slice, 'search-keyword')
    })
  }
}
window.addEventListener('load', () => {
// Search
  const { path, top_n_per_article, unescape, languages } = GLOBAL_CONFIG.localSearch
  const localSearch = new LocalSearch({
    path,
    top_n_per_article,
    unescape
  })
  const input = document.querySelector('#local-search-input input')
  const statsItem = document.getElementById('local-search-stats-wrap')
  const $loadingStatus = document.getElementById('loading-status')
  const isXml = !path.endsWith('json')
  const inputEventFunction = () => {
    if (!localSearch.isfetched) return
    let searchText = input.value.trim().toLowerCase()
    isXml && (searchText = searchText.replace(/</g, '&lt;').replace(/>/g, '&gt;'))
    if (searchText !== '') $loadingStatus.innerHTML = '<i class="fas fa-spinner fa-pulse"></i>'
    const keywords = searchText.split(/[-\s]+/)
    const container = document.getElementById('local-search-results')
    let resultItems = []
    if (searchText.length > 0) {
    // Perform local searching
      resultItems = localSearch.getResultItems(keywords)
    }
    if (keywords.length === 1 && keywords[0] === '') {
      container.textContent = ''
      statsItem.textContent = ''
    } else if (resultItems.length === 0) {
      container.textContent = ''
      const statsDiv = document.createElement('div')
      statsDiv.className = 'search-result-stats'
      statsDiv.textContent = languages.hits_empty.replace(/\$\{query}/, searchText)
      statsItem.innerHTML = statsDiv.outerHTML
    } else {
      resultItems.sort((left, right) => {
        if (left.includedCount !== right.includedCount) {
          return right.includedCount - left.includedCount
        } else if (left.hitCount !== right.hitCount) {
          return right.hitCount - left.hitCount
        }
        return right.id - left.id
      })
      const stats = languages.hits_stats.replace(/\$\{hits}/, resultItems.length)
      container.innerHTML = `<div class="search-result-list">${resultItems.map(result => result.item).join('')}</div>`
      statsItem.innerHTML = `<hr><div class="search-result-stats">${stats}</div>`
      window.pjax && window.pjax.refresh(container)
    }
    $loadingStatus.textContent = ''
  }
  let loadFlag = false
  const $searchMask = document.getElementById('search-mask')
  const $searchDialog = document.querySelector('#local-search .search-dialog')
  // fix safari
  const fixSafariHeight = () => {
    if (window.innerWidth < 768) {
      $searchDialog.style.setProperty('--search-height', window.innerHeight + 'px')
    }
  }
  const openSearch = () => {
    const bodyStyle = document.body.style
    bodyStyle.width = '100%'
    bodyStyle.overflow = 'hidden'
    btf.animateIn($searchMask, 'to_show 0.5s')
    btf.animateIn($searchDialog, 'titleScale 0.5s')
    setTimeout(() => { input.focus() }, 300)
    if (!loadFlag) {
      !localSearch.isfetched && localSearch.fetchData()
      input.addEventListener('input', inputEventFunction)
      loadFlag = true
    }
    // shortcut: ESC
    document.addEventListener('keydown', function f (event) {
      if (event.code === 'Escape') {
        closeSearch()
        document.removeEventListener('keydown', f)
      }
    })
    fixSafariHeight()
    window.addEventListener('resize', fixSafariHeight)
  }
  const closeSearch = () => {
    const bodyStyle = document.body.style
    bodyStyle.width = ''
    bodyStyle.overflow = ''
    btf.animateOut($searchDialog, 'search_close .5s')
    btf.animateOut($searchMask, 'to_hide 0.5s')
    window.removeEventListener('resize', fixSafariHeight)
  }
  const searchClickFn = () => {
    btf.addEventListenerPjax(document.querySelector('#search-button > .search'), 'click', openSearch)
  }
  const searchFnOnce = () => {
    document.querySelector('#local-search .search-close-button').addEventListener('click', closeSearch)
    $searchMask.addEventListener('click', closeSearch)
    if (GLOBAL_CONFIG.localSearch.preload) {
      localSearch.fetchData()
    }
    localSearch.highlightSearchWords(document.getElementById('article-container'))
  }
  window.addEventListener('search:loaded', () => {
    const $loadDataItem = document.getElementById('loading-database')
    $loadDataItem.nextElementSibling.style.display = 'block'
    $loadDataItem.remove()
  })
  searchClickFn()
  searchFnOnce()
  // pjax
  window.addEventListener('pjax:complete', () => {
    !btf.isHidden($searchMask) && closeSearch()
    localSearch.highlightSearchWords(document.getElementById('article-container'))
    searchClickFn()
  })
})

数据与前端

XML

来源:https://raw.githubusercontent.com/f-droid/fdroidclient/master/app/lint.xml

<?xml version="1.0" encoding="UTF-8"?>
<lint>
    <!-- TODO bump our targetSdkVersion when we are ready for it -->
    <issue id="ExpiredTargetSdkVersion" severity="ignore" />
    <!-- TODO This should be handled as part of an overhaul of Bluetooth swap -->
    <issue id="MissingPermission" severity="">
        <ignore path="src/full/java/org/fdroid/fdroid/nearby/BluetoothManager.java" />
        <ignore path="src/full/java/org/fdroid/fdroid/nearby/SwapWorkflowActivity.java" />
    </issue>
    <!-- Our translations are crowd-sourced -->
    <issue id="MissingTranslation" severity="ignore" />
    <issue id="ExtraTranslation" severity="warning" />
    <!-- to make CI fail on errors until this is fixed
         https://github.com/rtyley/spongycastle/issues/7 -->
    <issue id="InvalidPackage" severity="warning" />
    <issue id="ImpliedQuantity" severity="error" />
    <issue id="DefaultLocale" severity="error" />
    <issue id="SimpleDateFormat" severity="error" />
    <issue id="NewApi" severity="error" />
    <issue id="InlinedApi" severity="error" />
    <!-- These are important to us, so promote from warning to error -->
    <issue id="UnusedResources" severity="error">
        <ignore path="src/main/res/drawable/category_**.png" />
        <ignore path="src/main/res/values/dimens.xml" />
        <ignore path="src/main/res/values/styles.xml" />
        <ignore path="src/full/res/values/styles.xml" />
        <!-- keep a single strings.xml for all build flavors -->
        <ignore path="src/main/res/values**/strings.xml" />
    </issue>
    <issue id="AppCompatMethod" severity="error" />
    <issue id="NestedScrolling" severity="error" />
    <issue id="Typos" severity="error" />
    <issue id="StringFormatCount" severity="error" />
    <issue id="UnsafeProtectedBroadcastReceiver" severity="error" />
    <issue id="GetInstance" severity="error" />
    <issue id="PackageManagerGetSignatures" severity="error" />
    <issue id="HardwareIds" severity="error" />
    <issue id="TrustAllX509TrustManager" severity="error">
        <!-- these come from included libraries -->
        <ignore path="org/apache/commons/net/ftp/FTPSTrustManager.class" />
        <ignore path="org/bouncycastle/est/jcajce/JcaJceUtils$1.class" />
        <ignore path="org/bouncycastle/est/jcajce/JcaJceUtils$2.class" />
        <ignore path="org/apache/commons/net/util/TrustManagerUtils$TrustManager.class" />
        <ignore path="\*/bcpkix-jdk15to18-1.71.jar" />
        <ignore path="\*/commons-net-3.6.jar" />
    </issue>
    <issue id="PluralsCandidate" severity="error" />
    <issue id="HardcodedText" severity="error" />
    <issue id="RtlCompat" severity="error" />
    <issue id="RtlEnabled" severity="error" />
    <!-- both the correct and deprecated locales need to be present for
         them to be recognized on all devices -->
    <issue id="LocaleFolder" severity="error">
        <ignore path="src/main/res/values-he" />
        <ignore path="src/main/res/values-id" />
    </issue>
    <!-- Weblate doesn't handle these yet: https://github.com/WeblateOrg/weblate/issues/7520 -->
    <issue id="MissingQuantity" severity="error">
        <ignore path="src/main/res/values-cs" />
        <ignore path="src/main/res/values-fr" />
        <ignore path="src/main/res/values-lt" />
        <ignore path="src/main/res/values-sk" />
    </issue>
    <issue id="SetWorldReadable" severity="error">
        <ignore path="src/main/java/org/fdroid/fdroid/installer/ApkFileProvider.java" />
    </issue>
    <issue id="ProtectedPermissions" severity="error">
        <ignore path="src/debug/AndroidManifest.xml" />
        <ignore path="src/full/AndroidManifest.xml" />
    </issue>
    <!-- these should be fixed, but it'll be a chunk of work -->
    <issue id="SetTextI18n" severity="error">
        <ignore path="src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java" />
        <ignore path="src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java" />
    </issue>
</lint>

Stylus

来源:https://raw.githubusercontent.com/jerryc127/hexo-theme-butterfly/master/source/css/_layout/post.styl

beautify()
  headStyle(fontsize)
    padding-left: unit(fontsize + 8, 'px')
    &:before
      font-size: unit(fontsize - 2, 'px')
    &:hover
      padding-left: unit(fontsize + 12, 'px')
  h1,
  h2,
  h3,
  h4,
  h5,
  h6
    transition: all .2s ease-out
    &:before
      position: absolute
      top: calc(50% - 7px)
      color: $title-prefix-icon-color
      content: $title-prefix-icon
      left: 0
      line-height: 1
      transition: all .2s ease-out
      @extend .fontawesomeIcon
    &:hover
      &:before
        color: $light-blue
  h1
    headStyle(20)
  h2
    headStyle(18)
  h3
    headStyle(16)
  h4
    headStyle(14)
  h5
    headStyle(12)
  h6
    headStyle(12)
  ol,
  ul
    p
      margin: 0 0 8px
  li
    &::marker
      color: $light-blue
      font-weight: 600
      font-size: 1.05em
    &:hover
      &::marker
        color: var(--pseudo-hover)
  ul > li
    list-style-type: circle
  
  hr
    @extend .custom-hr
#article-container
  word-wrap: break-word
  overflow-wrap: break-word
  if hexo-config('text_align_justify')
    text-align: justify
  a
    color: $theme-link-color
    &:hover
      text-decoration: underline
  img
    display: block
    margin: 0 auto 20px
    max-width: 100%
    transition: filter 375ms ease-in .2s
  p
    margin: 0 0 16px
  iframe
    margin: 0 0 20px
  kbd
    margin: 0 3px
    padding: 3px 5px
    border: 1px solid #b4b4b4
    border-radius: 3px
    background-color: #f8f8f8
    box-shadow: 0 1px 3px rgba(0, 0, 0, .25), 0 2px 1px 0 rgba(255, 255, 255, .6) inset
    color: #34495e
    white-space: nowrap
    font-weight: 600
    font-size: .9em
    font-family: Monaco, 'Ubuntu Mono', monospace
    line-height: 1em
  if hexo-config('anchor.click_to_scroll')
    h1,
    h2,
    h3,
    h4,
    h5,
    h6
      width: fit-content
      a:not(.headerlink)
        position relative
        z-index 10
      a.headerlink
        position: absolute
        top: 0
        right: 0
        left 0
        bottom: 0
        width 100%
        height: 100%
  ol,
  ul
    ol,
    ul
      padding-left: 20px
    li
      margin: 4px 0
    p
      margin: 0 0 8px
  > :last-child
    margin-bottom: 0 !important
  hr
    margin: 20px 0
  if hexo-config('beautify.enable')
    if hexo-config('beautify.field') == 'site'
      beautify()
    else if hexo-config('beautify.field') == 'post'
      &.post-content
        beautify()
#post
  .tag_share
    &:after
      display: block
      clear: both
      content: ''
    .post-meta
      &__tag-list
        display: inline-block
      &__tags
        display: inline-block
        margin: 8px 8px 8px 0
        padding: 0 12px
        width: fit-content
        border: 1px solid $light-blue
        border-radius: 12px
        color: $light-blue
        font-size: .85em
        transition: all .2s ease-in-out
        &:hover
          background: $light-blue
          color: var(--white)
    .post_share
      display: inline-block
      float: right
      margin: 8px 0 0
      width: fit-content
      .social-share
        font-size: .85em
        .social-share-icon
          margin: 0 4px
          width: w = 1.85em
          height: w
          font-size: 1.2em
          line-height: w
  .post-copyright
    position: relative
    margin: 40px 0 10px
    padding: 10px 16px
    border: 1px solid var(--light-grey)
    transition: box-shadow .3s ease-in-out
    &:before
      @extend .fontawesomeIcon
      position: absolute
      top: 2px
      right: 12px
      color: $theme-color
      content: '\f1f9'
      font-size: 1.3em
    &:hover
      box-shadow: 0 0 8px 0 rgba(232, 237, 250, .6), 0 2px 4px 0 rgba(232, 237, 250, .5)
    .post-copyright
      &-meta
        color: $light-blue
        font-weight: bold
        i
          margin-right: 3px
      &-info
        padding-left: 6px
        a
          text-decoration: underline
          word-break: break-word
          &:hover
            text-decoration: none
  .post-outdate-notice
    position: relative
    margin: 0 0 20px
    padding: .5em 1.2em
    border-radius: 3px
    background-color: $noticeOutdate-bg
    color: $noticeOutdate-color
    if hexo-config('noticeOutdate.style') == 'flat'
      padding: .5em 1em .5em 2.6em
      border-left: 5px solid $noticeOutdate-border
      &:before
        @extend .fontawesomeIcon
        position: absolute
        top: 50%
        left: .9em
        color: $noticeOutdate-border
        content: '\f071'
        transform: translateY(-50%)
  .ads-wrap
    margin: 40px 0

HTML

来源:https://raw.githubusercontent.com/h5bp/html5-boilerplate/main/src/index.html

<!doctype html>
<html class="no-js" lang="">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title></title>
  <link rel="stylesheet" href="css/style.css">
  <meta name="description" content="">
  <meta property="og:title" content="">
  <meta property="og:type" content="">
  <meta property="og:url" content="">
  <meta property="og:image" content="">
  <meta property="og:image:alt" content="">
  <link rel="icon" href="/favicon.ico" sizes="any">
  <link rel="icon" href="/icon.svg" type="image/svg+xml">
  <link rel="apple-touch-icon" href="icon.png">
  <link rel="manifest" href="site.webmanifest">
  <meta name="theme-color" content="#fafafa">
</head>
<body>
  <!-- Add your site or application content here -->
  <p>Hello world! This is HTML5 Boilerplate.</p>
  <script src="js/app.js"></script>
</body>
</html>

数学

单行公式

$F = G=m_\textsf {物} g = 1kg・9.8N / kg = 9.8N$

数学文字混杂

让我们尝试证明一个稍微复杂一些的等式:

$\sum_{k=0}^{n} \binom{n}{k} = 2^n$

我们可以使用二项式定理证明这个等式。二项式定理表述为:

$(a + b)^n = \sum_{k=0}^{n} \binom{n}{k} a^{n-k} b^k$

现在,令 $a = 1$, $b = 1$,然后代入:

$(1 + 1)^n = \sum_{k=0}^{n} \binom{n}{k} 1^{n-k} 1^k$

$2^n = \sum_{k=0}^{n} \binom{n}{k}$

这证明了 $\sum_{k = 0}^{n} \binom {n}{k} = 2^n$。

渲染器拓展语法测试

这是 被注释文本

上标示例

下标示例

脚注示例 [1]

提示 这是一个提示

注意 这是一个警告

警告 这是一个危险信号

成功 这是一个成功信号


  1. 脚注是指附在文章页面的最底端的,对某些东西加以说明的注文。 ↩︎