Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e10104c
chore: added claude.md
Novout Jun 10, 2026
4f718a2
chore: typo
Novout Jun 10, 2026
739e444
chore: update vite
Novout Jun 10, 2026
6a512ff
chore: ignore deprecation typescript
Novout Jun 10, 2026
8662824
chore: remove dropbox support
Novout Jun 10, 2026
3d3ef9b
refactor: remove head dynamic set
Novout Jun 10, 2026
6be2b61
fix!: clean save in before set entity raw
Novout Jun 10, 2026
fb0b5db
feat: initial .epub implementation
Novout Jun 12, 2026
bb3f4aa
chore(epub): toc title
Novout Jun 12, 2026
21f27ac
chore: update epub-gen-rs
Novout Jun 12, 2026
a06d38b
feat(epub): ignore fixed and image case
Novout Jun 13, 2026
3c5b472
feat: bionic reading docx and pdf
Novout Jun 13, 2026
d4a88e0
feat!: encrypt option for .bw
Novout Jun 13, 2026
d117e8b
refactor: alert erros in def components
Novout Jun 13, 2026
43d3826
chore: lint
Novout Jun 13, 2026
e3099f3
fix: gap in header principal item
Novout Jun 13, 2026
537c0d5
refactor: unique bionic option
Novout Jun 14, 2026
9db0712
refactor!: voice typing implement
Novout Jun 14, 2026
003e717
refactor: choice microphone
Novout Jun 14, 2026
34f392d
chore: update vite to v8
Novout Jun 14, 2026
300b3ce
chore revert vite to v7
Novout Jun 14, 2026
5fd1435
chore: epub in app package field
Novout Jun 14, 2026
249d073
test: use file in setup
Novout Jun 14, 2026
8f3b809
feat: some features for editor and preferences
Novout Jun 14, 2026
8d09f5d
feat: landing background
Novout Jun 14, 2026
ead66f4
ci: fix stubs in runner text
Novout Jun 14, 2026
316d0eb
chore: update epub-gen3
Novout Jun 15, 2026
d398c25
fix(pdf): cover with correct creator field size
Novout Jun 15, 2026
50acf76
v1.4.0
Novout Jun 15, 2026
4c44bdf
chore: readme
Novout Jun 15, 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
33 changes: 32 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,38 @@
# Changelog (03/11/2025)
# Changelog (15/06/2026)

Changelog was generated by [Generi](https://github.com/betterwrite/generi). Any questions, consult the documentation.

### v1.4.0

* **🔧 fix(pdf):** cover with correct creator field size - [[d398c258](https://github.com/Novout/betterwrite/commit/d398c258)]
* **🚧 chore:** update epub-gen3 - [[316d0ebe](https://github.com/Novout/betterwrite/commit/316d0ebe)]
* **🗿 ci:** fix stubs in runner text - [[ead66f46](https://github.com/Novout/betterwrite/commit/ead66f46)]
* **🎉 feat:** landing background - [[8d09f5d1](https://github.com/Novout/betterwrite/commit/8d09f5d1)]
* **🎉 feat:** some features for editor and preferences - [[8f3b8091](https://github.com/Novout/betterwrite/commit/8f3b8091)]
* **🔧 test:** use file in setup - [[249d0738](https://github.com/Novout/betterwrite/commit/249d0738)]
* **🚧 chore:** epub in app package field - [[5fd1435b](https://github.com/Novout/betterwrite/commit/5fd1435b)]
* **🚧 chore:** update vite to v8 - [[34f392d8](https://github.com/Novout/betterwrite/commit/34f392d8)]
* **🚩 refactor:** choice microphone - [[003e717f](https://github.com/Novout/betterwrite/commit/003e717f)]
* **🚩 refactor!:** voice typing implement - [[9db0712a](https://github.com/Novout/betterwrite/commit/9db0712a)]
* **🚩 refactor:** unique bionic option - [[537c0d57](https://github.com/Novout/betterwrite/commit/537c0d57)]
* **🔧 fix:** gap in header principal item - [[e3099f3b](https://github.com/Novout/betterwrite/commit/e3099f3b)]
* **🚧 chore:** lint - [[43d38269](https://github.com/Novout/betterwrite/commit/43d38269)]
* **🚩 refactor:** alert erros in def components - [[d117e8b1](https://github.com/Novout/betterwrite/commit/d117e8b1)]
* **🎉 feat!:** encrypt option for .bw - [[d4a88e04](https://github.com/Novout/betterwrite/commit/d4a88e04)]
* **🎉 feat:** bionic reading docx and pdf - [[3c5b472e](https://github.com/Novout/betterwrite/commit/3c5b472e)]
* **🎉 feat(epub):** ignore fixed and image case - [[a06d38b2](https://github.com/Novout/betterwrite/commit/a06d38b2)]
* **🚧 chore:** update epub-gen-rs - [[21f27acd](https://github.com/Novout/betterwrite/commit/21f27acd)]
* **🚧 chore(epub):** toc title - [[bb3f4aa8](https://github.com/Novout/betterwrite/commit/bb3f4aa8)]
* **🎉 feat:** initial .epub implementation - [[fb0b5db8](https://github.com/Novout/betterwrite/commit/fb0b5db8)]
* **🔧 fix!:** clean save in before set entity raw - [[6be2b614](https://github.com/Novout/betterwrite/commit/6be2b614)]
* **🚩 refactor:** remove head dynamic set - [[3d3ef9b2](https://github.com/Novout/betterwrite/commit/3d3ef9b2)]
* **🚧 chore:** remove dropbox support - [[86628245](https://github.com/Novout/betterwrite/commit/86628245)]
* **🚧 chore:** ignore deprecation typescript - [[6a512ff2](https://github.com/Novout/betterwrite/commit/6a512ff2)]
* **🚧 chore:** update vite - [[739e444f](https://github.com/Novout/betterwrite/commit/739e444f)]
* **🚧 chore:** added claude.md - [[e10104cd](https://github.com/Novout/betterwrite/commit/e10104cd)]
* **🎉 feat:** top file - [[7db91b93](https://github.com/Novout/betterwrite/commit/7db91b93)]
* **🚧 chore:** readme - [[c94ebf14](https://github.com/Novout/betterwrite/commit/c94ebf14)]

### v1.3.27

* **🚧 chore:** update generi - [[9929a27e](https://github.com/Novout/betterwrite/commit/9929a27e)]
Expand Down
113 changes: 113 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# betterwrite — CLAUDE.md

## Project Overview

**Better Write** is a 100% client-side creative word processor built as a Vue 3 + TypeScript monorepo. There is no backend — all data lives in the browser (IndexedDB). Exports (PDF, DOCX, HTML, TXT, EPUB) are generated entirely in the browser. The project is deployed as a PWA at betterwrite.io.

## Monorepo Structure

Managed with **pnpm workspaces** + **Lerna** + **Nx** (task caching).

```
packages/
app/ # Main Vue 3 SPA — the entry point for everything
better-write-types/ # Shared TypeScript types — check here first before defining new types
contenteditable-ast/ # Custom AST for the entity-model text editor
client-storage/ # IndexedDB abstraction layer
extension/ # .bw file format (ZIP + data.json)
plugin-*/ # Feature plugins (characters, theme, shortcuts, voice, etc.)
plugin-exporter-*/ # Export generators (pdf, docx, html, txt, epub)
plugin-importer/ # File import handling
google-fonts-api/ # Google Fonts integration
color-converter/ # Color utility
image-converter/ # Image processing
languages/ # i18n translation files
```

## Key Architecture Concepts

### Entity Model
The document is not stored as raw HTML. Content is a tree of **entities** (paragraphs, headings, images, page breaks, etc.), each with a `type`, `raw` text, and optional visual overrides. See `packages/better-write-types` and `docs/ENTITY_MODEL.md`.

### Plugin System
Plugins receive three injection points: `emitter` (mitt event bus), `stores` (Pinia), and `hooks` (lifecycle). All plugins initialize in `packages/app/src/App.vue`. When adding a feature, check whether it belongs in an existing plugin or as a new `plugin-*` package. See `docs/PLUGIN_SYSTEM.md`.

### State Management
Pinia stores live in `packages/app/src/store/`. There are ~14 stores. Prefer composables in `packages/app/src/use/` over direct store access in components.

### Export Pipeline
Each export format is a separate `plugin-exporter-*` package. Generators receive the entity tree and produce browser-downloadable output. See `docs/GENERATOR_FLOW.md`.

### .bw Extension Format
Project files saved as `.bw` are a ZIP archive containing `data.json`. Handled by `packages/extension/`. See `docs/PROJECT_FLOW.md`.

## Development Setup

```bash
# Install dependencies
pnpm install

# Start dev server (port 3000)
pnpm dev

# Build all packages
pnpm build

# Run tests
pnpm test

# Format code
pnpm lint
```

Requires a `.env.local` file in `packages/app/` — copy from `.env.example`.

Node 16+ and pnpm 8+ required.

## Coding Conventions

- **TypeScript strict mode** — no `any` without justification.
- **No semicolons**, **single quotes**, **2-space indent** (Prettier enforced).
- **Conventional Commits** required: `feat:`, `fix:`, `chore:`, `refactor:`, `docs:`, `ci:`.
- **Vue 3 Composition API** — use `<script setup>` syntax exclusively, no Options API.
- **Windi CSS** for styling — utility-first, custom `wb-*` shortcuts defined in `packages/app/windi.config.ts`. Do not add raw inline styles.
- CSS custom properties drive theming; never hardcode colors directly.
- i18n keys for all user-facing strings — translations live in `packages/languages/`.

## Versioning

Versioning uses `generi` via conventional commits:

```bash
pnpm patch # patch bump
pnpm minor # minor bump
pnpm major # major bump
```

Do not manually edit version fields in `package.json` or `lerna.json`.

## Testing

Tests use **Vitest** with `happy-dom`. Run with `pnpm test`. Tests live alongside source files or in `__tests__/` subdirectories within packages. Keep tests focused on pure logic (generators, AST transforms, utilities) — do not test Vue component internals directly.

## Important Constraints

- **No server-side code.** Everything must run in the browser. Avoid Node-only APIs.
- **No new direct `localStorage` usage.** Use the `client-storage` package (IndexedDB-backed) to avoid the 10 MB limit.
- **Nx caches build outputs** in `dist/**`. If builds seem stale, run `pnpm build` from root to let Nx decide what to rebuild.
- **Shameful hoisting is enabled** in pnpm — shared deps may hoist unexpectedly; prefer explicit imports from the package that owns the type.
- The app targets **ESNext** — no need for legacy polyfills except where explicitly noted in `vite.config.ts`.

## Where to Look First

| Question | Where to look |
|---|---|
| Shared types | `packages/better-write-types/src/` |
| App routing | `packages/app/src/routes.ts` |
| Global events | `packages/better-write-types/src/` → `Events` interface |
| Pinia stores | `packages/app/src/store/` |
| Composables | `packages/app/src/use/` |
| Theme tokens | `packages/plugin-theme/` + `packages/app/windi.config.ts` |
| Export logic | `packages/plugin-exporter-*/` |
| i18n strings | `packages/languages/` |
| Plugin init | `packages/app/src/App.vue` |
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ betterwrite.io
# Features

- ✅ Entity-Model Editor
- ✅ PDF, DOCX, HTML and TXT Generator
- ✅ PDF, DOCX, EPUB, HTML and TXT Generator
- ✅ Full Customization
- ✅ Mobile & Desktop (PWA)
- ✅ Offline First
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"npmClient": "pnpm",
"useWorkspaces": true,
"version": "1.3.27"
"version": "1.4.0"
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
"devDependencies": {
"generi": "2.0.4",
"happy-dom": "20.0.10",
"prettier": "3.6.2",
"lerna": "6.6.2",
"vitest": "4.0.6"
"prettier": "3.6.2",
"vitest": "4.1.8"
},
"packageManager": "pnpm@8.5.1"
}
50 changes: 25 additions & 25 deletions packages/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "better-write-app",
"version": "1.3.27",
"version": "1.4.0",
"repository": "https://github.com/Novout/betterwrite",
"description": "A Creative Word Processor",
"author": {
Expand Down Expand Up @@ -44,35 +44,35 @@
"@vueuse/shared": "9.13.0",
"@vueuse/sound": "2.0.1",
"@zip.js/zip.js": "2.8.8",
"better-write-client-storage": "^1.3.27",
"better-write-color-converter": "^1.3.27",
"better-write-contenteditable-ast": "^1.3.27",
"better-write-extension": "^1.3.27",
"better-write-google-fonts-api": "^1.3.27",
"better-write-image-converter": "^1.3.27",
"better-write-languages": "^1.3.27",
"better-write-plugin-characters": "^1.3.27",
"better-write-plugin-core": "^1.3.27",
"better-write-plugin-dropbox": "^1.3.27",
"better-write-plugin-editor-window": "^1.3.27",
"better-write-plugin-entity-history": "^1.3.27",
"better-write-plugin-exporter-docx": "^1.3.27",
"better-write-plugin-exporter-epub": "^1.3.27",
"better-write-plugin-exporter-html": "^1.3.27",
"better-write-plugin-exporter-pdf": "^1.3.27",
"better-write-plugin-exporter-txt": "^1.3.27",
"better-write-plugin-importer": "^1.3.27",
"better-write-plugin-progress-bar": "^1.3.27",
"better-write-plugin-schemas": "^1.3.27",
"better-write-plugin-shortcuts": "^1.3.27",
"better-write-plugin-theme": "^1.3.27",
"better-write-plugin-voice-typing": "^1.3.27",
"better-write-types": "^1.3.27",
"better-write-client-storage": "^1.4.0",
"better-write-color-converter": "^1.4.0",
"better-write-contenteditable-ast": "^1.4.0",
"better-write-extension": "^1.4.0",
"better-write-google-fonts-api": "^1.4.0",
"better-write-image-converter": "^1.4.0",
"better-write-languages": "^1.4.0",
"better-write-plugin-characters": "^1.4.0",
"better-write-plugin-core": "^1.4.0",
"better-write-plugin-editor-window": "^1.4.0",
"better-write-plugin-entity-history": "^1.4.0",
"better-write-plugin-exporter-docx": "^1.4.0",
"better-write-plugin-exporter-epub": "^1.4.0",
"better-write-plugin-exporter-html": "^1.4.0",
"better-write-plugin-exporter-pdf": "^1.4.0",
"better-write-plugin-exporter-txt": "^1.4.0",
"better-write-plugin-importer": "^1.4.0",
"better-write-plugin-progress-bar": "^1.4.0",
"better-write-plugin-schemas": "^1.4.0",
"better-write-plugin-shortcuts": "^1.4.0",
"better-write-plugin-theme": "^1.4.0",
"better-write-plugin-voice-typing": "^1.4.0",
"better-write-types": "^1.4.0",
"compressorjs": "1.2.1",
"destr": "2.0.5",
"docx": "7.5.0",
"drauu": "0.4.3",
"dropbox": "10.34.0",
"epub-gen3": "0.4.0",
"file-saver": "2.0.5",
"floating-vue": "2.0.0-beta.20",
"hast": "1.0.0",
Expand Down
6 changes: 2 additions & 4 deletions packages/app/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { ImporterPlugin } from 'better-write-plugin-importer'
import { PDFPlugin } from 'better-write-plugin-exporter-pdf'
import { DocxPlugin } from 'better-write-plugin-exporter-docx'
// import { EpubPlugin } from 'better-write-plugin-exporter-epub'
import { EpubPlugin } from 'better-write-plugin-exporter-epub'
import { TxtPlugin } from 'better-write-plugin-exporter-txt'
import { HtmlPlugin } from 'better-write-plugin-exporter-html'
import { SchemasPlugin } from 'better-write-plugin-schemas'
Expand All @@ -19,14 +19,13 @@
import { CharactersPlugin } from 'better-write-plugin-characters'
import { ProgressBarPlugin } from 'better-write-plugin-progress-bar'
import { EditorWindowPlugin } from 'better-write-plugin-editor-window'
import { DropboxPlugin } from 'better-write-plugin-dropbox'

useStart([
ThemePlugin(),
ImporterPlugin(),
PDFPlugin(),
DocxPlugin(),
// EpubPlugin(),
EpubPlugin(),
TxtPlugin(),
HtmlPlugin(),
SchemasPlugin(),
Expand All @@ -36,6 +35,5 @@
CharactersPlugin(),
ProgressBarPlugin(),
EditorWindowPlugin(),
DropboxPlugin(),
]).init()
</script>
3 changes: 0 additions & 3 deletions packages/app/src/components/page/about/AboutInfo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,6 @@
</AboutInfoItem>
</div>
<div class="flex flex-col gap-10">
<AboutInfoItem :content="t('about.initial.list.3')">
<IconVoid class="w-11 h-11" />
</AboutInfoItem>
<AboutInfoItem :content="t('about.initial.list.4')">
<IconProjectTypeDocument class="w-11 h-11" />
</AboutInfoItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
fontFamily: EDITOR.styles.text?.fontFamily || 'Raleway',
fontWeight: EDITOR.styles.text?.fontWeight || 500,
fontSize: `${EDITOR.styles.text?.fontSize || 16}px`,
lineHeight: EDITOR.styles.text?.lineHeight ?? 1.5,
letterSpacing: `${EDITOR.styles.text?.letterSpacing ?? 0}px`,
}"
class="editable whitespace-pre-line text-justify text-theme-editor-entity-text hover:text-theme-editor-entity-text-hover active:text-theme-editor-entity-text-active"
:spellcheck="true"
:spellcheck="EDITOR.configuration.entity?.spellcheck ?? true"
:contenteditable="true"
:data-placeholder="focused ? t('editor.text.placeholder.base') : ''"
@input="block.onInput"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
plugin.emit('plugin-pdf-generate', {
chapters: chapters.value,
color: color.value,
bionicReading: ABSOLUTE.bionicReading,
})
}, 100)
}
Expand Down
Loading
Loading