Various HTML improvements

This commit is contained in:
sebastien committed 2026-11-03 10:02:10 +01:00
1 parent fd943696fb
commit 8c1fa0af57
16 files changed
+403 -45

No files matched your search

+4 -2
View File
@@ -26,6 +26,8 @@ def _files_digest(paths) -> str:
class AnnotationReadCache:
state_file = STATE_FILE
def __init__(self, workspace, mode: str, data: dict, group_dirs=()):
self.workspace = workspace
self.mode = mode
@@ -59,7 +61,7 @@ class AnnotationReadCache:
return False
directory = self.workspace.root / self.mode / f"Copie{copy_id}"
try:
state = read_json(directory / STATE_FILE)
state = read_json(directory / self.state_file)
return (state["signature"] == self.signatures[copy_id]
and bool(state["outputs"])
and all(Path(path).is_file() for path in state["outputs"]))
@@ -82,7 +84,7 @@ class AnnotationReadCache:
stem = destination.name.rsplit(" (", 1)[0]
outputs.extend(path for suffix in (".jpg", ".pdf")
if (path := destination / (stem + suffix)).is_file())
atomic_write_json(directory / STATE_FILE, {
atomic_write_json(directory / self.state_file, {
"signature": self.signatures[copy_id],
"outputs": [str(path) for path in outputs],
})
+14 -1
View File
@@ -38,6 +38,7 @@ def build_parser() -> argparse.ArgumentParser:
"directories, matching folders by their trailing copy ID"
),
)
parser.add_argument("--incremental", action="store_true", help="Skip unchanged return folders")
return parser
@@ -147,6 +148,7 @@ def update_named_return_answers(
def prepare_named_returns(
workspace: EvaluationWorkspace,
annotation_dir_name: str,
*, incremental: bool = False,
) -> ExitCode:
workspace.require_directories("Copies", annotation_dir_name)
workspace.return_dir.mkdir(parents=True, exist_ok=True)
@@ -198,6 +200,12 @@ def prepare_named_returns(
assigned_names.add(name)
destination = existing_returns.get(int(copy_id), workspace.return_dir / f"{safe_name} ({copy_id})")
output_name = destination.name.rsplit(" (", 1)[0]
from copienator.reviewed_cache import ReturnCache
cache = ReturnCache(workspace.root, source_folder, destination) if incremental else None
if cache and cache.unchanged():
print(f"Skipping unchanged return {destination.name}")
continue
copy_failed = False
destination.mkdir(parents=True, exist_ok=True)
try:
publish_answer_returns(workspace.root, source_folder, destination)
@@ -238,6 +246,9 @@ def prepare_named_returns(
file=sys.stderr,
)
had_errors = True
copy_failed = True
if cache and not copy_failed:
cache.remember()
unassigned = expected_names - assigned_names
if unassigned:
@@ -253,10 +264,11 @@ def run(
*,
annotation_dir: str,
update: bool = False,
incremental: bool = False,
) -> ExitCode:
if update:
return update_named_return_answers(workspace, annotation_dir)
return prepare_named_returns(workspace, annotation_dir)
return prepare_named_returns(workspace, annotation_dir, incremental=incremental)
def main(argv: Sequence[str] | None = None) -> int:
@@ -268,6 +280,7 @@ def main(argv: Sequence[str] | None = None) -> int:
workspace_from_args(args, repository=Path.cwd()),
annotation_dir=args.annotation_dir,
update=args.update,
incremental=args.incremental,
),
)
+10 -3
View File
@@ -25,16 +25,22 @@ from copienator.annotation_modifications import (
from copienator.filesystem import publish_file_sets
def run(workspace: EvaluationWorkspace, *, annotation_dir: str = "BGnot") -> ExitCode:
def run(workspace: EvaluationWorkspace, *, annotation_dir: str = "BGnot", incremental: bool = False) -> ExitCode:
workspace.require_files("labels", DRAFT)
workspace.require_directories("Copies", annotation_dir)
text = (workspace.root / DRAFT).read_text(encoding="utf-8")
instructions = read_instructions(text, annotation_dir)
validate_instructions(workspace.root, annotation_dir, instructions)
directory = workspace.root / annotation_dir
applied = directory / APPLIED
previous = read_instructions(applied.read_text(encoding="utf-8"), annotation_dir) if applied.exists() else []
if incremental and previous == instructions:
print("Skipping unchanged transfer instructions")
return ExitCode.SUCCESS
validate_instructions(workspace.root, annotation_dir, instructions)
copy_ids = sorted({item.copy_id for item in [*previous, *instructions]})
if incremental:
copy_ids = [cid for cid in copy_ids
if [i for i in previous if i.copy_id == cid] != [i for i in instructions if i.copy_id == cid]]
# Prepare every copy before publishing anything, including the applied file.
with TemporaryDirectory(prefix=".annotation-edits-", dir=directory) as temporary:
staging_root = Path(temporary)
@@ -61,12 +67,13 @@ def run(workspace: EvaluationWorkspace, *, annotation_dir: str = "BGnot") -> Exi
def build_parser():
parser = evaluation_parser("Modify reviewed answer bodies using annotation_modifications.txt")
parser.add_argument("--annotation-dir", choices=MODES, default="BGnot")
parser.add_argument("--incremental", action="store_true", help="Skip unchanged transfer instructions")
return parser
def main(argv: Sequence[str] | None = None) -> int:
return execute(build_parser(), argv, lambda args: run(
workspace_from_args(args), annotation_dir=args.annotation_dir))
workspace_from_args(args), annotation_dir=args.annotation_dir, incremental=args.incremental))
if __name__ == "__main__":
+16 -2
View File
@@ -5,9 +5,19 @@ from copienator.commands.reading_annotations import apply_actions_and_regenerate
from copienator.commands.reading_grouped_annotations import apply_actions_and_regenerate_grouped
def run(workspace: EvaluationWorkspace, mode: str):
def run(workspace: EvaluationWorkspace, mode: str, *, incremental: bool = False):
directories = sorted((p for p in (workspace.root / mode).glob('Copie*')
if p.is_dir() and p.name[5:].isdigit() and (p / 'score.json').is_file()))
from copienator.reviewed_cache import ReviewedReadCache
cache = ReviewedReadCache(workspace, mode, directories) if incremental else None
if cache:
pending = []
for directory in directories:
if cache.unchanged(directory.name[5:]):
print(f"Skipping unchanged {directory.name}", flush=True)
else:
pending.append(directory)
directories = pending
# Validate every snapshot before starting to publish any copy.
for directory in directories:
if not (directory / FILENAME).is_file():
@@ -29,10 +39,14 @@ def run(workspace: EvaluationWorkspace, mode: str):
print(log, flush=True)
if result != ExitCode.SUCCESS:
status = ExitCode.PARTIAL
elif cache:
# Rendering can normalize the saved snapshot; fingerprint the published inputs.
ReviewedReadCache(workspace, mode, [directory]).remember([cid])
return status
def main(argv=None):
parser = evaluation_parser('Regenerate from saved reviewed inputs without rescanning handwriting')
parser.add_argument('--annotation-dir', choices=('Bnot', 'BGnot'), required=True)
return execute(parser, argv, lambda args: run(workspace_from_args(args), args.annotation_dir))
parser.add_argument("--incremental", action="store_true", help="Skip unchanged copies")
return execute(parser, argv, lambda args: run(workspace_from_args(args), args.annotation_dir, incremental=args.incremental))
+55 -10
View File
@@ -9,7 +9,7 @@ import subprocess
import sys
import threading
from copienator import atomic_write_json, read_json, utils
from copienator import EvaluationWorkspace, atomic_write_json, read_json, utils
from copienator import custom_remarks, general_remarks
from copienator.annotation_modifications import DRAFT, APPLIED, BASELINE, read_instructions, validate_instructions
from copienator.return_identity import return_folders, RETURN_FOLDER_PATTERN, set_copy_name
@@ -17,8 +17,9 @@ from copienator.score_editing import set_score
class Finalisation:
def __init__(self, root: Path, mode: str, token: str, lock_fd: int | None = None):
def __init__(self, root: Path, mode: str, token: str, lock_fd: int | None = None, *, repository: Path | None = None):
self.root = root.resolve()
self.repository = (repository or Path.cwd()).resolve()
if mode not in ('Bnot', 'BGnot'):
raise ValueError('Choisissez Bnot ou BGnot.')
self.mode, self.token = mode, token
@@ -80,10 +81,13 @@ class Finalisation:
'image': (directory / f'{label}.jpg').is_file(),
'editableBody': label in base.get('images', {}),
'info': info.get(label, {})} for label in self.labels]})
names_path = EvaluationWorkspace(self.root, self.repository).names_file()
expected_names = (sorted({line.strip() for line in names_path.read_text(encoding='utf-8').splitlines()
if line.strip()}, key=str.casefold) if names_path.is_file() else None)
applied = self.root / self.mode / APPLIED
applied_text = applied.read_text() if applied.exists() else f'### Source: {self.mode}\n'
return {'evaluation': self.root.name, 'mode': self.mode, 'labels': self.labels, 'copies': copies,
'draft': self.draft(), 'applied': applied_text, 'revision': self.revision(),
'expectedNames': expected_names, 'draft': self.draft(), 'applied': applied_text, 'revision': self.revision(),
'dirty': read_json(self.dirty_path, default={}).get('dirty', False), 'job': dict(self.job)}
def save(self, data):
@@ -116,10 +120,20 @@ class Finalisation:
else:
raise ValueError('Modification inconnue.')
self.dirty_path.parent.mkdir(exist_ok=True)
atomic_write_json(self.dirty_path, {'dirty': True})
pending = read_json(self.dirty_path, default={'dirty': False})
kinds = set(pending.get('kinds', ['unknown'] if pending.get('dirty') else []))
kinds.add(kind)
render_inputs = None
if kinds == {'name'}:
render_inputs = (pending.get('render_inputs') if pending.get('kinds') == ['name']
else self._render_inputs())
pending = {'dirty': True, 'kinds': sorted(kinds)}
if kinds == {'name'}:
pending['render_inputs'] = render_inputs
atomic_write_json(self.dirty_path, pending)
return self.state()
def start_job(self, revision: str, apply_draft: bool):
def start_job(self, revision: str, apply_draft: bool, incremental: bool = True):
with self.mutex:
if self.job['running'] or self.closing or revision != self.revision():
raise ValueError('Traitement déjà en cours ou données modifiées. Rechargez la page.')
@@ -128,12 +142,12 @@ class Finalisation:
if not (self.root / DRAFT).exists():
(self.root / DRAFT).write_text(self.draft())
self.job = {'running': True, 'status': 'running', 'log': ''}
threading.Thread(target=self._run_job, args=(apply_draft,), daemon=True).start()
threading.Thread(target=self._run_job, args=(apply_draft, incremental), daemon=True).start()
return dict(self.job)
def _run_job(self, apply_draft):
def _run_job(self, apply_draft, incremental=True):
try:
self._regenerate(apply_draft)
self._regenerate(apply_draft, incremental)
except Exception as exc:
with self.mutex:
self.job.update(status='failed', log=self.job['log'] + '\n' + str(exc))
@@ -141,7 +155,26 @@ class Finalisation:
with self.mutex:
self.job['running'] = False
def _regenerate(self, apply_draft):
def _render_inputs(self):
from copienator.reviewed_cache import ReviewedReadCache
from copienator.review_snapshot import FILENAME
directories = self.copies()
if not directories or not all((d / FILENAME).is_file() for d in directories):
return None
cache = ReviewedReadCache(EvaluationWorkspace(self.root), self.mode, directories)
# Keep the fast path conservative when an existing rendered file disappears.
outputs = []
for directory in directories:
info = read_json(directory / 'info.json', default={})
paths = [directory / 'info.json', directory / BASELINE]
paths += [directory / f'{label}.jpg' for label, entry in info.items() if entry['present']]
paths += list(directory.glob('Concat*.*'))
if any(not path.is_file() for path in paths):
return None
outputs.extend(str(p) for p in paths)
return {'signatures': cache.signatures, 'outputs': sorted(outputs)}
def _regenerate(self, apply_draft, incremental=True):
from copienator.return_answers import load_return_answer_options
options = load_return_answer_options(self.root)
reader = 'read-grouped' if self.mode == 'BGnot' else 'read-annotations'
@@ -152,13 +185,25 @@ class Finalisation:
if apply_draft:
commands.append(['modify-annotations', str(self.root), '--annotation-dir', self.mode])
commands.append(['giving-names', str(self.root), self.mode])
pending = read_json(self.dirty_path, default={})
names_only = (incremental and not apply_draft and pending.get('kinds') == ['name']
and pending.get('render_inputs') is not None
and pending['render_inputs'] == self._render_inputs())
from copienator.review_snapshot import FILENAME
if all((directory / FILENAME).is_file() for directory in self.copies()):
if names_only:
commands = [['giving-names', str(self.root), self.mode]]
with self.mutex:
self.job['log'] += 'Noms seuls modifiés : conservation des images et PDF de correction.\n'
elif all((directory / FILENAME).is_file() for directory in self.copies()):
commands[0] = ['regenerate-reviewed', str(self.root), '--annotation-dir', self.mode]
elif (self.root / 'BRnot').is_dir():
with self.mutex:
self.job.update(running=False, status='failed', log='Résultat de lecture sauvegardé absent pour une évaluation avec reprise. Relisez le passage principal puis fusionnez la reprise avant de régénérer dans le navigateur. Aucun résultat n’a été remplacé.')
return
if incremental:
for command in commands:
if command[0] in ('regenerate-reviewed', 'modify-annotations', 'giving-names'):
command.append('--incremental')
env = dict(os.environ, COPIENATOR_FINALISATION_TOKEN=self.token, PYTHONUNBUFFERED='1')
from copienator import configuration
if configuration.CONFIG_PATH is not None:
+4 -3
View File
@@ -33,6 +33,7 @@ class Change(BaseModel):
class Regenerate(BaseModel):
revision: str
apply_draft: bool = False
incremental: bool = True
class Preview(BaseModel):
@@ -91,7 +92,7 @@ def create_app(service: Finalisation, browser_token: str, stop=lambda: None):
@app.post('/api/regenerate')
def regenerate(data: Regenerate):
return service.start_job(data.revision, data.apply_draft)
return service.start_job(data.revision, data.apply_draft, data.incremental)
@app.post('/api/stop')
def shutdown():
@@ -174,11 +175,11 @@ def create_app(service: Finalisation, browser_token: str, stop=lambda: None):
class WebSession:
def __init__(self, root: Path, mode: str):
def __init__(self, root: Path, mode: str, *, repository: Path | None = None):
self.lease = EvaluationLock(root)
self.lease.__enter__()
try:
self.service = Finalisation(root, mode, self.lease.token, self.lease.stream.fileno())
self.service = Finalisation(root, mode, self.lease.token, self.lease.stream.fileno(), repository=repository)
if not self.service.copies():
raise ValueError('Lisez les annotations manuscrites dans ce mode avant d’ouvrir la finalisation web.')
self.token = secrets.token_urlsafe(32)
+71
View File
@@ -0,0 +1,71 @@
"""Content-based caches for saved-review rendering and return publication."""
from dataclasses import asdict
from hashlib import sha256
import json
from pathlib import Path
from copienator import atomic_write_json, configuration, read_json, general_remarks
from copienator.annotation_read_cache import AnnotationReadCache, _files_digest
from copienator.annotation_modifications import APPLIED, read_instructions
from copienator.custom_remarks import load_remarks, copy_key
from copienator.return_answers import load_return_answer_options
from copienator.review_snapshot import FILENAME
def output_options(root):
return (load_return_answer_options(root), configuration.RETURN_ANSWERS_ENABLED,
configuration.RETURN_JPEG_ENABLED, configuration.RETURN_PDF_ENABLED)
def signature(payload):
return sha256(json.dumps(payload, sort_keys=True, default=str).encode()).hexdigest()
class ReviewedReadCache(AnnotationReadCache):
state_file = '.reviewed-render-state.json'
def __init__(self, workspace, mode, directories):
self.workspace, self.mode, self.signatures = workspace, mode, {}
root = workspace.root
shared = [root / 'labels']
for folder in ('Text2', 'Sol2'):
shared.extend((root / folder).glob('*.pdf'))
common = (_files_digest(shared), configuration.LATEX_ANOT_BEFORE, configuration.LATEX_ANOT_AFTER)
remarks, general = load_remarks(root), general_remarks.load_remarks(root)
applied = root / mode / APPLIED
instructions = read_instructions(applied.read_text(), mode) if applied.is_file() else []
for directory in directories:
cid = directory.name[5:]
saved = read_json(directory / FILENAME)
paths = [directory / FILENAME, directory / 'score.json']
for item in saved['data'].values():
pdf = Path(item['pdf_path'])
pdf = pdf if pdf.is_absolute() else root / pdf
paths.extend((pdf, pdf.with_suffix('.layout.json')))
paths.extend(directory / note['file'] for note in saved['notes'].values() if note['file'])
self.signatures[cid] = signature((1, common, _files_digest(paths),
remarks.get(copy_key(cid), {}), general.get(copy_key(cid), []),
[asdict(i) for i in instructions if i.copy_id == cid]))
class ReturnCache:
def __init__(self, root, source, destination):
self.path = source / '.return-publication-state.json'
self.destination = destination
paths = [source / 'score.json', source / 'info.json']
paths.extend(source.glob('*.jpg'))
paths.extend(source.glob('*.pdf'))
for folder in ('Text2', 'Sol2'):
paths.extend((root / folder).glob('*.pdf'))
self.signature = signature((1, str(destination), _files_digest(paths), output_options(root)))
def unchanged(self):
try:
state = read_json(self.path)
return (state['signature'] == self.signature and bool(state['outputs'])
and all((self.destination / name).is_file() for name in state['outputs']))
except (OSError, ValueError, KeyError, TypeError):
return False
def remember(self):
outputs = [str(p.relative_to(self.destination)) for p in self.destination.rglob('*') if p.is_file()]
atomic_write_json(self.path, {'signature': self.signature, 'outputs': outputs})
@@ -1,15 +0,0 @@
(function(){const G=document.createElement("link").relList;if(G&&G.supports&&G.supports("modulepreload"))return;for(const q of document.querySelectorAll('link[rel="modulepreload"]'))y(q);new MutationObserver(q=>{for(const $ of q)if($.type==="childList")for(const rl of $.addedNodes)rl.tagName==="LINK"&&rl.rel==="modulepreload"&&y(rl)}).observe(document,{childList:!0,subtree:!0});function Y(q){const $={};return q.integrity&&($.integrity=q.integrity),q.referrerPolicy&&($.referrerPolicy=q.referrerPolicy),q.crossOrigin==="use-credentials"?$.credentials="include":q.crossOrigin==="anonymous"?$.credentials="omit":$.credentials="same-origin",$}function y(q){if(q.ep)return;q.ep=!0;const $=Y(q);fetch(q.href,$)}})();var zs={exports:{}},nn={};var lv;function ih(){if(lv)return nn;lv=1;var _=Symbol.for("react.transitional.element"),G=Symbol.for("react.fragment");function Y(y,q,$){var rl=null;if($!==void 0&&(rl=""+$),q.key!==void 0&&(rl=""+q.key),"key"in q){$={};for(var Gl in q)Gl!=="key"&&($[Gl]=q[Gl])}else $=q;return q=$.ref,{$$typeof:_,type:y,key:rl,ref:q!==void 0?q:null,props:$}}return nn.Fragment=G,nn.jsx=Y,nn.jsxs=Y,nn}var tv;function ch(){return tv||(tv=1,zs.exports=ih()),zs.exports}var d=ch(),_s={exports:{}},Z={};var uv;function fh(){if(uv)return Z;uv=1;var _=Symbol.for("react.transitional.element"),G=Symbol.for("react.portal"),Y=Symbol.for("react.fragment"),y=Symbol.for("react.strict_mode"),q=Symbol.for("react.profiler"),$=Symbol.for("react.consumer"),rl=Symbol.for("react.context"),Gl=Symbol.for("react.forward_ref"),ul=Symbol.for("react.suspense"),Dl=Symbol.for("react.memo"),R=Symbol.for("react.lazy"),b=Symbol.for("react.activity"),M=Symbol.for("react.view_transition"),_l=Symbol.iterator;function Xl(s){return s===null||typeof s!="object"?null:(s=_l&&s[_l]||s["@@iterator"],typeof s=="function"?s:null)}var Ul={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},il=Object.assign,et={};function Ol(s,E,D){this.props=s,this.context=E,this.refs=et,this.updater=D||Ul}Ol.prototype.isReactComponent={},Ol.prototype.setState=function(s,E){if(typeof s!="object"&&typeof s!="function"&&s!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,s,E,"setState")},Ol.prototype.forceUpdate=function(s){this.updater.enqueueForceUpdate(this,s,"forceUpdate")};function gt(){}gt.prototype=Ol.prototype;function Ql(s,E,D){this.props=s,this.context=E,this.refs=et,this.updater=D||Ul}var at=Ql.prototype=new gt;at.constructor=Ql,il(at,Ol.prototype),at.isPureReactComponent=!0;var xl=Array.isArray;function K(){}var F={H:null,A:null,T:null,S:null},Vl=Object.prototype.hasOwnProperty;function Nl(s,E,D){var j=D.ref;return{$$typeof:_,type:s,key:E,ref:j!==void 0?j:null,props:D}}function pl(s,E){return Nl(s.type,E,s.props)}function Ll(s){return typeof s=="object"&&s!==null&&s.$$typeof===_}function jt(s){var E={"=":"=0",":":"=2"};return"$"+s.replace(/[=:]/g,function(D){return E[D]})}var L=/\/+/g;function hl(s,E){return typeof s=="object"&&s!==null&&s.key!=null?jt(""+s.key):E.toString(36)}function C(s){switch(s.status){case"fulfilled":return s.value;case"rejected":throw s.reason;default:switch(typeof s.status=="string"?s.then(K,K):(s.status="pending",s.then(function(E){s.status==="pending"&&(s.status="fulfilled",s.value=E)},function(E){s.status==="pending"&&(s.status="rejected",s.reason=E)})),s.status){case"fulfilled":return s.value;case"rejected":throw s.reason}}throw s}function B(s,E,D,j,k){var al=typeof s;(al==="undefined"||al==="boolean")&&(s=null);var sl=!1;if(s===null)sl=!0;else switch(al){case"bigint":case"string":case"number":sl=!0;break;case"object":switch(s.$$typeof){case _:case G:sl=!0;break;case R:return sl=s._init,B(sl(s._payload),E,D,j,k)}}if(sl)return k=k(s),sl=j===""?"."+hl(s,0):j,xl(k)?(D="",sl!=null&&(D=sl.replace(L,"$&/")+"/"),B(k,E,D,"",function(nu){return nu})):k!=null&&(Ll(k)&&(k=pl(k,D+(k.key==null||s&&s.key===k.key?"":(""+k.key).replace(L,"$&/")+"/")+sl)),E.push(k)),1;sl=0;var x=j===""?".":j+":";if(xl(s))for(var V=0;V<s.length;V++)j=s[V],al=x+hl(j,V),sl+=B(j,E,D,al,k);else if(V=Xl(s),typeof V=="function")for(s=V.call(s),V=0;!(j=s.next()).done;)j=j.value,al=x+hl(j,V++),sl+=B(j,E,D,al,k);else if(al==="object"){if(typeof s.then=="function")return B(C(s),E,D,j,k);throw E=String(s),Error("Objects are not valid as a React child (found: "+(E==="[object Object]"?"object with keys {"+Object.keys(s).join(", ")+"}":E)+"). If you meant to render a collection of children, use an array instead.")}return sl}function X(s,E,D){if(s==null)return s;var j=[],k=0;return B(s,j,"","",function(al){return E.call(D,al,k++)}),j}function cl(s){if(s._status===-1){var E=s._result,D=E();D.then(function(j){(s._status===0||s._status===-1)&&(s._status=1,s._result=j,D.status===void 0&&(D.status="fulfilled",D.value=j))},function(j){(s._status===0||s._status===-1)&&(s._status=2,s._result=j,D.statLine truncated
at`)?" (<anonymous>)":-1<u.stack.indexOf("@")?"@unknown:0:0":""}return`
`+Vi+l+js}var Zi=!1;function Li(l,t){if(!l||Zi)return"";Zi=!0;var u=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var e={DetermineComponentFrameRoot:function(){try{if(t){var z=function(){throw Error()};if(Object.defineProperty(z.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(z,[])}catch(A){var r=A}Reflect.construct(l,[],z)}else{try{z.call()}catch(A){r=A}z=!1;try{var g=Object.getOwnPropertyDescriptor(l.prototype,"props");Object.defineProperty(l.prototype,"props",{configurable:!0,set:function(){throw Error()}}),z=!0,new l}finally{z&&(g!==void 0?Object.defineProperty(l.prototype,"props",g):delete l.prototype.props)}}}else{try{throw Error()}catch(A){r=A}(z=l())&&typeof z.catch=="function"&&z.catch(function(){})}}catch(A){if(A&&r&&typeof A.stack=="string")return[A.stack,r.stack]}return[null,null]}};e.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var a=Object.getOwnPropertyDescriptor(e.DetermineComponentFrameRoot,"name");a&&a.configurable&&Object.defineProperty(e.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var n=e.DetermineComponentFrameRoot(),i=n[0],c=n[1];if(i&&c){var f=i.split(`
`),m=c.split(`
`);for(a=e=0;e<f.length&&!f[e].includes("DetermineComponentFrameRoot");)e++;for(;a<m.length&&!m[a].includes("DetermineComponentFrameRoot");)a++;if(e===f.length||a===m.length)for(e=f.length-1,a=m.length-1;1<=e&&0<=a&&f[e]!==m[a];)a--;for(;1<=e&&0<=a;e--,a--)if(f[e]!==m[a]){if(e!==1||a!==1)do if(e--,a--,0>a||f[e]!==m[a]){var S=`
`+f[e].replace(" at new "," at ");return l.displayName&&S.includes("<anonymous>")&&(S=S.replace("<anonymous>",l.displayName)),S}while(1<=e&&0<=a);break}}}finally{Zi=!1,Error.prepareStackTrace=u}return(u=l?l.displayName||l.name:"")?zu(u):""}function ov(l,t){switch(l.tag){case 26:case 27:case 5:return zu(l.type);case 16:return zu("Lazy");case 13:return l.child!==t&&t!==null?zu("Suspense Fallback"):zu("Suspense");case 19:return zu("SuspenseList");case 0:case 15:return Li(l.type,!1);case 11:return Li(l.type.render,!1);case 1:return Li(l.type,!0);case 31:return zu("Activity");case 30:return zu("ViewTransition");default:return""}}function Ms(l){try{var t="",u=null;do t+=ov(l,u),u=l,l=l.return;while(l);return t}catch(e){return`
Error generating stack: `+e.message+`
`+e.stack}}var Ki=Object.prototype.hasOwnProperty,Ji=_.unstable_scheduleCallback,wi=_.unstable_cancelCallback,rv=_.unstable_shouldYield,dv=_.unstable_requestPaint,St=_.unstable_now,vv=_.unstable_getCurrentPriorityLevel,Us=_.unstable_ImmediatePriority,ps=_.unstable_UserBlockingPriority,dn=_.unstable_NormalPriority,mv=_.unstable_LowPriority,Rs=_.unstable_IdlePriority,yv=_.log,hv=_.unstable_setDisableYieldValue,da=null,bt=null;function _u(l){if(typeof yv=="function"&&hv(l),bt&&typeof bt.setStrictMode=="function")try{bt.setStrictMode(da,l)}catch{}}var Tt=Math.clz32?Math.clz32:bv,gv=Math.log,Sv=Math.LN2;function bv(l){return l>>>=0,l===0?32:31-(gv(l)/Sv|0)|0}var vn=256,mn=262144,yn=4194304;function Wu(l){var t=l&42;if(t!==0)return t;switch(l&-l){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return l&-l;case 262144:case 524288:case 1048576:case 2097152:return l&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return l&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return l}}function hn(l,t,u){var e=l.pendingLanes;if(e===0)return 0;var a=0,n=l.suspendedLanes,i=l.pingedLanes;l=l.warmLanes;var c=e&134217727;return c!==0?(e=c&~n,e!==0?a=Wu(e):(i&=c,i!==0?a=Wu(i):u||(u=c&~l,u!==0&&(a=Wu(u))))):(c=e&~n,c!==0?a=Wu(c):i!==0?a=Wu(i):u||(u=e&~l,u!==0&&(a=Wu(u)))),a===0?0:t!==0&&t!==a&&(t&n)===0&&(n=a&-a,u=t&-t,n>=u||n===32&&(u&4194048)!==0)?t:a}function va(l,t){return(l.pendingLanes&~(l.suspendedLanes&~l.pingedLanes)&t)===0}function xs(l,t){(t&8)!==0&&(t|=t&32);var u=l.entangledLanes;if(u!==0)for(l=l.entanglements,u&=t;0<u;){var e=31-Tt(u),a=1<<e;t|=l[e],u&=~a}return t}function Tv(l,t){switch(l){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Hs(){var l=yn;return yn<<=1,(yn&62914560)===0&&(yn=4194304),l}function $i(l){for(var t=[],u=0;31>u;u++)t.push(l);return t}function ma(l,t){l.pendingLanes|=t,t!==268435456&&(l.suspendedLanes=0,l.pingedLanes=0,l.warmLanes=0)}function Ev(l,t,u,e,a,n){var i=l.pendingLanes;l.pendingLanes=u,l.suspendedLanes=0,l.pingedLanes=0,l.warmLanes=0,l.expiredLanes&=u,l.entangledLanes&=u,l.errorRecoveryDisabledLanes&=u,l.shellSuspendCounter=0;var c=l.entanglements,f=l.expirationTimes,m=l.hiddenUpdates;for(u=i&~u;0<u;){var S=31-Tt(u),z=1<<S;c[S]=0,f[S]=-1;var r=m[S];if(r!==null)for(m[S]=null,S=0;S<r.length;S++){var g=r[S];g!==null&&(g.lane&=-536870913)}u&=~z}e!==0&&qs(l,e,0),n!==0&&a===0&&l.tag!==0&&(l.suspendedLanes|=n&~(i&~t))}function qs(l,t,u){l.pendingLanes|=t,l.suspendedLanes&=~t;var e=31-Tt(t);l.entangledLanes|=t,l.entanglements[e]=l.entanglements[e]|1073741824|u&261930}function Ys(l,t){var u=l.entangledLanes|=t;for(l=l.entanglements;u;){var e=31-Tt(u),a=1<<e;a&t|l[e]&t&&(l[e]|=t),u&=~a}}function Bs(l,t){var u=t&-t;return u=(u&42)!==0?1:Fi(u),(u&(l.suspendedLanes|t))!==0?0:u}function Fi(l){switch(l){case 2:l=1;break;case 8:l=4;break;case 32:l=16;break;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:l=128;break;case 268435456:l=134217728;break;default:l=0}return l}function Wi(l){return l&=-l,2<l?8<l?(l&134217727)!==0?32:268435456:8:2}function Gs(){var l=V.p;return l!==0?l:(l=window.event,l===void 0?32:w0(l.type))}function Xs(l,t){var u=V.p;try{return V.p=l,t()}finally{V.p=u}}var iu=Math.random().toString(36).slice(2),Wl="__reactFiber$"+iu,rt="__reactProps$"+iu,Ee="__reactContainer$"+iu,Qs="__reactEvents$"+iu,zv="__reactListeners$"+iu,_v="__reactHandles$"+iu,Vs="__reactResources$"+iu,ya="__reactMarker$"+iu,gn="__reactLoad$"+iu;function Sn(l){delete l[Wl],delete l[rt],delete l[zv],delete l[_v]}function ku(l){var t;if(t=l[Wl])return t;for(var u=l.parentNode;u;){if(t=u[Ee]||u[Wl]){if(u=t.alternate,t.child!==null||u!==null&&u.child!==null)for(l=C0(l);l!==null;){if(u=l[Wl])return u;l=C0(l)}return t}l=u,u=l.parentNode}return null}function ze(l){if(l=l[Wl]||l[Ee]){var t=l.tag;if(t===5||t===6||t===13||t===31||t===26||t===27||t===3)return l}return null}function ha(l){var t=l.tag;if(t===5||t===26||t===27||t===6)return l.stateNode;throw Error(y(33))}function _e(l){var t=l[Vs];return t||(t=l[Vs]={hoistableStyles:new Map,hoistableScripts:new Map}),t}function Kl(l){l[ya]=!0}function Zs(l){l[gn]=void 0}var Ls=new Set,Ks={};function Iu(l,t){Oe(l,t),Oe(l+"Capture",t)}function Line truncated
`).replace(oy,"")}function c0(l,t){return t=i0(t),i0(l)===t}function yl(l,t,u,e,a,n){switch(u){case"children":if(typeof e=="string")t==="body"||t==="textarea"&&e===""||Ae(l,e);else if(typeof e=="number"||typeof e=="bigint")t!=="body"&&Ae(l,""+e);else return;break;case"className":Tn(l,"class",e);break;case"tabIndex":Tn(l,"tabindex",e);break;case"dir":case"role":case"viewBox":case"width":case"height":Tn(l,u,e);break;case"style":to(l,e,n);return;case"data":if(t!=="object"){Tn(l,"data",e);break}case"src":case"href":if(e===""&&(t!=="a"||u!=="href")){l.removeAttribute(u);break}if(e==null||typeof e=="function"||typeof e=="symbol"||typeof e=="boolean"){l.removeAttribute(u);break}e=En(e),l.setAttribute(u,e);break;case"action":case"formAction":if(typeof e=="function"){l.setAttribute(u,"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')");break}else typeof n=="function"&&(u==="formAction"?(t!=="input"&&yl(l,t,"name",a.name,a,null),yl(l,t,"formEncType",a.formEncType,a,null),yl(l,t,"formMethod",a.formMethod,a,null),yl(l,t,"formTarget",a.formTarget,a,null)):(yl(l,t,"encType",a.encType,a,null),yl(l,t,"method",a.method,a,null),yl(l,t,"target",a.target,a,null)));if(e==null||typeof e=="symbol"||typeof e=="boolean"){l.removeAttribute(u);break}e=En(e),l.setAttribute(u,e);break;case"onClick":e!=null&&(l.onclick=$t);return;case"onScroll":e!=null&&P("scroll",l);return;case"onScrollEnd":e!=null&&P("scrollend",l);return;case"dangerouslySetInnerHTML":if(e!=null){if(typeof e!="object"||!("__html"in e))throw Error(y(61));if(u=e.__html,u!=null){if(a.children!=null)throw Error(y(60));n?.__html!==u&&(l.innerHTML=u)}}break;case"multiple":l.multiple=e&&typeof e!="function"&&typeof e!="symbol";break;case"muted":l.muted=e&&typeof e!="function"&&typeof e!="symbol";break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":break;case"autoFocus":break;case"xlinkHref":if(e==null||typeof e=="function"||typeof e=="boolean"||typeof e=="symbol"){l.removeAttribute("xlink:href");break}u=En(e),l.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",u);break;case"contentEditable":case"spellCheck":case"draggable":case"value":case"autoReverse":case"externalResourcesRequired":case"focusable":case"preserveAlpha":e!=null&&typeof e!="function"&&typeof e!="symbol"?l.setAttribute(u,e):l.removeAttribute(u);break;case"inert":case"allowFullScreen":case"async":case"autoPlay":case"controls":case"credentialless":case"default":case"defer":case"disabled":case"disablePictureInPicture":case"disableRemotePlayback":case"formNoValidate":case"hidden":case"loop":case"noModule":case"noValidate":case"open":case"playsInline":case"readOnly":case"required":case"reversed":case"scoped":case"seamless":case"itemScope":e&&typeof e!="function"&&typeof e!="symbol"?l.setAttribute(u,""):l.removeAttribute(u);break;case"capture":case"download":e===!0?l.setAttribute(u,""):e!==!1&&e!=null&&typeof e!="function"&&typeof e!="symbol"?l.setAttribute(u,e):l.removeAttribute(u);break;case"cols":case"rows":case"size":case"span":e!=null&&typeof e!="function"&&typeof e!="symbol"&&!isNaN(e)&&1<=e?l.setAttribute(u,e):l.removeAttribute(u);break;case"rowSpan":case"start":e==null||typeof e=="function"||typeof e=="symbol"||isNaN(e)?l.removeAttribute(u):l.setAttribute(u,e);break;case"popover":P("beforetoggle",l),P("toggle",l),bn(l,"popover",e);break;case"xlinkActuate":cu(l,"http://www.w3.org/1999/xlink","xlink:actuate",e);break;case"xlinkArcrole":cu(l,"http://www.w3.org/1999/xlink","xlink:arcrole",e);break;case"xlinkRole":cu(l,"http://www.w3.org/1999/xlink","xlink:role",e);break;case"xlinkShow":cu(l,"http://www.w3.org/1999/xlink","xlink:show",e);break;case"xlinkTitle":cu(l,"http://www.w3.org/1999/xlink","xlink:title",e);break;case"xlinkType":cu(l,"http://www.w3.org/1999/xlink","xlink:type",e);break;case"xmlBase":cu(l,"http://www.w3.org/XML/1998/namespace","xml:base",e);break;case"xmlLang":cu(l,"http://www.w3.org/XML/1998/namespace","xml:lang",e);break;case"xmlSpace":cu(l,"http://www.w3.org/XML/1998/namespace","xml:space",e);break;case"is":bn(l,"is",e);break;case"innerText":case"textContent":return;default:if(!(2<u.length)||u[0]!=="o"&&u[0]!=="O"||u[1]!=="n"&&u[1]!=="N")u=jv.get(u)||u,bn(l,u,e);else return}fl=!0}function Pf(l,t,u,e,a,n){switch(u){case"style":to(l,e,n);return;case"dangerouslySetInnerHTML":if(e!=null){if(typeof e!="object"||!("__html"in e))throw Error(y(61));if(u=e.__html,u!=null){if(a.children!=null)throw Error(y(60));n?.__html!==u&&(l.innerHTML=u)}}break;case"children":if(typeof e=="string")Ae(l,e);else if(typeof e=="number"||typeof e=="bigint")Ae(l,""+e);else return;break;case"onScroll":e!=null&&P("scroll",l);return;case"onScrollEnd":e!=null&&P("scrollend",l);return;case"onClick":e!=null Line truncated
`).map((O,N,s)=>O.trim()&&!O.trim().startsWith("###")?d.jsxs("div",{className:"draft-row",children:[d.jsx("code",{children:O}),d.jsx("button",{"aria-label":`Monter l’instruction ${N}`,disabled:N===0||s[N-1].startsWith("###"),onClick:()=>{const E=[...s];[E[N-1],E[N]]=[E[N],E[N-1]],Nl("draft","","",E.join(`
`))},children:"↑"}),d.jsx("button",{"aria-label":`Descendre l’instruction ${N}`,disabled:N===s.length-1||!s[N+1].trim(),onClick:()=>{const E=[...s];[E[N+1],E[N]]=[E[N],E[N+1]],Nl("draft","","",E.join(`
`))},children:"↓"}),d.jsx("button",{onClick:()=>Nl("draft","","",s.filter((E,D)=>D!==N).join(`
`)),children:"Retirer"})]},N):null)}),d.jsxs("details",{children:[d.jsx("summary",{children:"Éditer les instructions en texte"}),d.jsx("textarea",{className:"instructions","aria-label":"Brouillon des transferts",value:C,onChange:O=>Nl("draft","","",O.target.value)})]}),d.jsx("button",{disabled:!M[Gt("draft")],onClick:()=>pl([Gt("draft")]),children:"Enregistrer le brouillon"}),d.jsxs("details",{children:[d.jsx("summary",{children:"Instructions actuellement appliquées"}),d.jsx("pre",{children:_.applied})]})]}),d.jsxs("div",{className:"results",children:[d.jsxs("h2",{children:[cl.length," réponse",cl.length!==1?"s":""]}),d.jsx("span",{children:!rl&&!ul?"Vue d’ensemble · sélectionnez une copie ou un label pour cibler la vérification":"Images annotées après lecture manuscrite"})]}),d.jsx("div",{className:"answer-grid",children:cl.slice(0,R).map(({c:O,a:N})=>d.jsxs("article",{className:"answer",children:[d.jsxs("div",{className:"card-heading",children:[d.jsxs("div",{children:[d.jsxs("span",{className:"eyebrow",children:["COPIE ",O.id," · ",O.name]}),d.jsx("h3",{children:N.label})]}),d.jsx("span",{className:"badge",children:N.info.present?N.info.not_empty?N.info.touched?"Dans le PDF":"Hors PDF filtré":"Réponse vide":"Absente"})]}),d.jsx("div",{className:"image-space",children:N.image?d.jsxs("button",{className:"image-button","aria-label":`Agrandir copie ${O.id} ${N.label}`,onClick:()=>at({copy:O.id,label:N.label}),children:[d.jsx("img",{loading:"lazy",src:Gi(O.id,N.label,!1,_.revision),alt:`Réponse annotée — copie ${O.id}, ${N.label}`}),d.jsx("span",{children:"Agrandir ↗"})]}):d.jsx("p",{className:"empty",children:"Aucune image pour ce label."})}),d.jsxs("div",{className:"answer-form",children:[d.jsxs("label",{children:["Score",d.jsx("input",{inputMode:"decimal",value:Vl("score",O.id,N.label,N.score),onChange:s=>Nl("score",O.id,N.label,s.target.value)})]}),d.jsxs("label",{children:["Remarque personnelle",d.jsx("textarea",{placeholder:"Ajouter une remarque (LaTeX accepté)",value:Vl("remark",O.id,N.label,N.remark),onChange:s=>Nl("remark",O.id,N.label,s.target.value)})]})]}),d.jsxs("div",{className:"card-actions",children:[d.jsx("button",{disabled:!M[Gt("score",O.id,N.label)]&&!M[Gt("remark",O.id,N.label)],onClick:()=>pl([Gt("score",O.id,N.label),Gt("remark",O.id,N.label)]),children:"Enregistrer"}),d.jsx("button",{disabled:!N.editableBody,onClick:()=>gt({copy:O.id,label:N.label}),children:"Couper / transférer"}),d.jsx("a",{href:fn(O.id,"context",N.label),target:"_blank",rel:"noreferrer",children:"Contexte ↗"}),d.jsx("a",{href:fn(O.id,"question",N.label),target:"_blank",rel:"noreferrer",children:"Énoncé ↗"}),d.jsx("a",{href:fn(O.id,"solution",N.label),target:"_blank",rel:"noreferrer",children:"Corrigé ↗"}),d.jsx("a",{href:fn(O.id,"output"),target:"_blank",rel:"noreferrer",children:"PDF final ↗"})]})]},O.id+"|"+N.label))}),cl.length===0&&d.jsx("p",{className:"empty",children:"Aucune réponse ne correspond à ces filtres."}),cl.length>R&&d.jsx("button",{className:"load-more",onClick:()=>b(R+20),children:"Afficher les 20 suivantes"})]}),(_.job.log||_.job.running)&&d.jsxs("details",{className:"panel",open:_.job.status==="failed",children:[d.jsxs("summary",{children:["Journal de régénération · ",_.job.status==="failed"?"Échec / résultat incomplet":_.job.running?"En cours":"Terminée"]}),d.jsx("pre",{className:"log",children:_.job.log})]})]}),Ol&&d.jsx(Sh,{copy:Ol.copy,source:Ol.label,labels:_.labels,mode:_.mode,revision:_.revision,draft:C,close:()=>gt(null),accept:O=>{Nl("draft","","",O),gt(null),$("Instruction ajoutée au brouillon. Enregistrez-le avant de l’appliquer.")}}),Ql&&d.jsx(gh,{copy:Ql.copy,label:Ql.label,revision:_.revision,close:()=>at(null)})]})}function gh({copy:_,label:G,revision:Y,close:y}){const[q,$]=Sl.useState(100);return Sl.useEffect(()=>{const rl=Gl=>{Gl.key==="Escape"&&y()};return window.addEventListener("keydown",rl),()=>window.removeEventListener("keydown",rl)},[]),d.jsx("div",{className:"overlay",children:d.jsxs("section",{className:"modal viewer",role:"dialog","aria-modal":"true","aria-label":`Copie ${_} ${G}`,children:[d.jsxs("div",{className:"modal-head",children:[d.jsxs("h2",{children:["Copie ",_," · ",G]}),d.jsxs("label",{children:["Zoom ",d.jsx("input",{type:"range",min:"40",max:"200",value:q,onChange:rl=>$(Number(rl.target.value))}),q,"%"]}),d.jsx("button",{autoFocus:!0,onClick:y,children:"Fermer"})]}),d.jsx("div",{className:"zoom-scroll",children:d.jsx("img",{src:Gi(_,G,!1,Y),alt:`Copie ${_}, ${G}`,style:{width:q+"%",maxWidth:"none"}})})]})})}function Sh({copy:_,source:G,labels:Y,mode:y,revision:q,draft:$,close:rl,accept:Gl}){const[ul,Dl]=Sl.useState(Y.find(L=>L!==G)||""),[R,b]=Sl.useState("cut"),[M,_l]=Sl.useState(50),[Xl,Ul]=Sl.useState("1"),[il,et]=Sl.useState(">"),[Ol,gt]=Sl.useState(!1),[Ql,at]=Sl.useState(""),[xl,K]=Sl.useState(null),[F,Vl]=Sl.useState(!1),Nl=R==="cut"?`c{${M}}${Xl}${il}`:R,pl=`Copie${_} ${G} ${Nl} ${Ol?"|":""}${ul}${Ol?"":"|Line truncated
`+pl+`
`;Sl.useEffect(()=>K(null),[ul,R,M,Xl,il,Ol,$]);const jt=async()=>{Vl(!0),at("");try{K(await oa("preview",{copy:_,source:G,target:ul,text:Ll}))}catch(L){at(String(L))}finally{Vl(!1)}};return d.jsx("div",{className:"overlay",children:d.jsxs("section",{className:"modal",role:"dialog","aria-modal":"true","aria-label":"Couper ou transférer une réponse",children:[d.jsxs("div",{className:"modal-head",children:[d.jsxs("div",{children:[d.jsxs("span",{className:"eyebrow",children:["COPIE ",_," · ",y]}),d.jsx("h2",{children:"Couper ou transférer"})]}),d.jsx("button",{autoFocus:!0,onClick:rl,children:"Fermer"})]}),d.jsx("p",{children:"Seul le corps de la réponse est déplacé. Cliquez sur l’image source pour placer la coupe."}),Ql&&d.jsx("p",{role:"alert",className:"alert",children:Ql}),d.jsxs("div",{className:"cut-controls",children:[d.jsxs("label",{children:["Destination",d.jsx("select",{value:ul,onChange:L=>Dl(L.target.value),children:Y.filter(L=>L!==G).map(L=>d.jsx("option",{children:L},L))})]}),d.jsxs("label",{children:["Opération",d.jsxs("select",{value:R,onChange:L=>b(L.target.value),children:[d.jsx("option",{value:"cut",children:"Couper la source"}),d.jsx("option",{value:"x>",children:"Déplacer et ajouter"}),d.jsx("option",{value:"->",children:"Copier et ajouter"}),d.jsx("option",{value:"xx",children:"Déplacer et remplacer"}),d.jsx("option",{value:"-x",children:"Copier et remplacer"}),d.jsx("option",{value:"xs",children:"Vider la source"}),d.jsx("option",{value:"sx",children:"Vider la cible"}),d.jsx("option",{value:"ss",children:"Conserver les deux"})]})]}),R==="cut"&&d.jsxs(d.Fragment,{children:[d.jsxs("label",{children:["Coupe : ",M,"%",d.jsx("input",{type:"range",min:"1",max:"99",step:"0.1",value:M,onChange:L=>_l(Number(L.target.value))})]}),d.jsxs("label",{children:["Conserver à la source",d.jsxs("select",{value:Xl,onChange:L=>Ul(L.target.value),children:[d.jsx("option",{value:"1",children:"La partie haute"}),d.jsx("option",{value:"2",children:"La partie basse"})]})]}),d.jsxs("label",{children:["À la destination",d.jsxs("select",{value:il,onChange:L=>et(L.target.value),children:[d.jsx("option",{value:">",children:"Ajouter le fragment"}),d.jsx("option",{value:"x",children:"Remplacer le corps"})]})]})]}),d.jsxs("label",{className:"check",children:[d.jsx("input",{type:"checkbox",checked:Ol,onChange:L=>gt(L.target.checked)}),"Placer avant la cible"]})]}),d.jsxs("div",{className:"compare",children:[d.jsxs("div",{children:[d.jsxs("h3",{children:["Source · ",G]}),d.jsxs("div",{className:"cut-image",onClick:L=>{if(R==="cut"){const hl=L.currentTarget.getBoundingClientRect();_l(Math.min(99,Math.max(1,Math.round((L.clientY-hl.top)/hl.height*1e3)/10)))}},children:[d.jsx("img",{src:Gi(_,G,!0,q),alt:"Corps source sans en-tête"}),R==="cut"&&d.jsx("div",{className:"cut-line",style:{top:M+"%"},children:d.jsxs("span",{children:[M,"%"]})})]})]}),d.jsxs("div",{children:[d.jsxs("h3",{children:["Cible · ",ul]}),d.jsx(bh,{copy:_,label:ul,revision:q},ul)]})]}),d.jsx("code",{className:"instruction-line",children:pl}),d.jsxs("div",{className:"modal-actions",children:[d.jsx("button",{disabled:F||!ul,onClick:jt,children:F?"Calcul de l’aperçu…":"Aperçu du résultat"}),d.jsx("button",{className:"primary",disabled:!xl||F,onClick:()=>Gl(Ll),children:"Ajouter au brouillon"})]}),xl&&d.jsxs(d.Fragment,{children:[d.jsx("h3",{children:"Résultat proposé après toutes les instructions du brouillon"}),d.jsx("div",{className:"compare",children:[G,ul].map(L=>d.jsxs("div",{children:[d.jsx("h4",{children:L}),xl[L]?d.jsx("img",{src:xl[L],alt:"Aperçu "+L}):d.jsx("p",{className:"empty",children:"Corps vide"})]},L))})]}),d.jsx("p",{className:"muted",children:"Une instruction par source ; une cible ne peut pas être source. Modifiez ou retirez une instruction existante dans le panneau « Transferts et coupes »."})]})})}function bh({copy:_,label:G,revision:Y}){const[y,q]=Sl.useState(!1);return y?d.jsx("p",{className:"empty",children:"Pas de corps existant. Le transfert peut remplir cette cible."}):d.jsx("img",{src:Gi(_,G,!0,Y),alt:"Corps cible sans en-tête",onError:()=>q(!0)})}yh.createRoot(document.getElementById("root")).render(d.jsx(hh,{}));
@@ -0,0 +1,15 @@
(function(){const X=document.createElement("link").relList;if(X&&X.supports&&X.supports("modulepreload"))return;for(const B of document.querySelectorAll('link[rel="modulepreload"]'))h(B);new MutationObserver(B=>{for(const $ of B)if($.type==="childList")for(const sl of $.addedNodes)sl.tagName==="LINK"&&sl.rel==="modulepreload"&&h(sl)}).observe(document,{childList:!0,subtree:!0});function G(B){const $={};return B.integrity&&($.integrity=B.integrity),B.referrerPolicy&&($.referrerPolicy=B.referrerPolicy),B.crossOrigin==="use-credentials"?$.credentials="include":B.crossOrigin==="anonymous"?$.credentials="omit":$.credentials="same-origin",$}function h(B){if(B.ep)return;B.ep=!0;const $=G(B);fetch(B.href,$)}})();var zs={exports:{}},cn={};var lv;function iy(){if(lv)return cn;lv=1;var N=Symbol.for("react.transitional.element"),X=Symbol.for("react.fragment");function G(h,B,$){var sl=null;if($!==void 0&&(sl=""+$),B.key!==void 0&&(sl=""+B.key),"key"in B){$={};for(var Bl in B)Bl!=="key"&&($[Bl]=B[Bl])}else $=B;return B=$.ref,{$$typeof:N,type:h,key:sl,ref:B!==void 0?B:null,props:$}}return cn.Fragment=X,cn.jsx=G,cn.jsxs=G,cn}var tv;function cy(){return tv||(tv=1,zs.exports=iy()),zs.exports}var r=cy(),Ns={exports:{}},L={};var ev;function fy(){if(ev)return L;ev=1;var N=Symbol.for("react.transitional.element"),X=Symbol.for("react.portal"),G=Symbol.for("react.fragment"),h=Symbol.for("react.strict_mode"),B=Symbol.for("react.profiler"),$=Symbol.for("react.consumer"),sl=Symbol.for("react.context"),Bl=Symbol.for("react.forward_ref"),el=Symbol.for("react.suspense"),Al=Symbol.for("react.memo"),R=Symbol.for("react.lazy"),T=Symbol.for("react.activity"),M=Symbol.for("react.view_transition"),zl=Symbol.iterator;function Gl(o){return o===null||typeof o!="object"?null:(o=zl&&o[zl]||o["@@iterator"],typeof o=="function"?o:null)}var Dl={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},il=Object.assign,et={};function Nl(o,O,x){this.props=o,this.context=O,this.refs=et,this.updater=x||Dl}Nl.prototype.isReactComponent={},Nl.prototype.setState=function(o,O){if(typeof o!="object"&&typeof o!="function"&&o!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,o,O,"setState")},Nl.prototype.forceUpdate=function(o){this.updater.enqueueForceUpdate(this,o,"forceUpdate")};function St(){}St.prototype=Nl.prototype;function Xl(o,O,x){this.props=o,this.context=O,this.refs=et,this.updater=x||Dl}var ut=Xl.prototype=new St;ut.constructor=Xl,il(ut,Nl.prototype),ut.isPureReactComponent=!0;var Rl=Array.isArray;function K(){}var k={H:null,A:null,T:null,S:null},it=Object.prototype.hasOwnProperty;function Jl(o,O,x){var H=x.ref;return{$$typeof:N,type:o,key:O,ref:H!==void 0?H:null,props:x}}function wl(o,O){return Jl(o.type,O,o.props)}function Ml(o){return typeof o=="object"&&o!==null&&o.$$typeof===N}function at(o){var O={"=":"=0",":":"=2"};return"$"+o.replace(/[=:]/g,function(x){return O[x]})}var Z=/\/+/g;function gl(o,O){return typeof o=="object"&&o!==null&&o.key!=null?at(""+o.key):O.toString(36)}function j(o){switch(o.status){case"fulfilled":return o.value;case"rejected":throw o.reason;default:switch(typeof o.status=="string"?o.then(K,K):(o.status="pending",o.then(function(O){o.status==="pending"&&(o.status="fulfilled",o.value=O)},function(O){o.status==="pending"&&(o.status="rejected",o.reason=O)})),o.status){case"fulfilled":return o.value;case"rejected":throw o.reason}}throw o}function Q(o,O,x,H,F){var b=typeof o;(b==="undefined"||b==="boolean")&&(o=null);var _=!1;if(o===null)_=!0;else switch(b){case"bigint":case"string":case"number":_=!0;break;case"object":switch(o.$$typeof){case N:case X:_=!0;break;case R:return _=o._init,Q(_(o._payload),O,x,H,F)}}if(_)return F=F(o),_=H===""?"."+gl(o,0):H,Rl(F)?(x="",_!=null&&(x=_.replace(Z,"$&/")+"/"),Q(F,O,x,"",function(ol){return ol})):F!=null&&(Ml(F)&&(F=wl(F,x+(F.key==null||o&&o.key===F.key?"":(""+F.key).replace(Z,"$&/")+"/")+_)),O.push(F)),1;_=0;var A=H===""?".":H+":";if(Rl(o))for(var D=0;D<o.length;D++)H=o[D],b=A+gl(H,D),_+=Q(H,O,x,b,F);else if(D=Gl(o),typeof D=="function")for(o=D.call(o),D=0;!(H=o.next()).done;)H=H.value,b=A+gl(H,D++),_+=Q(H,O,x,b,F);else if(b==="object"){if(typeof o.then=="function")return Q(j(o),O,x,H,F);throw O=String(o),Error("Objects are not valid as a React child (found: "+(O==="[object Object]"?"object with keys {"+Object.keys(o).join(", ")+"}":O)+"). If you meant to render a collection of children, use an array instead.")}return _}function Y(o,O,x){if(o==null)return o;var H=[],F=0;return Q(o,H,"","",function(b){return O.call(x,b,F++)}),H}function ul(o){if(o._status===-1){var O=o._result,x=O();x.then(function(H){(o._status===0||o._status===-1)&&(o._status=1,o._result=H,x.status===void 0&&(x.status="fulfilled",x.value=H))},function(H){(o._status===0||o._status===-1)&&(o._status=2,o._result=H,x.status===void 0&&(x.status="reLine truncated
at`)?" (<anonymous>)":-1<e.stack.indexOf("@")?"@unknown:0:0":""}return`
`+Vi+l+Ds}var Zi=!1;function Li(l,t){if(!l||Zi)return"";Zi=!0;var e=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var u={DetermineComponentFrameRoot:function(){try{if(t){var z=function(){throw Error()};if(Object.defineProperty(z.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(z,[])}catch(C){var d=C}Reflect.construct(l,[],z)}else{try{z.call()}catch(C){d=C}z=!1;try{var g=Object.getOwnPropertyDescriptor(l.prototype,"props");Object.defineProperty(l.prototype,"props",{configurable:!0,set:function(){throw Error()}}),z=!0,new l}finally{z&&(g!==void 0?Object.defineProperty(l.prototype,"props",g):delete l.prototype.props)}}}else{try{throw Error()}catch(C){d=C}(z=l())&&typeof z.catch=="function"&&z.catch(function(){})}}catch(C){if(C&&d&&typeof C.stack=="string")return[C.stack,d.stack]}return[null,null]}};u.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var a=Object.getOwnPropertyDescriptor(u.DetermineComponentFrameRoot,"name");a&&a.configurable&&Object.defineProperty(u.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var n=u.DetermineComponentFrameRoot(),i=n[0],c=n[1];if(i&&c){var f=i.split(`
`),m=c.split(`
`);for(a=u=0;u<f.length&&!f[u].includes("DetermineComponentFrameRoot");)u++;for(;a<m.length&&!m[a].includes("DetermineComponentFrameRoot");)a++;if(u===f.length||a===m.length)for(u=f.length-1,a=m.length-1;1<=u&&0<=a&&f[u]!==m[a];)a--;for(;1<=u&&0<=a;u--,a--)if(f[u]!==m[a]){if(u!==1||a!==1)do if(u--,a--,0>a||f[u]!==m[a]){var S=`
`+f[u].replace(" at new "," at ");return l.displayName&&S.includes("<anonymous>")&&(S=S.replace("<anonymous>",l.displayName)),S}while(1<=u&&0<=a);break}}}finally{Zi=!1,Error.prepareStackTrace=e}return(e=l?l.displayName||l.name:"")?_e(e):""}function ov(l,t){switch(l.tag){case 26:case 27:case 5:return _e(l.type);case 16:return _e("Lazy");case 13:return l.child!==t&&t!==null?_e("Suspense Fallback"):_e("Suspense");case 19:return _e("SuspenseList");case 0:case 15:return Li(l.type,!1);case 11:return Li(l.type.render,!1);case 1:return Li(l.type,!0);case 31:return _e("Activity");case 30:return _e("ViewTransition");default:return""}}function Ms(l){try{var t="",e=null;do t+=ov(l,e),e=l,l=l.return;while(l);return t}catch(u){return`
Error generating stack: `+u.message+`
`+u.stack}}var Ki=Object.prototype.hasOwnProperty,Ji=N.unstable_scheduleCallback,wi=N.unstable_cancelCallback,rv=N.unstable_shouldYield,dv=N.unstable_requestPaint,Tt=N.unstable_now,vv=N.unstable_getCurrentPriorityLevel,ps=N.unstable_ImmediatePriority,Us=N.unstable_UserBlockingPriority,vn=N.unstable_NormalPriority,mv=N.unstable_LowPriority,Rs=N.unstable_IdlePriority,hv=N.log,yv=N.unstable_setDisableYieldValue,va=null,Et=null;function Oe(l){if(typeof hv=="function"&&yv(l),Et&&typeof Et.setStrictMode=="function")try{Et.setStrictMode(va,l)}catch{}}var zt=Math.clz32?Math.clz32:bv,gv=Math.log,Sv=Math.LN2;function bv(l){return l>>>=0,l===0?32:31-(gv(l)/Sv|0)|0}var mn=256,hn=262144,yn=4194304;function Ie(l){var t=l&42;if(t!==0)return t;switch(l&-l){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return l&-l;case 262144:case 524288:case 1048576:case 2097152:return l&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return l&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return l}}function gn(l,t,e){var u=l.pendingLanes;if(u===0)return 0;var a=0,n=l.suspendedLanes,i=l.pingedLanes;l=l.warmLanes;var c=u&134217727;return c!==0?(u=c&~n,u!==0?a=Ie(u):(i&=c,i!==0?a=Ie(i):e||(e=c&~l,e!==0&&(a=Ie(e))))):(c=u&~n,c!==0?a=Ie(c):i!==0?a=Ie(i):e||(e=u&~l,e!==0&&(a=Ie(e)))),a===0?0:t!==0&&t!==a&&(t&n)===0&&(n=a&-a,e=t&-t,n>=e||n===32&&(e&4194048)!==0)?t:a}function ma(l,t){return(l.pendingLanes&~(l.suspendedLanes&~l.pingedLanes)&t)===0}function xs(l,t){(t&8)!==0&&(t|=t&32);var e=l.entangledLanes;if(e!==0)for(l=l.entanglements,e&=t;0<e;){var u=31-zt(e),a=1<<u;t|=l[u],e&=~a}return t}function Tv(l,t){switch(l){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Hs(){var l=yn;return yn<<=1,(yn&62914560)===0&&(yn=4194304),l}function $i(l){for(var t=[],e=0;31>e;e++)t.push(l);return t}function ha(l,t){l.pendingLanes|=t,t!==268435456&&(l.suspendedLanes=0,l.pingedLanes=0,l.warmLanes=0)}function Ev(l,t,e,u,a,n){var i=l.pendingLanes;l.pendingLanes=e,l.suspendedLanes=0,l.pingedLanes=0,l.warmLanes=0,l.expiredLanes&=e,l.entangledLanes&=e,l.errorRecoveryDisabledLanes&=e,l.shellSuspendCounter=0;var c=l.entanglements,f=l.expirationTimes,m=l.hiddenUpdates;for(e=i&~e;0<e;){var S=31-zt(e),z=1<<S;c[S]=0,f[S]=-1;var d=m[S];if(d!==null)for(m[S]=null,S=0;S<d.length;S++){var g=d[S];g!==null&&(g.lane&=-536870913)}e&=~z}u!==0&&qs(l,u,0),n!==0&&a===0&&l.tag!==0&&(l.suspendedLanes|=n&~(i&~t))}function qs(l,t,e){l.pendingLanes|=t,l.suspendedLanes&=~t;var u=31-zt(t);l.entangledLanes|=t,l.entanglements[u]=l.entanglements[u]|1073741824|e&261930}function Ys(l,t){var e=l.entangledLanes|=t;for(l=l.entanglements;e;){var u=31-zt(e),a=1<<u;a&t|l[u]&t&&(l[u]|=t),e&=~a}}function Bs(l,t){var e=t&-t;return e=(e&42)!==0?1:Fi(e),(e&(l.suspendedLanes|t))!==0?0:e}function Fi(l){switch(l){case 2:l=1;break;case 8:l=4;break;case 32:l=16;break;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:l=128;break;case 268435456:l=134217728;break;default:l=0}return l}function Wi(l){return l&=-l,2<l?8<l?(l&134217727)!==0?32:268435456:8:2}function Gs(){var l=D.p;return l!==0?l:(l=window.event,l===void 0?32:w0(l.type))}function Xs(l,t){var e=D.p;try{return D.p=l,t()}finally{D.p=e}}var fe=Math.random().toString(36).slice(2),Fl="__reactFiber$"+fe,dt="__reactProps$"+fe,zu="__reactContainer$"+fe,Qs="__reactEvents$"+fe,zv="__reactListeners$"+fe,Nv="__reactHandles$"+fe,Vs="__reactResources$"+fe,ya="__reactMarker$"+fe,Sn="__reactLoad$"+fe;function bn(l){delete l[Fl],delete l[dt],delete l[zv],delete l[Nv]}function Pe(l){var t;if(t=l[Fl])return t;for(var e=l.parentNode;e;){if(t=e[zu]||e[Fl]){if(e=t.alternate,t.child!==null||e!==null&&e.child!==null)for(l=C0(l);l!==null;){if(e=l[Fl])return e;l=C0(l)}return t}l=e,e=l.parentNode}return null}function Nu(l){if(l=l[Fl]||l[zu]){var t=l.tag;if(t===5||t===6||t===13||t===31||t===26||t===27||t===3)return l}return null}function ga(l){var t=l.tag;if(t===5||t===26||t===27||t===6)return l.stateNode;throw Error(h(33))}function _u(l){var t=l[Vs];return t||(t=l[Vs]={hoistableStyles:new Map,hoistableScripts:new Map}),t}function Vl(l){l[ya]=!0}function Zs(l){l[Sn]=void 0}var Ls=new Set,Ks={};function lu(l,t){Ou(l,t),Ou(l+"Capture",t)}function Line truncated
`).replace(oh,"")}function c0(l,t){return t=i0(t),i0(l)===t}function hl(l,t,e,u,a,n){switch(e){case"children":if(typeof u=="string")t==="body"||t==="textarea"&&u===""||Cu(l,u);else if(typeof u=="number"||typeof u=="bigint")t!=="body"&&Cu(l,""+u);else return;break;case"className":En(l,"class",u);break;case"tabIndex":En(l,"tabindex",u);break;case"dir":case"role":case"viewBox":case"width":case"height":En(l,e,u);break;case"style":to(l,u,n);return;case"data":if(t!=="object"){En(l,"data",u);break}case"src":case"href":if(u===""&&(t!=="a"||e!=="href")){l.removeAttribute(e);break}if(u==null||typeof u=="function"||typeof u=="symbol"||typeof u=="boolean"){l.removeAttribute(e);break}u=zn(u),l.setAttribute(e,u);break;case"action":case"formAction":if(typeof u=="function"){l.setAttribute(e,"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')");break}else typeof n=="function"&&(e==="formAction"?(t!=="input"&&hl(l,t,"name",a.name,a,null),hl(l,t,"formEncType",a.formEncType,a,null),hl(l,t,"formMethod",a.formMethod,a,null),hl(l,t,"formTarget",a.formTarget,a,null)):(hl(l,t,"encType",a.encType,a,null),hl(l,t,"method",a.method,a,null),hl(l,t,"target",a.target,a,null)));if(u==null||typeof u=="symbol"||typeof u=="boolean"){l.removeAttribute(e);break}u=zn(u),l.setAttribute(e,u);break;case"onClick":u!=null&&(l.onclick=kt);return;case"onScroll":u!=null&&P("scroll",l);return;case"onScrollEnd":u!=null&&P("scrollend",l);return;case"dangerouslySetInnerHTML":if(u!=null){if(typeof u!="object"||!("__html"in u))throw Error(h(61));if(e=u.__html,e!=null){if(a.children!=null)throw Error(h(60));n?.__html!==e&&(l.innerHTML=e)}}break;case"multiple":l.multiple=u&&typeof u!="function"&&typeof u!="symbol";break;case"muted":l.muted=u&&typeof u!="function"&&typeof u!="symbol";break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":break;case"autoFocus":break;case"xlinkHref":if(u==null||typeof u=="function"||typeof u=="boolean"||typeof u=="symbol"){l.removeAttribute("xlink:href");break}e=zn(u),l.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",e);break;case"contentEditable":case"spellCheck":case"draggable":case"value":case"autoReverse":case"externalResourcesRequired":case"focusable":case"preserveAlpha":u!=null&&typeof u!="function"&&typeof u!="symbol"?l.setAttribute(e,u):l.removeAttribute(e);break;case"inert":case"allowFullScreen":case"async":case"autoPlay":case"controls":case"credentialless":case"default":case"defer":case"disabled":case"disablePictureInPicture":case"disableRemotePlayback":case"formNoValidate":case"hidden":case"loop":case"noModule":case"noValidate":case"open":case"playsInline":case"readOnly":case"required":case"reversed":case"scoped":case"seamless":case"itemScope":u&&typeof u!="function"&&typeof u!="symbol"?l.setAttribute(e,""):l.removeAttribute(e);break;case"capture":case"download":u===!0?l.setAttribute(e,""):u!==!1&&u!=null&&typeof u!="function"&&typeof u!="symbol"?l.setAttribute(e,u):l.removeAttribute(e);break;case"cols":case"rows":case"size":case"span":u!=null&&typeof u!="function"&&typeof u!="symbol"&&!isNaN(u)&&1<=u?l.setAttribute(e,u):l.removeAttribute(e);break;case"rowSpan":case"start":u==null||typeof u=="function"||typeof u=="symbol"||isNaN(u)?l.removeAttribute(e):l.setAttribute(e,u);break;case"popover":P("beforetoggle",l),P("toggle",l),Tn(l,"popover",u);break;case"xlinkActuate":se(l,"http://www.w3.org/1999/xlink","xlink:actuate",u);break;case"xlinkArcrole":se(l,"http://www.w3.org/1999/xlink","xlink:arcrole",u);break;case"xlinkRole":se(l,"http://www.w3.org/1999/xlink","xlink:role",u);break;case"xlinkShow":se(l,"http://www.w3.org/1999/xlink","xlink:show",u);break;case"xlinkTitle":se(l,"http://www.w3.org/1999/xlink","xlink:title",u);break;case"xlinkType":se(l,"http://www.w3.org/1999/xlink","xlink:type",u);break;case"xmlBase":se(l,"http://www.w3.org/XML/1998/namespace","xml:base",u);break;case"xmlLang":se(l,"http://www.w3.org/XML/1998/namespace","xml:lang",u);break;case"xmlSpace":se(l,"http://www.w3.org/XML/1998/namespace","xml:space",u);break;case"is":Tn(l,"is",u);break;case"innerText":case"textContent":return;default:if(!(2<e.length)||e[0]!=="o"&&e[0]!=="O"||e[1]!=="n"&&e[1]!=="N")e=Dv.get(e)||e,Tn(l,e,u);else return}cl=!0}function Pf(l,t,e,u,a,n){switch(e){case"style":to(l,u,n);return;case"dangerouslySetInnerHTML":if(u!=null){if(typeof u!="object"||!("__html"in u))throw Error(h(61));if(e=u.__html,e!=null){if(a.children!=null)throw Error(h(60));n?.__html!==e&&(l.innerHTML=e)}}break;case"children":if(typeof u=="string")Cu(l,u);else if(typeof u=="number"||typeof u=="bigint")Cu(l,""+u);else return;break;case"onScroll":u!=null&&P("scroll",l);return;case"onScrollEnd":u!=null&&P("scrollend",l);return;case"onClick":u!=null Line truncated
`).map((b,_,A)=>b.trim()&&!b.trim().startsWith("###")?r.jsxs("div",{className:"draft-row",children:[r.jsx("code",{children:b}),r.jsx("button",{"aria-label":`Monter l’instruction ${_}`,disabled:_===0||A[_-1].startsWith("###"),onClick:()=>{const D=[...A];[D[_-1],D[_]]=[D[_],D[_-1]],Z("draft","","",D.join(`
`))},children:"↑"}),r.jsx("button",{"aria-label":`Descendre l’instruction ${_}`,disabled:_===A.length-1||!A[_+1].trim(),onClick:()=>{const D=[...A];[D[_+1],D[_]]=[D[_],D[_+1]],Z("draft","","",D.join(`
`))},children:"↓"}),r.jsx("button",{onClick:()=>Z("draft","","",A.filter((D,ol)=>ol!==_).join(`
`)),children:"Retirer"})]},_):null)}),r.jsxs("details",{children:[r.jsx("summary",{children:"Éditer les instructions en texte"}),r.jsx("textarea",{className:"instructions","aria-label":"Brouillon des transferts",value:al,onChange:b=>Z("draft","","",b.target.value)})]}),r.jsx("button",{disabled:!M[Vt("draft")],onClick:()=>gl([Vt("draft")]),children:"Enregistrer le brouillon"}),r.jsxs("details",{children:[r.jsx("summary",{children:"Instructions actuellement appliquées"}),r.jsx("pre",{children:N.applied})]})]}),r.jsxs("div",{className:"results",children:[r.jsxs("h2",{children:[bt.length," réponse",bt.length!==1?"s":""]}),r.jsx("span",{children:!sl&&!el?"Vue d’ensemble · sélectionnez une copie ou un label pour cibler la vérification":"Images annotées après lecture manuscrite"})]}),r.jsx("div",{className:"answer-grid",children:bt.slice(0,R).map(({c:b,a:_})=>r.jsxs("article",{className:"answer",children:[r.jsxs("div",{className:"card-heading",children:[r.jsxs("div",{children:[r.jsxs("span",{className:"eyebrow",children:["COPIE ",b.id," · ",b.name]}),r.jsx("h3",{children:_.label})]}),r.jsx("span",{className:"badge",children:_.info.present?_.info.not_empty?_.info.touched?"Dans le PDF":"Hors PDF filtré":"Réponse vide":"Absente"})]}),r.jsx("div",{className:"image-space",children:_.image?r.jsxs("button",{className:"image-button","aria-label":`Agrandir copie ${b.id} ${_.label}`,onClick:()=>ut({copy:b.id,label:_.label}),children:[r.jsx("img",{loading:"lazy",src:Xi(b.id,_.label,!1,N.revision),alt:`Réponse annotée — copie ${b.id}, ${_.label}`}),r.jsx("span",{children:"Agrandir ↗"})]}):r.jsx("p",{className:"empty",children:"Aucune image pour ce label."})}),r.jsxs("div",{className:"answer-form",children:[r.jsxs("label",{children:["Score",r.jsx("input",{inputMode:"decimal",value:at("score",b.id,_.label,_.score),onChange:A=>Z("score",b.id,_.label,A.target.value)})]}),r.jsxs("label",{children:["Remarque personnelle",r.jsx("textarea",{placeholder:"Ajouter une remarque (LaTeX accepté)",value:at("remark",b.id,_.label,_.remark),onChange:A=>Z("remark",b.id,_.label,A.target.value)})]})]}),r.jsxs("div",{className:"card-actions",children:[r.jsx("button",{disabled:!M[Vt("score",b.id,_.label)]&&!M[Vt("remark",b.id,_.label)],onClick:()=>gl([Vt("score",b.id,_.label),Vt("remark",b.id,_.label)]),children:"Enregistrer"}),r.jsx("button",{disabled:!_.editableBody,onClick:()=>St({copy:b.id,label:_.label}),children:"Couper / transférer"}),r.jsx("a",{href:sn(b.id,"context",_.label),target:"_blank",rel:"noreferrer",children:"Contexte ↗"}),r.jsx("a",{href:sn(b.id,"question",_.label),target:"_blank",rel:"noreferrer",children:"Énoncé ↗"}),r.jsx("a",{href:sn(b.id,"solution",_.label),target:"_blank",rel:"noreferrer",children:"Corrigé ↗"}),r.jsx("a",{href:sn(b.id,"output"),target:"_blank",rel:"noreferrer",children:"PDF final ↗"})]})]},b.id+"|"+_.label))}),bt.length===0&&r.jsx("p",{className:"empty",children:"Aucune réponse ne correspond à ces filtres."}),bt.length>R&&r.jsx("button",{className:"load-more",onClick:()=>T(R+20),children:"Afficher les 20 suivantes"})]}),(N.job.log||N.job.running)&&r.jsxs("details",{className:"panel",open:N.job.status==="failed",children:[r.jsxs("summary",{children:["Journal de régénération · ",N.job.status==="failed"?"Échec / résultat incomplet":N.job.running?"En cours":"Terminée"]}),r.jsx("pre",{className:"log",children:N.job.log})]})]}),Nl&&r.jsx(Sy,{copy:Nl.copy,source:Nl.label,labels:N.labels,mode:N.mode,revision:N.revision,draft:al,close:()=>St(null),accept:b=>{Z("draft","","",b),St(null),$("Instruction ajoutée au brouillon. Enregistrez-le avant de l’appliquer.")}}),Xl&&r.jsx(gy,{copy:Xl.copy,label:Xl.label,revision:N.revision,close:()=>ut(null)})]})}function gy({copy:N,label:X,revision:G,close:h}){const[B,$]=dl.useState(100);return dl.useEffect(()=>{const sl=Bl=>{Bl.key==="Escape"&&h()};return window.addEventListener("keydown",sl),()=>window.removeEventListener("keydown",sl)},[]),r.jsx("div",{className:"overlay",children:r.jsxs("section",{className:"modal viewer",role:"dialog","aria-modal":"true","aria-label":`Copie ${N} ${X}`,children:[r.jsxs("div",{className:"modal-head",children:[r.jsxs("h2",{children:["Copie ",N," · ",X]}),r.jsxs("label",{children:["Zoom ",r.jsx("input",{type:"range",min:"40",max:"200",value:B,onChange:sl=>$(Number(sl.target.value))}),B,"%"]}),r.jsx("button",{autoFocus:!0,onClick:h,children:"Fermer"})]}),r.jsx("div",{className:"zoom-scroll",children:r.jsx("img",{src:Xi(N,X,!1,G),alt:`Copie ${N}, ${X}`,style:{width:B+"%",maxWidth:"none"}})})]})})}function Sy({copy:N,source:X,labels:G,mode:h,revision:B,draft:$,close:sl,accept:Bl}){const[el,Al]=dl.useState(G.find(Z=>Z!==X)||""),[R,T]=dl.useState("cut"),[M,zl]=dl.useState(50),[Gl,Dl]=dl.useState("1"),[il,et]=dl.useState(">"),[Nl,St]=dl.useState(!1),[Xl,ut]=dl.useState(""),[Rl,K]=dl.useState(null),[k,it]=dl.useState(!1),Jl=R==="cut"?`c{${M}}${Gl}${il}`:R,wl=`Copie${N} ${X} ${Jl} ${Nl?"|":""}${el}${Nl?"":"|"}Line truncated
`+wl+`
`;dl.useEffect(()=>K(null),[el,R,M,Gl,il,Nl,$]);const at=async()=>{it(!0),ut("");try{K(await ra("preview",{copy:N,source:X,target:el,text:Ml}))}catch(Z){ut(String(Z))}finally{it(!1)}};return r.jsx("div",{className:"overlay",children:r.jsxs("section",{className:"modal",role:"dialog","aria-modal":"true","aria-label":"Couper ou transférer une réponse",children:[r.jsxs("div",{className:"modal-head",children:[r.jsxs("div",{children:[r.jsxs("span",{className:"eyebrow",children:["COPIE ",N," · ",h]}),r.jsx("h2",{children:"Couper ou transférer"})]}),r.jsx("button",{autoFocus:!0,onClick:sl,children:"Fermer"})]}),r.jsx("p",{children:"Seul le corps de la réponse est déplacé. Cliquez sur l’image source pour placer la coupe."}),Xl&&r.jsx("p",{role:"alert",className:"alert",children:Xl}),r.jsxs("div",{className:"cut-controls",children:[r.jsxs("label",{children:["Destination",r.jsx("select",{value:el,onChange:Z=>Al(Z.target.value),children:G.filter(Z=>Z!==X).map(Z=>r.jsx("option",{children:Z},Z))})]}),r.jsxs("label",{children:["Opération",r.jsxs("select",{value:R,onChange:Z=>T(Z.target.value),children:[r.jsx("option",{value:"cut",children:"Couper la source"}),r.jsx("option",{value:"x>",children:"Déplacer et ajouter"}),r.jsx("option",{value:"->",children:"Copier et ajouter"}),r.jsx("option",{value:"xx",children:"Déplacer et remplacer"}),r.jsx("option",{value:"-x",children:"Copier et remplacer"}),r.jsx("option",{value:"xs",children:"Vider la source"}),r.jsx("option",{value:"sx",children:"Vider la cible"}),r.jsx("option",{value:"ss",children:"Conserver les deux"})]})]}),R==="cut"&&r.jsxs(r.Fragment,{children:[r.jsxs("label",{children:["Coupe : ",M,"%",r.jsx("input",{type:"range",min:"1",max:"99",step:"0.1",value:M,onChange:Z=>zl(Number(Z.target.value))})]}),r.jsxs("label",{children:["Conserver à la source",r.jsxs("select",{value:Gl,onChange:Z=>Dl(Z.target.value),children:[r.jsx("option",{value:"1",children:"La partie haute"}),r.jsx("option",{value:"2",children:"La partie basse"})]})]}),r.jsxs("label",{children:["À la destination",r.jsxs("select",{value:il,onChange:Z=>et(Z.target.value),children:[r.jsx("option",{value:">",children:"Ajouter le fragment"}),r.jsx("option",{value:"x",children:"Remplacer le corps"})]})]})]}),r.jsxs("label",{className:"check",children:[r.jsx("input",{type:"checkbox",checked:Nl,onChange:Z=>St(Z.target.checked)}),"Placer avant la cible"]})]}),r.jsxs("div",{className:"compare",children:[r.jsxs("div",{children:[r.jsxs("h3",{children:["Source · ",X]}),r.jsxs("div",{className:"cut-image",onClick:Z=>{if(R==="cut"){const gl=Z.currentTarget.getBoundingClientRect();zl(Math.min(99,Math.max(1,Math.round((Z.clientY-gl.top)/gl.height*1e3)/10)))}},children:[r.jsx("img",{src:Xi(N,X,!0,B),alt:"Corps source sans en-tête"}),R==="cut"&&r.jsx("div",{className:"cut-line",style:{top:M+"%"},children:r.jsxs("span",{children:[M,"%"]})})]})]}),r.jsxs("div",{children:[r.jsxs("h3",{children:["Cible · ",el]}),r.jsx(by,{copy:N,label:el,revision:B},el)]})]}),r.jsx("code",{className:"instruction-line",children:wl}),r.jsxs("div",{className:"modal-actions",children:[r.jsx("button",{disabled:k||!el,onClick:at,children:k?"Calcul de l’aperçu…":"Aperçu du résultat"}),r.jsx("button",{className:"primary",disabled:!Rl||k,onClick:()=>Bl(Ml),children:"Ajouter au brouillon"})]}),Rl&&r.jsxs(r.Fragment,{children:[r.jsx("h3",{children:"Résultat proposé après toutes les instructions du brouillon"}),r.jsx("div",{className:"compare",children:[X,el].map(Z=>r.jsxs("div",{children:[r.jsx("h4",{children:Z}),Rl[Z]?r.jsx("img",{src:Rl[Z],alt:"Aperçu "+Z}):r.jsx("p",{className:"empty",children:"Corps vide"})]},Z))})]}),r.jsx("p",{className:"muted",children:"Une instruction par source ; une cible ne peut pas être source. Modifiez ou retirez une instruction existante dans le panneau « Transferts et coupes »."})]})})}function by({copy:N,label:X,revision:G}){const[h,B]=dl.useState(!1);return h?r.jsx("p",{className:"empty",children:"Pas de corps existant. Le transfert peut remplir cette cible."}):r.jsx("img",{src:Xi(N,X,!0,G),alt:"Corps cible sans en-tête",onError:()=>B(!0)})}hy.createRoot(document.getElementById("root")).render(r.jsx(yy,{}));
@@ -1 +1 @@
:root{font-family:Inter,ui-sans-serif,system-ui,sans-serif;color:#23323a;background:#f3f5f4;font-synthesis:none;font-size:14px}*{box-sizing:border-box}body{margin:0}button,input,select,textarea{font:inherit}button,a,input,select,textarea{outline-offset:3px}button{border:1px solid #c8d3ce;border-radius:7px;background:#fff;color:#244b40;padding:9px 13px;cursor:pointer;font-weight:550}button:hover:not(:disabled){background:#e9f1ed;border-color:#769b88}button:disabled{opacity:.45;cursor:default}.primary{background:#205c48;color:#fff;border-color:#205c48}.primary:hover:not(:disabled){background:#174633;color:#fff}a{color:#21654e;text-decoration:none}a:hover{text-decoration:underline}header{display:flex;align-items:center;gap:32px;padding:20px 36px;border-bottom:1px solid #dbe3de;background:#fff}.brand{display:flex;align-items:center;gap:10px;font-size:23px;font-weight:800;color:#28684f}.brand span{font-size:16px;font-weight:650}.heading{flex:1;border-left:1px solid #dbe3de;padding-left:30px}h1{font-size:22px;margin:3px 0 0}h2{font-size:20px;margin:0 0 8px}h3{font-size:19px;margin:7px 0}p{line-height:1.55;margin:8px 0;color:#56666b}.eyebrow{font-size:11px;letter-spacing:.09em;color:#5c7470;font-weight:650}main{max-width:1540px;margin:auto;padding:30px 36px 60px}.toolbar{display:flex;align-items:end;justify-content:space-between;gap:24px;margin-bottom:24px}.filters{display:flex;gap:12px;align-items:end}label{display:flex;flex-direction:column;gap:6px;color:#405650;font-size:12px;font-weight:600}input,select,textarea{border:1px solid #c5d1cb;border-radius:6px;background:#fff;padding:9px 10px;color:#263d34;font-size:14px;min-width:0}textarea{resize:vertical;min-height:76px;line-height:1.5}input:focus,textarea:focus,select:focus{outline:2px solid #80b69c;border-color:#4d8269}input[type=checkbox]{width:16px;height:16px;accent-color:#205c48}.check{display:flex;flex-direction:row;align-items:center;max-width:310px;font-size:12px;line-height:1.4}.publish{display:flex;align-items:center;gap:16px;background:#e8f0eb;border:1px solid #d0e0d5;border-radius:10px;padding:17px 20px;margin:20px 0}.publish>div{flex:1}.publish small{display:block;margin-top:5px;color:#5b6e64}.panel{padding:18px 20px;background:#fff;border:1px solid #d9e2dd;border-radius:9px;margin-bottom:14px}summary{cursor:pointer;font-weight:600}summary span{float:right;font-size:12px;font-weight:400;color:#65766f}.identity{border-top:1px solid #e1e7e3;padding:18px 0;display:grid;grid-template-columns:280px 1fr;gap:28px}.identity-name{display:flex;flex-direction:column;gap:10px;align-items:start}.identity-name label{width:100%}.general button{margin:5px 6px 0 0}.remark-row{display:flex;align-items:center;gap:6px;margin-bottom:8px}.remark-row textarea{flex:1}.instructions{width:100%;font-family:ui-monospace,monospace;min-height:140px;margin:10px 0}.panel details{margin-top:16px}.results{display:flex;justify-content:space-between;align-items:center;margin:30px 0 12px}.results span{color:#677872;font-size:12px}.answer-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:22px}.answer{border:1px solid #d9e1dd;background:#fff;border-radius:10px;overflow:hidden}.card-heading{display:flex;justify-content:space-between;align-items:center;padding:18px 20px;gap:12px}.badge{font-size:11px;background:#edf1ee;color:#5a6e62;border-radius:4px;padding:5px 7px;white-space:nowrap}.warning{background:#fff2d8;color:#8a620b}.image-space{height:290px;overflow:hidden;background:#e7ece9;border-top:1px solid #e2e8e3;border-bottom:1px solid #e2e8e3;display:flex;justify-content:center;align-items:start;padding:12px}.image-button{position:relative;border:0;padding:0;background:#fff;width:100%;height:100%;overflow:hidden;border-radius:0}.image-button img{width:100%;display:block}.image-button span{position:absolute;bottom:8px;right:8px;background:#203d32e8;color:#fff;border-radius:5px;padding:7px 10px;font-size:12px}.answer-form{display:grid;grid-template-columns:88px 1fr;gap:15px;padding:18px 20px 10px}.card-actions{display:flex;flex-wrap:wrap;align-items:center;gap:10px;padding:8px 20px 20px}.card-actions a{font-size:12px}.empty{padding:32px;color:#77877e;font-style:italic;text-align:center}.load-more{display:block;margin:24px auto}.alert{background:#fff0eb;color:#8f3827;border:1px solid #e9b6a9;border-radius:7px;padding:12px;display:flex;gap:12px;align-items:center}.alert small{font-size:12px}.notice{color:#216044}.muted{font-size:12px}fieldset{padding:0;margin:0;border:0;min-width:0}.overlay{position:fixed;inset:0;background:#142d23b8;z-index:10;display:flex;align-items:center;justify-content:center;padding:24px}.modal{width:min(1300px,100%);max-height:94vh;overflow:auto;border-radius:12px;background:#f8faf8;padding:24px;box-shadow:0 24px 80px #001c2e55}.modal-head{display:flex;justify-content:space-between;align-items:center;gap:20px}.cut-controls{display:flex;gap:15px;flex-wrap:wrap;margin:20px 0}.compare{display:grid;grid-template-columns:1fr 1fr;gap:24px}.compare>div{mLine truncated
:root{font-family:Inter,ui-sans-serif,system-ui,sans-serif;color:#23323a;background:#f3f5f4;font-synthesis:none;font-size:14px}*{box-sizing:border-box}body{margin:0}button,input,select,textarea{font:inherit}button,a,input,select,textarea{outline-offset:3px}button{border:1px solid #c8d3ce;border-radius:7px;background:#fff;color:#244b40;padding:9px 13px;cursor:pointer;font-weight:550}button:hover:not(:disabled){background:#e9f1ed;border-color:#769b88}button:disabled{opacity:.45;cursor:default}.primary{background:#205c48;color:#fff;border-color:#205c48}.primary:hover:not(:disabled){background:#174633;color:#fff}a{color:#21654e;text-decoration:none}a:hover{text-decoration:underline}header{display:flex;align-items:center;gap:32px;padding:20px 36px;border-bottom:1px solid #dbe3de;background:#fff}.brand{display:flex;align-items:center;gap:10px;font-size:23px;font-weight:800;color:#28684f}.brand span{font-size:16px;font-weight:650}.heading{flex:1;border-left:1px solid #dbe3de;padding-left:30px}h1{font-size:22px;margin:3px 0 0}h2{font-size:20px;margin:0 0 8px}h3{font-size:19px;margin:7px 0}p{line-height:1.55;margin:8px 0;color:#56666b}.eyebrow{font-size:11px;letter-spacing:.09em;color:#5c7470;font-weight:650}main{max-width:1540px;margin:auto;padding:30px 36px 60px}.toolbar{display:flex;align-items:end;justify-content:space-between;gap:24px;margin-bottom:24px}.filters{display:flex;gap:12px;align-items:end}label{display:flex;flex-direction:column;gap:6px;color:#405650;font-size:12px;font-weight:600}input,select,textarea{border:1px solid #c5d1cb;border-radius:6px;background:#fff;padding:9px 10px;color:#263d34;font-size:14px;min-width:0}textarea{resize:vertical;min-height:76px;line-height:1.5}input:focus,textarea:focus,select:focus{outline:2px solid #80b69c;border-color:#4d8269}input[type=checkbox]{width:16px;height:16px;accent-color:#205c48}.check{display:flex;flex-direction:row;align-items:center;max-width:310px;font-size:12px;line-height:1.4}.publish{display:flex;align-items:center;gap:16px;background:#e8f0eb;border:1px solid #d0e0d5;border-radius:10px;padding:17px 20px;margin:20px 0}.publish>div{flex:1}.publish small{display:block;margin-top:5px;color:#5b6e64}.panel{padding:18px 20px;background:#fff;border:1px solid #d9e2dd;border-radius:9px;margin-bottom:14px}summary{cursor:pointer;font-weight:600}summary span{float:right;font-size:12px;font-weight:400;color:#65766f}.identity{border-top:1px solid #e1e7e3;padding:18px 0;display:grid;grid-template-columns:280px 1fr;gap:28px}.identity-name{display:flex;flex-direction:column;gap:10px;align-items:start}.identity-name label{width:100%}.general button{margin:5px 6px 0 0}.remark-row{display:flex;align-items:center;gap:6px;margin-bottom:8px}.remark-row textarea{flex:1}.instructions{width:100%;font-family:ui-monospace,monospace;min-height:140px;margin:10px 0}.panel details{margin-top:16px}.results{display:flex;justify-content:space-between;align-items:center;margin:30px 0 12px}.results span{color:#677872;font-size:12px}.answer-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:22px}.answer{border:1px solid #d9e1dd;background:#fff;border-radius:10px;overflow:hidden}.card-heading{display:flex;justify-content:space-between;align-items:center;padding:18px 20px;gap:12px}.badge{font-size:11px;background:#edf1ee;color:#5a6e62;border-radius:4px;padding:5px 7px;white-space:nowrap}.warning{background:#fff2d8;color:#8a620b}.image-space{height:290px;overflow:hidden;background:#e7ece9;border-top:1px solid #e2e8e3;border-bottom:1px solid #e2e8e3;display:flex;justify-content:center;align-items:start;padding:12px}.image-button{position:relative;border:0;padding:0;background:#fff;width:100%;height:100%;overflow:hidden;border-radius:0}.image-button img{width:100%;display:block}.image-button span{position:absolute;bottom:8px;right:8px;background:#203d32e8;color:#fff;border-radius:5px;padding:7px 10px;font-size:12px}.answer-form{display:grid;grid-template-columns:88px 1fr;gap:15px;padding:18px 20px 10px}.card-actions{display:flex;flex-wrap:wrap;align-items:center;gap:10px;padding:8px 20px 20px}.card-actions a{font-size:12px}.empty{padding:32px;color:#77877e;font-style:italic;text-align:center}.load-more{display:block;margin:24px auto}.alert{background:#fff0eb;color:#8f3827;border:1px solid #e9b6a9;border-radius:7px;padding:12px;display:flex;gap:12px;align-items:center}.alert small{font-size:12px}.notice{color:#216044}.muted{font-size:12px}fieldset{padding:0;margin:0;border:0;min-width:0}.overlay{position:fixed;inset:0;background:#142d23b8;z-index:10;display:flex;align-items:center;justify-content:center;padding:24px}.modal{width:min(1300px,100%);max-height:94vh;overflow:auto;border-radius:12px;background:#f8faf8;padding:24px;box-shadow:0 24px 80px #001c2e55}.modal-head{display:flex;justify-content:space-between;align-items:center;gap:20px}.cut-controls{display:flex;gap:15px;flex-wrap:wrap;margin:20px 0}.compare{display:grid;grid-template-columns:1fr 1fr;gap:24px}.compare>div{mLine truncated
+2 -2
View File
@@ -1,3 +1,3 @@
<!doctype html><html lang="fr"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Copienator · Finalisation</title> <script type="module" crossorigin src="./assets/index-B8v-OSfB.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-B6ukjHTb.css">
<!doctype html><html lang="fr"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Copienator · Finalisation</title> <script type="module" crossorigin src="./assets/index-D6hPIrMO.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-NHKKhh88.css">
</head><body><div id="root"></div></body></html>