mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-13 09:19:56 -05:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d74790c3a0 | |||
| 0e93908bb2 | |||
| 074147f64f | |||
| f91b46cfa5 | |||
| 71ee4052f3 | |||
| 5e2114810e | |||
| b3fca70f27 | |||
| 68f25a2ea7 | |||
| 3d6b6430ed | |||
| 54307d4411 | |||
| f8f080eff3 | |||
| f41f8a9c22 | |||
| 8cef79b2b4 | |||
| 6c39ef538f | |||
| 081becdd60 | |||
| c891cb349c | |||
| 8cef74bb98 | |||
| bf071dcfc7 | |||
| b0efb183e8 | |||
| 745b6993de | |||
| bd79169f71 | |||
| 6fccf02614 | |||
| fa7cfdc4e6 | |||
| 9c614b38f8 | |||
| 30e50b5a1b | |||
| 8ef0a14363 | |||
| 4e2ad6cae7 | |||
| 9e56b1176c | |||
| d025d07e07 | |||
| f988641446 |
+18
-10
@@ -1,30 +1,34 @@
|
|||||||
# --- Build Artifacts ---
|
|
||||||
node_modules/
|
node_modules/
|
||||||
suwayomi-raw/
|
suwayomi-raw/
|
||||||
suwayomi-windows.zip
|
suwayomi-windows.zip
|
||||||
suwayomi.zip
|
|
||||||
dist/
|
dist/
|
||||||
dist-tauri/
|
dist-tauri/
|
||||||
target/
|
target/
|
||||||
bin/
|
bin/
|
||||||
out/
|
out/
|
||||||
|
notes/
|
||||||
|
|
||||||
|
|
||||||
# --- Nix ---
|
|
||||||
.direnv/
|
.direnv/
|
||||||
result
|
result
|
||||||
result-*
|
result-*
|
||||||
|
|
||||||
# --- Logs ---
|
|
||||||
*.log
|
*.log
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
|
||||||
.env
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
!.env.test
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
|
|
||||||
# --- IDEs & OS ---
|
.output
|
||||||
|
.vercel
|
||||||
|
.netlify
|
||||||
|
.wrangler
|
||||||
|
/.svelte-kit
|
||||||
|
/build
|
||||||
|
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
@@ -34,15 +38,19 @@ yarn-error.log*
|
|||||||
*.sln
|
*.sln
|
||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
# --- Tauri specific ---
|
|
||||||
src-tauri/target/
|
src-tauri/target/
|
||||||
src-tauri/binaries/
|
src-tauri/binaries/
|
||||||
src-tauri/gen/
|
src-tauri/gen/
|
||||||
|
|
||||||
# --- Flatpak build artifacts ---
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
vite.config.js.timestamp-*
|
||||||
|
vite.config.ts.timestamp-*
|
||||||
|
|
||||||
build-dir/
|
build-dir/
|
||||||
repo/
|
repo/
|
||||||
dist/
|
dist/
|
||||||
packaging/frontend-dist.tar.gz
|
packaging/frontend-dist.tar.gz
|
||||||
*.flatpak
|
*.flatpak
|
||||||
.flatpak-builder/\n# --- Staged sidecar binaries (platform-specific, never commit) ---\nsrc-tauri/binaries/
|
./flatpak-builder
|
||||||
|
|||||||
@@ -1,173 +1,42 @@
|
|||||||
<div align="center">
|
# sv
|
||||||
<img src="docs/banner.svg" width="100%" alt="Moku" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div align="center">
|
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
|
||||||
|
|
||||||
[](https://github.com/moku-project/Moku/releases/latest)
|
## Creating a project
|
||||||

|
|
||||||
[](https://github.com/moku-project/Moku)
|
|
||||||
[](https://discord.gg/x97hj8zR72)
|
|
||||||
|
|
||||||
</div>
|
If you're seeing this, you've probably already done this step. Congrats!
|
||||||
|
|
||||||
<br/>
|
```sh
|
||||||
|
# create a new project
|
||||||
Moku is a fast, minimal manga reader frontend for [Suwayomi-Server](https://github.com/Suwayomi/Suwayomi-Server). It wraps Suwayomi's GraphQL API in a lightweight Tauri app — no Electron overhead.
|
npx sv create my-app
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Screenshots
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<img src="docs/screenshots/Moku-Home.png" width="100%" alt="Home" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<img src="docs/screenshots/Moku-Search.png" width="49%" alt="Search" />
|
|
||||||
<img src="docs/screenshots/Moku-TagSearch.png" width="49%" alt="Tag Search" />
|
|
||||||
<img src="docs/screenshots/Moku-Settings.png" width="49%" alt="Settings" />
|
|
||||||
<img src="docs/screenshots/Moku-Preview.png" width="49%" alt="Preview" />
|
|
||||||
<img src="docs/screenshots/Moku-Downloads.png" width="49%" alt="Downloads" />
|
|
||||||
<img src="docs/screenshots/Moku-ReaderSettings.png" width="49%" alt="Reader Settings" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<a href="docs/screenshots" style="color: #a8c4a8;">View all screenshots →</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Library management** — organize manga into folders, track unread counts, filter by genre
|
|
||||||
- **Per-folder sorting & filtering** — each folder has its own independent sort (unread, A–Z, recently read, latest chapter, and more) and publication status filter (Ongoing, Completed, Hiatus, etc.)
|
|
||||||
- **Built-in reader** — single page, long strip, configurable fit modes, customizable keybinds
|
|
||||||
- **Markers** — pin color-coded notes to any page while reading; markers appear as dots on the progress bar and are browseable under Series Detail → Manage → Markers
|
|
||||||
- **Extension support** — install and manage Suwayomi extensions directly from the app
|
|
||||||
- **Download management** — queue and monitor chapter downloads with progress toasts
|
|
||||||
- **Automation** — pre-download titles automatically and optionally delete chapters after they're marked as read (accessible from Series Detail)
|
|
||||||
- **Discord Rich Presence** — shows the manga title, current chapter, and an elapsed timer in your Discord status; configurable in Settings → General
|
|
||||||
- **Auto-start server** — optionally launch Suwayomi in the background on startup
|
|
||||||
- **Multiple themes** — Dark, Light, Midnight, Warm, High Contrast, and more
|
|
||||||
- **Auto-updates** — in-app update checker with silent background notifications
|
|
||||||
- **Improved NSFW filtering** — expanded tag parser gives the Hide NSFW setting better coverage across sources
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
### Windows
|
|
||||||
|
|
||||||
**winget:**
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
winget install Moku.Moku
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> Thanks to [@frozenKelp](https://github.com/frozenKelp) for setting up and maintaining the winget package through v0.9.0.
|
To recreate this project with the same configuration:
|
||||||
|
|
||||||
Or download the `.exe` installer from the [releases page](https://github.com/moku-project/Moku/releases/latest). Suwayomi-Server and a JRE are bundled.
|
```sh
|
||||||
|
# recreate this project
|
||||||
### Linux (Flatpak, recommended)
|
pnpm dlx sv@0.15.3 create --template minimal --types ts --install pnpm .
|
||||||
|
|
||||||
Suwayomi-Server and a bundled JRE are included — no separate install needed.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
flatpak install io.github.moku_app.Moku
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Or download the latest `moku.flatpak` from the [releases page](https://github.com/moku-project/Moku/releases/latest) and install manually:
|
## Developing
|
||||||
|
|
||||||
```bash
|
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||||
flatpak install moku.flatpak
|
|
||||||
|
```sh
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# or start the server and open the app in a new browser tab
|
||||||
|
npm run dev -- --open
|
||||||
```
|
```
|
||||||
|
|
||||||
### Nix
|
## Building
|
||||||
|
|
||||||
```bash
|
To create a production version of your app:
|
||||||
nix run github:moku-project/Moku
|
|
||||||
|
```sh
|
||||||
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
Add to your flake:
|
You can preview the production build with `npm run preview`.
|
||||||
|
|
||||||
```nix
|
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
|
||||||
inputs.moku.url = "github:moku-project/Moku";
|
|
||||||
```
|
|
||||||
|
|
||||||
### macOS
|
|
||||||
|
|
||||||
Download the `.dmg` from the [releases page](https://github.com/moku-project/Moku/releases/latest).
|
|
||||||
|
|
||||||
> **Note:** Builds are ad-hoc signed. On first launch you may need to run:
|
|
||||||
> ```bash
|
|
||||||
> xattr -rd com.apple.quarantine /Applications/Moku.app
|
|
||||||
> ```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
If you're not using the bundled Flatpak or Windows installer, [Suwayomi-Server](https://github.com/Suwayomi/Suwayomi-Server) must be running separately. By default Moku connects to `http://127.0.0.1:4567`.
|
|
||||||
|
|
||||||
You can point Moku at any Suwayomi instance — local or remote — via **Settings → General → Server URL**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
**Prerequisites:** [Rust](https://rustup.rs), [Node.js](https://nodejs.org), [pnpm](https://pnpm.io), and [Tauri v2 prerequisites](https://tauri.app/start/prerequisites/).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/moku-project/Moku
|
|
||||||
cd Moku
|
|
||||||
pnpm install
|
|
||||||
pnpm tauri:dev
|
|
||||||
```
|
|
||||||
|
|
||||||
Or with Nix:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
nix develop
|
|
||||||
pnpm install
|
|
||||||
pnpm tauri:dev
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Stack
|
|
||||||
|
|
||||||
| | |
|
|
||||||
|---|---|
|
|
||||||
| [Tauri v2](https://tauri.app) | Native app shell |
|
|
||||||
| [Svelte 5](https://svelte.dev) + [TypeScript](https://www.typescriptlang.org) | UI |
|
|
||||||
| [Vite](https://vitejs.dev) | Frontend bundler |
|
|
||||||
| [Crane](https://github.com/ipetkov/crane) | Nix Rust builds |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Community
|
|
||||||
|
|
||||||
Questions, feedback, or just want to hang out — join the Discord.
|
|
||||||
|
|
||||||
[](https://discord.gg/x97hj8zR72)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Distributed under the [Apache 2.0 License](./LICENSE).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Disclaimer
|
|
||||||
|
|
||||||
Moku does not host or distribute any content. The developers have no affiliation with any content providers accessible through connected sources.
|
|
||||||
|
|||||||
@@ -2,6 +2,12 @@ export const GET_RECENTLY_UPDATED = `
|
|||||||
query GetRecentlyUpdated {
|
query GetRecentlyUpdated {
|
||||||
chapters(orderBy: FETCHED_AT, orderByType: DESC, first: 300) {
|
chapters(orderBy: FETCHED_AT, orderByType: DESC, first: 300) {
|
||||||
nodes {
|
nodes {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
chapterNumber
|
||||||
|
sourceOrder
|
||||||
|
isRead
|
||||||
|
lastPageRead
|
||||||
mangaId
|
mangaId
|
||||||
fetchedAt
|
fetchedAt
|
||||||
manga { id title thumbnailUrl inLibrary }
|
manga { id title thumbnailUrl inLibrary }
|
||||||
@@ -19,4 +25,4 @@ export const GET_CHAPTERS = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@@ -75,6 +75,9 @@ export const LIBRARY_UPDATE_STATUS = `
|
|||||||
manga { id title thumbnailUrl unreadCount }
|
manga { id title thumbnailUrl unreadCount }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
lastUpdateTimestamp {
|
||||||
|
timestamp
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
| `GET_CATEGORIES` | — | All categories with order/settings and their assigned manga (minimal fields) |
|
| `GET_CATEGORIES` | — | All categories with order/settings and their assigned manga (minimal fields) |
|
||||||
| `GET_DOWNLOADED_CHAPTERS_PAGES` | — | Page counts for all downloaded chapters — used for storage stats |
|
| `GET_DOWNLOADED_CHAPTERS_PAGES` | — | Page counts for all downloaded chapters — used for storage stats |
|
||||||
| `GET_DOWNLOADS_PATH` | — | `downloadsPath` and `localSourcePath` from settings |
|
| `GET_DOWNLOADS_PATH` | — | `downloadsPath` and `localSourcePath` from settings |
|
||||||
| `LIBRARY_UPDATE_STATUS` | — | Current library update job — `jobsInfo` progress and `mangaUpdates` list with new chapters |
|
| `LIBRARY_UPDATE_STATUS` | — | Current library update job (`jobsInfo`, `mangaUpdates`) plus `lastUpdateTimestamp` for server-side update timing |
|
||||||
| `GET_RESTORE_STATUS` | `id: String!` | Backup restore job status by job ID — `mangaProgress`, `state`, `totalManga` |
|
| `GET_RESTORE_STATUS` | `id: String!` | Backup restore job status by job ID — `mangaProgress`, `state`, `totalManga` |
|
||||||
| `VALIDATE_BACKUP` | `backup: Upload!` | Validate a backup file before restore — returns missing sources and trackers |
|
| `VALIDATE_BACKUP` | `backup: Upload!` | Validate a backup file before restore — returns missing sources and trackers |
|
||||||
| `MANGAS_BY_GENRE` | `filter: MangaFilterInput`, `first: Int`, `offset: Int` | Paginated manga filtered by genre, ordered by `IN_LIBRARY_AT DESC` |
|
| `MANGAS_BY_GENRE` | `filter: MangaFilterInput`, `first: Int`, `offset: Int` | Paginated manga filtered by genre, ordered by `IN_LIBRARY_AT DESC` |
|
||||||
@@ -147,6 +147,7 @@ export const CACHE_GROUPS = {
|
|||||||
|
|
||||||
export const CACHE_KEYS = {
|
export const CACHE_KEYS = {
|
||||||
LIBRARY: "library",
|
LIBRARY: "library",
|
||||||
|
RECENT_UPDATES: "recent_updates",
|
||||||
ALL_MANGA: "all_manga_unfiltered",
|
ALL_MANGA: "all_manga_unfiltered",
|
||||||
CATEGORIES: "categories",
|
CATEGORIES: "categories",
|
||||||
SEARCH: "search_all_manga",
|
SEARCH: "search_all_manga",
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { CircleNotch } from "phosphor-svelte";
|
||||||
|
import DownloadItem from "./DownloadItem.svelte";
|
||||||
|
import type { DownloadQueueItem } from "@types/index";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
queue: DownloadQueueItem[];
|
||||||
|
loading: boolean;
|
||||||
|
isRunning: boolean;
|
||||||
|
dequeueing: Set<number>;
|
||||||
|
selected: Set<number>;
|
||||||
|
onRemove: (chapterId: number) => void;
|
||||||
|
onRetry: (chapterId: number) => void;
|
||||||
|
onReorder: (chapterId: number, dir: "up" | "down") => void;
|
||||||
|
onReorderEdge: (chapterId: number, edge: "top" | "bottom") => void;
|
||||||
|
onSelect: (chapterId: number, e: MouseEvent) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
queue, loading, isRunning, dequeueing, selected,
|
||||||
|
onRemove, onRetry, onReorder, onReorderEdge, onSelect,
|
||||||
|
}: Props = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if loading}
|
||||||
|
<div class="list">
|
||||||
|
{#each Array(5) as _, i (i)}
|
||||||
|
<div class="sk-row">
|
||||||
|
<div class="sk-thumb skeleton"></div>
|
||||||
|
|
||||||
|
<div class="sk-info">
|
||||||
|
<div class="skeleton sk-title"></div>
|
||||||
|
<div class="skeleton sk-chapter"></div>
|
||||||
|
<div class="sk-progress-row">
|
||||||
|
<div class="skeleton sk-bar"></div>
|
||||||
|
<div class="skeleton sk-pages"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sk-right">
|
||||||
|
<div class="skeleton sk-state"></div>
|
||||||
|
<div class="sk-actions">
|
||||||
|
<div class="skeleton sk-btn"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{:else if queue.length === 0}
|
||||||
|
<div class="empty">Queue is empty.</div>
|
||||||
|
{:else}
|
||||||
|
<div class="list">
|
||||||
|
{#each queue as item, i (item.chapter.id)}
|
||||||
|
<DownloadItem
|
||||||
|
{item}
|
||||||
|
isActive={i === 0 && isRunning}
|
||||||
|
isRemoving={dequeueing.has(item.chapter.id)}
|
||||||
|
isSelected={selected.has(item.chapter.id)}
|
||||||
|
{onRemove}
|
||||||
|
{onRetry}
|
||||||
|
{onReorder}
|
||||||
|
{onReorderEdge}
|
||||||
|
{onSelect}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.list { display: flex; flex-direction: column; gap: var(--sp-2); }
|
||||||
|
.empty { display: flex; align-items: center; justify-content: center; height: 160px; color: var(--text-faint); font-family: var(--font-ui); font-size: var(--text-xs); letter-spacing: var(--tracking-wide); }
|
||||||
|
|
||||||
|
@keyframes shimmer { from { background-position: -200% 0 } to { background-position: 200% 0 } }
|
||||||
|
.skeleton {
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
color-mix(in srgb, var(--bg-overlay, var(--bg-elevated)) 90%, var(--text-primary) 6%) 20%,
|
||||||
|
color-mix(in srgb, var(--bg-elevated, var(--bg-overlay)) 76%, var(--text-primary) 16%) 50%,
|
||||||
|
color-mix(in srgb, var(--bg-overlay, var(--bg-elevated)) 90%, var(--text-primary) 6%) 80%
|
||||||
|
);
|
||||||
|
background-size: 220% 100%;
|
||||||
|
animation: shimmer 1.45s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sk-row { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3); background: var(--bg-raised); border: 1px solid var(--border-dim); border-radius: var(--radius-md); pointer-events: none; }
|
||||||
|
.sk-thumb { width: 36px; height: 54px; flex-shrink: 0; }
|
||||||
|
.sk-info { flex: 1; display: flex; flex-direction: column; gap: 5px; overflow: hidden; min-width: 0; }
|
||||||
|
.sk-title { height: 12px; width: clamp(120px, 55%, 280px); }
|
||||||
|
.sk-chapter { height: 10px; width: clamp(80px, 35%, 200px); }
|
||||||
|
.sk-progress-row { display: flex; align-items: center; gap: var(--sp-2); }
|
||||||
|
.sk-bar { flex: 1; height: 2px; }
|
||||||
|
.sk-pages { width: 28px; height: 9px; }
|
||||||
|
.sk-right { display: flex; flex-direction: column; align-items: flex-end; gap: var(--sp-1); flex-shrink: 0; }
|
||||||
|
.sk-state { width: 54px; height: 9px; }
|
||||||
|
.sk-actions { display: flex; gap: 2px; }
|
||||||
|
.sk-btn { width: 20px; height: 20px; border-radius: var(--radius-sm); }
|
||||||
|
</style>
|
||||||
+10
-1
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { MagnifyingGlass, ArrowsClockwise, Plus, GitBranch, ArrowCircleUp } from "phosphor-svelte";
|
import { MagnifyingGlass, ArrowsClockwise, Plus, GitBranch, ArrowCircleUp, CheckCircle, Rows, Globe } from "phosphor-svelte";
|
||||||
import { FILTERS, type Filter, type Panel } from "../lib/extensionHelpers";
|
import { FILTERS, type Filter, type Panel } from "../lib/extensionHelpers";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -40,6 +40,15 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#each FILTERS as f}
|
{#each FILTERS as f}
|
||||||
<button class="tab" class:active={filter === f.id} onclick={() => onFilter(f.id)}>
|
<button class="tab" class:active={filter === f.id} onclick={() => onFilter(f.id)}>
|
||||||
|
{#if f.id === "installed"}
|
||||||
|
<CheckCircle size={11} weight="bold" />
|
||||||
|
{:else if f.id === "available"}
|
||||||
|
<Globe size={11} weight="bold" />
|
||||||
|
{:else if f.id === "updates"}
|
||||||
|
<ArrowCircleUp size={11} weight="bold" />
|
||||||
|
{:else if f.id === "all"}
|
||||||
|
<Rows size={11} weight="bold" />
|
||||||
|
{/if}
|
||||||
{f.id === "updates" && updateCount > 0 ? `Updates (${updateCount})` : f.label}
|
{f.id === "updates" && updateCount > 0 ? `Updates (${updateCount})` : f.label}
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user