mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-17 04:12:24 +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
@@ -0,0 +1,79 @@
|
||||
import json
|
||||
import subprocess
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
MODULE = (ROOT / 'static/js/editor/editor-bridge.js').as_uri()
|
||||
|
||||
|
||||
def run_node(body):
|
||||
script = textwrap.dedent(f"""
|
||||
import {{ startEditorBridge, BRIDGE_VERSION }} from {json.dumps(MODULE)};
|
||||
{body}
|
||||
""")
|
||||
result = subprocess.run(
|
||||
['node', '--input-type=module', '-e', script],
|
||||
cwd=ROOT, text=True, capture_output=True, check=True,
|
||||
)
|
||||
return json.loads(result.stdout)
|
||||
|
||||
|
||||
def test_bridge_runs_queued_command_and_posts_result():
|
||||
out = run_node("""
|
||||
const calls = [];
|
||||
const posts = [];
|
||||
class FakeES {
|
||||
constructor(url) { this.url = url; FakeES.last = this; }
|
||||
close() { this.closed = true; }
|
||||
}
|
||||
const control = { invoke(op, params) {
|
||||
calls.push(op);
|
||||
if (op === 'screenshot') return { ok: true, result: { dataUrl: 'data:image/png;base64,ZZ' } };
|
||||
return { ok: true, result: 'r:' + op };
|
||||
} };
|
||||
const fetchImpl = async (url, opts) => { posts.push({ url, body: JSON.parse(opts.body) }); return { ok: true, json: async () => ({ ok: true }) }; };
|
||||
const bridge = startEditorBridge({ control, session: 's1', baseUrl: '/x', EventSourceImpl: FakeES, fetchImpl });
|
||||
FakeES.last.onmessage({ data: JSON.stringify({ id: 'c1', op: 'history.undo', params: {} }) });
|
||||
await new Promise(r => setTimeout(r, 25));
|
||||
console.log(JSON.stringify({ esUrl: FakeES.last.url, calls, posts, session: bridge.session, version: BRIDGE_VERSION }));
|
||||
""")
|
||||
assert out['esUrl'] == '/x/api/editor/bridge/events?session=s1'
|
||||
assert 'history.undo' in out['calls']
|
||||
assert 'screenshot' in out['calls']
|
||||
assert out['posts'][0]['url'] == '/x/api/editor/bridge/result'
|
||||
assert out['posts'][0]['body']['id'] == 'c1'
|
||||
assert out['posts'][0]['body']['ok'] is True
|
||||
assert out['posts'][0]['body']['screenshot'].startswith('data:image/png')
|
||||
assert out['session'] == 's1'
|
||||
assert out['version'] == '1.0'
|
||||
|
||||
|
||||
def test_bridge_reports_op_failure_and_stop_closes_stream():
|
||||
out = run_node("""
|
||||
const posts = [];
|
||||
class FakeES {
|
||||
constructor(url) { this.url = url; FakeES.last = this; }
|
||||
close() { this.closed = true; }
|
||||
}
|
||||
const control = { invoke(op) {
|
||||
if (op === 'screenshot') return { ok: true, result: { dataUrl: 'data:image/png;base64,ZZ' } };
|
||||
return { ok: false, error: 'nope' };
|
||||
} };
|
||||
const fetchImpl = async (url, opts) => { posts.push(JSON.parse(opts.body)); return { ok: true, json: async () => ({}) }; };
|
||||
const bridge = startEditorBridge({ control, session: 'default', EventSourceImpl: FakeES, fetchImpl });
|
||||
FakeES.last.onmessage({ data: JSON.stringify({ id: 'x', op: 'layers.delete', params: { id: 'layer-9' } }) });
|
||||
await new Promise(r => setTimeout(r, 25));
|
||||
bridge.stop();
|
||||
console.log(JSON.stringify({ body: posts[0], closed: !!FakeES.last.closed }));
|
||||
""")
|
||||
assert out['body']['ok'] is False
|
||||
assert out['body']['error'] == 'nope'
|
||||
assert out['body']['result'] is None
|
||||
assert out['closed'] is True
|
||||
|
||||
|
||||
def test_bridge_module_is_precached_for_offline_use():
|
||||
sw = (ROOT / 'static/sw.js').read_text(encoding='utf-8')
|
||||
assert "'/static/js/editor/editor-bridge.js'" in sw
|
||||
Reference in New Issue
Block a user