mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-25 00:02:25 +02:00
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:
co-authored by
Claude Opus 5.5
parent
c74f475679
commit
5fac693c1c
@@ -144,21 +144,24 @@ export function createPrecisionGuides({ canvasArea, composite, saveState, schedu
|
||||
function drawDocumentOverlay(ctx) {
|
||||
if (!ctx || !state.imgWidth || !state.imgHeight) return;
|
||||
ctx.save();
|
||||
if (state.gridVisible) {
|
||||
const base = Math.max(2, Number(state.gridSize) || 16);
|
||||
const multiplier = Math.max(1, Math.ceil(5 / Math.max(0.0001, base * state.zoom)));
|
||||
const pixelBrush = !!state.brushPixel && (state.tool === 'brush' || state.tool === 'eraser');
|
||||
if (state.gridVisible || pixelBrush) {
|
||||
const base = pixelBrush
|
||||
? Math.max(1, Math.round(Number(state.brushSize) || 1))
|
||||
: Math.max(2, Number(state.gridSize) || 16);
|
||||
const multiplier = pixelBrush ? 1 : Math.max(1, Math.ceil(5 / Math.max(0.0001, base * state.zoom)));
|
||||
const spacing = base * multiplier;
|
||||
const strong = pixelBrush ? 'rgba(122, 208, 255, 0.55)' : 'rgba(110, 180, 220, 0.42)';
|
||||
const weak = pixelBrush ? 'rgba(122, 208, 255, 0.30)' : 'rgba(110, 180, 220, 0.22)';
|
||||
ctx.lineWidth = 1 / state.zoom;
|
||||
for (let x = spacing; x < state.imgWidth; x += spacing) {
|
||||
ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, state.imgHeight);
|
||||
ctx.strokeStyle = Math.round(x / spacing) % 4 === 0
|
||||
? 'rgba(110, 180, 220, 0.42)' : 'rgba(110, 180, 220, 0.22)';
|
||||
ctx.strokeStyle = Math.round(x / spacing) % 4 === 0 ? strong : weak;
|
||||
ctx.stroke();
|
||||
}
|
||||
for (let y = spacing; y < state.imgHeight; y += spacing) {
|
||||
ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(state.imgWidth, y);
|
||||
ctx.strokeStyle = Math.round(y / spacing) % 4 === 0
|
||||
? 'rgba(110, 180, 220, 0.42)' : 'rgba(110, 180, 220, 0.22)';
|
||||
ctx.strokeStyle = Math.round(y / spacing) % 4 === 0 ? strong : weak;
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
@@ -239,6 +242,21 @@ export function createPrecisionGuides({ canvasArea, composite, saveState, schedu
|
||||
return {
|
||||
drawDocumentOverlay,
|
||||
redrawRulers,
|
||||
addGuide(orientation, position) {
|
||||
const axis = orientation === 'horizontal' ? 'horizontal' : 'vertical';
|
||||
const limit = axis === 'horizontal' ? state.imgHeight : state.imgWidth;
|
||||
const value = Math.max(0, Math.min(limit, Math.round(Number(position))));
|
||||
if (!Number.isFinite(value)) return null;
|
||||
const guides = normalizedGuides();
|
||||
if (guides[axis].some(existing => Math.abs(existing - value) < 0.5)) return value;
|
||||
saveState?.('Add guide');
|
||||
guides[axis].push(value);
|
||||
guides[axis].sort((a, b) => a - b);
|
||||
schedulePersist?.();
|
||||
composite();
|
||||
redrawRulers();
|
||||
return value;
|
||||
},
|
||||
syncVisibility() { redrawRulers(); composite(); },
|
||||
clearGuides() {
|
||||
const guides = normalizedGuides();
|
||||
|
||||
Reference in New Issue
Block a user