Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
feb6429
refactor nav as function component
yang991178 Dec 27, 2025
282dcad
add copilot instructions file
yang991178 Mar 18, 2026
c2a1279
refactor: convert Menu to functional component with hooks
yang991178 Mar 28, 2026
74f7cd6
refactor: convert Page to functional component with hooks
yang991178 Mar 28, 2026
12d777d
refactor: convert Settings to functional component with hooks
yang991178 Mar 28, 2026
a89b0fc
refactor: convert Feed to functional component
yang991178 Mar 29, 2026
c8c9ccf
update electron and package dependencies
yang991178 Mar 30, 2026
0fbd68a
refactor about tab to use functional component
yang991178 Apr 5, 2026
07bc48f
version 1.2.0
yang991178 Apr 6, 2026
e4ae0fb
add griffel + fluent-ui v9 and enable tree shaking
yang991178 Apr 13, 2026
e7a08b3
migrate .btn elements to custom components with griffel
yang991178 Apr 13, 2026
1dfd450
fix group context menu and blurred nav
yang991178 Apr 13, 2026
ccddff3
fix nav behavior for full screen transitions
yang991178 Apr 13, 2026
d0f8b2b
fix griffel build error on windows
yang991178 Apr 13, 2026
2f2d6a7
Update Mac App Store link to id6761698838
Copilot Apr 21, 2026
d272ac3
Use https://apps.apple.com/app/id6761698838 for Mac App Store link
Copilot Apr 21, 2026
a0e7923
Merge pull request #767 from yang991178/copilot/update-mac-app-store-…
yang991178 Apr 21, 2026
ababf20
fix default view setting and fetch button background
yang991178 Apr 18, 2026
de3cb7b
Create pl.json
Zwatotem Jul 30, 2025
d425a18
Add relevant handling in _locales.ts
Zwatotem Jul 30, 2025
ad2b58e
Update readme and attribute myself
Zwatotem Jul 30, 2025
3b85774
migrate menu to fui v9 tree + griffel
yang991178 Apr 19, 2026
c69f2d8
update scroll + progress bar style and fix focus state
yang991178 Apr 20, 2026
e5e6674
fix miniflux service implementation
yang991178 Apr 24, 2026
d5b11e5
apply v9 theme globally and improve menu/nav behavior
yang991178 Apr 25, 2026
1100ea7
add unread sources filter in menu
yang991178 Apr 26, 2026
cd331fb
version 1.2.2
yang991178 Apr 26, 2026
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
149 changes: 149 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Copilot Instructions — Fluent Reader

## Overview

Fluent Reader is a **modern desktop RSS reader** built with **Electron + React + Redux + TypeScript**. It targets Windows, macOS (including Mac App Store), and Linux. The UI uses Microsoft's **Fluent UI (v7)** component library. Data is stored client-side using **Lovefield** (SQL-like browser DB) and **NeDB**. Articles are parsed with **Mercury Parser** and fetched via **rss-parser**. Settings are persisted with **electron-store**.

The repository is ~80 TypeScript/TSX source files under `src/`. There is no test suite. There is no ESLint — formatting is handled solely by **Prettier**.

## Build & Validate

Always run commands from the repository root.

### Install dependencies

```bash
npm install
```

Run this **before every build**. The lockfile (`package-lock.json`) is gitignored (`.lock` in `.gitignore`), so `npm install` resolves from `package.json` each time.

### Build (compile TypeScript via Webpack)

```bash
npm run build
```

This runs `webpack --config ./webpack.config.js`, which produces three bundles in `dist/`:
- `electron.js` — Electron main process (from `src/electron.ts`)
- `preload.js` — Preload script (from `src/preload.ts`)
- `index.js` + `index.html` — Renderer/React app (from `src/index.tsx`)

Build takes ~30 seconds. A successful build ends with three "compiled successfully" lines — one per webpack config entry.

### Run the app

```bash
npm run electron
```

Or combined install + build + run:

```bash
npm run start
```

### Format check (Prettier)

```bash
npx prettier --check .
```

To auto-fix formatting:

```bash
npm run format
```

**Always run `npx prettier --check .` after making changes** to ensure code style compliance. The Prettier config is in `.prettierrc.yml`: 4-space tabs, no semicolons, JSX bracket on same line, arrow parens avoided, consistent quote props. `.prettierignore` excludes `dist/`, `bin/`, `node_modules/`, HTML, Markdown, and most JSON (except `src/**/*.json`).

### Tests

There is **no test suite** in this project. Validation consists of:
1. `npm run build` — must compile without errors.
2. `npx prettier --check .` — must pass with no formatting violations.

### Packaging (not needed for typical changes)

- Windows: `npm run package-win`
- macOS: `npm run package-mac`
- Linux: `npm run package-linux`
- Mac App Store: `npm run package-mas` (requires provisioning profile and entitlements in `build/`)

## CI/CD

Two GitHub Actions workflows in `.github/workflows/`:

- **`release-main.yml`** — Triggered on version tags (`v*`). Runs on `windows-latest`. Steps: `npm install` → `npm run build` → `npm run package-win-ci`. Uploads `.exe` and `.zip` to a draft GitHub release.
- **`release-linux.yml`** — Triggered when a release is published. Runs on `ubuntu-latest`. Steps: `npm install` → `npm run build` → `npm run package-linux`. Uploads `.AppImage`.

Both CI pipelines run `npm install` then `npm run build`. There are no lint or test steps in CI.

## Project Layout

### Root files
| File | Purpose |
|---|---|
| `package.json` | Dependencies, scripts, metadata (v1.1.4) |
| `webpack.config.js` | Three webpack configs: main, preload, renderer |
| `tsconfig.json` | TypeScript: JSX=react, target=ES2019, module=CommonJS, resolveJsonModule |
| `electron-builder.yml` | Electron Builder config for Win/Mac/Linux distribution |
| `electron-builder-mas.yml` | Electron Builder config for Mac App Store |
| `.prettierrc.yml` | Prettier formatting rules |
| `.prettierignore` | Files excluded from Prettier |

### `src/` — All source code

| Path | Description |
|---|---|
| `electron.ts` | **Electron main process** entry. Creates app menu, initializes `WindowManager`. |
| `preload.ts` | **Preload script**. Exposes `settingsBridge` and `utilsBridge` via `contextBridge`. |
| `index.tsx` | **Renderer entry**. Mounts React `<Root>` with Redux `<Provider>`. |
| `schema-types.ts` | Shared TypeScript enums and types (ViewType, ThemeSettings, etc.) |
| `bridges/` | IPC bridges between renderer and main process (`settings.ts`, `utils.ts`). |
| `main/` | Electron main-process modules: `window.ts` (BrowserWindow), `settings.ts` (electron-store + IPC handlers), `utils.ts` (IPC utilities), `touchbar.ts`, `update-scripts.ts`. |
| `scripts/` | Renderer-side logic (runs in browser context). |
| `scripts/reducer.ts` | Root Redux store — combines: sources, items, feeds, groups, page, service, app. |
| `scripts/settings.ts` | Theme management, locale setup, Fluent UI theming. |
| `scripts/db.ts` | Lovefield database schema definitions (sources, items). |
| `scripts/utils.ts` | Shared utilities and type helpers. |
| `scripts/models/` | Redux slices: `app.ts`, `feed.ts`, `group.ts`, `item.ts`, `page.ts`, `rule.ts`, `service.ts`, `source.ts`, plus `services/` for RSS service integrations. |
| `scripts/i18n/` | Internationalization. `_locales.ts` maps locale codes to JSON files. 19 languages. Translations are JSON files (e.g., `en-US.json`). Uses `react-intl-universal`. |
| `components/` | React UI components. `root.tsx` is the top-level layout. Sub-dirs: `cards/` (article card variants), `feeds/` (feed list views), `settings/` (settings panels), `utils/` (shared UI helpers). |
| `containers/` | Redux-connected container components that map state/dispatch to component props. |

### `dist/` — Build output + static assets

The `dist/` directory contains **both webpack output and checked-in static assets**. Files like `dist/icons/`, `dist/article/`, `dist/styles/`, `dist/index.css`, `dist/fonts.vbs`, and `dist/fontlist` are static and tracked in git. The webpack-generated files (`*.js`, `*.js.map`, `*.html`, `*.LICENSE.txt`) are gitignored.

### `build/` — Packaging resources

Contains app icons (`build/icons/`), macOS entitlements plists, provisioning profiles, and `resignAndPackage.sh` for Mac App Store builds. Also has `build/appx/` for Windows Store assets.

## Architecture Notes

- **IPC pattern**: The renderer never imports Electron directly. All Electron APIs are accessed through `src/bridges/` which are exposed via `contextBridge` in `preload.ts`. Settings state flows: renderer → bridge (ipcRenderer) → main/settings.ts (ipcMain handlers) → electron-store.
- **State management**: Redux with `redux-thunk` for async actions. The store shape is defined by `RootState` in `scripts/reducer.ts`. Each model file in `scripts/models/` exports its own reducer, action types, and thunk action creators.
- **i18n**: To add or modify translations, edit JSON files in `src/scripts/i18n/`. Register new locales in `_locales.ts`.
- **RSS service integrations**: Located in `src/scripts/models/services/` (logic) and `src/components/settings/services/` (UI). Supported: Fever, Google Reader API (GReader), Inoreader, Feedbin, Miniflux, Nextcloud.

## Key Conventions

- All source is TypeScript (`.ts`/`.tsx`). No plain JavaScript in `src/`.
- No semicolons. 4-space indentation. See `.prettierrc.yml`.
- Enums use `const enum` pattern in `schema-types.ts`.
- Components use both class components and function components (no strict rule).
- Redux containers in `containers/` use `connect()` from react-redux.
- **Griffel styling**: Use `makeStyles` from `@griffel/react` for component-scoped styles. Import `mergeClasses` for combining classes. Prefer Griffel over adding new CSS rules to `dist/styles/`.
- **`styleClass` prop convention**: Reusable components expose a `styleClass?: string` prop applied to the component's main element, allowing parents to pass Griffel-generated class overrides. If a component has multiple customizable elements, use element-specific prop names (e.g., `buttonStyleClass?: string`). Combine base and override classes with `mergeClasses`.
- **Flat button components**: `FlatButton`, `FlatButtonGroup`, and `FlatButtonSeparator` in `src/components/utils/` implement the Griffel + `styleClass` pattern. Use these instead of raw `<button>`/`<div>` with CSS class names for toolbar-style buttons.

## Validation Checklist

After any code change, always run:
```bash
npm install && npm run build && npx prettier --check .
```
All three must succeed. If the build fails, fix TypeScript errors. If prettier fails, run `npm run format` then verify.

Trust these instructions. Only search the codebase if information here is incomplete or found to be incorrect.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ dist/*.js
dist/*.js.map
dist/*.html
dist/*.LICENSE.txt
dist/griffel.css
bin/*
.DS_Store
*.provisionprofile
Expand Down
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
node_modules
dist/**/*.js
dist/**/*.js.map
dist/griffel.css
bin/*
.DS_Store
*.provisionprofile
Expand Down
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"chat.tools.terminal.autoApprove": {
"npx prettier": true
}
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

For Windows 10 users, the recommended way of installation is through [Microsoft Store](https://www.microsoft.com/store/apps/9P71FC94LRH8?cid=github).
This enables auto-update and experimental ARM64 support.
macOS users can also get Fluent Reader from the [Mac App Store](https://apps.apple.com/app/id1520907427).
macOS users can also get Fluent Reader from the [Mac App Store](https://apps.apple.com/app/id6761698838).

If you are using Linux or an older version of Windows, you can [get Fluent Reader from GitHub releases](https://github.com/yang991178/fluent-reader/releases).

Expand Down
9 changes: 9 additions & 0 deletions build/entitlements.mac.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key><true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key><true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key><true/>
</dict>
</plist>
2 changes: 1 addition & 1 deletion build/entitlements.mas.plist
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<key>com.apple.security.app-sandbox</key><true/>
<key>com.apple.security.application-groups</key>
<array>
<string>EM8VE646TZ.DevHYLiu.FluentReader</string>
<string>P2CG8QD3BP.me.hyliu.fluent-reader-mas</string>
</array>
<!-- Put any entitlements your app requires here. Below is a example -->
<key>com.apple.security.network.client</key><true/>
Expand Down
9 changes: 4 additions & 5 deletions build/resignAndPackage.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Name of your app.
APP="Fluent Reader"
# Your Certificate name.
CERT="Jieyu Yan (EM8VE646TZ)"
CERT="Haoyuan Liu (P2CG8QD3BP)"
# The path of your app to sign.
APP_PATH="bin/darwin/universal/mas-universal/Fluent Reader.app"
# The path to the location you want to put the signed package.
Expand All @@ -19,17 +19,16 @@ FRAMEWORKS_PATH="$APP_PATH/Contents/Frameworks"
# FONTLIST_PATH="node_modules/font-list/libs/darwin/fontlist.m"
# clang -arch arm64 -arch x86_64 "$FONTLIST_PATH" -fmodules -o "dist/fontlist"
# Build the MAS app
CSC_IDENTITY_AUTO_DISCOVERY=false npx electron-builder -c electron-builder-mas.yml --mac mas:universal
CSC_IDENTITY_AUTO_DISCOVERY=false
npx electron-builder -c electron-builder-mas.yml --mac mas:universal
# Add ElectronTeamID to Info.plist
sed -i '' -e 's/<\/dict>/<key>ElectronTeamID<\/key><string>EM8VE646TZ<\/string><\/dict>/g' "bin/darwin/universal/mas-universal/Fluent Reader.app/Contents/Info.plist"
sed -i '' -e 's/<\/dict>/<key>ElectronTeamID<\/key><string>P2CG8QD3BP<\/string><\/dict>/g' "bin/darwin/universal/mas-universal/Fluent Reader.app/Contents/Info.plist"

printf "......................\nresignAndPackage start\n\n"
codesign --deep --force --verify --verbose=4 --timestamp --options runtime --entitlements "$CHILD_PLIST" -s "$APP_KEY" "$APP_PATH/Contents/Resources/app.asar.unpacked/dist/fontlist"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Electron Framework"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libEGL.dylib"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libGLESv2.dylib"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libswiftshader_libEGL.dylib"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libswiftshader_libGLESv2.dylib"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libvk_swiftshader.dylib"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libffmpeg.dylib"
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Libraries/libffmpeg.dylib"
Expand Down
8 changes: 4 additions & 4 deletions dist/article/article.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ body.vertical {

:root {
--gray: #484644;
--primary: #0078d4;
--primary-alt: #004578;
--primary: #0f6cbd;
--primary-alt: #115ea3;
}
@media (prefers-color-scheme: dark) {
:root {
color: #f8f8f8;
--gray: #a19f9d;
--primary: #4ba0e1;
--primary-alt: #65aee6;
--primary: #479ef5;
--primary-alt: #62abf5;
}
}

Expand Down
Binary file modified dist/fontlist
Binary file not shown.
8 changes: 0 additions & 8 deletions dist/styles/dark.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,6 @@
.ms-Button--commandBar.active .ms-Button-icon {
color: #c7e0f4;
}
.btn-group .btn:hover,
.ms-Nav-compositeLink:hover {
background-color: #fff1;
}
.btn-group .btn:active,
.ms-Nav-compositeLink:active {
background-color: #fff2;
}
.settings .loading {
background-color: #000a;
}
Expand Down
19 changes: 3 additions & 16 deletions dist/styles/feeds.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,6 @@
overflow: hidden;
animation-name: slideUp20;
}
.article-container .btn-group .btn {
color: #fff;
}
.article-container .btn-group {
position: absolute;
top: calc(50% - 32px);
}
.article-container .btn-group.prev {
left: calc(50% - 486px);
}
.article-container .btn-group.next {
right: calc(50% - 486px);
}
.article {
height: 100%;
user-select: none;
Expand All @@ -52,11 +39,11 @@
border-bottom: 1px solid var(--neutralQuaternaryAlt);
}
.article .actions .favicon,
.article .actions .ms-Spinner {
.article .actions .fui-Spinner {
margin: 8px 8px 11px 0;
}
.article .actions .ms-Spinner {
display: inline-block;
.article .actions .fui-Spinner {
display: inline-flex;
vertical-align: middle;
}
.article .actions .source-name {
Expand Down
Loading