Move cardToMiddle vertical-centering fix into the family-chart patch
Fold the fly-to vertical-centering fix into our patch-package patch (alongside the existing spouse-layout fix) instead of compensating in app code, and revert the in-app workaround so the two don't double-correct. - patches/family-chart+0.9.0.patch: cardToMiddle now scales datum.y by the zoom k in both dist builds (.js + .esm.js), matching datum.x. Verified the patch applies cleanly (patch-package --error-on-fail). - tree/page.tsx: the cardToMiddle caller passes raw y again (the patched library does the scaling now); pre-scaling here too would double-correct. Behavior is identical to the previous in-app fix — both center the node exactly. - CLAUDE.md: documents the two family-chart patches, how to regenerate them, and that both should be upstreamed. The cardToMiddle fix is submitted upstream (donatso/family-chart#103, issue #102); the spouse-layout fix is a TODO. The frontend Dockerfile already COPYs patches/ before npm ci, so the fix is in the production build. Signed-off-by: Justin Paul <justin@jpaul.me>
This commit is contained in:
@@ -77,6 +77,19 @@ Don't get ahead of the phases. GEDCOM and the assistant's propose-diff foundatio
|
|||||||
- **Privacy/assistant/hint code gets extra care** — these are the areas where bugs do real harm. Prefer a design note before a large change.
|
- **Privacy/assistant/hint code gets extra care** — these are the areas where bugs do real harm. Prefer a design note before a large change.
|
||||||
- **No secrets in the repo.** Config via env; provide `.env.example` with placeholders.
|
- **No secrets in the repo.** Config via env; provide `.env.example` with placeholders.
|
||||||
|
|
||||||
|
## Patched dependencies (family-chart)
|
||||||
|
|
||||||
|
The tree view uses **family-chart** (d3-based). Two adjustments live in the repo:
|
||||||
|
|
||||||
|
- **CSS is vendored** at `frontend/app/trees/[id]/tree/chart.css` — the package blocks its CSS subpath export, so we copy it in.
|
||||||
|
- **The library is patched** via `patch-package` (`frontend/patches/family-chart+0.9.0.patch`, applied by the `postinstall` hook; the backend/frontend Dockerfiles `COPY patches` before install). Both hunks touch `dist/family-chart.js` **and** `dist/family-chart.esm.js` (the app loads the `esm` build). Current fixes:
|
||||||
|
1. **Spouse-centering layout** (`setupSpouses` / `sortChildrenWithSpouses`) — center a person between two spouses with children under the correct pair.
|
||||||
|
2. **`cardToMiddle` vertical centering** — the lib scaled `datum.x` by the zoom factor `k` but not `datum.y`, so "fly to a node" drifted vertically at any zoom ≠ 1; we add the missing `* k`.
|
||||||
|
|
||||||
|
To change a patch: edit the file(s) under `node_modules/family-chart/dist/`, then `cd frontend && npx patch-package family-chart` to regenerate, and verify with `npx patch-package --error-on-fail`.
|
||||||
|
|
||||||
|
**Upstream these.** Both are general library bugfixes, not app-specific. The `cardToMiddle` fix is submitted — **donatso/family-chart#103** (issue **#102**). The spouse-layout fix still needs upstreaming; do it when there's time. When a fixed release ships, drop the corresponding patch hunk **and** remove any in-app compensation (e.g. the `cardToMiddle` caller in `tree/page.tsx` passes raw `y` precisely because the patch fixes it — pre-scaling there too would double-correct).
|
||||||
|
|
||||||
## License & contribution terms
|
## License & contribution terms
|
||||||
|
|
||||||
Provenance is **source-available** under **BUSL-1.1** (see [LICENSE](LICENSE)): free for personal/family/non-commercial use, no third-party commercial hosting, and each release converts to **AGPL-3.0** four years after it ships. The DCO sign-off keeps the licensing chain clean so the maintainer can manage that conversion and a possible future hosted offering. Don't add code under an incompatible license, and don't vendor dependencies whose licenses conflict with eventual AGPL distribution.
|
Provenance is **source-available** under **BUSL-1.1** (see [LICENSE](LICENSE)): free for personal/family/non-commercial use, no third-party commercial hosting, and each release converts to **AGPL-3.0** four years after it ships. The DCO sign-off keeps the licensing chain clean so the maintainer can manage that conversion and a possible future hosted offering. Don't add code under an incompatible license, and don't vendor dependencies whose licenses conflict with eventual AGPL distribution.
|
||||||
|
|||||||
@@ -315,12 +315,12 @@ export default function TreePage() {
|
|||||||
try {
|
try {
|
||||||
const rect = svg.getBoundingClientRect();
|
const rect = svg.getBoundingClientRect();
|
||||||
const scale = handlers.getCurrentZoom ? handlers.getCurrentZoom(svg).k : 1;
|
const scale = handlers.getCurrentZoom ? handlers.getCurrentZoom(svg).k : 1;
|
||||||
// family-chart's cardToMiddle scales datum.x by the zoom but NOT
|
// cardToMiddle centers the datum at the current zoom. (Its vertical
|
||||||
// datum.y (a library bug), so vertical centering is only correct at
|
// centering at non-1 zoom is fixed in our family-chart patch — see
|
||||||
// scale 1 and drifts by datum.y·(k−1) otherwise — landing "below the
|
// CLAUDE.md / upstream PR donatso/family-chart#103 — so we pass the
|
||||||
// tree". Pre-multiply y by the scale to cancel the missing ·k.
|
// raw y; do NOT pre-scale it here or it double-corrects.)
|
||||||
handlers.cardToMiddle({
|
handlers.cardToMiddle({
|
||||||
datum: { x: xy.x, y: xy.y * scale },
|
datum: xy,
|
||||||
svg,
|
svg,
|
||||||
svg_dim: { width: rect.width, height: rect.height },
|
svg_dim: { width: rect.width, height: rect.height },
|
||||||
scale,
|
scale,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
diff --git a/node_modules/family-chart/dist/family-chart.esm.js b/node_modules/family-chart/dist/family-chart.esm.js
|
diff --git a/node_modules/family-chart/dist/family-chart.esm.js b/node_modules/family-chart/dist/family-chart.esm.js
|
||||||
index 3867be0..560c99e 100644
|
index 3867be0..656fafa 100644
|
||||||
--- a/node_modules/family-chart/dist/family-chart.esm.js
|
--- a/node_modules/family-chart/dist/family-chart.esm.js
|
||||||
+++ b/node_modules/family-chart/dist/family-chart.esm.js
|
+++ b/node_modules/family-chart/dist/family-chart.esm.js
|
||||||
@@ -10,10 +10,10 @@ function sortChildrenWithSpouses(children, datum, data) {
|
@@ -10,10 +10,10 @@ function sortChildrenWithSpouses(children, datum, data) {
|
||||||
@@ -61,8 +61,17 @@ index 3867be0..560c99e 100644
|
|||||||
if (!d.spouses)
|
if (!d.spouses)
|
||||||
d.spouses = [];
|
d.spouses = [];
|
||||||
d.spouses.push(spouse);
|
d.spouses.push(spouse);
|
||||||
|
@@ -1073,7 +1091,7 @@ function calculateTreeFit(svg_dim, tree_dim) {
|
||||||
|
return { k, x, y };
|
||||||
|
}
|
||||||
|
function cardToMiddle({ datum, svg, svg_dim, scale, transition_time }) {
|
||||||
|
- const k = scale || 1, x = svg_dim.width / 2 - datum.x * k, y = svg_dim.height / 2 - datum.y, t = { k, x: x / k, y: y / k };
|
||||||
|
+ const k = scale || 1, x = svg_dim.width / 2 - datum.x * k, y = svg_dim.height / 2 - datum.y * k, t = { k, x: x / k, y: y / k };
|
||||||
|
positionTree({ t, svg, transition_time });
|
||||||
|
}
|
||||||
|
function manualZoom({ amount, svg, transition_time = 500 }) {
|
||||||
diff --git a/node_modules/family-chart/dist/family-chart.js b/node_modules/family-chart/dist/family-chart.js
|
diff --git a/node_modules/family-chart/dist/family-chart.js b/node_modules/family-chart/dist/family-chart.js
|
||||||
index 1c750d4..47efcc2 100644
|
index 1c750d4..edeb804 100644
|
||||||
--- a/node_modules/family-chart/dist/family-chart.js
|
--- a/node_modules/family-chart/dist/family-chart.js
|
||||||
+++ b/node_modules/family-chart/dist/family-chart.js
|
+++ b/node_modules/family-chart/dist/family-chart.js
|
||||||
@@ -33,10 +33,9 @@
|
@@ -33,10 +33,9 @@
|
||||||
@@ -116,3 +125,12 @@ index 1c750d4..47efcc2 100644
|
|||||||
if (!d.spouses)
|
if (!d.spouses)
|
||||||
d.spouses = [];
|
d.spouses = [];
|
||||||
d.spouses.push(spouse);
|
d.spouses.push(spouse);
|
||||||
|
@@ -1096,7 +1106,7 @@
|
||||||
|
return { k, x, y };
|
||||||
|
}
|
||||||
|
function cardToMiddle({ datum, svg, svg_dim, scale, transition_time }) {
|
||||||
|
- const k = scale || 1, x = svg_dim.width / 2 - datum.x * k, y = svg_dim.height / 2 - datum.y, t = { k, x: x / k, y: y / k };
|
||||||
|
+ const k = scale || 1, x = svg_dim.width / 2 - datum.x * k, y = svg_dim.height / 2 - datum.y * k, t = { k, x: x / k, y: y / k };
|
||||||
|
positionTree({ t, svg, transition_time });
|
||||||
|
}
|
||||||
|
function manualZoom({ amount, svg, transition_time = 500 }) {
|
||||||
|
|||||||
Reference in New Issue
Block a user