|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## What This Is |
| 6 | + |
| 7 | +A Flutter/Dart package providing a **viewer** (`EditorJSView`) and **editor** (`EditorJSEditor`) widget compatible with the [EditorJS](https://editorjs.io/) JSON schema. Currently being refactored to Clean Architecture + SOLID for production readiness. |
| 8 | + |
| 9 | +## Commands |
| 10 | + |
| 11 | +```bash |
| 12 | +# Install dependencies |
| 13 | +fvm flutter pub get |
| 14 | + |
| 15 | +# Run the demo app |
| 16 | +cd demo && fvm flutter run |
| 17 | + |
| 18 | +# Run tests |
| 19 | +fvm flutter test |
| 20 | + |
| 21 | +# Analyze code |
| 22 | +fvm flutter analyze |
| 23 | + |
| 24 | +# Format code |
| 25 | +dart format lib/ test/ demo/lib/ |
| 26 | +``` |
| 27 | + |
| 28 | +Single test file: |
| 29 | +```bash |
| 30 | +fvm flutter test test/editorjs_flutter_test.dart |
| 31 | +``` |
| 32 | + |
| 33 | +> All Flutter commands must use `fvm flutter ...` — never plain `flutter`. |
| 34 | +
|
| 35 | +--- |
| 36 | + |
| 37 | +## Architecture |
| 38 | + |
| 39 | +The package follows **Clean Architecture** with three strict layers. Dependencies only flow inward (presentation → data → domain). No new external dependencies — only `flutter_html`, `image_picker`, `url_launcher`, and Flutter's built-in `ChangeNotifier`. |
| 40 | + |
| 41 | +``` |
| 42 | +lib/src/ |
| 43 | +├── domain/ ← pure Dart, zero Flutter imports |
| 44 | +│ ├── entities/ ← BlockEntity (abstract), BlockDocument, StyleConfig, one class per block type |
| 45 | +│ ├── repositories/ ← DocumentRepository (abstract interface) |
| 46 | +│ └── usecases/ ← ParseDocument, SerializeDocument |
| 47 | +├── data/ ← JSON parsing only |
| 48 | +│ ├── mappers/ ← BlockMapper<T> (abstract) + one concrete mapper per block type |
| 49 | +│ ├── datasources/ ← JsonDocumentSource implements DocumentRepository |
| 50 | +│ └── registry/ ← BlockTypeRegistry — the OCP extension point for custom block types |
| 51 | +└── presentation/ ← Flutter widgets |
| 52 | + ├── controller/ ← EditorController (ChangeNotifier, List<BlockEntity>, getContent()) |
| 53 | + ├── config/ ← EditorConfig (wires registries + styleConfig) |
| 54 | + ├── registry/ ← BlockRendererRegistry (maps type strings → builder functions) |
| 55 | + ├── blocks/ ← base_block_renderer.dart, base_block_editor.dart, one subdir per block type |
| 56 | + └── widgets/ ← EditorJSView, EditorJSEditor, EditorJSToolbar (thin delegators) |
| 57 | +``` |
| 58 | + |
| 59 | +### Key Contracts |
| 60 | + |
| 61 | +| Contract | Layer | Responsibility | |
| 62 | +|---|---|---| |
| 63 | +| `BlockEntity` | Domain | Abstract base: `String get type`, `Map<String,dynamic> toJson()` | |
| 64 | +| `DocumentRepository` | Domain | `BlockDocument parse(String)`, `String serialize(BlockDocument)` | |
| 65 | +| `BlockMapper<T>` | Data | `String get supportedType`, `T fromJson(Map<String,dynamic>)` | |
| 66 | +| `BlockRenderer<T>` | Presentation | Abstract `StatelessWidget` with `T block`, `StyleConfig? styleConfig` | |
| 67 | +| `BlockEditor<T>` | Presentation | Abstract `StatefulWidget` with `T block`, `ValueChanged<T> onChanged` | |
| 68 | + |
| 69 | +### EditorController |
| 70 | + |
| 71 | +Replaces the previous `List<Widget>` anti-pattern. The editor stores **data, not widgets**. |
| 72 | + |
| 73 | +```dart |
| 74 | +class EditorController extends ChangeNotifier { |
| 75 | + List<BlockEntity> get blocks |
| 76 | + void addBlock(BlockEntity block) |
| 77 | + void updateBlock(int index, BlockEntity updated) |
| 78 | + void removeBlock(int index) |
| 79 | + void moveBlock(int from, int to) |
| 80 | + String getContent() // returns EditorJS-compliant JSON string |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +### Block Registry Pattern (Open/Closed Principle) |
| 85 | + |
| 86 | +New block types are added by registering into the registries — **never by editing switch statements**. |
| 87 | + |
| 88 | +```dart |
| 89 | +// Adding a custom block — zero changes to core package files |
| 90 | +final config = EditorConfig( |
| 91 | + typeRegistry: BlockTypeRegistry()..register(MyMapper()), |
| 92 | + rendererRegistry: BlockRendererRegistry() |
| 93 | + ..registerRenderer('my_type', (b, s) => MyRenderer(block: b as MyBlock)) |
| 94 | + ..registerEditor('my_type', (b, cb) => MyEditor(block: b as MyBlock, onChanged: cb)), |
| 95 | +); |
| 96 | +``` |
| 97 | + |
| 98 | +### SOLID Rules (must be enforced in all PRs) |
| 99 | + |
| 100 | +- **SRP**: Each block type has its own entity, mapper, renderer, and editor — no god objects |
| 101 | +- **OCP**: Core files never change to support new block types; extend via registry |
| 102 | +- **LSP**: All `BlockRenderer<T>` and `BlockEditor<T>` subclasses are interchangeable |
| 103 | +- **ISP**: Viewer callers only need `EditorJSView` + optional `EditorConfig` — never `EditorController` |
| 104 | +- **DIP**: `EditorJSToolbar` communicates only via `EditorController`, never holds a parent widget reference |
| 105 | + |
| 106 | +--- |
| 107 | + |
| 108 | +## Supported Block Types |
| 109 | + |
| 110 | +| Block | Viewer | Editor | Notes | |
| 111 | +|---|---|---|---| |
| 112 | +| `header` | H1–H6 | Yes | | |
| 113 | +| `paragraph` | HTML via flutter_html | Yes | | |
| 114 | +| `list` | Flat ordered/unordered | Yes | Nested lists not yet supported | |
| 115 | +| `delimiter` | Divider | Yes | | |
| 116 | +| `image` | Network URL | Yes | caption/border/stretch not yet rendered | |
| 117 | +| `quote` | Planned | Planned | | |
| 118 | +| `code` | Planned | Planned | | |
| 119 | +| `table` | Planned | Planned | | |
| 120 | +| `checklist` | Planned | Planned | | |
| 121 | + |
| 122 | +--- |
| 123 | + |
| 124 | +## Public API |
| 125 | + |
| 126 | +`lib/editorjs_flutter.dart` is the sole entry point. It exports: |
| 127 | + |
| 128 | +- **Widgets**: `EditorJSView`, `EditorJSEditor` |
| 129 | +- **Controller**: `EditorController` |
| 130 | +- **Config**: `EditorConfig` |
| 131 | +- **Registries**: `BlockTypeRegistry`, `BlockRendererRegistry` |
| 132 | +- **Contracts** (for custom block authors): `BlockMapper`, `BlockRenderer`, `BlockEditor`, `BlockEntity`, `BlockDocument`, `StyleConfig` |
| 133 | +- **Built-in entities**: `HeaderBlock`, `ParagraphBlock`, `ListBlock`, `DelimiterBlock`, `ImageBlock` |
| 134 | + |
| 135 | +Nothing from `lib/src/` should be imported directly by consumers. |
| 136 | + |
| 137 | +--- |
| 138 | + |
| 139 | +## Implementation Order |
| 140 | + |
| 141 | +When building new features, always work bottom-up: |
| 142 | + |
| 143 | +1. Domain entity (pure Dart, no Flutter) |
| 144 | +2. `BlockMapper<T>` in data layer + register in `BlockTypeRegistry` |
| 145 | +3. `BlockRenderer<T>` + `BlockEditor<T>` in presentation + register in `BlockRendererRegistry` |
| 146 | +4. Unit tests for mapper, widget tests for renderer |
| 147 | + |
| 148 | +--- |
| 149 | + |
| 150 | +## Key Source Locations |
| 151 | + |
| 152 | +- `lib/editorjs_flutter.dart` — public API barrel file |
| 153 | +- `lib/src/domain/entities/` — block entity classes |
| 154 | +- `lib/src/data/registry/block_type_registry.dart` — JSON parsing extension point |
| 155 | +- `lib/src/presentation/registry/block_renderer_registry.dart` — rendering extension point |
| 156 | +- `lib/src/presentation/controller/editor_controller.dart` — editor state + `getContent()` |
| 157 | +- `lib/src/presentation/widgets/` — thin shell widgets |
| 158 | +- `demo/lib/main.dart` — viewer usage example |
| 159 | +- `demo/lib/createnote.dart` — editor usage example |
| 160 | +- `demo/test_data/` — sample EditorJS JSON and styles JSON |
| 161 | + |
| 162 | +## Git Workflow |
| 163 | + |
| 164 | +**This repo has branch protection. Direct commits to `master` are not allowed.** |
| 165 | + |
| 166 | +### Before starting any coding task |
| 167 | + |
| 168 | +1. Ensure `master` is up to date and create a new branch: |
| 169 | + ```bash |
| 170 | + git checkout master |
| 171 | + git pull |
| 172 | + git checkout -b <prefix>/<short-description> |
| 173 | + ``` |
| 174 | +2. Use these branch name prefixes: |
| 175 | + - `feat/` — new feature or block type |
| 176 | + - `fix/` — bug fix |
| 177 | + - `refactor/` — internal restructuring with no behaviour change |
| 178 | + - `chore/` — dependency bumps, tooling, docs |
| 179 | + |
| 180 | +### Before finishing any coding task |
| 181 | + |
| 182 | +1. Run `fvm flutter analyze` — must report **no issues** before marking work done. |
| 183 | +2. Generate a `PR_DESCRIPTION.md` file at the repo root (see template below). |
| 184 | +3. All changes go to `master` via Pull Request only — never push directly. |
| 185 | +4. Delete `PR_DESCRIPTION.md` after the PR is merged. |
| 186 | + |
| 187 | +### PR_DESCRIPTION.md template |
| 188 | + |
| 189 | +```markdown |
| 190 | +## Title |
| 191 | +<concise PR title> |
| 192 | + |
| 193 | +## Type of change |
| 194 | +- [ ] Bug fix |
| 195 | +- [ ] New feature |
| 196 | +- [ ] Refactor |
| 197 | +- [ ] Chore / dependency update |
| 198 | +- [ ] Documentation |
| 199 | + |
| 200 | +## Summary |
| 201 | +- <bullet point summary of what changed and why> |
| 202 | + |
| 203 | +## Breaking changes |
| 204 | +<list any public API changes, or "None"> |
| 205 | + |
| 206 | +## Migration notes |
| 207 | +<instructions for consumers upgrading, or "None"> |
| 208 | + |
| 209 | +## Test plan |
| 210 | +- [ ] `fvm flutter analyze` passes with no issues |
| 211 | +- [ ] Relevant unit/widget tests added or updated |
| 212 | +- [ ] Demo app tested manually on at least one platform |
| 213 | +``` |
| 214 | + |
| 215 | +--- |
| 216 | + |
| 217 | +## Versioning |
| 218 | + |
| 219 | +Current version: `0.1.0` (in `pubspec.yaml`). |
| 220 | + |
| 221 | +When making changes, always update `CHANGELOG.md` with a new entry at the top following the existing format: |
| 222 | +- Bump the patch version for bug fixes |
| 223 | +- Bump the minor version for new features or breaking API changes |
| 224 | +- Keep `pubspec.yaml` version in sync with `CHANGELOG.md` |
| 225 | + |
| 226 | +## Known Issues / Backlog |
| 227 | + |
| 228 | +- Image `caption`, `withBorder`, `stretched`, `withBackground` fields parsed but not rendered |
| 229 | +- Nested lists not supported |
| 230 | +- No HTML sanitization before passing to `flutter_html` (XSS risk with untrusted JSON) |
| 231 | +- All tests are commented out — need full test coverage |
| 232 | +- `quote`, `code`, `table`, `checklist` blocks not yet implemented |
0 commit comments