Skip storage objects touched less than 2 hours ago, matching the pattern
used by upload-session-gc. Update all affected tests to advance the clock
past the threshold using ct/*clock* bindings.
Three critical fixes for app.common.geom.shapes.grid-layout.layout-data:
1. case dispatch on runtime booleans in get-cell-data (case→cond fix)
In get-cell-data, column-gap and row-gap were computed with (case ...)
using boolean locals auto-width? and auto-height? as dispatch values.
In Clojure/ClojureScript, case compares against compile-time constants,
so those branches never matched at runtime. Replaced both case forms
with cond, using explicit equality tests for keyword branches.
2. divide-by-zero guards in fr/auto/span calc (JVM ArithmeticException fix)
Guard against JVM ArithmeticException when all grid tracks are fixed
(no flex or auto tracks):
- (get allocated %1) → (get allocated %1 0) in set-auto-multi-span
- (get allocate-fr-tracks %1) → (get allocate-fr-tracks %1 0) in set-flex-multi-span
- (/ fr-column/row-space column/row-frs) guarded with (zero?) check
- (/ auto-column/row-space column/row-autos) guarded with (zero?) check
In JS, integer division by zero produces Infinity (caught by mth/finite),
but on the JVM it throws before mth/finite can intercept.
3. Exhaustive tests for set-auto-multi-span behavior
Cover all code paths and edge cases:
- span=1 cells filtered out (unchanged track-list)
- empty shape-cells no-op
- even split across multiple auto tracks
- gap deduction per extra span step
- fixed track reducing budget; only auto tracks grow
- smaller children not shrinking existing track sizes (max semantics)
- flex tracks causing cell exclusion (handled by set-flex-multi-span)
- non-spanned tracks preserved via (get allocated %1 0) default
- :row type symmetry with :column type
- row-gap correctly deducted in :row mode
- documents that (sort-by span -) yields ascending order (smaller spans
first), correcting the misleading code comment
All tests pass on both JS (Node.js) and JVM environments.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
In the CLJS branch of resolve-modif-tree-ids, get-parent-seq returns
shape maps, but the js/Set was populated with UUIDs. As a result,
.has and .add were passing full shape maps instead of their :id
values, so parent deduplication never worked in ClojureScript.
Fixed both .has and .add calls to extract (:id %) from the shape map.
Also update the collinear-overlap test in geom-shapes-intersect-test
to expect true now that the ::coplanar keyword fix (commit 847bf51)
makes on-segment? collinear checks actually reachable.
drop_area.cljc: v-end? was guarded by row? instead of col?, making
vertical-end alignment check fire under horizontal layout conditions.
Aligned with v-center? which correctly uses col?.
positions.cljc: In get-base-line, the col? around? branch passed 2 as
a third argument to max instead of as a divisor in (/ free-width
num-lines 2). This made the offset clamp to at least 2 pixels rather
than computing half the per-line free space. Fixed parenthesization.
layout_data.cljc: The second cond branch (and col? space-evenly?
auto-height?) was permanently unreachable because the preceding branch
(and col? space-evenly?) is a strict superset. Removed the dead branch.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
All concrete constraint-modifier methods accept 6 arguments
(type, axis, child-before, parent-before, child-after, parent-after)
but the :default fallback only declared 5 parameters. Any unknown
constraint type would therefore receive 6 args and throw an arity
error at runtime. Added the missing sixth underscore parameter.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
When both dot-x and dot-y were negative (both axes flipped),
(update :rotation -) was applied twice which cancelled itself out,
leaving rotation unchanged. The intended behaviour is to negate
rotation once per flip, but flipping both axes simultaneously is
equivalent to a 180° rotation and should not alter the stored angle.
Replaced the two separate conditional rotation updates with a single
one gated on (not= (neg? dot-x) (neg? dot-y)) so the rotation is
negated only when exactly one axis is flipped.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
orientation returns the auto-qualified keyword ::coplanar
(app.common.geom.shapes.intersect/coplanar) but intersect-segments?
was comparing against the plain unqualified :coplanar keyword, which
never matches. This caused all collinear/on-segment edge cases to be
silently skipped, potentially missing valid segment intersections.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
update-rect with :size type was only updating :x2 and :y2 but not
:x1 and :y1, leaving the Rect record in an inconsistent state (x1/y1
would not match x/y). Aligned its behaviour with update-rect! which
correctly updates all four corner fields.
corners->rect was calling unqualified abs which is not imported in
app.common.geom.rect namespace. Replaced with mth/abs which is
the proper namespaced version already available in the ns require.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
gpt/multiply had a copy-paste docstring from gpt/subtract claiming it
performs subtraction; corrected to accurately describe multiplication.
gpt/abs was using clojure.core/update on a Point record, which returns
a plain IPersistentMap instead of a Point instance, causing point?
checks on the result to return false. Replaced with a direct pos->Point
constructor call using mth/abs on each coordinate.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
matrix->str was producing malformed strings like '1,0,0,1,0,0,'
instead of '1,0,0,1,0,0', breaking string serialization of matrix
values used in transit and print-dup handlers.
Also remove the first pp/simple-dispatch registration for Matrix at
line 362 which was dead code shadowed by the identical registration
further down in the file.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
* 🚑 Fix RangeError from re-entrant error handling in errors.cljs
Two complementary changes to prevent 'RangeError: Maximum call stack
size exceeded' when an error fires while the potok store error pipeline
is still on the call stack:
1. Re-entrancy guard on on-error: a volatile flag (handling-error?)
is set true for the duration of each on-error invocation. Any
nested call (e.g. from a notification emit that itself throws) is
suppressed with a console.error instead of recursing indefinitely.
2. Async notification in flash: the st/emit!(ntf/show ...) call is
now wrapped in ts/schedule (setTimeout 0) so the notification event
is pushed to the store on the next event-loop tick, outside the
error-handler call stack. This matches the pattern already used by
the :worker-error, :svg-parser and :comment-error handlers.
* 🐛 Add unit tests for app.main.errors
Test coverage for the error-handling module:
- stale-asset-error?: 6 cases covering keyword-constant and
protocol-dispatch mismatch signatures, plus negative cases
- exception->error-data: plain JS Error, ex-info with/without :hint
- on-error dispatch: map errors routed via ptk/handle-error, JS
exceptions wrapped into error-data before dispatch
- Re-entrancy guard: verifies that a second on-error call issued
from within a handle-error method is suppressed (exactly one
handler invocation)
---------
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
When d/seek finds no matching font variant (e.g. the variant-id stored
on the typography no longer exists in the font's variants list),
variant-data is nil and (:name nil) produces nil, resulting in a
malformed label like "14px | ". Fall back to "--" in that case.
Without this, dragging the value/opacity sliders in the harmony tab
never called on-finish-drag, so the undo transaction started by
on-start-drag was never committed.
slider-selector (slider_selector.cljs):
- Rename to slider-selector*
- Rename prop vertical? to is-vertical
- Remove prop reverse? entirely: it was never passed by any callsite,
so the related reversal logic in calculate-pos and handler positioning
is also removed as dead code
value-saturation-selector (ramp.cljs):
- Rename to value-saturation-selector*
- Update internal call site to [:> value-saturation-selector* ...]
- Update slider-selector call sites to [:> slider-selector* ...]
harmony-selector (harmony.cljs):
- Rename to harmony-selector*
- Update slider-selector call sites to [:> slider-selector* ...] with
renamed is-vertical prop
- Remove stale duplicate :vertical true prop
- Fix spurious extra wrapping vector around the opacity slider in the
when branch
hsva-selector (hsva.cljs):
- Rename to hsva-selector*
- Update all four slider-selector call sites to [:> slider-selector* ...]
- Remove no-op :reverse? false prop from the value slider
color-inputs (color_inputs.cljs):
- Rename to color-inputs*
colorpicker.cljs:
- Update :refer imports for color-inputs*, harmony-selector*,
hsva-selector* and libraries*
- Update all corresponding call sites from [:& ...] to [:> ...]
Convert typography-item, palette and text-palette to typography-item*,
palette* and text-palette* using {:keys [...]} destructuring. Rename
prop name-only? to is-name-only in typography-item*. Update internal
call sites to [:> ...] and update the :refer import in palette.cljs.
Convert active-sessions to active-sessions* (zero-prop component).
Update call site in right_header.cljs to use [:> ...] and update the
:refer import accordingly.
Convert coordinates to coordinates* using {:keys [...]} destructuring
and rename prop colorpalette? to is-colorpalette. Update call site in
workspace.cljs to use [:> ...] with new prop name.
Convert viewport-scrollbars to viewport-scrollbars* using {:keys [...]}
destructuring and update call sites in viewport.cljs and viewport_wasm.cljs
to use [:> ...].
Convert shape-distance-segment to shape-distance-segment* using {:keys [...]}
destructuring and update its internal call site in shape-distance to use [:> ...].
Clojure's = uses .equals on doubles, and Double.equals(Double.NaN)
returns true, so (not= v v) was always false for NaN. Use
Double/isNaN with a number? guard instead.
The CLJS branch of num-string? checked (string? v) first, but the
JVM branch did not. Passing non-string values (nil, keywords, etc.)
would rely on exception handling inside parse-double for control
flow. Add the string? check for consistency and to avoid using
exceptions for normal control flow.
The removev function used 'fn' as its predicate parameter name,
which shadows clojure.core/fn. Rename to 'pred' for clarity and
to follow the naming convention used elsewhere in the namespace.
When called with an empty string as the base class, append-class
was producing " bar" (with a leading space) because (some? "")
returns true. Use (seq class) instead to treat both nil and empty
string as absent, avoiding invalid CSS class strings with leading
whitespace.