* ⚡ Improve performance and fix orphan detection in validate-file
- Add `*ref-shape-cache*` dynamic var to memoize `find-ref-shape`
lookups per page, avoiding repeated O(depth) ancestor walks.
- Add `*children-sets*` pre-computed maps for O(1) parent-child
containment checks, replacing linear `some` scans.
- Short-circuit `inside-component-main?` when the shape context
already implies a main component.
- Use single-pass reduce with early exit for duplicate detection
(children, swap slots) instead of count/distinct or frequencies.
- Guard `check-missing-slot` to skip expensive `find-near-match`
when the shape already has a swap slot.
- Refactor variant-set validation to use `run!` with direct `get`.
- Refactor `check-ref-cycles` to use a single `reduce-kv` pass.
- Fix `get-orphan-shapes`: the original `map` pipeline produced
nils so orphan shapes were never validated; rewrite with
`reduce-kv` for correct results.
- Add `validate-file-affected!` for change-scoped validation,
replacing full file validation in `process-changes-and-validate`
to only validate pages and components touched by the changes.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
* ✨ Improved validation
---------
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
Do not show the library sync popup when the only differences are global x/y changes on library components. We now generate the actual sync changes and only notify if there are real redo-changes to apply.
Run cll/generate-sync-file-changes for candidate libraries and filter out those with empty :redo-changes. The expensive check is deferred via rx/timer 0 so it runs asynchronously and does not block the UI.
Why: Position-only changes are normalized during sync (via reposition-shape) and never propagate to copies; showing the popup in that case was a false positive.
Performance: The check is deferred to the next tick to avoid UI stutter on large files with many libraries.
Add focused common JavaScript test execution with log-level control and a quiet test wrapper matching the frontend workflow.
Update developer docs and testing memories to reflect the common/frontend test split, and document why runner helper extraction is deferred.
Signed-off-by: Codex <codex@openai.com>
Memories use a system of progressive disclosure:
Starting from a root memory, memories reference other memories using explicit
references.
The new system of hierarchical memories replaces AGENTS.md files.
GitHub #9215
Co-authored-by: Michael Panchenko <michael.panchenko@oraios-ai.de>
Co-authored-by: Codex <codex@openai.com>
* ✨ Add svg-attrs casing fix migration
* ⚡ Optimize set-shape-svg-attrs by removing redundant operations
- Remove backward compatibility for kebab-case SVG attribute keys
(fill-rule, stroke-linecap, stroke-linejoin) since svg-attrs are
already normalized to camelCase by the attrs->props migration.
- Remove unnecessary select-keys filtering and intermediate map
construction (dissoc :style + merge style).
- Directly extract values from style and attrs using or, avoiding
any intermediate map allocation.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
* 🐛 Filter non-http(s) URLs in upload-images to prevent invalid calls
Skip upload for image items that are not data URIs and do not have
an http:// or https:// URL, avoiding unnecessary RPC calls with
invalid URLs to create-file-media-object-from-url.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
---------
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Add a shared `schema:font-family` whitelist validator in
app.common.types.font that only allows letters, digits, spaces,
hyphens, underscores, and dots in font family names. Apply the schema
to create-font-variant and update-font RPC endpoints on the
backend, and add client-side validation in the dashboard fonts UI.
Include unit tests for the schema and integration tests for the RPC
handlers.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
* ✨ Add additional logging and validation for image upload
* 🎉 Add chunked upload support for font variants
Extend the font variant upload flow across frontend, backend, and common
to support the standardized chunked upload protocol.
**Backend:**
- Add \`:font-max-file-size\` config default (30 MiB) and schema entry
- Add \`validate-font-size!\` in \`media.clj\` (mirrors
\`validate-media-size!\`, raises \`:font-max-file-size-reached\`)
- Extend \`schema:create-font-variant\` to accept either \`:data\`
(legacy bytes or chunk-vector) or \`:uploads\` (new chunked session
map), with a validator requiring exactly one
- Add \`prepare-font-data-from-uploads\`: assembles each chunked
session via \`cmedia/assemble-chunks\`, validates type+size
- Add \`prepare-font-data-from-legacy\`: normalises legacy byte/chunk
entries, writing to a tempfile (joining via SequenceInputStream),
validates type+size
- Add structured logging ("init"/"end") with \`:size\`, \`:mtypes\`,
and \`:elapsed\` in \`create-font-variant\`
**Frontend:**
- \`upload-blob-chunked\` accepts a per-caller \`:chunk-size\` option
- Add \`font-upload-chunk-size\` (10 MiB) and \`upload-font-variant\`
fn that uploads each mtype as a separate chunked session
- \`on-upload*\` in dashboard fonts now calls \`upload-font-variant\`
instead of issuing \`create-font-variant\` RPC directly
- \`process-upload\` stores raw ArrayBuffer instead of chunking
client-side
**Common:**
- Replace \`"font/opentype"\` with \`"font/woff2"\` in \`font-types\`
**Tests:**
- 25 tests / 224 assertions covering all three upload paths (direct
bytes, legacy chunk-vector, new chunked sessions), size validation,
and media type validation
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
* 📎 Add a script for check the commit format locally
---------
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
* 📎 Add test that surfaces the bug described in #14070
The bug: alt-drag-duplicating a variant master into the variant container
auto-creates a new variant component cloned from the source
via duplicate-component, which preserves :touched on every cloned shape.
The resulting copy's children inherit those :geometry-group touched flags
through add-touched-from-ref-chain on switch.
variants-switch -> component-swap (keep-touched? true) -> generate-keep-touched
then runs update-attrs-on-switch for each touched child. The new shape's
:y is correctly skipped by the per-attr "different masters" guard, but
:selrect/:points fall through to a width/height-based safety check that
uses exact equality. In practice, the alt-drag modifier path leaves a
sub-pixel drift in :width on the copy, so equal-geometry? returns false,
the safety check is bypassed, and the :else branch copies the source
variant's :selrect verbatim onto the freshly instantiated target shape.
The shape ends up with :y from the target master but :selrect.y from the
source — the renderer reads :selrect, so the child appears at the source
position inside a parent that has resized to the target's dimensions:
the visible "button cut off" symptom.
The new test sets up a variant container whose children are themselves
component copies (matching production: variant masters' children carry
:touched), introduces the same kind of width drift on the copy that the
alt-drag path produces, and runs the swap directly via the existing
test harness. It asserts (a) :y matches the target, (b) :selrect.y
matches the target, and (c) :y and :selrect.y agree. With the current
code (a) passes and (b)/(c) fail — capturing both the wrong value and
the internal inconsistency that causes the visible regression.
* 🐛Fix#14070 by no longer comparing floats for exact equality in equal-geometry?
Remove the :canary flag from the flags definition and make all
features gated behind it always available:
- Enable "download font" option in dashboard fonts context menu
- Enable Tab/Shift+Tab keyboard navigation for renaming shapes
in layer items
- Enable "duplicate color" option in asset panel when applicable
- Enable "duplicate typography" option in asset panel when applicable
- Enable "copy as image" context menu option for frame shapes
Also remove unused [app.config :as cf] requires from files that
no longer reference it after the materialization.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>