mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-16 20:02:20 +02:00
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>
54 lines
2.8 KiB
Python
54 lines
2.8 KiB
Python
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
SRC = ROOT / "static/js/editor/clipboard-and-drop.js"
|
|
|
|
|
|
def test_editor_paste_owns_clipboard_images_while_editor_open():
|
|
"""Pasting an image with the editor open must not fall through to chat.
|
|
|
|
The editor wires a window-capture ``paste`` listener. The global chat
|
|
attach listener in ``static/app.js`` also listens on ``window`` (bubble)
|
|
and stages any ``item.kind === 'file'`` clipboard entry into the chatbox.
|
|
If the editor's listener bailed whenever the event target was *any*
|
|
input/textarea/``[role=dialog]``, then a paste whose focus sat on the chat
|
|
composer behind the editor modal went to the chatbox and never reached the
|
|
canvas. The guard must only defer to editable fields that live INSIDE the
|
|
editor container — and it must NOT include ``[role=dialog]``, because the
|
|
editor itself sits inside the gallery modal's ``role=dialog`` content.
|
|
"""
|
|
src = SRC.read_text(encoding="utf-8")
|
|
assert "wireClipboardAndDrop" in src
|
|
# The bail-out must be scoped to targets inside the editor.
|
|
assert "const insideEditor = target && container && container.contains(target);" in src
|
|
assert (
|
|
"if (insideEditor && (target.isContentEditable || "
|
|
"target.closest?.('input, textarea, select'))) return;"
|
|
) in src
|
|
# ``[role="dialog"]`` must NOT appear in the paste guard (the editor lives
|
|
# inside the gallery modal's role=dialog modal-content).
|
|
assert "target.closest?.('input, textarea, select, [role=\"dialog\"]')" not in src
|
|
# Capture-phase interception must preventDefault + stop the chat listener.
|
|
assert "e.stopImmediatePropagation();" in src
|
|
assert "e.preventDefault();" in src
|
|
|
|
|
|
def test_editor_ctrl_v_pastes_the_internal_selection_clipboard():
|
|
"""Ctrl/Cmd+V must paste the internal selection clipboard directly.
|
|
|
|
On an insecure origin (the editor is served over http on a LAN IP)
|
|
``navigator.clipboard`` is unavailable, so copying a selection never reaches
|
|
the OS clipboard and the native ``paste`` event carries stale ``text/plain``
|
|
-> the internal-paste branch is skipped and Ctrl+V silently does nothing
|
|
(the toolbar buttons worked because they bypass the event entirely).
|
|
"""
|
|
src = SRC.read_text(encoding="utf-8")
|
|
# Shared, hoisted paste helper (also used by the native paste listener).
|
|
assert "const pasteAsLayer = (imgSource, label, offset = { x: 0, y: 0 }) => {" in src
|
|
# Explicit keyboard handler for the internal clipboard.
|
|
assert "window.addEventListener('keydown', (e) => {" in src
|
|
assert "if (String(e.key || '').toLowerCase() !== 'v') return;" in src
|
|
assert "if (!state.internalClipboard) return;" in src
|
|
assert "pasteAsLayer(state.internalClipboard, 'Pasted Selection'" in src
|