mirror of
https://github.com/crosspoint-reader/crosspoint-reader.git
synced 2026-08-13 14:21:57 -07:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
896b9de9b6 | ||
|
|
7e3110d296 |
No files matched your search
@@ -1,33 +0,0 @@
|
||||
# CrossPoint Reader: Claude Code skills
|
||||
|
||||
Project skills for Claude Code. Claude auto-discovers them and loads one when the
|
||||
task matches its `description`; you do not invoke them by hand. They encode how
|
||||
this project wants C/C++ written: the judgment calls and self-review gates that
|
||||
keep the firmware small, stable, and reviewable.
|
||||
|
||||
These are written for capable agents, not beginners. They are principle- and
|
||||
decision-focused on purpose. They deliberately avoid line-number citations,
|
||||
which drift; they anchor on durable names (APIs, types, macros, files).
|
||||
|
||||
This is separate from `.skills/SKILL.md`, the GitHub coding-agent guide that
|
||||
mirrors CLAUDE.md. CLAUDE.md stays the always-loaded rule set; these skills are
|
||||
the applied decision procedures that load on demand and add the judgment layer
|
||||
CLAUDE.md does not carry.
|
||||
|
||||
| Skill | Loads when you are... |
|
||||
|---|---|
|
||||
| `heap-discipline` | allocating memory: new/malloc/vector/string, buffers, caches |
|
||||
| `control-flow-clarity` | writing branching logic, state flags, modes, if/else ladders |
|
||||
| `hal-and-abstractions` | touching storage, input, display, settings, i18n, rendering |
|
||||
| `scope-discipline` | adding a feature, activity, lib, setting, or dependency |
|
||||
| `refactor-for-review` | refactoring, cleaning up, or preparing a change for PR |
|
||||
|
||||
Each skill ends with a self-review checklist Claude runs against its own diff
|
||||
before handing it back. Reviewing a PR? Those checklists double as a fast rubric.
|
||||
|
||||
## Maintaining these
|
||||
|
||||
Edit the `SKILL.md` under each directory. Keep them tight. Do not restate
|
||||
CLAUDE.md; add the judgment CLAUDE.md cannot afford to carry. Trigger quality
|
||||
lives in the `description` field: it must name the situations that should pull
|
||||
the skill in, in the words a contributor's task would use.
|
||||
@@ -1,56 +0,0 @@
|
||||
---
|
||||
name: control-flow-clarity
|
||||
description: Branching and state-modeling clarity in C/C++. Use when writing or refactoring logic that branches on discrete values: if/else-if ladders, status flags, mode or state ints, or anything that should be an enum plus an exhaustive switch. Covers enum class over magic ints, exhaustive switch over nested if, early-return guard clauses, table dispatch, and when each is the right call.
|
||||
---
|
||||
|
||||
# Control-Flow Clarity
|
||||
|
||||
The goal: a reviewer verifies correctness by reading, not by tracing. Branching
|
||||
that mirrors the problem's shape is self-evident; branching that encodes it in
|
||||
ad-hoc ints and nesting forces the reader to reconstruct intent.
|
||||
|
||||
## Core moves
|
||||
|
||||
- **Model a closed set of states/modes as an `enum class`, not ints or bools.**
|
||||
A variable kept honest by a comment ("0 = hidden, 1 = showing, 2 = confirm")
|
||||
is a latent bug. Make it an enum and the comment becomes the type.
|
||||
- **Dispatch on an enum with an exhaustive `switch`, no `default`.** This
|
||||
codebase relies on it: omitting `default` lets the compiler flag the
|
||||
unhandled case when someone adds an enum value. A `default:` that swallows the
|
||||
unknown case throws that safety away. Add `default` only when "every other
|
||||
value does nothing" is a deliberate, documented decision.
|
||||
- **Replace nested `if/else-if` ladders that branch on one discriminant with a
|
||||
`switch`.** If each branch only maps input to a value, prefer a lookup table
|
||||
(`static constexpr` array) over both.
|
||||
- **Prefer early-return guard clauses over nested success bodies.** Handle the
|
||||
error/empty/skip cases first and return; keep the main path at the left
|
||||
margin.
|
||||
|
||||
## When NOT to switch
|
||||
|
||||
- Branches test unrelated conditions, not one discriminant: a guarded `if`
|
||||
sequence is honest; a switch would be forced.
|
||||
- Two outcomes on a genuine boolean: keep the `if`.
|
||||
- The discriminant is an open or unbounded set (arbitrary ints, strings): table
|
||||
or map, not a switch.
|
||||
|
||||
## Enum hygiene
|
||||
|
||||
- `enum class` by default for type safety. Plain `enum` only when values must
|
||||
implicitly convert (e.g. a value that doubles as a UI dropdown index), and
|
||||
then give it a trailing `_COUNT` sentinel for safe bounds/iteration, matching
|
||||
the existing settings enums.
|
||||
- Name the discriminant after what it selects, not its storage:
|
||||
`Orientation orientation`, not `uint8_t mode`.
|
||||
- No magic numeric codes for states. If you write a comment mapping numbers to
|
||||
meanings, you owe an enum.
|
||||
|
||||
## Self-review
|
||||
|
||||
- [ ] No int/bool standing in for a closed set of modes; it is an `enum class`.
|
||||
- [ ] Enum dispatch is an exhaustive `switch` with no catch-all `default` (or
|
||||
the `default` is a documented deliberate choice).
|
||||
- [ ] No nested if/else-if ladder on a single discriminant that should be a
|
||||
switch or a table.
|
||||
- [ ] Error/skip cases are early-return guards; the happy path is not buried.
|
||||
- [ ] No magic numbers where a named enum or `constexpr` would state the intent.
|
||||
@@ -1,59 +0,0 @@
|
||||
---
|
||||
name: hal-and-abstractions
|
||||
description: Layering and abstraction discipline for the firmware. Use when touching storage, input, display, settings, i18n, or rendering, or any code that could reach into the SDK. Covers routing through the HAL (HalStorage / HalGPIO / HalDisplay) instead of raw SDK classes, MappedInputManager logical buttons instead of raw GPIO indices, UITheme/GUI for all rendering, the singleton macros, tr() for user-facing text, and where a new abstraction boundary belongs.
|
||||
---
|
||||
|
||||
# HAL and Abstractions
|
||||
|
||||
CLAUDE.md lists the HAL classes and the SdFat-concurrency reason they exist.
|
||||
This is when and how to route through them, and where to draw a new boundary.
|
||||
|
||||
## Route through the layer, always
|
||||
|
||||
- **SD card I/O:** `Storage` (HalStorage) and `HalFile`. Never `SdFat`,
|
||||
`FsFile`, `SdSpiCard`, `FsBaseFile`, or `SDCardManager` directly. The HAL
|
||||
serializes every SD access through one mutex; bypassing it races the SPI state
|
||||
machine and panics FreeRTOS (CLAUDE.md has the failure mode). This is a
|
||||
correctness boundary, not a style preference.
|
||||
- **Display:** `HalDisplay` over `EInkDisplay`. **Input:** `HalGPIO` over
|
||||
`InputManager`.
|
||||
- **Rendering:** everything through the `GUI` macro (UITheme) and the renderer's
|
||||
oriented metrics. No hardcoded fonts, colors, coordinates, or 800/480
|
||||
literals; ask the renderer for width/height and use the oriented viewable
|
||||
area.
|
||||
- **Input in activities:** `MappedInputManager::Button` logical enums
|
||||
(`Button::Confirm`, `Button::PageForward`, ...). Never raw `HalGPIO::BTN_*`
|
||||
indices outside `ButtonRemapActivity`. Logical buttons survive user remapping
|
||||
and orientation; raw indices do not.
|
||||
- **Shared state:** the singleton macros (`SETTINGS`, `APP_STATE`, `GUI`,
|
||||
`Storage`, `I18N`), not threaded pointers.
|
||||
|
||||
## User-facing text
|
||||
|
||||
Every string a user reads goes through `tr(STR_*)`. Add the key to the English
|
||||
YAML, regenerate with `scripts/gen_i18n.py`, then use the `StrId`. Log lines
|
||||
(`LOG_*`) stay hardcoded.
|
||||
|
||||
## Drawing a new boundary
|
||||
|
||||
When you need an SDK capability the HAL does not expose yet, **add the method to
|
||||
the HAL; do not reach around it.** The new method inherits the mutex, logging,
|
||||
and error contract the rest of the HAL carries. A one-off direct SDK call in an
|
||||
activity is exactly the layering violation the mutex discipline cannot tolerate.
|
||||
|
||||
Keep abstractions thin. A wrapper that only renames an SDK call without adding
|
||||
the mutex, logging, or an error contract is dead weight. Add a layer only when
|
||||
it carries one of those contracts or hides a real implementation choice.
|
||||
|
||||
## Self-review
|
||||
|
||||
- [ ] No direct SdFat / FsFile / SDCardManager / EInkDisplay / InputManager use
|
||||
outside `lib/hal`.
|
||||
- [ ] File access uses `HalFile`; no `.close()` on a local handle
|
||||
(DESTRUCTOR_CLOSES_FILE); members closed in `onExit`.
|
||||
- [ ] Input uses `MappedInputManager::Button`, not raw `BTN_*` indices.
|
||||
- [ ] Rendering goes through GUI/UITheme and oriented metrics; no 800/480 or
|
||||
hardcoded fonts/coords.
|
||||
- [ ] User-facing strings use `tr(STR_*)`; new keys added to YAML and
|
||||
regenerated.
|
||||
- [ ] Any new SDK capability is exposed as a HAL method, not called inline.
|
||||
@@ -1,65 +0,0 @@
|
||||
---
|
||||
name: heap-discipline
|
||||
description: Memory allocation discipline for the ESP32-C3 (~380KB RAM, no PSRAM, single 48KB framebuffer). Use whenever writing or reviewing code that allocates: new / malloc / std::vector / std::string, buffers, caches, or anything held across a loop or an activity lifecycle. Covers makeUniqueNoThrow vs raw new/malloc, fragmentation avoidance, reserve-before-push_back, alloc-once-reuse, stack vs heap sizing, and the chunked grayscale buffer pattern.
|
||||
---
|
||||
|
||||
# Heap Discipline (ESP32-C3)
|
||||
|
||||
CLAUDE.md states the allocation rules. This is the procedure you run while
|
||||
writing the code and the gate you run before handing it back.
|
||||
|
||||
The constraint that makes every call matter: ~380KB RAM, no PSRAM, one 48KB
|
||||
framebuffer. **Fragmentation, not total usage, is what kills this device.**
|
||||
Free-heap can read fine while the largest free block is too small for the next
|
||||
allocation. Optimize for not leaving holes, not just for using fewer bytes.
|
||||
|
||||
## Allocation decision procedure
|
||||
|
||||
Ask in order; stop at the first yes.
|
||||
|
||||
1. **Stack?** Local, bounded, under ~256 bytes total: plain array/struct. No
|
||||
heap, no fragmentation. Keep frames lean; the task stack is small.
|
||||
2. **Compile-time constant?** `static constexpr` lives in flash, costs zero DRAM.
|
||||
3. **Allocated once and reused for an activity's lifetime?** Allocate in
|
||||
`onEnter`, hold in a member, release in `onExit`. Never per-frame, never
|
||||
per-iteration.
|
||||
4. **Dynamic and fallible?** `makeUniqueNoThrow<T>(...)` /
|
||||
`makeUniqueNoThrow<T[]>(n)` from `lib/Memory/Memory.h`. Null-check, `LOG_ERR`
|
||||
with the size, return false. It frees on every exit path.
|
||||
5. **A C/SDK API takes ownership and frees it itself?** Only then raw
|
||||
`new (std::nothrow)` / `malloc`, with a comment naming who frees it.
|
||||
|
||||
Bare `new` / `new[]` is never correct here: under `-fno-exceptions` it calls
|
||||
`abort()` on OOM instead of returning null.
|
||||
|
||||
## Fragmentation rules
|
||||
|
||||
- `std::vector`: `reserve(n)` before any `push_back` loop. Each growth is
|
||||
alloc-copy-free (three heap ops) and leaves a hole. Unknown n: estimate high.
|
||||
- No repeated `new`/`delete` or growing containers inside a loop or render path.
|
||||
Hoist the allocation out of the loop.
|
||||
- Large contiguous blocks fragment worst. Full-screen-class buffers use the
|
||||
chunked `storeBwBuffer` / `restoreBwBuffer` path in `GfxRenderer` so they
|
||||
never demand one contiguous 48KB block. Reuse that path. Do not malloc a
|
||||
second full-screen buffer.
|
||||
- `std::string` / Arduino `String`: acceptable on cold paths (file I/O, one-shot
|
||||
setup). Banned on hot/render paths. Build text with a stack `char[]` +
|
||||
`snprintf`; if a `std::string` is unavoidable, `reserve` it first.
|
||||
|
||||
## Justify every allocation
|
||||
|
||||
Per CLAUDE.md's evidence rule: when you add a heap allocation, state in one line
|
||||
why stack/static/reuse was rejected and the worst-case size. If you cannot name
|
||||
the size, you cannot budget it, and you should not allocate it.
|
||||
|
||||
## Self-review before handoff
|
||||
|
||||
- [ ] No bare `new`/`new[]`. Every fallible alloc is `makeUniqueNoThrow`, or a
|
||||
raw alloc with an explicit owner comment.
|
||||
- [ ] Every allocation is null-checked with `LOG_ERR` before the error return.
|
||||
- [ ] No allocation inside a loop or render path that could be hoisted.
|
||||
- [ ] Every `push_back` loop has a preceding `reserve`.
|
||||
- [ ] Anything allocated in `onEnter` is released in `onExit`; member `HalFile`
|
||||
closed there too.
|
||||
- [ ] No second full-screen buffer; grayscale uses store/restoreBwBuffer.
|
||||
- [ ] Each new allocation carries a one-line size + why-not-stack/static note.
|
||||
@@ -1,59 +0,0 @@
|
||||
---
|
||||
name: refactor-for-review
|
||||
description: Producing small, single-concern, reviewable changes. Use when refactoring, cleaning up, restructuring, decomposing, or preparing a change for PR, especially in this multi-contributor AI-assisted codebase that is prone to sprawl diffs. Covers one-concern-per-commit, extracting helpers without widening scope, not bundling unrelated edits, decomposing oversized activities, comment hygiene, and a pre-handoff self-review checklist.
|
||||
---
|
||||
|
||||
# Refactor for Review
|
||||
|
||||
This is a multi-contributor, AI-assisted codebase, and the dominant failure mode
|
||||
is the sprawl diff: a one-line intent that touches thirty files. The goal is a
|
||||
change a reviewer can verify in one sitting. Cleaner structure that makes the
|
||||
next change easier is the win, not lines added.
|
||||
|
||||
## One concern per change
|
||||
|
||||
- A commit/PR does one thing. A bug fix is not also a rename is not also a
|
||||
reformat. If you spot an unrelated improvement mid-change, leave it or capture
|
||||
it separately; do not fold it in.
|
||||
- When the working tree has bundled two changes, separate them with the
|
||||
copy-affected-files-aside, reset, re-apply one concern, restore the rest
|
||||
pattern, not by committing the tangle.
|
||||
- Refactor and behavior change do not ride together. A pure refactor must not
|
||||
alter behavior; a behavior change should not drag a refactor along. If both
|
||||
are needed: two commits, refactor first.
|
||||
|
||||
## Keep the diff narrow
|
||||
|
||||
- Extract a helper to remove real duplication or to name a concept, not to chase
|
||||
abstraction. Three-plus copies, or a block that needs a name to be understood:
|
||||
extract. Two similar lines: leave them.
|
||||
- No "while I'm here" scope creep. A signature or type change that ripples to
|
||||
many call sites is its own PR: map every caller first, update them in one
|
||||
topological pass, and land it separately, not as a rider on a feature.
|
||||
- Match the surrounding code: comment density, naming, idiom. The diff should
|
||||
read like the file, not like a different author.
|
||||
|
||||
## Decompose oversized units
|
||||
|
||||
An activity or function that has outgrown one screen of responsibility (multiple
|
||||
unrelated state machines, or a file far larger than its siblings) is a
|
||||
decomposition candidate. Extract a cohesive sub-responsibility into its own
|
||||
unit, as a standalone behavior-preserving refactor, verified on its own, never
|
||||
mixed into a feature change.
|
||||
|
||||
## Comments earn their place
|
||||
|
||||
Comments explain why: an invariant, a defense, a past incident, a non-obvious
|
||||
constraint. Never what the next line already says. Delete narration, phase-marker
|
||||
comments ("now we loop over..."), and restated function names. If a comment and
|
||||
the code it sits on say the same thing, the comment is the thing to cut.
|
||||
|
||||
## Self-review before handoff
|
||||
|
||||
- [ ] The change does exactly one thing; nothing unrelated rode along.
|
||||
- [ ] Refactor and behavior change are not mixed in one commit.
|
||||
- [ ] No "while I'm here" creep; rename/signature ripples are split out.
|
||||
- [ ] Extractions remove real duplication or name a real concept, not
|
||||
speculative abstraction.
|
||||
- [ ] New comments say why, not what; no narration or phase markers.
|
||||
- [ ] A reviewer can understand the diff without running it.
|
||||
@@ -1,54 +0,0 @@
|
||||
---
|
||||
name: scope-discipline
|
||||
description: Feature-scope discipline for a dedicated e-reader (not a Swiss Army knife). Use when adding a feature, a new activity, a new lib, a setting, or a dependency, or when a request would grow the firmware's surface. Covers the SCOPE.md test, the RAM-cost vs reading-benefit gate, preferring no-code or existing-mechanism solutions, awareness of the existing activity surface, and how to push back on out-of-scope asks.
|
||||
---
|
||||
|
||||
# Scope Discipline
|
||||
|
||||
The mission: do one thing exceptionally well, focused reading on constrained
|
||||
hardware. `SCOPE.md` is the source of truth for what is in and out. Read it
|
||||
before adding surface. This is the gate to run before writing a new feature.
|
||||
|
||||
## The gate
|
||||
|
||||
Before adding a feature, activity, lib, setting, or dependency, answer in order:
|
||||
|
||||
1. **Is it in `SCOPE.md`?** Explicitly out: interactive apps (notepad,
|
||||
calculator, games), active connectivity (RSS, news, browser), media/audio
|
||||
playback. If it is out, say so and stop.
|
||||
2. **Does it materially improve focused reading?** If the benefit is
|
||||
"nice to have" or serves a different use case, it is out. This is not a PDA.
|
||||
3. **What does it cost in RAM and in the largest-free-block budget?** A feature
|
||||
that adds steady-state RAM or a large transient allocation needs a reading
|
||||
benefit that clearly outweighs it. Quantify with `firmware_size_history.py`
|
||||
and `script_profile_mem.sh` rather than guessing.
|
||||
4. **Can it be done with no new code?** Prefer an existing activity, an existing
|
||||
setting, or a doc over a new code path. The cheapest feature is the one
|
||||
already built.
|
||||
|
||||
If a request fails the gate, push back with the specific reason and the
|
||||
`SCOPE.md` basis, and offer the in-scope alternative. Make the call and say why;
|
||||
do not just hand over a menu.
|
||||
|
||||
## Surface awareness
|
||||
|
||||
The firmware already carries dozens of activities. Each new one is permanent
|
||||
RAM, permanent maintenance, and another thing every future refactor must not
|
||||
break. Default to extending an existing activity or setting before adding a new
|
||||
screen. New top-level surface needs a real justification, not "it would be
|
||||
convenient."
|
||||
|
||||
## Settings are not free
|
||||
|
||||
A new setting is a field to persist, migrate, validate, translate, and render,
|
||||
plus combinatorial test burden. Add one only when users genuinely need the
|
||||
choice; otherwise pick a sensible fixed default.
|
||||
|
||||
## Self-review
|
||||
|
||||
- [ ] Checked against `SCOPE.md`; not on the out-of-scope list.
|
||||
- [ ] Stated the concrete reading benefit, not a generic "useful."
|
||||
- [ ] Named the RAM/size cost (measured, not guessed) and why the benefit wins.
|
||||
- [ ] Checked whether an existing activity/setting/doc already covers it.
|
||||
- [ ] New setting (if any) is justified by a real user need, not added
|
||||
"just in case."
|
||||
@@ -1,13 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Scope or Roadmap Question (start here if unsure)
|
||||
url: https://github.com/crosspoint-reader/crosspoint-reader/discussions
|
||||
about: |
|
||||
Not sure if your idea fits CrossPoint's scope? Start a Discussion before filing an issue.
|
||||
See SCOPE.md and ROADMAP.md for what is in, out, and currently paused.
|
||||
- name: Read the Scope document
|
||||
url: https://github.com/crosspoint-reader/crosspoint-reader/blob/master/SCOPE.md
|
||||
about: The authoritative list of what CrossPoint will and will not accept.
|
||||
- name: Read the Roadmap
|
||||
url: https://github.com/crosspoint-reader/crosspoint-reader/blob/master/ROADMAP.md
|
||||
about: Current phase, what is being closed out, and what comes next.
|
||||
@@ -1,80 +0,0 @@
|
||||
name: Feature Request
|
||||
description: Propose a new feature, enhancement, or change to CrossPoint
|
||||
title: "Short, descriptive title of the request"
|
||||
labels: ["enhancement", "needs-scope-review"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to propose a change to CrossPoint!
|
||||
|
||||
**Before you continue, please read [SCOPE.md](../blob/master/SCOPE.md) and [ROADMAP.md](../blob/master/ROADMAP.md).**
|
||||
|
||||
CrossPoint is intentionally narrow. Most rejected proposals are rejected for scope reasons that are already
|
||||
documented. The checklist below exists to save both of us time.
|
||||
|
||||
If you are not sure whether your idea fits, open a [Discussion](../../discussions) first instead of filing
|
||||
this issue.
|
||||
|
||||
- type: checkboxes
|
||||
id: scope-check
|
||||
attributes:
|
||||
label: Scope Self-Check (required)
|
||||
description: Please confirm each of the following. If any are unchecked, your issue will likely be closed.
|
||||
options:
|
||||
- label: I have read SCOPE.md and ROADMAP.md.
|
||||
required: true
|
||||
- label: This is **not** a new theme or theming change (themes are temporarily closed pending the move to SD-loaded themes).
|
||||
required: true
|
||||
- label: This is **not** a new external network connector (sync engine, cloud storage, remote file access, OPDS extensions beyond what exists, or any new "talk to a server" feature).
|
||||
required: true
|
||||
- label: This is **not** an interactive app (game, calculator, notepad), writing/authoring tool, RSS/news/browser feature, media playback feature, or PDF rendering.
|
||||
required: true
|
||||
- label: The stock firmware does **not** already handle this well.
|
||||
required: true
|
||||
- label: No other popular CrossPoint fork already handles this well (or, if one does, I explain below why CrossPoint still needs it).
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: Problem this solves
|
||||
description: What user-facing problem or reading-experience gap does this address? Be concrete.
|
||||
placeholder: e.g., "When reading in landscape, paragraph breaks are inconsistent because..."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: stock-gap
|
||||
attributes:
|
||||
label: Why the stock firmware (and other forks) do not already solve this
|
||||
description: Explain which existing solutions you checked and why they fall short. This is the core scope filter.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: proposal
|
||||
attributes:
|
||||
label: Proposed change
|
||||
description: A short description of what you would build or change. Focus on user impact; implementation details can come later.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: tradeoffs
|
||||
attributes:
|
||||
label: Memory / flash / complexity cost
|
||||
description: |
|
||||
CrossPoint runs on 380KB of RAM. Roughly how much DRAM, flash, or code complexity does this add?
|
||||
"Don't know" is a valid answer, but please attempt an estimate.
|
||||
placeholder: e.g., "Adds ~2KB flash for the new font tables, no DRAM impact at runtime."
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Anything else relevant (links, screenshots, related discussions).
|
||||
validations:
|
||||
required: false
|
||||
@@ -3,34 +3,16 @@
|
||||
* **What is the goal of this PR?** (e.g., Implements the new feature for file uploading.)
|
||||
* **What changes are included?**
|
||||
|
||||
## Scope Check
|
||||
|
||||
CrossPoint is intentionally narrow. See [SCOPE.md](../blob/master/SCOPE.md) and [ROADMAP.md](../blob/master/ROADMAP.md).
|
||||
Please confirm:
|
||||
|
||||
- [ ] I have read SCOPE.md and ROADMAP.md.
|
||||
- [ ] This PR is **not** a new built-in theme (themes are temporarily closed pending the move to SD-loaded themes).
|
||||
- [ ] This PR is **not** a new external network connector (sync engine, cloud storage, remote file access, etc.).
|
||||
- [ ] This PR is **not** an interactive app, writing tool, RSS/news/browser, media playback, or PDF feature.
|
||||
- [ ] The stock firmware does not already handle this well, **and** no other popular CrossPoint fork already does
|
||||
(or, if one does, I explain why CrossPoint still needs it below).
|
||||
- [ ] If this PR touches `freeink-sdk/`, `lib/hal/`, the bootloader, OTA, or recovery code, I have coordinated with
|
||||
the relevant maintainer.
|
||||
|
||||
**If this PR was opened against the previous (broader) scope and was already in flight under Phase 0, link the
|
||||
relevant Discussion or issue so reviewers can see the history.**
|
||||
|
||||
## Additional Context
|
||||
|
||||
* Add any other information that might be helpful for the reviewer (e.g., performance implications, potential risks,
|
||||
* Add any other information that might be helpful for the reviewer (e.g., performance implications, potential risks,
|
||||
specific areas to focus on).
|
||||
* Memory / flash impact, if known.
|
||||
|
||||
---
|
||||
|
||||
### AI Usage
|
||||
|
||||
While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it
|
||||
While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it
|
||||
helps set the right context for reviewers.
|
||||
|
||||
Did you use AI tools to help write this code? _**< YES | PARTIALLY | NO >**_
|
||||
@@ -53,18 +53,10 @@ jobs:
|
||||
- name: Install PlatformIO Core
|
||||
run: uv pip install --system -U https://github.com/pioarduino/platformio-core/archive/refs/tags/v6.1.19.zip
|
||||
|
||||
- name: Cache PlatformIO packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: pio-${{ runner.os }}-${{ hashFiles('platformio.ini') }}
|
||||
restore-keys: pio-${{ runner.os }}-
|
||||
|
||||
- name: Run cppcheck
|
||||
run: pio check --fail-on-defect low --fail-on-defect medium --fail-on-defect high
|
||||
|
||||
build-default:
|
||||
name: Build default
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
@@ -84,17 +76,10 @@ jobs:
|
||||
- name: Install PlatformIO Core
|
||||
run: uv pip install --system -U https://github.com/pioarduino/platformio-core/archive/refs/tags/v6.1.19.zip
|
||||
|
||||
- name: Cache PlatformIO packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: pio-${{ runner.os }}-${{ hashFiles('platformio.ini') }}
|
||||
restore-keys: pio-${{ runner.os }}-
|
||||
|
||||
- name: Build default firmware
|
||||
- name: Build CrossPoint
|
||||
run: |
|
||||
set -euo pipefail
|
||||
pio run -e default | tee pio.log
|
||||
pio run | tee pio.log
|
||||
|
||||
- name: Extract firmware stats
|
||||
|
||||
@@ -117,89 +102,14 @@ jobs:
|
||||
path: .pio/build/default/firmware.bin
|
||||
if-no-files-found: error
|
||||
|
||||
build-sticky:
|
||||
name: Build sticky
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.14'
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
version: "latest"
|
||||
enable-cache: false
|
||||
|
||||
- name: Install PlatformIO Core
|
||||
run: uv pip install --system -U https://github.com/pioarduino/platformio-core/archive/refs/tags/v6.1.19.zip
|
||||
|
||||
- name: Cache PlatformIO packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: pio-${{ runner.os }}-${{ hashFiles('platformio.ini') }}
|
||||
restore-keys: pio-${{ runner.os }}-
|
||||
|
||||
- name: Build sticky firmware
|
||||
run: |
|
||||
set -euo pipefail
|
||||
pio run -e sticky | tee pio.log
|
||||
|
||||
- name: Extract firmware stats
|
||||
run: |
|
||||
set -euo pipefail
|
||||
ram_line="$(grep -E "RAM:\\s" -m1 pio.log || true)"
|
||||
flash_line="$(grep -E "Flash:\\s" -m1 pio.log || true)"
|
||||
echo "ram_line=${ram_line}" >> "$GITHUB_OUTPUT"
|
||||
echo "flash_line=${flash_line}" >> "$GITHUB_OUTPUT"
|
||||
{
|
||||
echo "## Sticky firmware build stats"
|
||||
if [ -n "$ram_line" ]; then echo "- ${ram_line}"; else echo "- RAM: not found"; fi
|
||||
if [ -n "$flash_line" ]; then echo "- ${flash_line}"; else echo "- Flash: not found"; fi
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
unit-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install build tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y cmake ninja-build
|
||||
|
||||
- name: Cache googletest source
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: build/test/_deps/googletest-src
|
||||
key: ${{ runner.os }}-googletest-${{ hashFiles('test/CMakeLists.txt') }}
|
||||
|
||||
- name: Configure
|
||||
run: cmake -S test -B build/test -G Ninja -DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build/test
|
||||
|
||||
- name: Run tests
|
||||
run: ctest --test-dir build/test --output-on-failure -j
|
||||
|
||||
# This job is used as the PR required actions check, allows for changes to other steps in the future without breaking
|
||||
# PR requirements.
|
||||
test-status:
|
||||
name: Test Status
|
||||
needs:
|
||||
- build-default
|
||||
- build-sticky
|
||||
- build
|
||||
- clang-format
|
||||
- cppcheck
|
||||
- unit-tests
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
name: Compile Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.14'
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
version: "latest"
|
||||
enable-cache: false
|
||||
|
||||
- name: Install PlatformIO Core
|
||||
run: uv pip install --system -U https://github.com/pioarduino/platformio-core/archive/refs/tags/v6.1.19.zip
|
||||
|
||||
- name: Build CrossPoint
|
||||
run: pio run -e gh_release
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: CrossPoint-${{ github.ref_name }}
|
||||
path: |
|
||||
.pio/build/gh_release/bootloader.bin
|
||||
.pio/build/gh_release/firmware.bin
|
||||
.pio/build/gh_release/firmware.elf
|
||||
.pio/build/gh_release/firmware.map
|
||||
.pio/build/gh_release/partitions.bin
|
||||
@@ -25,13 +25,6 @@ jobs:
|
||||
- name: Install PlatformIO Core
|
||||
run: uv pip install --system -U https://github.com/pioarduino/platformio-core/archive/refs/tags/v6.1.19.zip
|
||||
|
||||
- name: Cache PlatformIO packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: pio-${{ runner.os }}-${{ hashFiles('platformio.ini') }}
|
||||
restore-keys: pio-${{ runner.os }}-
|
||||
|
||||
- name: Extract env
|
||||
run: |
|
||||
echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV
|
||||
|
||||
-14
@@ -2,8 +2,6 @@
|
||||
.idea
|
||||
.DS_Store
|
||||
.vscode
|
||||
open-x4-sdk
|
||||
fs_
|
||||
lib/EpdFont/fontsrc
|
||||
lib/I18n/I18nKeys.h
|
||||
lib/I18n/I18nStrings.h
|
||||
@@ -21,15 +19,3 @@ build
|
||||
lib/EpdFont/scripts/downloaded_fonts/
|
||||
lib/EpdFont/scripts/instanced_fonts/
|
||||
lib/EpdFont/scripts/output/
|
||||
# Claude Code: share project skills with the team, keep local agent state
|
||||
# (worktrees, scheduled-task locks, settings.local, scout CLEANUP.md) out.
|
||||
.claude/*
|
||||
!.claude/skills/
|
||||
/managed_components
|
||||
/.dummy
|
||||
/CMakeLists.txt
|
||||
/dependencies.lock
|
||||
/sdkconfig.*
|
||||
sdkconfig.sticky
|
||||
sdkconfig.x4pro
|
||||
flake.*
|
||||
+3
-4
@@ -1,4 +1,3 @@
|
||||
[submodule "freeink-sdk"]
|
||||
path = freeink-sdk
|
||||
url = https://github.com/Free-Ink/freeink-sdk.git
|
||||
branch = main
|
||||
[submodule "open-x4-sdk"]
|
||||
path = open-x4-sdk
|
||||
url = https://github.com/crosspoint-reader/community-sdk.git
|
||||
+80
-189
File diff suppressed because it is too large.
Load diff
@@ -8,11 +8,9 @@ CrossPoint is open-source e-reader firmware - community-built, fully hackable, f
|
||||
|
||||

|
||||
|
||||
> If you're planning to buy an Xteink device, consider purchasing an **X3/X4 Developer Edition** through https://crosspointreader.com. CrossPoint receives a small share of each sale, helping fund development costs.
|
||||
|
||||
## What can CrossPoint do?
|
||||
|
||||
- **Reader engine**: EPUB 2/3 rendering with embedded-style option, image handling, hyphenation, kerning, chapter navigation, footnotes, bookmarks, dictionary lookups ([StarDict](docs/dictionary.md)), go-to-percent, auto page turn, orientation control, focus reading, KOReader progress sync and more.
|
||||
- **Reader engine**: EPUB 2/3 rendering with embedded-style option, image handling, hyphenation, kerning, chapter navigation, footnotes, go-to-percent, auto page turn, orientation control, focus reading, KOReader progress sync and more.
|
||||
|
||||
- **Various formats**: native handling for `.epub`, `.xtc/.xtch`, `.txt`, and `.bmp`.
|
||||
|
||||
@@ -31,17 +29,23 @@ CrossPoint is open-source e-reader firmware - community-built, fully hackable, f
|
||||
- Web settings UI/API (edit many device settings from browser)
|
||||
- WebSocket fast uploads
|
||||
- WebDAV handler
|
||||
- AP mode (hotspot) and STA mode (join existing Wi-Fi), both with QR helpers
|
||||
- AP mode (hotspot) and STA mode (join existing WiFi), both with QR helpers
|
||||
- Calibre wireless connect flow
|
||||
- OPDS browser with saved servers (up to 8), search, pagination, and direct download
|
||||
- OTA update checks and installs from GitHub releases
|
||||
|
||||
- **Customization**: multiple themes (Classic, Lyra, Lyra Extended, RoundedRaff), sleep screen modes, front/side button remapping, status bar controls, power-button behavior, refresh cadence, and more.
|
||||
|
||||
- **Localization**: 24 UI languages and counting. RTL support.
|
||||
- **Localization**: 22 UI languages and counting.
|
||||
|
||||
### Coming soon:
|
||||
|
||||
- RTL support — Arabic, Hebrew, and Farsi.
|
||||
|
||||
- Bookmarks.
|
||||
|
||||
- Dictionary lookup — inline word lookup without leaving the reader.
|
||||
|
||||
- More themes.
|
||||
|
||||
- Much more! stay tuned.
|
||||
@@ -67,6 +71,10 @@ USB port or browser before assuming the device is locked. Only reach for the unl
|
||||
> Flashing any other firmware on a USB-locked device may **permanently brick the device** or leave it **permanently
|
||||
> stuck on that firmware with no recovery path**. Once USB flashing is re-locked, your only way back is via OTA, and if
|
||||
> the firmware you flashed doesn't support OTA, **there is no way out**.
|
||||
>
|
||||
> **The Papyrix fork has removed OTA update support from its code.** If you flash Papyrix onto a
|
||||
> USB-locked unit, you will have **zero update or recovery path** and will be stuck on it forever. **Do not flash
|
||||
> Papyrix (or any other unsupported firmware) on a locked device.**
|
||||
|
||||
## Install firmware
|
||||
|
||||
@@ -136,7 +144,6 @@ Conversion runs the firmware repo's `lib/EpdFont/scripts/fontconvert_sdcard.py`
|
||||
- [Web server endpoints](./docs/webserver-endpoints.md)
|
||||
- [Project scope](./SCOPE.md)
|
||||
- [Contributing docs](./docs/contributing/README.md)
|
||||
- [Touch and UI development](./docs/contributing/touch-and-ui.md) - how to build new screens on the FreeInkUI activity bases (UiListActivity and friends), plus build envs for the non-Xteink touch devices
|
||||
|
||||
---
|
||||
|
||||
@@ -159,24 +166,6 @@ cd crosspoint-reader
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
### Nix/NixOS
|
||||
|
||||
Nix/NixOS users can enter the development shell with either `nix develop` (flakes) or `nix-shell`:
|
||||
|
||||
```bash
|
||||
nix develop -f nix
|
||||
# or
|
||||
nix-shell nix
|
||||
```
|
||||
|
||||
To flash a connected ESP32-C3 device, enable PlatformIO's udev rules in your NixOS configuration:
|
||||
|
||||
```nix
|
||||
services.udev.packages = with pkgs; [ platformio-core.udev ];
|
||||
```
|
||||
|
||||
After rebuilding the system configuration, reconnect the device or reload udev rules.
|
||||
|
||||
### Build / flash / monitor
|
||||
|
||||
```bash
|
||||
@@ -222,7 +211,7 @@ CrossPoint Reader is pretty aggressive about caching data down to the SD card to
|
||||
|
||||
### Data caching
|
||||
|
||||
The first time chapters of a book are loaded, they are cached to the SD card. Subsequent loads are served from the
|
||||
The first time chapters of a book are loaded, they are cached to the SD card. Subsequent loads are served from the
|
||||
cache. This cache directory exists at `.crosspoint` on the SD card. The structure is as follows:
|
||||
|
||||
```text
|
||||
@@ -231,18 +220,13 @@ cache. This cache directory exists at `.crosspoint` on the SD card. The structur
|
||||
│ ├── progress.bin # reading position (chapter, page, etc.)
|
||||
│ ├── cover.bmp # generated cover image
|
||||
│ ├── book.bin # metadata: title, author, spine, TOC
|
||||
│ ├── css_rules.cache # parsed CSS rule cache
|
||||
│ ├── img_* # rendered image cache files
|
||||
│ └── sections/ # per-chapter layout cache
|
||||
│ ├── 0.bin
|
||||
│ ├── 1.bin
|
||||
│ └── ...
|
||||
├── settings.json # device settings
|
||||
├── state.json # resume/runtime state
|
||||
└── recent.json # recent books list
|
||||
```
|
||||
|
||||
Removing `/.crosspoint` clears all cached metadata and forces a full regeneration on next open. Book deletes, overwrites, and moves done through the firmware or web UI clear or re-key matching caches; manual SD-card edits may leave stale cache directories behind.
|
||||
Removing `/.crosspoint` clears all cached metadata and forces a full regeneration on next open. Note: the cache isn't cleared automatically when you delete a book, and moving a file to a new path resets its reading progress.
|
||||
|
||||
For more details on the internal file structures, see the [file formats document](./docs/file-formats.md).
|
||||
|
||||
@@ -264,7 +248,9 @@ One of the best things about open source is that anyone can take the code in a d
|
||||
|
||||
- [papyrix-reader](https://github.com/bigbag/papyrix-reader) — Adds FB2 and MD format support. Actively maintained with Arabic script support. Custom themes via SD card.
|
||||
|
||||
- ~~[crosspet](https://github.com/trilwu/crosspet) — A Vietnamese fork that adds a Tamagotchi-style virtual chicken that grows based on your reading milestones (pages read, streaks, care). Also: Flashcards, Weather, Pomodoro timer, and mini-games.~~ (Unmaintained)
|
||||
- [crosspet](https://github.com/trilwu/crosspet) — A Vietnamese fork that adds a Tamagotchi-style virtual chicken that grows based on your reading milestones (pages read, streaks, care). Also: Flashcards, Weather, Pomodoro timer, and mini-games.
|
||||
|
||||
- [crosspoint-reader (jpirnay)](https://github.com/jpirnay/crosspoint-reader) — Faster integration of functionality. Tracks upstream PRs and integrates the good ones ahead of the official merge.
|
||||
|
||||
- [crosspoint-reader-cjk](https://github.com/aBER0724/crosspoint-reader-cjk) — Purpose-built for Chinese, Japanese, and Korean reading.
|
||||
|
||||
@@ -274,8 +260,6 @@ One of the best things about open source is that anyone can take the code in a d
|
||||
|
||||
- [crosspoint-reader-papers3](https://github.com/juicecultus/crosspoint-reader-papers3) — Crosspoint port for M5Stack Paper S3.
|
||||
|
||||
- [t5s3-reader](https://github.com/ShallowGreen123/t5s3-reader) — Crosspoint port for LilyGo T5 ePaper S3 / T5S3 4.7-inch e-paper device.
|
||||
|
||||
**Note:** Many of these features will make their way into CrossPoint over time. We maintain a slower pace to ensure rock-solid stability and squash bugs before they reach your device.
|
||||
|
||||
Want to build your own device? Be sure to check out the [de-link](https://github.com/iandchasse/de-link) project.
|
||||
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
# CrossPoint Reader Roadmap
|
||||
|
||||
This roadmap describes how CrossPoint is moving through the tighter scope defined in [SCOPE.md](SCOPE.md). It is
|
||||
intentionally phased: Phase 0 closed out the commitments already in flight before locking down to the stricter
|
||||
"fill gaps the stock firmware leaves" delineator.
|
||||
|
||||
Phases are sequential. We do not start the next phase until the prior one is wrapped or explicitly carried over.
|
||||
|
||||
---
|
||||
|
||||
## Phase 0 - Close Out Legacy Scope Items — **COMPLETE**
|
||||
|
||||
**Goal:** Land the work that was already in motion under the prior, broader scope so contributors are not left
|
||||
hanging, and so we enter the stricter phases with a clean slate.
|
||||
|
||||
**Landed in Phase 0:**
|
||||
|
||||
* **RTL support PRs.** The in-flight right-to-left work was reviewed, iterated, and merged.
|
||||
* **Dictionary PR.** The offline dictionary lookup work was reviewed and merged.
|
||||
* **Bookmarks** feature. First-class navigation markers in EPUBs.
|
||||
* ~~**Transparent sleep screens.**~~ Shelved; not picked back up under the stricter phases.
|
||||
|
||||
Phase 0 is closed. The tighter scope in [SCOPE.md](SCOPE.md) is now fully enforced. "But it was on the old roadmap"
|
||||
is not a valid argument for accepting a PR.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 - Consolidation, Footprint, and Multi-Device Support — **IN PROGRESS**
|
||||
|
||||
**Goal:** Reduce memory and flash usage, clean up the codebase, and land the SDK / HAL generalization work so
|
||||
CrossPoint runs cleanly on ESP32-based e-reader hardware beyond Xteink (X3 / X4), including ESP32-S3 class devices.
|
||||
|
||||
**Focus areas:**
|
||||
|
||||
* DRAM and heap fragmentation reduction across the reader core.
|
||||
* Flash footprint reduction (dead code, redundant strings, oversized tables).
|
||||
* Refactors that tighten the HAL / SDK boundary.
|
||||
* Pluggable per-device SDK layers (display, input, storage, battery) and per-device build configuration without
|
||||
forking the reader core.
|
||||
* Documentation for adding a new ESP32 e-reader target.
|
||||
* E-ink driver refinement (ghosting, partial update behavior).
|
||||
|
||||
**Closed during this phase:** new themes built into firmware, new external network connectors (sync engines, cloud
|
||||
storage, remote file access).
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 - Languages, Fonts, and Themes
|
||||
|
||||
**Goal:** With the codebase smaller and portable, make reading great in every language: multi-language support,
|
||||
better font support with custom fonts, UI translations, and themes loaded from the SD card instead of consuming
|
||||
flash.
|
||||
|
||||
**Focus areas:**
|
||||
|
||||
* Multi-language reading support (underserved languages, complex script support where realistic on ESP32 hardware).
|
||||
* Better font support and custom fonts.
|
||||
* UI languages and localization.
|
||||
* Moving themes off-firmware to SD-loaded assets (see SCOPE.md Section 6).
|
||||
* **Moving hyphenation files off-firmware.** Hyphenation rules vary per language and the files are large (German
|
||||
alone is ~200KB). Today these eat flash budget that should be available for the reader core. The plan is to build
|
||||
a downloader analogous to the existing font downloader and store the dictionaries on SD / SPIFFS, loading on
|
||||
demand. This unlocks better hyphenation for long-word languages (German, Finnish, Norwegian, etc.) without paying
|
||||
the flash cost up front.
|
||||
|
||||
This phase depends on Phase 1 cleanup landing first; otherwise we generalize a moving target.
|
||||
|
||||
---
|
||||
|
||||
## Out of Roadmap
|
||||
|
||||
The following are explicitly *not* on the roadmap. They may live in other CrossPoint forks; they will not be picked
|
||||
up here:
|
||||
|
||||
* Interactive apps (games, calculators, notepads).
|
||||
* Writing / authoring tools.
|
||||
* Active connectivity features (RSS, news, browsers).
|
||||
* PDF rendering as a first-class format.
|
||||
|
||||
See [SCOPE.md](SCOPE.md) for the full rationale.
|
||||
|
||||
---
|
||||
|
||||
## How This Roadmap Changes
|
||||
|
||||
* Phase boundaries are decided by maintainers, not by individual PRs.
|
||||
* If a phase needs to be extended or an item carried over, that is documented here with a short note.
|
||||
* Proposals for new phases or reordering should go through a Discussion first.
|
||||
@@ -1,100 +1,62 @@
|
||||
# Project Vision & Scope: CrossPoint Reader
|
||||
|
||||
The goal of CrossPoint Reader is to create an efficient, open-source reading experience for ESP32-based e-reader devices. Xteink hardware (X3, X4) is where the project started and remains a primary target, but CrossPoint is explicitly broadening to support the wider ecosystem of small ESP32 e-ink readers. We believe a dedicated e-reader should do one thing exceptionally well: **facilitate focused reading.**
|
||||
The goal of CrossPoint Reader is to create an efficient, open-source reading experience for the Xteink X4. We believe a
|
||||
dedicated e-reader should do one thing exceptionally well: **facilitate focused reading.**
|
||||
|
||||
## 1. Core Mission
|
||||
|
||||
To provide a lightweight, high-performance firmware that maximizes the potential of ESP32-based e-reader hardware, prioritizing legibility, performance, and usability over "swiss-army-knife" functionality.
|
||||
To provide a lightweight, high-performance firmware that maximizes the potential of the X4, prioritizing legibility and
|
||||
usability over "swiss-army-knife" functionality.
|
||||
|
||||
CrossPoint is **not** a kitchen-sink firmware, and it is **not** Xteink-only. We want clean, maintainable code that the community can build on, and that runs across the range of ESP32 e-reader devices (ESP32-C3, ESP32-S3, and adjacent variants). Every accepted change should make that goal easier, not harder. Device-specific code should live behind the HAL / SDK boundary so the reader core stays portable.
|
||||
|
||||
## 2. Guiding Principle: Fill Gaps the Stock Firmware Leaves
|
||||
|
||||
CrossPoint exists to do the things the stock firmware does poorly or not at all. New work is evaluated against that delineator:
|
||||
|
||||
* **Does the stock firmware already do this well?** We should hit that bar or surpass it
|
||||
* **Is another popular CrossPoint fork already solving this well?** If yes, we generally defer to that fork if it's not part of the core reading experience. e.g. stats
|
||||
* **Does this directly improve the reading experience or the firmware's long-term maintainability?** If no, it is out of scope.
|
||||
|
||||
## 3. Current Focus
|
||||
|
||||
We are intentionally narrowing scope to consolidate the codebase as we open it up to more ESP32 e-reader devices.
|
||||
|
||||
During this period, the priorities are:
|
||||
|
||||
* **Memory footprint:** Reducing DRAM usage and heap fragmentation. The ESP32-C3 is the tightest target and sets the ceiling, but the gains benefit every ESP32 variant we run on.
|
||||
* **Flash footprint:** Trimming binary size to leave room for additional device targets and features.
|
||||
* **Code cleanup:** Refactoring, removing dead code, tightening abstractions, and improving readability.
|
||||
* **Reading experience:** EPUB parsing and rendering, typography, hyphenation, line spacing, font handling, and legibility improvements.
|
||||
|
||||
### Temporarily Closed Areas
|
||||
|
||||
PRs in the following areas will be closed until this notice is lifted. Adding these now makes the cleanup and multi-device work materially harder:
|
||||
|
||||
* **New themes.** The existing theming surface is frozen.
|
||||
* **New external network connectors.** This includes sync engines, cloud storage clients, OPDS extensions beyond what exists, remote file access, and any new "talk to a server" feature. We now have our own CrossPoint KOSync server which gives us a way to sync to 3rd party systems like Hardcover at an API level instead of bloating the firmware. If you're interested in helping here, the sync server is also open source.
|
||||
|
||||
If you are unsure whether your idea falls into one of these categories, open a Discussion first.
|
||||
|
||||
## 4. Scope
|
||||
## 2. Scope
|
||||
|
||||
### In-Scope
|
||||
|
||||
*Features that directly improve the core reading experience or the firmware's maintainability.*
|
||||
*These are features that directly improve the primary purpose of the device.*
|
||||
|
||||
* **EPUB Rendering & Optimization:** Improvements to the rendering engine, CSS/image handling, and parsing performance.
|
||||
* **Typography & Legibility:** Custom font support, hyphenation, line and paragraph spacing, margins.
|
||||
* **E-Ink Driver Refinement:** Reducing full-screen flashes (ghosting management) and improving general rendering.
|
||||
* **Reading UX:** Bookmarks, progress tracking, button mapping, page navigation, and other in-reader interactions.
|
||||
* **Library Management:** Simple, intuitive ways to organize and navigate a local book collection.
|
||||
* **Memory, Flash, and Code Quality:** Refactors and cleanups that reduce resource use or improve maintainability, even without a user-visible feature.
|
||||
* **User Experience:** E.g. User-friendly interfaces, and interactions, both inside the reader and navigating the
|
||||
firmware. This includes things like button mapping, book loading, and book navigation like bookmarks.
|
||||
* **Document Rendering:** E.g. Support for rendering documents (primarily EPUB) and improvements to the rendering
|
||||
engine.
|
||||
* **Format Optimization:** E.g. Efficiently parsing EPUB (CSS/Images) and other documents within the device's
|
||||
capabilities.
|
||||
* **Typography & Legibility:** E.g. Custom font support, hyphenation engines, and adjustable line spacing.
|
||||
* **E-Ink Driver Refinement:** E.g. Reducing full-screen flashes (ghosting management) and improving general rendering.
|
||||
* **Library Management:** E.g. Simple, intuitive ways to organize and navigate a collection of books.
|
||||
* **Local Transfer:** E.g. Simple, "pull" based book loading via a basic web-server or public and widely-used standards.
|
||||
* **Language Support:** E.g. Support for multiple languages both in the reader and in the interfaces.
|
||||
* **Reference Tools:** E.g. Local dictionary lookup. Providing quick, offline definitions to enhance comprehension
|
||||
without breaking focus.
|
||||
* **Clock Display (device dependent):**
|
||||
|
||||
| Device | Scope |
|
||||
| -- | -- |
|
||||
| X3 | The X3 uses a dedicated DS3231 RTC, which maintains accurate time across sleep cycles and can be treated as a reliable wall clock. |
|
||||
| X4 | The X4 relies on the ESP32-C3's internal RTC, which drifts significantly during deep sleep. NTP sync could correct this, with an appropriate user experience around connecting to the internet on wake or on demand. This causes some tension with the **Active Connectivity** section below, so please open a discussion about this UX if it's a feature you would find useful. |
|
||||
|
||||
### Out-of-Scope
|
||||
|
||||
*Rejected because they compromise the device's stability, maintainability, or core mission.*
|
||||
*These items are rejected because they compromise the device's stability or mission.*
|
||||
|
||||
* **Interactive Apps:** No notepads, calculators, or games. These belong in other forks and are not part of CrossPoint's focus.
|
||||
* **Writing / Authoring Tools:** No typed notes, journals, or editors. Input hardware and RAM are wrong for this, and other forks already explore this space.
|
||||
* **Active Connectivity:** No RSS readers, news aggregators, or web browsers. Background Wi-Fi drains the battery and complicates the single-core CPU.
|
||||
* **PDF Rendering:** PDFs are fixed-layout documents, so rendering them requires displaying pages as images rather than reflowable text, resulting in constant panning and zooming that makes for a poor reading experience on e-ink. Out of scope on the current hardware class.
|
||||
* **Interactive Apps:** No Notepads, Calculators, or Games. This is a reader, not a PDA.
|
||||
* **Active Connectivity:** No RSS readers, News aggregators, or Web browsers. Background Wi-Fi tasks drain the battery
|
||||
and complicate the single-core CPU's execution.
|
||||
* **Media Playback:** No Audio players or Audio-books.
|
||||
* **Complex Annotation:** No typed out notes. These features are better suited for devices with better input
|
||||
capabilities and more powerful chips.
|
||||
|
||||
## 5. Calls to Action
|
||||
### In-scope — Technically Unsupported
|
||||
|
||||
These are the areas where contributor help is most valuable right now. If you want to take one of these on, open a Discussion or issue first so we can coordinate.
|
||||
*These features align with CrossPoint's goals but are impractical on the current hardware or produce poor UX.*
|
||||
|
||||
### Theme System: Move Themes Off-Firmware
|
||||
* **PDF Rendering:** PDFs are fixed-layout documents, so rendering them requires displaying pages as images rather than reflowable text — resulting in constant panning and zooming that makes for a poor reading experience on e-ink.
|
||||
|
||||
We want to abstract themes out of the firmware entirely so they no longer consume flash, and instead load from the SD card. This directly supports the current focus on flash footprint and code cleanup.
|
||||
## 3. Idea Evaluation
|
||||
|
||||
* **Status:** [@itsthisjustin](https://github.com/itsthisjustin) plans to take this on eventually but is very open to someone else claiming it sooner.
|
||||
* **Why it matters:** Every built-in theme costs flash that we would rather spend on rendering, fonts, or future device support. SD-loaded themes also let users customize without rebuilding firmware. It also leads to SD font loading for better language support in the UI.
|
||||
* **How to claim:** Comment on the relevant Discussion (or open one) before starting.
|
||||
While I appreciate the desire to add new and exciting features to CrossPoint Reader, CrossPoint Reader is designed to be
|
||||
a lightweight, reliable, and performant e-reader. Things which distract or compromise the device's core mission will not
|
||||
be accepted. As a guiding question, consider if your idea improve the "core reading experience" for the average user,
|
||||
and, critically, not distract from that reading experience.
|
||||
|
||||
### Identifying Other Stock-Firmware Gaps
|
||||
|
||||
We want help cataloguing things the stock firmware (and other popular CrossPoint forks) handle poorly or not at all, so future work has a clear target list. Particularly interested in:
|
||||
|
||||
* **RTL (right-to-left) text support:** Arabic, Hebrew, Persian, and similar scripts.
|
||||
* **Languages with poor stock and fork coverage:** Especially those that need shaping, complex layout, or non-Latin font work that nobody is handling well today.
|
||||
* **Other gaps:** Rendering edge cases, accessibility issues, input quirks, anything stock does badly and existing forks have not fixed.
|
||||
|
||||
If you can read or use the device in one of these languages, your feedback (even without code) is genuinely useful. Open a Discussion with concrete examples (screenshots, sample EPUBs, expected vs actual behavior) and we will prioritize from there.
|
||||
|
||||
## 6. Funding and Contributor Sustainability
|
||||
|
||||
CrossPoint uses [Royalty.dev](https://royalty.dev) (yes, a product built by [@itsthisjustin](https://github.com/itsthisjustin)) to fund contributors. There has been some tension in the community around this, so the intent is being clarified here directly.
|
||||
|
||||
**Why we do this:**
|
||||
|
||||
* To maintain long-term interest from contributors and maintainers, in direct response to substantial community requests for a way to give back.
|
||||
* To motivate contributors to invest in the *core* project rather than spinning up competing forks.
|
||||
* To help pay for new ESP32 devices so we can port CrossPoint to additional hardware.
|
||||
* To give the project a credible long-term path to sustainability.
|
||||
|
||||
**How it works:**
|
||||
|
||||
* Funds are distributed automatically to contributors based on impact to the codebase and tenure on the project.
|
||||
* Over **$600** was raised in the first few hours after opening up funding, which is a signal the demand is real.
|
||||
* The exact scoring methodology is published at <https://app.royalty.dev/transparency>.
|
||||
|
||||
**This is not fixed in stone.** The weighting, eligibility, and distribution rules can be tweaked as we learn what works for this project. If you have concerns or suggestions about how funds are allocated, open a Discussion. The goal is a system that fairly recognizes the people doing the work, not a perfect one on day one.
|
||||
> **Note to Contributors:** If you are unsure if your idea fits the scope, please open a **Discussion** before you start
|
||||
> coding!
|
||||
+76
-327
File diff suppressed because it is too large.
Load diff
Loaded 20 of 477 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user