Ledger primitives
@cynco/journals renders journal entries and virtualized registers. @cynco/accounts renders the chart of accounts. Vanilla TypeScript core, React adapters, declarative shadow DOM SSR. Amounts are integer minor units — no floats, anywhere.
v0.1.0-beta.1 · MIT · TypeScript · web components + React
Journal entries, server-rendered
A six-posting payroll run — EPF and SOCSO splits — rendered on the server as declarative shadow DOM and adopted at hydration, with no client re-render. Every color resolves through the @cynco/theme chain: override → role → default, per color scheme.
A register that scales
10,000 seeded entries against one cash account. Fixed row heights reduce windowing to arithmetic — no measurement, no layout thrash — so the register mounts the same number of DOM nodes at 100 rows or 100,000.
10,000 entries · … register rows · a seeded fixture, so every visit renders byte-identical data.
Comfortable
Payee and narration stacked — two lines per row.
Compact
One line per row — twice the rows per viewport.
Registers also take a projection-level filter: setFilter reshapes the visible rows in place — group summaries recomputed over the matched rows, matched substrings highlighted — while selection and every public index stay in full-data space.
Reconciliation as conflict resolution
Statement lines left, book postings right, proposed matches as tinted pairs. Accept ✓ or reject ✗ from the center gutter, like resolving a merge conflict. The difference is integer arithmetic and reads reconciled only at exactly zero.
Hover a matched pair to accept or reject it, or accept every exact match at once. Matching is deterministic and 1:1 — read the matching engine docs.
| Indicator | State | Description |
|---|---|---|
proposed | Suggested by the matching engine — accept or reject | |
accepted | Cleared against the statement | |
outstanding | In the books, not yet presented to the bank | |
missing | On the statement, no book entry — create one |
Every edit, diffed like code
Two versions of one entry rendered as an audit-trail diff — word highlights on the narration, tag and link pills added, postings aligned by account and currency with exact before/after amounts. Server-rendered via preloadEntryDiffHTML and adopted at hydration, like every card on this page.
The revised quote splits out a delivery leg — both versions balance to exactly zero, and the diff is pure data from diffEntryVersions.
The chart of accounts as a tree
Canonical colon-delimited paths — Assets:Current:Cash-Maybank — materialize into a keyboard-navigable tree with rolled-up balances and status dots. A file tree with git status, for accounts.
250 entries · rolled-up balances · status dots via setAccountStatus() · search expands ancestors of every match and restores the previous expansion on clear.
Expanded
Every status dot sits on the account that owns it.
Status roll-up
Collapsed groups inherit the highest-severity descendant status.
Honest about imbalance
The store never repairs a broken entry. When postings don't sum to zero per currency, the renderer flags the entry and reports the exact residual.
The cash leg above is exactly RM 1.00 short — rendered, flagged, never repaired.
Built at Cynco
Accounting infrastructure, built the way systems engineers build databases: integer minor units end to end, entries that balance by construction, rendering that holds at six-figure row counts. These packages are the primitives under everything we ship.