Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add OpenDyslexic font and pave way to support other fonts #386

Merged
merged 6 commits into from
Aug 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions apps/server/src/routers/api/v1/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ use stump_core::{
config::StumpConfig,
db::{
entity::{
AgeRestriction, Arrangement, LoginActivity, NavigationItem, User,
UserPermission, UserPreferences,
AgeRestriction, Arrangement, LoginActivity, NavigationItem, SupportedFont,
User, UserPermission, UserPreferences,
},
query::pagination::{Pageable, Pagination, PaginationQuery},
},
Expand Down Expand Up @@ -382,6 +382,7 @@ async fn update_preferences(
input.preferred_layout_mode.to_owned(),
),
user_preferences::app_theme::set(input.app_theme.to_owned()),
user_preferences::app_font::set(input.app_font.to_string()),
user_preferences::primary_navigation_mode::set(
input.primary_navigation_mode.to_owned(),
),
Expand Down Expand Up @@ -564,6 +565,7 @@ pub struct UpdateUserPreferences {
pub primary_navigation_mode: String,
pub layout_max_width_px: Option<i32>,
pub app_theme: String,
pub app_font: SupportedFont,
pub show_query_indicator: bool,
pub enable_live_refetch: bool,
pub enable_discord_presence: bool,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_user_preferences" (
"id" TEXT NOT NULL PRIMARY KEY,
"preferred_layout_mode" TEXT NOT NULL DEFAULT 'GRID',
"locale" TEXT NOT NULL DEFAULT 'en',
"app_theme" TEXT NOT NULL DEFAULT 'LIGHT',
"app_font" TEXT NOT NULL DEFAULT 'inter',
"primary_navigation_mode" TEXT NOT NULL DEFAULT 'SIDEBAR',
"layout_max_width_px" INTEGER DEFAULT 1280,
"show_query_indicator" BOOLEAN NOT NULL DEFAULT false,
"enable_live_refetch" BOOLEAN NOT NULL DEFAULT false,
"enable_discord_presence" BOOLEAN NOT NULL DEFAULT false,
"enable_compact_display" BOOLEAN NOT NULL DEFAULT false,
"enable_double_sidebar" BOOLEAN NOT NULL DEFAULT true,
"enable_replace_primary_sidebar" BOOLEAN NOT NULL DEFAULT false,
"enable_hide_scrollbar" BOOLEAN NOT NULL DEFAULT false,
"prefer_accent_color" BOOLEAN NOT NULL DEFAULT true,
"show_thumbnails_in_headers" BOOLEAN NOT NULL DEFAULT false,
"navigation_arrangement" BLOB,
"home_arrangement" BLOB,
"user_id" TEXT
);
INSERT INTO "new_user_preferences" ("app_theme", "enable_compact_display", "enable_discord_presence", "enable_double_sidebar", "enable_hide_scrollbar", "enable_live_refetch", "enable_replace_primary_sidebar", "home_arrangement", "id", "layout_max_width_px", "locale", "navigation_arrangement", "prefer_accent_color", "preferred_layout_mode", "primary_navigation_mode", "show_query_indicator", "show_thumbnails_in_headers", "user_id") SELECT "app_theme", "enable_compact_display", "enable_discord_presence", "enable_double_sidebar", "enable_hide_scrollbar", "enable_live_refetch", "enable_replace_primary_sidebar", "home_arrangement", "id", "layout_max_width_px", "locale", "navigation_arrangement", "prefer_accent_color", "preferred_layout_mode", "primary_navigation_mode", "show_query_indicator", "show_thumbnails_in_headers", "user_id" FROM "user_preferences";
DROP TABLE "user_preferences";
ALTER TABLE "new_user_preferences" RENAME TO "user_preferences";
CREATE UNIQUE INDEX "user_preferences_user_id_key" ON "user_preferences"("user_id");
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;
11 changes: 6 additions & 5 deletions core/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ model MediaMetadata {
teams String?
// *** End of group ***

page_count Int?
page_count Int?
page_dimensions PageDimensions?

media Media? @relation(fields: [media_id], references: [id], onDelete: Cascade)
Expand All @@ -253,10 +253,10 @@ model MediaMetadata {
}

model PageDimensions {
id String @id @default(cuid())
dimensions String
metadata MediaMetadata @relation(fields: [metadata_id], references: [id], onDelete: Cascade)
metadata_id String @unique
id String @id @default(cuid())
dimensions String
metadata MediaMetadata @relation(fields: [metadata_id], references: [id], onDelete: Cascade)
metadata_id String @unique

@@map("page_dimensions")
}
Expand Down Expand Up @@ -700,6 +700,7 @@ model UserPreferences {
preferred_layout_mode String @default("GRID")
locale String @default("en")
app_theme String @default("LIGHT")
app_font String @default("inter")

primary_navigation_mode String @default("SIDEBAR")
layout_max_width_px Int? @default(1280)
Expand Down
37 changes: 37 additions & 0 deletions core/src/db/entity/user/preferences.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::fmt::Display;

use serde::{Deserialize, Serialize};
use specta::Type;
use utoipa::ToSchema;
Expand Down Expand Up @@ -123,6 +125,36 @@ impl<I> Arrangement<I> {
}
}

#[derive(Default, Debug, Clone, Serialize, Deserialize, Type, ToSchema)]
#[serde(rename_all = "lowercase")]
pub enum SupportedFont {
#[default]
Inter,
OpenDyslexic,
// TODO(383): Support custom fonts
// Custom(String),
}

impl Display for SupportedFont {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SupportedFont::Inter => write!(f, "inter"),
SupportedFont::OpenDyslexic => write!(f, "opendyslexic"),
}
}
}

impl From<String> for SupportedFont {
fn from(value: String) -> Self {
match value.to_lowercase().as_str() {
"opendyslexic" => SupportedFont::OpenDyslexic,
// Note: for now we just always default to Inter. This will be acceptable
// until we have custom font support.
_ => SupportedFont::Inter,
}
}
}

fn default_navigation_mode() -> String {
"SIDEBAR".to_string()
}
Expand All @@ -144,6 +176,9 @@ pub struct UserPreferences {
pub id: String,
pub locale: String,
pub app_theme: String,
#[serde(default)]
pub app_font: SupportedFont,
#[serde(default)]
pub show_query_indicator: bool,
#[serde(default)]
pub enable_live_refetch: bool,
Expand Down Expand Up @@ -186,6 +221,7 @@ impl Default for UserPreferences {
primary_navigation_mode: "SIDEBAR".to_string(),
layout_max_width_px: Some(1280),
app_theme: "LIGHT".to_string(),
app_font: SupportedFont::Inter,
show_query_indicator: false,
enable_live_refetch: false,
enable_discord_presence: false,
Expand Down Expand Up @@ -237,6 +273,7 @@ impl From<prisma::user_preferences::Data> for UserPreferences {
primary_navigation_mode: data.primary_navigation_mode,
layout_max_width_px: data.layout_max_width_px,
app_theme: data.app_theme,
app_font: data.app_font.into(),
show_query_indicator: data.show_query_indicator,
enable_live_refetch: data.enable_live_refetch,
enable_discord_presence: data.enable_discord_presence,
Expand Down
2 changes: 2 additions & 0 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,8 @@ mod tests {
file.write_all(format!("{}\n\n", ts_export::<UserPermission>()?).as_bytes())?;
file.write_all(format!("{}\n\n", ts_export::<AgeRestriction>()?).as_bytes())?;

file.write_all(format!("{}\n\n", ts_export::<SupportedFont>()?).as_bytes())?;

file.write_all(format!("{}\n\n", ts_export::<NavigationMode>()?).as_bytes())?;
file.write_all(format!("{}\n\n", ts_export::<HomeItem>()?).as_bytes())?;
file.write_all(
Expand Down
77 changes: 74 additions & 3 deletions docs/pages/guides/basics/thumbnails.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,78 @@ import { Callout } from 'nextra-theme-docs'

# Thumbnails

<Callout emoji="🚧">
The documentation for this functionality is not yet implemented. Check back soon for updated
information.
Stump uses thumbnails to display images of your media throughout the app. By default, Stump will pull the first image from a media file to use as a thumbnail. However, you can customize this behavior per-library by configuring the thumbnail generation settings

## Thumbnail Generation

The generation of thumbnails is controlled by the configuration options for a library. These options include:

- Explicitly sizing each thumbnail (height and width in pixels)
- Scaling both dimensions of each thumbnail by a factor (e.g. 0.5)
- The format to encode the thumbnail in (e.g. JPEG, PNG, WebP, AVIF)
- The quality of the encoding (e.g. 0-1.0)

Thumbnail generation is optional, and you can disable it entirely if you prefer to use the default image as a thumbnail.

<Callout emoji="🛜" type="info">
The advantage of configuring thumbnail generation is that it allows you to minimize the size of
thumbnails being sent to the client, which greatly improves performance. Otherwise, the full-size
image would be sent to the client, which can be very large and slow to load.
</Callout>

### Image Candidacy

When generating thumbnails, Stump will use the very first image it encounters in the media file as the thumbnail.

## Supported Formats

### Generation

Stump supports generating thumbnails to and from the following formats:

- JPEG
- PNG
- WebP
- AVIF

You should ensure that your browser supports the format you plan to use. You can visit the following links to check:

- [WebP](https://caniuse.com/webp)
- [AVIF](https://caniuse.com/avif)

### Display

Stump supports the following formats for displaying thumbnails, in addition to the above:

- [GIF](https://caniuse.com/gif)

The reason for this separation is that while Stump supports rendering these on the UI, there is no process to convert them.

## Management

Thumbnail management can be done in either the settings page associated with a particular entity or in the settings page for the library as a whole. For example:

- `/books/:bookId/manage`
- `/series/:seriesId/settings`
- `/libraries/:libraryId/settings`

### Thumbnail Selection

You have the option to manually select an image as a thumbnail. This is useful when either the automatic selection is incorrect or when you want to use a special image. The selection is done on a media file, so if you are selecting for a:

- `Library`: You will select a particular series, then a particular book, then a particular page
- `Series`: You will select a particular book, then a particular page
- `Book`: You will select a particular page

You may also upload a custom image to use as a thumbnail.

### Regeneration

You have the ability to regenerate thumbnails in a given library's settings page. The options are:

- Generate only missing thumbnails
- Force regenerate all thumbnails

### Deletion

You can delete a thumbnail in a given library's settings page. This will remove all thumbnails associated with the library and its entities.
69 changes: 67 additions & 2 deletions docs/pages/guides/configuration/theming.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Theming

## Color palettes

Stump supports code-based theming. This means that you can create your own themes by writing a few lines of code. This is done by creating a new, exported object that defines each required piece of the palette.

For example, the default dark theme for Stump can be found [here](https://github.com/stumpapp/stump/tree/develop/packages/components/themes/dark.ts). For brevity, the entire theme won't be shown here, but let's say we wanted to make a new theme called "midnight" that is based on the dark theme, but with a darker background:
Expand Down Expand Up @@ -51,6 +53,69 @@ Then we should add it to the main [`ThemeSelect` component](https://github.com/s

Note that the `label` is a translation key, so we should add it to the [`locales/en.json` file](https://github.com/stumpapp/stump/tree/develop/packages/browser/src/i18n/locales/en.json), as well.

## Future theming features
## Fonts

Stump has a limited number of fonts built-in:

- [Inter](https://rsms.me/inter/)
- [OpenDyslexic](https://opendyslexic.org/)

If you would like a new font, it will have to be added to the project manually. This can be done by appropriately defining the font in CSS, such as:

```css
@font-face {
font-family: 'MyFont';
src: url('path/to/font.woff2') format('woff2');
}
```

You can see how this is done in the [fonts](https://github.com/stumpapp/stump/tree/main/packages/components/src/font) directory of the `components` package.

Once the font is defined, it can be added to the `fontFamily` object in the `tailwind.js` file:

```js
// tailwind.js
module.exports = {
// ... stuff here ...
theme: {
fontFamily: {
// ... other fonts
'font-my-font': ['MyFont', 'sans'], // <- the `font-` prefix is important!
},
},
// ... other configurations
}
```

Then, the font can be used in the `FontSelect` component:

```tsx
// scenes/settings/app/appearance/FontSelect.tsx
<NativeSelect
value={app_font || 'inter'}
options={[
{ label: 'Inter', value: 'inter' },
{ label: 'OpenDyslexic', value: 'opendyslexic' },
{ label: 'MyFont', value: 'my-font' }, // <- our new font
]}
onChange={(e) => changeFont(e.target.value)}
/>
```

This will allow users to select the new font from the settings page. In the future, it is planned to allow for the mapping of local fonts so that users can use their own fonts without needing to modify the source code. See the [Future plans](#future-plans) section for more information.

### Restrictions

There are some restrictions for which fonts can be added:

- The font must be free to use and distribute (e.g. under the [SIL Open Font License](https://opensource.org/licenses/OFL-1.1))\*
- The font must be in a format that can be used on the web (e.g. WOFF2)
- The font must not add a significant amount of weight to the app (e.g. 1MB+)

\* This is to ensure that the project remains in compliance with open-source standards and guidelines, and free to use for everyone. Stump is licensed under [MIT](https://opensource.org/licenses/MIT), so the font must be compatible with this license

## Future plans

In the future, it is planned to support arbitrary, local CSS files for overriding Stump's default theme(s). This will allow for the same level of customization but without the need for a full release cycle or the need to share your coveted theme with the world.

In the future, it is planned to support arbitrary, local CSS files for overriding Stump's default theme(s). This will allow for the same level of customization but without the need for a full release cycle.
You can track the progress of this feature [here](https://github.com/stumpapp/stump/issues/383).
36 changes: 27 additions & 9 deletions docs/pages/guides/opds.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,41 @@ Open Publication Distribution System ([OPDS](https://opds.io/)) is a specificati

So long as an OPDS client _properly_ implements the OPDS specification, you should be able to use it with Stump. The general structure of the URL to connect to your Stump server is:

`http(s)://your-server(:10801)(/baseUrl)/opds/v1.2/catalog`
`http(s)://your-server(:10801)(/baseUrl)/opds/(v1.2|v2.0)/catalog`

For example, if you are running Stump on your local machine, with the default port and base URL, the URL would be:

`http://localhost:10801/opds/v1.2/catalog`
`http://localhost:10801/opds/v1.2/catalog` or `http://localhost:10801/opds/v2.0/catalog`

Each OPDS client might have a different way of collecting this information during their onboarding steps. If you have any trouble using your preferred client, please open an issue on [GitHub](https://github.com/stumpapp/stump/issues/new/choose) or feel free to pop into the [Discord](https://discord.gg/63Ybb7J3as) for help. This page can be updated with your findings to help others in the future.

## Tested OPDS clients
## Supported OPDS versions

Stump fully supports OPDS 1.2, and has experimental support for 2.0. At the time of writing, there are few clients which actually support 2.0, so it is difficult to test. If you have any experiences with OPDS 2.0, please consider updating this page with your findings!

## OPDS 1.2

> **Note:** The ✨ emoji indicates a client which the developer of Stump personally uses

### Tested Clients

The following clients have been tested with Stump:

| OS | Application | Page Streaming | Issues/Notes |
| ------- | :------------------------------------------------------------------------------------: | -------------: | ----------------------: |
| iOS | [Panels](https://panels.app/) | ✅ | |
| iOS | [Chunky Reader](https://apps.apple.com/us/app/chunky-comic-reader/id663567628) | ✅ | |
| Android | [Moon+ Reader](https://play.google.com/store/apps/details?id=com.flyersoft.moonreader) | ❌ | No testing at this time |
| Android | [KyBook 3](http://kybook-reader.com/) | ❌ | No testing at this time |
| OS | Application | Page Streaming | Issues/Notes |
| ------- | :------------------------------------------------------------------------------------: | -------------: | -------------------------: |
| iOS | [Panels](https://panels.app/) | ✅ | |
| iOS | [Chunky Reader](https://apps.apple.com/us/app/chunky-comic-reader/id663567628) | ✅ | |
| Android | [Moon+ Reader](https://play.google.com/store/apps/details?id=com.flyersoft.moonreader) | ❌ | Users report OK experience |
| Android | [KyBook 3](http://kybook-reader.com/) | ❌ | No testing at this time |

If you have any experiences, good or bad, using any of these clients or another client not listed here, please consider updating this page with your findings.

## OPDS 2.0

### Tested Clients

The following clients have been tested with Stump:

| OS | Application | Page Streaming | Issues/Notes |
| --- | :-------------------------------------------------------------------------------: | -------------: | -----------------------------------------------------------------------------------------------------------------------------------: |
| iOS | [Cantook by Aldiko](https://apps.apple.com/us/app/cantook-by-aldiko/id1476410111) | ❓ | Supports catalog traversal and media downloads. Issues with rendering media covers, however this appears to be an issue on their end |
14 changes: 12 additions & 2 deletions docs/pages/installation/_meta.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
{
"getting-started": "Getting Started",
"docker": "Docker",
"executable": "Executable",
"docker": {
"title": "Docker",
"theme": {
"toc": false
}
},
"executable": {
"title": "Executable",
"theme": {
"toc": false
}
},
"source": "Source"
}
Loading
Loading