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
+17 -10
View File
@@ -2,10 +2,12 @@
import { applyAdjustment } from './fx/pixel-pass.js';
import { defaultAdjParams } from './layer-helpers.js';
import { renderWithLayerMasks } from './composite-helpers.js';
import { blendDraw } from './blend-modes.js';
const TYPES = new Set([
'brightness-contrast',
'exposure',
'develop',
'white-balance',
'hue-saturation',
'vibrance',
@@ -16,6 +18,12 @@ const TYPES = new Set([
'color-balance',
'selective-color',
'gradient-map',
'invert',
'posterize',
'threshold',
'photo-filter',
'channel-mixer',
'color-lookup',
]);
function clone(value) {
@@ -52,6 +60,11 @@ export function normalizeAdjustmentData(value) {
params.ranges[name] = { ...clone(defaults.ranges[name]), ...(params.ranges[name] || {}) };
}
}
if (type === 'channel-mixer') {
for (const name of ['red', 'green', 'blue', 'constant']) {
params[name] = { ...clone(defaults[name]), ...(params[name] || {}) };
}
}
return { type, params };
}
@@ -109,11 +122,8 @@ export function drawAdjustmentLayer(target, editorState, layer, clippingBase, re
adjusted = clipped;
}
target.globalAlpha = Number.isFinite(layer.opacity) ? layer.opacity : 1;
target.globalCompositeOperation = layer.blendMode || 'source-over';
target.drawImage(adjusted, 0, 0);
target.globalAlpha = 1;
target.globalCompositeOperation = 'source-over';
const opacity = Number.isFinite(layer.opacity) ? layer.opacity : 1;
blendDraw(target, adjusted, layer.blendMode, opacity, 0, 0);
return true;
}
@@ -154,11 +164,8 @@ export async function drawAdjustmentLayerAsync(target, editorState, layer, clipp
clippedCtx.globalCompositeOperation = 'source-over';
adjusted = clipped;
}
target.globalAlpha = Number.isFinite(layer.opacity) ? layer.opacity : 1;
target.globalCompositeOperation = layer.blendMode || 'source-over';
target.drawImage(adjusted, 0, 0);
target.globalAlpha = 1;
target.globalCompositeOperation = 'source-over';
const opacity = Number.isFinite(layer.opacity) ? layer.opacity : 1;
blendDraw(target, adjusted, layer.blendMode, opacity, 0, 0);
return true;
}