Skip to content
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
43 changes: 19 additions & 24 deletions .github/memory-bank/active-context.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# DexReader Active Context

**Last Updated**: 25 May 2026
**Version**: v1.6.0
**Version**: v1.7.0
**Mode**: Active Development

> **Purpose**: This is your session dashboard. Read this FIRST when resuming work to understand what's happening NOW, what was decided recently, and what to work on next. Keep all entries as short, concise as possible
Expand All @@ -10,18 +10,18 @@

## Current Status

**v1.6.0 Released**: 25 May 2026 ✅
**v1.7.0 Released**: 25 May 2026 ✅

**Monitoring Period**: Now through ~8 June 2026

- Monitor for user-reported issues or bugs in sandboxing implementation
- Watch for any regression in functionality
- Monitor for any issues with ESM migration
- Watch for compatibility issues with dependencies
- Collect feedback on stability and performance

**Next Planned Work:**

- P2-T02: ESM Migration (v1.7.0) - Planned to start after monitoring period
- Monitor for `drizzle-kit` updates to resolve transitive esbuild vulnerability
- Plan next feature development cycle

---

Expand All @@ -48,30 +48,25 @@

## Recent Changes (Last 1-2 Weeks)

### 25 May 2026 - v1.6.0 Release ✅
### 25 May 2026 - v1.7.0 Release ✅

- **Type**: Security Enhancement
- **Summary**: Enabled Electron renderer sandboxing for improved security posture
- **Type**: Technical Enhancement
- **Summary**: Migrated entire codebase to ECMAScript Modules (ESM)
- **Key Changes**:
- Enabled sandbox mode in BrowserWindow webPreferences
- Fixed preload bundling: changed `externalizeDeps: false` in electron.vite.config
- Sandboxed preload now bundles dependencies (cannot access node_modules at runtime)
- Localized unsaved changes dialogs (window close & navigation blocking)
- Comprehensive testing: all features verified working
- **Impact**: Improved security against malicious content, better Electron compliance
- Updated package.json to specify `"type": "module"` for native ESM support
- Refactored main process for full ESM compatibility
- Implemented CommonJS compatibility workaround for electron-updater
- Fixed IPC response handling in DownloadsView and dialog components
- Fixed filesystem deleteDir recursive flag handling
- Enhanced translation coverage for Downloads and favorite actions
- **Impact**: Modernized codebase with better Node.js ecosystem alignment, improved module loading
- **Status**: ✅ Released
- **CHANGELOG**: All changes documented in CHANGELOG.md v1.6.0 section
- **CHANGELOG**: All changes documented in CHANGELOG.md v1.7.0 section

### 22 May 2026 - v1.5.0 Release
### 25 May 2026 - Previous Releases Summary

- **Type**: Release
- **Summary**: Content Language Settings and enhanced settings management
- **Key Features**:
- Content Language Settings with priority language selection (up to 3 languages)
- Settings infrastructure migrated from JSON to electron-store
- Translation improvements across all locales
- **Impact**: Users can filter manga content by preferred languages
- **Status**: ✅ Released
- **v1.6.0**: Renderer sandboxing for enhanced security
- **v1.5.0**: Content Language Settings with priority language selection

<!-- Archived entries moved to historical/archived-milestones.md -->

Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ on:
push:
branches: [main]

permissions:
contents: write # Required for creating and pushing tags

jobs:
# Single job that runs all CI checks sequentially
# More efficient than parallel jobs (1x checkout + 1x npm ci vs 3x each)
Expand Down
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog],
and this project adheres to [Semantic Versioning].

## [1.7.0] - 2026-05-25

### Changed

- Migrate project to ECMAScript Modules (ESM)
- Update package.json to specify `"type": "module"` for native ESM support
- Refactor main process for full ESM compatibility
- Modernize codebase with native ESM imports/exports throughout application
- Implement compatibility workaround for electron-updater CommonJS dependency
- Improved module loading and better alignment with Node.js ecosystem standards

### Fixed

- Fix IPC response handling issues in DownloadsView and dialog components
- Fix filesystem deleteDir method not consistently applying recursive flag when removing directories
- Fix missing translation keys for Downloads view and favorite/unfavorite actions
- Fix Vietnamese locale translation coverage gaps

---

## [1.6.0] - 2026-05-25

### Added
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,12 @@ npm run build:linux

## Project Status

**Current Version**: 1.6.0 (May 25, 2026) 🔒
**Current Version**: 1.7.0 (May 25, 2026) 🚀

DexReader v1.6.0 brings enhanced security with Electron renderer sandboxing:
DexReader v1.7.0 brings modernized codebase with ECMAScript Modules (ESM):

- ✅ Complete MangaDex extensive manga library integration (browse, search, read)
- ✅ **ESM Migration** - Modernized module system for better Node.js ecosystem alignment
- ✅ **Renderer Sandboxing** - Enhanced security isolation for improved protection against malicious content
- ✅ **Content Language Preferences** - Configure up to 3 priority languages for manga content filtering
- ✅ **Multi-Language UI** - Three locales supported (British English, American English, Vietnamese)
Expand Down
49 changes: 31 additions & 18 deletions electron.vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
import fs from 'node:fs/promises'
import { defineConfig } from 'electron-vite'
import react from '@vitejs/plugin-react'
import path from 'node:path'
import { dirname, join, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'

// ESM: Get __dirname equivalent
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)

export default defineConfig({
main: {
build: {
rollupOptions: {
external: [
// Exclude scripts directory from production build
/^.*\/scripts\/.*/
]
/^.*\/scripts\/.*/,
// CommonJS-only modules (must stay external in ESM)
'electron-updater',
'better-sqlite3'
],
output: {
format: 'es', // ESM format to match project type
entryFileNames: '[name].js'
}
},
externalizeDeps: true
},
Expand All @@ -19,17 +31,17 @@ export default defineConfig({
name: 'copy-migrations',
async writeBundle() {
// Copy migrations folder to output directory
const src = path.resolve(__dirname, 'src/main/database/migrations')
const dest = path.resolve(__dirname, 'out/main/database/migrations')
const src = resolve(__dirname, 'src/main/database/migrations')
const dest = resolve(__dirname, 'out/main/database/migrations')

// Create destination directory
await fs.mkdir(dest, { recursive: true })

// Copy all files
const files = await fs.readdir(src)
files.forEach(async (file: string) => {
const srcPath = path.join(src, file)
const destPath = path.join(dest, file)
const srcPath = join(src, file)
const destPath = join(dest, file)

if ((await fs.stat(srcPath)).isDirectory()) {
// Copy directory recursively (for meta folder)
Expand All @@ -44,15 +56,15 @@ export default defineConfig({
{
name: 'copy-protobuf-schema',
async writeBundle() {
const src = path.resolve(__dirname, 'src/main/services/protobuf/schemas')
const dest = path.resolve(__dirname, 'out/main/services/protobuf/schemas')
const src = resolve(__dirname, 'src/main/services/protobuf/schemas')
const dest = resolve(__dirname, 'out/main/services/protobuf/schemas')

await fs.mkdir(dest, { recursive: true })

const files = await fs.readdir(src, { recursive: true })
files.forEach(async (file: string) => {
const srcPath = path.join(src, file)
const destPath = path.join(dest, file)
const srcPath = join(src, file)
const destPath = join(dest, file)

if ((await fs.stat(srcPath)).isDirectory()) {
fs.cp(srcPath, destPath, { recursive: true })
Expand All @@ -65,23 +77,23 @@ export default defineConfig({
{
name: 'copy-locales',
async writeBundle() {
const src = path.resolve(__dirname, 'src/locales')
const dest = path.resolve(__dirname, 'out/locales')
const src = resolve(__dirname, 'src/locales')
const dest = resolve(__dirname, 'out/locales')

await fs.mkdir(dest, { recursive: true })

const files = await fs.readdir(src)
for (const file of files) {
const srcPath = path.join(src, file)
const destPath = path.join(dest, file)
const srcPath = join(src, file)
const destPath = join(dest, file)

if ((await fs.stat(srcPath)).isDirectory()) {
// Copy language directory, but only JSON files
await fs.mkdir(destPath, { recursive: true })
const langFiles = await fs.readdir(srcPath)
for (const langFile of langFiles) {
if (langFile.endsWith('.json')) {
await fs.copyFile(path.join(srcPath, langFile), path.join(destPath, langFile))
await fs.copyFile(join(srcPath, langFile), join(destPath, langFile))
}
}
} else if (file.endsWith('.json')) {
Expand All @@ -100,15 +112,16 @@ export default defineConfig({
externalizeDeps: false,
rollupOptions: {
output: {
format: 'cjs' // Preload must be CommonJS (Electron requirement)
format: 'cjs', // Preload MUST be CommonJS (Electron sandboxed limitation)
entryFileNames: '[name].cjs' // Output as .cjs to avoid ESM confusion
}
}
}
},
renderer: {
resolve: {
alias: {
'@renderer': path.resolve('src/renderer/src')
'@renderer': resolve('src/renderer/src')
}
},
build: {
Expand Down
7 changes: 0 additions & 7 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,5 @@ export default defineConfig(
...eslintPluginReactRefresh.configs.vite.rules
}
},
{
// Allow CommonJS in script files
files: ['scripts/**/*.js'],
rules: {
'@typescript-eslint/no-require-imports': 'off'
}
},
eslintConfigPrettier
)
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"name": "dexreader",
"productName": "DexReader",
"version": "1.6.0",
"description": "An Electron application with React and TypeScript",
"version": "1.7.0",
"description": "A personal MangaDex client built with Electron and React.",
"type": "module",
"main": "./out/main/index.js",
"author": "remichan97",
"homepage": "https://electron-vite.org",
"homepage": "https://github.com/remichan97/dexreader",
"scripts": {
"format": "prettier --write .",
"lint": "eslint --cache .",
Expand Down
57 changes: 0 additions & 57 deletions scripts/run-analyze-plans.js

This file was deleted.

32 changes: 31 additions & 1 deletion src/locales/en-GB/dialogs.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,39 @@
"deleteChapterDownload": {
"title": "Remove Download",
"message": "You are about to remove \"{{title}}\", which will delete downloaded chapter files.\nYou can also choose to just hide it from the list if you want to keep the files for offline reading.\n\nHow should we proceed?",
"buttons": {
"cancel": "Cancel",
"hideFromView": "Hide from View (Keep Files for Offline Reading)",
"deleteForever": "Delete Forever (Cannot be Undone)"
},
"finalConfirmation": {
"title": "Are you absolutely certain?",
"message": "This will be your last chance to back out before the chapter files are permanently deleted. File deletion cannot be undone, but you can always re-download the chapter if you change your mind.\n\nJust a reminder, you are deleting chapter: {{title}}",
"confirmButton": "Yes, Delete Permanently",
"cancelButton": "Nevermind"
}
},
"removeFromLibrary": {
"withDownloads": {
"title": "Remove from library?",
"message": "{{title}}\n\nThis manga has {{count}} downloaded chapter{{plural}} ({{size}}).\n\nDownloads will still be accessible in the Downloads view unless you choose to delete them.",
"buttons": {
"keepDownloads": "Remove from library (keep downloads)",
"deleteEverything": "Remove everything (both bookmark and downloads will be removed)",
"cancel": "Cancel"
}
},
"noDownloads": {
"title": "Remove from library?",
"message": "{{title}}\n\nYou can always add it back later.",
"confirmButton": "Remove",
"cancelButton": "Cancel"
},
"finalConfirmation": {
"title": "Are you absolutely certain?",
"message": "This will be your last chance to back out before the chapter files are permanently deleted. File deletion cannot be undone, but you can always re-download the chapter if you change your mind.\n\nJust a reminder, you are deleting chapter: {{title}}"
"message": "This will be your last chance to back out before the chapter files are permanently deleted. File deletion cannot be undone, but you can always re-download the manga if you change your mind.\n\nJust a reminder, you are deleting downloads for: {{title}}",
"confirmButton": "Yes, Delete Permanently",
"cancelButton": "Nevermind"
}
},
"clearCoverCache": {
Expand Down
Loading
Loading