feat(photo-editor): Ajax Image Editor client (Photoshop-parity tools)

Photoshop-parity editor client: layers, selection/masking, brushes,
smart filters, LUT import, Quick Edit, mobile gestures, workspaces,
batch operations, and the service-worker versioning (v978->v1006).

Source: vendor/photocraft (ad1cff44) photo-editor client files, merged
onto upstream/dev (a8c147b2). Conflict resolution documented in
pr_work/A_conflicts.md; editor files taken from vendor/photocraft
(upstream's editor is an earlier snapshot of the same feature).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
pewdiepie-archdaemon
2026-10-15 11:57:35 +00:00
co-authored by Claude Opus 5.5
parent c74f475679
commit 5fac693c1c
290 changed files with 34358 additions and 766 deletions
+14 -2
View File
@@ -41,8 +41,14 @@ export function transformedLayerGeometry(snapshot, sourceBounds, target) {
const centerY = target.centerY + relativeX * sin + relativeY * cos;
const scaledWidth = snapshot.width * scaleX;
const scaledHeight = snapshot.height * scaleY;
const width = Math.max(1, Math.round(scaledWidth * absCos + scaledHeight * absSin));
const height = Math.max(1, Math.round(scaledWidth * absSin + scaledHeight * absCos));
const skewX = target.skewX || 0;
const skewY = target.skewY || 0;
const perspectiveX = target.perspectiveX || 0;
const perspectiveY = target.perspectiveY || 0;
const skewPadX = Math.abs(Math.tan(skewX * Math.PI / 180)) * scaledHeight;
const skewPadY = Math.abs(Math.tan(skewY * Math.PI / 180)) * scaledWidth;
const width = Math.max(1, Math.round(scaledWidth * absCos + scaledHeight * absSin + skewPadX));
const height = Math.max(1, Math.round(scaledWidth * absSin + scaledHeight * absCos + skewPadY));
return {
scaleX,
scaleY,
@@ -50,6 +56,10 @@ export function transformedLayerGeometry(snapshot, sourceBounds, target) {
signedScaleY,
rotation: target.rotation,
radians,
skewX,
skewY,
perspectiveX,
perspectiveY,
width,
height,
centerX,
@@ -94,6 +104,8 @@ export function planLayerTransform(editorState, snapshots, sourceBounds, target,
const numericTarget = [
target?.width, target?.height, target?.rotation,
target?.centerX, target?.centerY,
target?.skewX ?? 0, target?.skewY ?? 0,
target?.perspectiveX ?? 0, target?.perspectiveY ?? 0,
].every(Number.isFinite);
if (!numericTarget || target.width < 1 || target.height < 1) {
return { ok: false, reason: 'Transform values must be finite positive numbers.' };