From 9331ef66807a9b73994f3fbc3c3a2176d67ea343 Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Mon, 14 Sep 2026 15:26:28 +0200 Subject: [PATCH 01/42] feat: add the execution backend contract Five verbs the core programs against, so a remote execution service can be plugged in without the runner learning its API. BackendState already speaks csauto's status vocabulary; translating a provider's own names is the backend's job. --- csauto/backends/__init__.py | 36 ++++++++++++++++++++++++++ csauto/backends/base.py | 50 +++++++++++++++++++++++++++++++++++++ tests/unit/test_backends.py | 33 ++++++++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 csauto/backends/__init__.py create mode 100644 csauto/backends/base.py create mode 100644 tests/unit/test_backends.py diff --git a/csauto/backends/__init__.py b/csauto/backends/__init__.py new file mode 100644 index 0000000..2fb2391 --- /dev/null +++ b/csauto/backends/__init__.py @@ -0,0 +1,36 @@ +"""Execution backend registry. + +The `backend` field on a case selects which service runs it. Implementation +modules are imported lazily so that loading configuration never pulls in an +optional SDK. +""" + +from __future__ import annotations + +import functools + +from .base import BackendState, ExecutionBackend + +_BACKEND_NAMES = ("fake",) + + +def available_backends() -> tuple[str, ...]: + return _BACKEND_NAMES + + +def get_backend(name: str) -> ExecutionBackend: + """Return the memoized backend instance for a name.""" + return _backend_for(str(name or "").strip().lower()) + + +@functools.cache +def _backend_for(normalized: str) -> ExecutionBackend: + if normalized == "fake": + from .fake import FakeBackend + + return FakeBackend() + choices = ", ".join(available_backends()) + raise ValueError(f"Unknown backend: {normalized!r}. Choices: {choices}") + + +__all__ = ["BackendState", "ExecutionBackend", "available_backends", "get_backend"] diff --git a/csauto/backends/base.py b/csauto/backends/base.py new file mode 100644 index 0000000..3078e8b --- /dev/null +++ b/csauto/backends/base.py @@ -0,0 +1,50 @@ +"""Execution backend boundary. + +`ExecutionBackend` is the contract a remote execution service implements. The +generic core (runner, the sync pass, kill) programs against these five verbs and +never against a provider's API. Translating a provider's own vocabulary into +csauto statuses is the backend's job, the same discipline `SolverAdapter` +applies to solvers. +""" + +from __future__ import annotations + +from collections.abc import Sequence +from dataclasses import dataclass +from pathlib import Path +from typing import Protocol, runtime_checkable + + +@dataclass(frozen=True) +class BackendState: + """One observation of a remote task, already in csauto's vocabulary.""" + + status: str # PENDING / RUNNING / DONE / FAILED + progress: float | None = None + stdout_delta: str = "" + stderr_delta: str = "" + + +@runtime_checkable +class ExecutionBackend(Protocol): + """What the core is allowed to know about a remote execution service.""" + + name: str + + def submit( + self, + case_dir: Path, + argv: Sequence[str], + image: str, + nprocs: int, + nt: int, + observability_globs: Sequence[str] = (), + ) -> str: ... + + def poll(self, task_id: str) -> BackendState: ... + + def sync(self, task_id: str, case_dir: Path) -> None: ... + + def fetch_final(self, task_id: str, case_dir: Path) -> None: ... + + def cancel(self, task_id: str) -> None: ... diff --git a/tests/unit/test_backends.py b/tests/unit/test_backends.py new file mode 100644 index 0000000..9c60efb --- /dev/null +++ b/tests/unit/test_backends.py @@ -0,0 +1,33 @@ +from __future__ import annotations + +import dataclasses + +import pytest + +from csauto.backends import available_backends, get_backend +from csauto.backends.base import BackendState, ExecutionBackend + + +def test_backend_state_is_frozen_and_carries_csauto_vocabulary() -> None: + state = BackendState(status="RUNNING", progress=0.42, stdout_delta="x", stderr_delta="") + assert state.status == "RUNNING" + assert state.progress == 0.42 + with pytest.raises(dataclasses.FrozenInstanceError): + state.status = "DONE" # type: ignore[misc] + + +def test_get_backend_is_memoized_and_normalizes_the_name() -> None: + assert get_backend("fake") is get_backend(" Fake ") + + +def test_unknown_backend_raises_with_the_choices() -> None: + with pytest.raises(ValueError, match="Unknown backend"): + get_backend("nope") + + +def test_available_backends_lists_fake() -> None: + assert "fake" in available_backends() + + +def test_fake_backend_satisfies_the_protocol() -> None: + assert isinstance(get_backend("fake"), ExecutionBackend) From a60d8d71268c5156ccbafec172c0c6281ce956bc Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Mon, 14 Sep 2026 15:27:42 +0200 Subject: [PATCH 02/42] feat: add a scripted fake execution backend What StubAdapter is to solvers: it satisfies the contract with local files and a scripted state sequence, so the remote lifecycle can be tested with no network, no account and no credit. It can also be told to fail a poll or a final download, which the sync pass has to survive. --- csauto/backends/fake.py | 115 ++++++++++++++++++++++++++++++++++++ tests/unit/test_backends.py | 83 ++++++++++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 csauto/backends/fake.py diff --git a/csauto/backends/fake.py b/csauto/backends/fake.py new file mode 100644 index 0000000..5052d6d --- /dev/null +++ b/csauto/backends/fake.py @@ -0,0 +1,115 @@ +"""A shipped fake execution backend. + +What `StubAdapter` is to solvers: it satisfies the contract with local files and +a scripted state sequence, so the whole remote lifecycle can be tested without a +network, an account or credit. +""" + +from __future__ import annotations + +import itertools +from collections.abc import Mapping, Sequence +from pathlib import Path + +from .base import BackendState + + +class _FakeTask: + def __init__(self, case_dir: Path) -> None: + self.case_dir = case_dir + self.step = 0 + self.stdout_cursor = 0 + self.cancelled = False + self.polls = 0 + + +class FakeBackend: + """Scripted backend. Every behaviour a test needs is an explicit argument.""" + + name = "fake" + + def __init__( + self, + script: Sequence[str] = ("PENDING", "RUNNING", "DONE"), + stdout_lines: Sequence[str] = (), + stderr_lines: Sequence[str] = (), + sync_files: Mapping[str, str] | None = None, + final_files: Mapping[str, str] | None = None, + fail_polls: int = 0, + fail_fetch_final: int = 0, + ) -> None: + self.script = tuple(script) + self.stdout_lines = tuple(stdout_lines) + self.stderr_lines = tuple(stderr_lines) + self.sync_files = dict(sync_files or {}) + self.final_files = dict(final_files or {}) + self.fail_polls = fail_polls + self.fail_fetch_final = fail_fetch_final + self._tasks: dict[str, _FakeTask] = {} + self._ids = itertools.count(1) + + def submit( + self, + case_dir: Path, + argv: Sequence[str], + image: str, + nprocs: int, + nt: int, + observability_globs: Sequence[str] = (), + ) -> str: + task_id = f"fake-{next(self._ids):04d}" + self._tasks[task_id] = _FakeTask(case_dir) + return task_id + + def poll(self, task_id: str) -> BackendState: + task = self._task(task_id) + task.polls += 1 + if task.polls <= self.fail_polls: + raise RuntimeError("fake poll failure") + if task.cancelled: + return BackendState(status="FAILED") + + index = min(task.step, len(self.script) - 1) + status = self.script[index] + task.step += 1 + + stdout_delta = "" + if task.stdout_cursor < len(self.stdout_lines): + stdout_delta = self.stdout_lines[task.stdout_cursor] + "\n" + task.stdout_cursor += 1 + + stderr_delta = "".join(line + "\n" for line in self.stderr_lines) if task.step == 1 else "" + progress = min(1.0, task.step / max(1, len(self.script))) + return BackendState( + status=status, + progress=progress, + stdout_delta=stdout_delta, + stderr_delta=stderr_delta, + ) + + def sync(self, task_id: str, case_dir: Path) -> None: + self._task(task_id) + self._write(case_dir, self.sync_files) + + def fetch_final(self, task_id: str, case_dir: Path) -> None: + self._task(task_id) + if self.fail_fetch_final > 0: + self.fail_fetch_final -= 1 + raise RuntimeError("fake fetch_final failure") + self._write(case_dir, self.final_files) + + def cancel(self, task_id: str) -> None: + self._task(task_id).cancelled = True + + def _task(self, task_id: str) -> _FakeTask: + try: + return self._tasks[task_id] + except KeyError: + raise KeyError(f"Unknown fake task: {task_id}") from None + + @staticmethod + def _write(case_dir: Path, files: Mapping[str, str]) -> None: + for relative, content in files.items(): + target = case_dir / relative + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(content, encoding="utf-8") diff --git a/tests/unit/test_backends.py b/tests/unit/test_backends.py index 9c60efb..2fe1430 100644 --- a/tests/unit/test_backends.py +++ b/tests/unit/test_backends.py @@ -31,3 +31,86 @@ def test_available_backends_lists_fake() -> None: def test_fake_backend_satisfies_the_protocol() -> None: assert isinstance(get_backend("fake"), ExecutionBackend) + + +def test_fake_backend_walks_its_scripted_states(tmp_path) -> None: + from csauto.backends.fake import FakeBackend + + case_dir = tmp_path / "case0001" + case_dir.mkdir() + backend = FakeBackend(script=["PENDING", "RUNNING", "DONE"]) + + task_id = backend.submit(case_dir, ["run"], "image", 1, 1) + assert backend.poll(task_id).status == "PENDING" + assert backend.poll(task_id).status == "RUNNING" + assert backend.poll(task_id).status == "DONE" + assert backend.poll(task_id).status == "DONE" # stays on the last state + + +def test_fake_backend_emits_output_deltas_once(tmp_path) -> None: + from csauto.backends.fake import FakeBackend + + case_dir = tmp_path / "case0001" + case_dir.mkdir() + backend = FakeBackend(script=["RUNNING", "DONE"], stdout_lines=["step 1", "step 2"]) + + task_id = backend.submit(case_dir, ["run"], "image", 1, 1) + first = backend.poll(task_id) + second = backend.poll(task_id) + + assert first.stdout_delta == "step 1\n" + assert second.stdout_delta == "step 2\n" + assert backend.poll(task_id).stdout_delta == "" + + +def test_fake_backend_sync_writes_observability_files(tmp_path) -> None: + from csauto.backends.fake import FakeBackend + + case_dir = tmp_path / "case0001" + case_dir.mkdir() + backend = FakeBackend(script=["RUNNING"], sync_files={"OUT/run_0001/progress.csv": "it,res\n1,1e-2\n"}) + + task_id = backend.submit(case_dir, ["run"], "image", 1, 1) + backend.sync(task_id, case_dir) + + assert (case_dir / "OUT" / "run_0001" / "progress.csv").read_text() == "it,res\n1,1e-2\n" + + +def test_fake_backend_fetch_final_writes_the_result_marker(tmp_path) -> None: + from csauto.backends.fake import FakeBackend + + case_dir = tmp_path / "case0001" + case_dir.mkdir() + backend = FakeBackend(script=["DONE"], final_files={"OUT/run_0001/result.dat": "done\n"}) + + task_id = backend.submit(case_dir, ["run"], "image", 1, 1) + backend.fetch_final(task_id, case_dir) + + assert (case_dir / "OUT" / "run_0001" / "result.dat").read_text() == "done\n" + + +def test_fake_backend_cancel_forces_the_failed_state(tmp_path) -> None: + from csauto.backends.fake import FakeBackend + + case_dir = tmp_path / "case0001" + case_dir.mkdir() + backend = FakeBackend(script=["RUNNING", "RUNNING", "RUNNING"]) + + task_id = backend.submit(case_dir, ["run"], "image", 1, 1) + backend.cancel(task_id) + + assert backend.poll(task_id).status == "FAILED" + + +def test_fake_backend_can_be_told_to_fail_a_poll(tmp_path) -> None: + """The sync pass must survive a transient failure; the fake can produce one.""" + from csauto.backends.fake import FakeBackend + + case_dir = tmp_path / "case0001" + case_dir.mkdir() + backend = FakeBackend(script=["RUNNING"], fail_polls=1) + + task_id = backend.submit(case_dir, ["run"], "image", 1, 1) + with pytest.raises(RuntimeError, match="fake poll failure"): + backend.poll(task_id) + assert backend.poll(task_id).status == "RUNNING" From 85cbcdcd675a66f50bd9d90c7588914516c001a9 Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Mon, 14 Sep 2026 15:28:56 +0200 Subject: [PATCH 03/42] feat: launch a case through an execution backend A third branch beside the local process and the Slurm job. The case carries a backend name and a task id instead of a PID, and a failed submission leaves it FAILED rather than PENDING. --- csauto/runner.py | 70 ++++++++++++++++++++++++++++++++++++- tests/runner/test_runner.py | 57 ++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 1 deletion(-) diff --git a/csauto/runner.py b/csauto/runner.py index e6e60f2..a4b717c 100644 --- a/csauto/runner.py +++ b/csauto/runner.py @@ -293,6 +293,7 @@ def _start_case( restart_path: str | None, mpi_exec_options: str | None, source: str, + backend_name: str | None = None, ) -> bool: """Launch a single case. Returns True if launched, False if skipped.""" case_id = case_dir.name @@ -350,7 +351,20 @@ def _start_case( env_vars=container_env, ) - if use_slurm_scheduler: + if backend_name: + _launch_backend( + case_dir, + case_id, + adapter.run_argv(case_dir, nprocs, nt, restart_args), + selection.docker_image, + nprocs, + nt, + backend_name, + getattr(adapter, "observability_globs", ()), + registry, + base_update, + ) + elif use_slurm_scheduler: _launch_slurm( case_dir, case_id, @@ -458,6 +472,58 @@ def _launch_slurm( ) +def _launch_backend( + case_dir: Path, + case_id: str, + argv: Sequence[str], + image: str, + nprocs: int, + nt: int, + backend_name: str, + observability_globs: Sequence[str], + registry: dict[str, Any], + base_update: dict[str, Any], +) -> None: + """Submit the case to a remote execution backend. + + No PID and no scheduler job: the case is identified by backend and task_id, + and the synchronisation pass owns its status from here on. + """ + from .backends import get_backend + + backend = get_backend(backend_name) + try: + print(f"Submitting {case_id} to backend {backend_name}.") + task_id = backend.submit(case_dir, argv, image, nprocs, nt, observability_globs) + except Exception as exc: + update_case( + registry, + case_id, + **base_update, + status=STATUS_FAILED, + start_time=None, + end_time=timestamp_now(), + pid=None, + job_id=None, + backend=backend_name, + task_id=None, + ) + raise RuntimeError(f"Failed to submit {case_id}: {exc}") from exc + + update_case( + registry, + case_id, + **base_update, + status=STATUS_RUNNING, + start_time=timestamp_now(), + end_time=None, + pid=None, + job_id=None, + backend=backend_name, + task_id=task_id, + ) + + def _launch_local( case_dir: Path, case_id: str, @@ -541,6 +607,7 @@ def run_cases( mpi_exec_options: str | None = None, source: str = "cli", adapter: SolverAdapter | None = None, + backend: str | None = None, ) -> None: """Launch solver runs for each case directory.""" if nprocs <= 0 or nt <= 0: @@ -613,6 +680,7 @@ def _do_start(selected_case: Path = case_dir) -> None: restart_path=restart_path, mpi_exec_options=mpi_exec_options, source=source, + backend_name=backend, ): launched += 1 diff --git a/tests/runner/test_runner.py b/tests/runner/test_runner.py index 686e0db..9b8bdc4 100644 --- a/tests/runner/test_runner.py +++ b/tests/runner/test_runner.py @@ -1086,3 +1086,60 @@ def popen_stub(*_args, **_kwargs): ) assert load_registry(runs_dir)["case0001"]["status"] == STATUS_FAILED + + +def test_run_cases_with_a_backend_records_backend_and_task_id(monkeypatch, runs_dir: Path, case_factory) -> None: + case_factory(runs_dir, "case0001") + + def no_popen(*_args, **_kwargs): + raise AssertionError("Popen must not be used for a backend launch") + + monkeypatch.setattr("subprocess.Popen", no_popen) + monkeypatch.setattr("shutil.which", lambda _name: "/bin/true") + + run_cases( + runs_dir, + nprocs=2, + nt=3, + max_parallel=1, + case_filter=["case0001"], + docker_image="my_image", + resume_only_failed=False, + source="test", + backend="fake", + ) + + record = load_registry(runs_dir)["case0001"] + assert record["status"] == STATUS_RUNNING + assert record["backend"] == "fake" + assert record["task_id"].startswith("fake-") + assert record["pid"] is None + assert record["job_id"] is None + assert record["nprocs"] == 2 + assert record["nt"] == 3 + + +def test_local_launch_records_no_backend(monkeypatch, runs_dir: Path, case_factory) -> None: + """Non-regression: a local run must not gain a backend field.""" + case_factory(runs_dir, "case0001") + + class DummyProc: + pid = 4242 + + monkeypatch.setattr("subprocess.Popen", lambda *_a, **_k: DummyProc()) + monkeypatch.setattr("shutil.which", lambda _name: "/bin/true") + + run_cases( + runs_dir, + nprocs=1, + nt=1, + max_parallel=1, + case_filter=["case0001"], + docker_image="image", + resume_only_failed=False, + source="test", + ) + + record = load_registry(runs_dir)["case0001"] + assert record.get("backend") is None + assert record["pid"] From 19a168ee0b993c7868949c36d4f4caf81a910e46 Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Mon, 14 Sep 2026 15:29:43 +0200 Subject: [PATCH 04/42] fix: do not finalize a case an execution backend owns A backend case has neither a PID nor a scheduler job, so should_finalize was true on the first refresh and the STATUS_FAILED fallback marked it failed before the remote service had started it. --- csauto/runner.py | 10 ++++--- tests/runner/test_runner.py | 53 +++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/csauto/runner.py b/csauto/runner.py index a4b717c..999418f 100644 --- a/csauto/runner.py +++ b/csauto/runner.py @@ -879,8 +879,12 @@ def _compute_refresh_result( end_time = record.get("end_time") has_started = bool(start_time) job_id = _normalize_job_id(record.get("job_id")) + # A case a remote backend owns has neither a PID nor a scheduler job, so the + # finalisation below would mark it FAILED on the first refresh, before the + # service had even started it. The synchronisation pass owns its status. + backend_name = str(record.get("backend") or "") - if status == STATUS_RUNNING: + if status == STATUS_RUNNING and not backend_name: pid_alive = False if pid is not None: try: @@ -897,7 +901,7 @@ def _compute_refresh_result( end_time = end_time or timestamp_now() pid = None job_id = None - if status == STATUS_RUNNING and has_started: + if status == STATUS_RUNNING and has_started and not backend_name: # Even if the PID is stale, trust the log end markers. outcome = adapter.detect_outcome(case_dir, start_time) if outcome: @@ -906,7 +910,7 @@ def _compute_refresh_result( end_time = timestamp_now() pid = None job_id = None - elif status != STATUS_DONE and has_started: + elif status != STATUS_DONE and has_started and not backend_name: # Only check for completion if a run was actually started. outcome = adapter.detect_outcome(case_dir, start_time) if outcome: diff --git a/tests/runner/test_runner.py b/tests/runner/test_runner.py index 9b8bdc4..669c738 100644 --- a/tests/runner/test_runner.py +++ b/tests/runner/test_runner.py @@ -1143,3 +1143,56 @@ class DummyProc: record = load_registry(runs_dir)["case0001"] assert record.get("backend") is None assert record["pid"] + + +def test_refresh_status_does_not_finalize_a_backend_case(runs_dir: Path, case_factory) -> None: + """A backend case has no PID and no job id; the sync pass owns its status. + + Without the guard, should_finalize is True on the first refresh and the + STATUS_FAILED fallback marks the case failed before it has started. + """ + case_dir = case_factory(runs_dir, "case0001") + save_registry( + runs_dir, + { + "case0001": { + "case_id": "case0001", + "path": str(case_dir), + "status": STATUS_RUNNING, + "backend": "fake", + "task_id": "fake-0001", + "pid": None, + "job_id": None, + "start_time": datetime.now().isoformat(timespec="seconds"), + } + }, + ) + + rows = refresh_status(runs_dir) + + assert rows[0]["status"] == STATUS_RUNNING + assert load_registry(runs_dir)["case0001"]["status"] == STATUS_RUNNING + + +def test_refresh_status_still_finalizes_a_local_case_whose_process_is_gone( + runs_dir: Path, case_factory +) -> None: + """Non-regression: the guard must not disable finalisation for local cases.""" + case_dir = case_factory(runs_dir, "case0001") + save_registry( + runs_dir, + { + "case0001": { + "case_id": "case0001", + "path": str(case_dir), + "status": STATUS_RUNNING, + "pid": None, + "job_id": None, + "start_time": datetime.now().isoformat(timespec="seconds"), + } + }, + ) + + rows = refresh_status(runs_dir) + + assert rows[0]["status"] == STATUS_FAILED From aa71896b71d13da30fee48c72e2924ff24875137 Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Mon, 14 Sep 2026 15:31:10 +0200 Subject: [PATCH 05/42] feat: add the backend synchronisation pass Polls each unfinished backend case once, appends the output deltas to csauto.stdout and csauto.stderr (which the Log Tail and Recent Errors already read), pulls the targeted snapshot, and marks the case DONE only once the results are actually on local disk. A failed poll never changes the status. Three phases like _count_running_cases: snapshot under a brief lock, network work with no lock held, write back under a new one. --- csauto/backend_sync.py | 134 ++++++++++++++++++++++++++ tests/runner/test_runner.py | 4 +- tests/unit/test_backend_sync.py | 165 ++++++++++++++++++++++++++++++++ 3 files changed, 300 insertions(+), 3 deletions(-) create mode 100644 csauto/backend_sync.py create mode 100644 tests/unit/test_backend_sync.py diff --git a/csauto/backend_sync.py b/csauto/backend_sync.py new file mode 100644 index 0000000..cf92679 --- /dev/null +++ b/csauto/backend_sync.py @@ -0,0 +1,134 @@ +"""One synchronisation pass over the cases a remote backend owns. + +Kept out of runner.py, which is already the largest module in the project. The +web server calls this on a timer; `csauto status` calls it once, throttled. + +Follows the three-phase pattern `_count_running_cases` uses: snapshot under a +brief lock, do the network work with no lock held, write back under a new lock. +Holding the registry lock across a network round trip would block the CLI and +every other writer for its duration. +""" + +from __future__ import annotations + +from collections.abc import Callable +from pathlib import Path +from typing import Any + +from .backends import ExecutionBackend, get_backend +from .registry import ( + STATUS_DONE, + STATUS_FAILED, + STATUS_RUNNING, + load_registry, + mutate_registry, + timestamp_now, +) +from .warn import warn + +TERMINAL_STATUSES = (STATUS_DONE, STATUS_FAILED) + + +def sync_backend_cases( + runs_dir: Path, + *, + sync_files: bool = True, + backend_factory: Callable[[str], ExecutionBackend] = get_backend, +) -> int: + """Poll every unfinished backend case once. Returns how many were touched.""" + # Phase 1: snapshot the cases a backend owns, under a brief lock. + snapshot: dict[str, dict[str, Any]] = {} + for case_id, record in load_registry(runs_dir).items(): + if not str(record.get("backend") or "") or not str(record.get("task_id") or ""): + continue + if record.get("status") in TERMINAL_STATUSES and record.get("results_fetched"): + continue + snapshot[case_id] = dict(record) + + # Phase 2: network work, with no lock held. + updates: dict[str, dict[str, Any]] = {} + for case_id, record in snapshot.items(): + backend = backend_factory(str(record["backend"])) + updates[case_id] = _observe(runs_dir, case_id, record, backend, str(record["task_id"]), sync_files) + + # Phase 3: write back under a new lock, skipping any case relaunched meanwhile. + if updates: + + def apply(registry: dict[str, dict[str, Any]]) -> bool: + changed = False + for case_id, fields in updates.items(): + current = registry.get(case_id) + if current is None: + continue + if str(current.get("task_id") or "") != str(snapshot[case_id].get("task_id") or ""): + continue + current.update(fields) + current["last_update"] = timestamp_now() + changed = True + return changed + + mutate_registry(runs_dir, apply) + return len(snapshot) + + +def _observe( + runs_dir: Path, + case_id: str, + record: dict[str, Any], + backend: ExecutionBackend, + task_id: str, + sync_files: bool, +) -> dict[str, Any]: + """Talk to the backend and return the registry fields to write. No lock held.""" + case_dir = Path(record.get("path") or (runs_dir / case_id)) + + try: + state = backend.poll(task_id) + except Exception as exc: + # A transient failure must never change the status: a flaky network + # would otherwise mark a whole campaign as failed. + warn(f"{case_id}: backend poll failed ({exc})") + return {"backend_poll_failures": int(record.get("backend_poll_failures") or 0) + 1} + + fields: dict[str, Any] = {"backend_poll_failures": 0} + if state.progress is not None: + fields["backend_progress"] = state.progress + _append(case_dir / "csauto.stdout", state.stdout_delta) + _append(case_dir / "csauto.stderr", state.stderr_delta) + + if sync_files and state.status == STATUS_RUNNING: + try: + backend.sync(task_id, case_dir) + except Exception as exc: + warn(f"{case_id}: backend sync failed ({exc})") + + if state.status not in TERMINAL_STATUSES: + fields["status"] = state.status + return fields + + try: + backend.fetch_final(task_id, case_dir) + except Exception as exc: + # The remote task finished but the results are not local yet. DONE must + # mean the results are on disk, so leave the status alone and retry. + warn(f"{case_id}: downloading results failed, will retry ({exc})") + return fields + + fields["results_fetched"] = True + fields["status"] = state.status + fields["end_time"] = record.get("end_time") or timestamp_now() + return fields + + +def _append(path: Path, text: str) -> None: + if not text: + return + try: + path.parent.mkdir(parents=True, exist_ok=True) + with path.open("a", encoding="utf-8") as handle: + handle.write(text) + except OSError: + return + + +__all__ = ["sync_backend_cases"] diff --git a/tests/runner/test_runner.py b/tests/runner/test_runner.py index 669c738..a26d870 100644 --- a/tests/runner/test_runner.py +++ b/tests/runner/test_runner.py @@ -1174,9 +1174,7 @@ def test_refresh_status_does_not_finalize_a_backend_case(runs_dir: Path, case_fa assert load_registry(runs_dir)["case0001"]["status"] == STATUS_RUNNING -def test_refresh_status_still_finalizes_a_local_case_whose_process_is_gone( - runs_dir: Path, case_factory -) -> None: +def test_refresh_status_still_finalizes_a_local_case_whose_process_is_gone(runs_dir: Path, case_factory) -> None: """Non-regression: the guard must not disable finalisation for local cases.""" case_dir = case_factory(runs_dir, "case0001") save_registry( diff --git a/tests/unit/test_backend_sync.py b/tests/unit/test_backend_sync.py new file mode 100644 index 0000000..5f0f11e --- /dev/null +++ b/tests/unit/test_backend_sync.py @@ -0,0 +1,165 @@ +from __future__ import annotations + +from pathlib import Path + +from csauto.backend_sync import sync_backend_cases +from csauto.backends.fake import FakeBackend +from csauto.registry import STATUS_DONE, STATUS_FAILED, STATUS_RUNNING, load_registry, save_registry + + +def _register(runs_dir: Path, case_dir: Path, task_id: str = "fake-0001") -> None: + save_registry( + runs_dir, + { + "case0001": { + "case_id": "case0001", + "path": str(case_dir), + "status": STATUS_RUNNING, + "backend": "fake", + "task_id": task_id, + } + }, + ) + + +def test_sync_appends_stdout_to_the_launcher_log(runs_dir: Path, case_factory) -> None: + """csauto.stdout is what the Log Tail and Recent Errors already read.""" + case_dir = case_factory(runs_dir, "case0001") + backend = FakeBackend(script=["RUNNING", "RUNNING"], stdout_lines=["step 1", "step 2"]) + task_id = backend.submit(case_dir, ["run"], "img", 1, 1) + _register(runs_dir, case_dir, task_id) + + sync_backend_cases(runs_dir, backend_factory=lambda _name: backend) + sync_backend_cases(runs_dir, backend_factory=lambda _name: backend) + + assert (case_dir / "csauto.stdout").read_text() == "step 1\nstep 2\n" + + +def test_sync_records_progress(runs_dir: Path, case_factory) -> None: + case_dir = case_factory(runs_dir, "case0001") + backend = FakeBackend(script=["RUNNING", "RUNNING"]) + task_id = backend.submit(case_dir, ["run"], "img", 1, 1) + _register(runs_dir, case_dir, task_id) + + sync_backend_cases(runs_dir, backend_factory=lambda _name: backend) + + assert load_registry(runs_dir)["case0001"]["backend_progress"] == 0.5 + + +def test_a_failed_poll_leaves_the_status_untouched(runs_dir: Path, case_factory) -> None: + """A flaky network must not mark cases as failed.""" + case_dir = case_factory(runs_dir, "case0001") + backend = FakeBackend(script=["RUNNING"], fail_polls=1) + task_id = backend.submit(case_dir, ["run"], "img", 1, 1) + _register(runs_dir, case_dir, task_id) + + sync_backend_cases(runs_dir, backend_factory=lambda _name: backend) + + record = load_registry(runs_dir)["case0001"] + assert record["status"] == STATUS_RUNNING + assert record["backend_poll_failures"] == 1 + + +def test_a_successful_poll_clears_the_failure_counter(runs_dir: Path, case_factory) -> None: + case_dir = case_factory(runs_dir, "case0001") + backend = FakeBackend(script=["RUNNING"], fail_polls=1) + task_id = backend.submit(case_dir, ["run"], "img", 1, 1) + _register(runs_dir, case_dir, task_id) + + sync_backend_cases(runs_dir, backend_factory=lambda _name: backend) + sync_backend_cases(runs_dir, backend_factory=lambda _name: backend) + + assert load_registry(runs_dir)["case0001"]["backend_poll_failures"] == 0 + + +def test_done_only_after_the_results_are_local(runs_dir: Path, case_factory) -> None: + """DONE means the results are on local disk; Compare and Clean assume it.""" + case_dir = case_factory(runs_dir, "case0001") + backend = FakeBackend( + script=["DONE"], + final_files={"OUT/run_0001/result.dat": "done\n"}, + fail_fetch_final=1, + ) + task_id = backend.submit(case_dir, ["run"], "img", 1, 1) + _register(runs_dir, case_dir, task_id) + + sync_backend_cases(runs_dir, backend_factory=lambda _name: backend) + assert load_registry(runs_dir)["case0001"]["status"] == STATUS_RUNNING + assert not (case_dir / "OUT" / "run_0001" / "result.dat").exists() + + sync_backend_cases(runs_dir, backend_factory=lambda _name: backend) + record = load_registry(runs_dir)["case0001"] + assert record["status"] == STATUS_DONE + assert record["results_fetched"] is True + assert (case_dir / "OUT" / "run_0001" / "result.dat").read_text() == "done\n" + + +def test_a_failed_remote_task_is_reported_failed(runs_dir: Path, case_factory) -> None: + case_dir = case_factory(runs_dir, "case0001") + backend = FakeBackend(script=["FAILED"]) + task_id = backend.submit(case_dir, ["run"], "img", 1, 1) + _register(runs_dir, case_dir, task_id) + + sync_backend_cases(runs_dir, backend_factory=lambda _name: backend) + + assert load_registry(runs_dir)["case0001"]["status"] == STATUS_FAILED + + +def test_finished_cases_are_skipped(runs_dir: Path, case_factory) -> None: + case_dir = case_factory(runs_dir, "case0001") + backend = FakeBackend(script=["DONE"]) + task_id = backend.submit(case_dir, ["run"], "img", 1, 1) + save_registry( + runs_dir, + { + "case0001": { + "case_id": "case0001", + "path": str(case_dir), + "status": STATUS_DONE, + "backend": "fake", + "task_id": task_id, + "results_fetched": True, + } + }, + ) + + assert sync_backend_cases(runs_dir, backend_factory=lambda _name: backend) == 0 + + +def test_cases_without_a_backend_are_ignored(runs_dir: Path, case_factory) -> None: + case_dir = case_factory(runs_dir, "case0001") + save_registry( + runs_dir, + {"case0001": {"case_id": "case0001", "path": str(case_dir), "status": STATUS_RUNNING, "pid": 1}}, + ) + + assert sync_backend_cases(runs_dir) == 0 + + +def test_a_relaunched_case_is_not_overwritten(runs_dir: Path, case_factory) -> None: + """Phase 3 must skip a case whose task id changed while the network call ran.""" + case_dir = case_factory(runs_dir, "case0001") + backend = FakeBackend(script=["DONE"], final_files={"OUT/x": "y\n"}) + task_id = backend.submit(case_dir, ["run"], "img", 1, 1) + _register(runs_dir, case_dir, task_id) + + def relaunch_then_poll(_name: str): + save_registry( + runs_dir, + { + "case0001": { + "case_id": "case0001", + "path": str(case_dir), + "status": STATUS_RUNNING, + "backend": "fake", + "task_id": "fake-9999", + } + }, + ) + return backend + + sync_backend_cases(runs_dir, backend_factory=relaunch_then_poll) + + record = load_registry(runs_dir)["case0001"] + assert record["task_id"] == "fake-9999" + assert record["status"] == STATUS_RUNNING From fa177e8b18275eb24b2f67eac236d6a10d2bad00 Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Mon, 14 Sep 2026 15:33:50 +0200 Subject: [PATCH 06/42] feat: poll execution backends from a background loop Deliberately not inside refresh_status, which runs about twice a second because the dashboard polls every second and the server caches for half of one. A network round trip there would abuse the provider's API and make the dashboard lag. Two cadences: a light poll, and a less frequent file sync. The test drives TestClient as a context manager, because the shared helper does not and the FastAPI lifespan never starts there. --- csauto/config.py | 14 ++++++++++++++ csauto/fastapi_app.py | 36 ++++++++++++++++++++++++++++++++---- tests/unit/test_config.py | 25 +++++++++++++++++++++++++ tests/web/test_api.py | 30 ++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 4 deletions(-) diff --git a/csauto/config.py b/csauto/config.py index 509a1e8..90dadba 100644 --- a/csauto/config.py +++ b/csauto/config.py @@ -22,6 +22,8 @@ class Config: use_slurm: bool | None = None mpi_exec_options: str | None = None max_parallel: int = 1 + backend_poll_interval_s: int = 15 + backend_sync_interval_s: int = 60 mesh_mode: str = "symlink" host: str = "127.0.0.1" port: int = 8000 @@ -179,6 +181,18 @@ def load_config(path: Path | None = None) -> Config: config_path=config_path, min_value=1, ) + for interval_field in ("backend_poll_interval_s", "backend_sync_interval_s"): + if interval_field in data: + setattr( + config, + interval_field, + _parse_required_int( + data.get(interval_field), + field=interval_field, + config_path=config_path, + min_value=1, + ), + ) if "mesh_mode" in data: config.mesh_mode = _parse_mesh_mode(data.get("mesh_mode"), config_path=config_path) if "host" in data: diff --git a/csauto/fastapi_app.py b/csauto/fastapi_app.py index 1ff419d..a0cf219 100644 --- a/csauto/fastapi_app.py +++ b/csauto/fastapi_app.py @@ -65,6 +65,8 @@ def create_fastapi_app( singularity_bin: str | None = None, use_slurm: bool | None = None, mpi_exec_options: str | None = None, + backend_poll_interval_s: int = 15, + backend_sync_interval_s: int = 60, ) -> Any: components = _import_fastapi_components() FastAPI = components["FastAPI"] @@ -113,13 +115,39 @@ async def _heartbeat_loop() -> None: except Exception: pass + async def _backend_sync_loop() -> None: + """Poll the cases a remote backend owns. + + Deliberately not inside refresh_status, which runs about twice a second + because the dashboard polls every second and the server caches for half + of one. A network round trip there would abuse the provider's API and + make the dashboard lag. + """ + from . import backend_sync + + elapsed_since_file_sync = 0.0 + while True: + await asyncio.sleep(backend_poll_interval_s) + elapsed_since_file_sync += backend_poll_interval_s + pull_files = elapsed_since_file_sync >= backend_sync_interval_s + if pull_files: + elapsed_since_file_sync = 0.0 + try: + touched = await asyncio.to_thread(backend_sync.sync_backend_cases, runs_dir, sync_files=pull_files) + except Exception: + continue + if touched: + ctx.invalidate_status_cache() + @contextlib.asynccontextmanager async def _lifespan(_app: Any) -> AsyncIterator[None]: - task = asyncio.create_task(_heartbeat_loop()) + tasks = [asyncio.create_task(_heartbeat_loop()), asyncio.create_task(_backend_sync_loop())] yield - task.cancel() - with contextlib.suppress(asyncio.CancelledError): - await task + for task in tasks: + task.cancel() + for task in tasks: + with contextlib.suppress(asyncio.CancelledError): + await task app = FastAPI(title="csauto", lifespan=_lifespan) diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py index 71d17dc..73846a1 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -85,3 +85,28 @@ def test_load_config_invalid_mesh_mode_raises(tmp_path: Path) -> None: with pytest.raises(ValueError, match="mesh_mode"): load_config(cfg) + + +def test_backend_intervals_have_defaults(tmp_path) -> None: + from csauto.config import load_config + + config_path = tmp_path / "csauto.toml" + config_path.write_text("runtime = 'native'\n", encoding="utf-8") + config = load_config(config_path) + + assert config.backend_poll_interval_s == 15 + assert config.backend_sync_interval_s == 60 + + +def test_backend_intervals_are_read_and_validated(tmp_path) -> None: + from csauto.config import load_config + + config_path = tmp_path / "csauto.toml" + config_path.write_text("backend_poll_interval_s = 30\nbackend_sync_interval_s = 120\n", encoding="utf-8") + config = load_config(config_path) + assert config.backend_poll_interval_s == 30 + assert config.backend_sync_interval_s == 120 + + config_path.write_text("backend_poll_interval_s = 0\n", encoding="utf-8") + with pytest.raises(ValueError, match="backend_poll_interval_s"): + load_config(config_path) diff --git a/tests/web/test_api.py b/tests/web/test_api.py index 8cbc934..386e8ba 100644 --- a/tests/web/test_api.py +++ b/tests/web/test_api.py @@ -1322,3 +1322,33 @@ def test_run_without_restart_is_not_blocked_by_the_restart_guard(aster_env) -> N # A launch failure (no runtime available in CI) is acceptable here; # a capability rejection is not. assert "does not support restart" not in exc.read().decode("utf-8") + + +def test_the_server_runs_a_backend_sync_pass(runs_dir: Path, case_factory, registry_factory, monkeypatch) -> None: + """The loop lives outside refresh_status, which runs about twice a second. + + Uses TestClient as a context manager on purpose: the shared _start_server + helper does not, so the FastAPI lifespan (and therefore the loop) never + starts there. + """ + pytest.importorskip("fastapi") + from fastapi.testclient import TestClient + + calls = {"count": 0} + + def fake_sync(_runs_dir, **_kwargs): + calls["count"] += 1 + return 0 + + monkeypatch.setattr("csauto.backend_sync.sync_backend_cases", fake_sync) + case_dir = case_factory(runs_dir, "case0001") + registry_factory(runs_dir, "case0001", case_dir, status="PREPARED") + + app = create_fastapi_app(runs_dir, backend_poll_interval_s=1) + with TestClient(app) as client: + client.get("/api/status") + deadline = time.time() + 8.0 + while time.time() < deadline and calls["count"] == 0: + time.sleep(0.2) + + assert calls["count"] >= 1 From 09aa4748de05cf2417dc5ae2cb098aba2cd869b6 Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Mon, 14 Sep 2026 15:40:41 +0200 Subject: [PATCH 07/42] feat: cancel and synchronise backend cases from the CLI and the kill route --- csauto/cli.py | 36 +++++++++++++++++++++++++++++++- csauto/web_support.py | 20 ++++++++++++++++-- tests/unit/test_cli.py | 17 +++++++++++++++ tests/web/test_api.py | 28 +++++++++++++++++++++++++ tests/web/test_fastapi_server.py | 2 +- 5 files changed, 99 insertions(+), 4 deletions(-) diff --git a/csauto/cli.py b/csauto/cli.py index 6aac9c8..b2b53cd 100644 --- a/csauto/cli.py +++ b/csauto/cli.py @@ -1,8 +1,10 @@ from __future__ import annotations import argparse +import contextlib import sys -from collections.abc import Mapping, Sequence +from collections.abc import Callable, Mapping, Sequence +from datetime import datetime from pathlib import Path from typing import Any @@ -410,6 +412,35 @@ def _preparse_config(argv: Sequence[str]) -> Path | None: return known.config +CLI_BACKEND_SYNC_THROTTLE_S = 10.0 + + +def _throttled_backend_sync(runs_dir: Path, sync: Callable[..., int]) -> None: + """Run one backend sync pass, at most once every 10 s. + + The background loop lives in `csauto serve`; this keeps `csauto status` + honest for a CLI-only workflow without duplicating the loop. The marker + lives under a reserved `_backend` key at the top of registry.json, which no + case id can collide with: _resolve_case_id rejects ids not matching + ^[A-Za-z0-9][A-Za-z0-9_.-]*$. + """ + from .registry import load_registry, mutate_registry, timestamp_now + + marker = load_registry(runs_dir).get("_backend", {}).get("last_sync") + if marker: + with contextlib.suppress(ValueError): + if (datetime.now() - datetime.fromisoformat(str(marker))).total_seconds() < CLI_BACKEND_SYNC_THROTTLE_S: + return + + def stamp(registry: dict[str, dict[str, Any]]) -> bool: + registry.setdefault("_backend", {})["last_sync"] = timestamp_now() + return True + + mutate_registry(runs_dir, stamp) + with contextlib.suppress(Exception): + sync(runs_dir) + + def _print_doctor(items: Sequence[object]) -> bool: failed = False for item in items: @@ -509,6 +540,9 @@ def main(argv: Sequence[str] | None = None) -> int: adapter=adapter, ) elif args.command == "status": + from . import backend_sync + + _throttled_backend_sync(args.runs_dir, backend_sync.sync_backend_cases) rows = refresh_status(args.runs_dir, adapter=adapter) print_status_table(rows) elif args.command == "residuals": diff --git a/csauto/web_support.py b/csauto/web_support.py index 1ec7daf..2e5f276 100644 --- a/csauto/web_support.py +++ b/csauto/web_support.py @@ -314,15 +314,29 @@ def kill_case( start_time = record.get("start_time") previous_end_time = record.get("end_time") pid_raw = record.get("pid") + backend_name = str(record.get("backend") or "") + task_id = str(record.get("task_id") or "") - if pid_raw is None and not container_id and not job_id: - raise ValueError(f"No PID, container_id or job_id for {case_id}") + if pid_raw is None and not container_id and not job_id and not task_id: + raise ValueError(f"No PID, container_id, job_id or task_id for {case_id}") details: dict[str, Any] = { "pid": pid_raw, "container_id": container_id, "job_id": job_id, } + if task_id: + details["task_id"] = task_id + + backend_error: Exception | None = None + if backend_name and task_id: + try: + from .backends import get_backend + + get_backend(backend_name).cancel(task_id) + details["backend_cancelled"] = True + except Exception as exc: + backend_error = exc container_error: Exception | None = None if container_id: @@ -352,6 +366,8 @@ def kill_case( except Exception as exc: job_error = exc + if backend_error and pid_raw is None and not container_id and not job_id: + raise backend_error if container_error and pid_raw is None and job_id is None: raise container_error if job_error and pid_raw is None and not container_id: diff --git a/tests/unit/test_cli.py b/tests/unit/test_cli.py index 49508fd..d271143 100644 --- a/tests/unit/test_cli.py +++ b/tests/unit/test_cli.py @@ -321,3 +321,20 @@ def test_prepare_without_strict_only_warns_on_unused_column(tmp_path: Path, caps assert exit_code == 0 assert "extra_col" in capsys.readouterr().err assert (tmp_path / "RUNS" / "case0001").is_dir() + + +def test_status_runs_one_throttled_backend_sync(monkeypatch, runs_dir: Path, case_factory) -> None: + """The sync loop lives in the server; the CLI must not be useless without it.""" + calls = {"count": 0} + + def fake_sync(*_args, **_kwargs): + calls["count"] += 1 + return 0 + + monkeypatch.setattr("csauto.backend_sync.sync_backend_cases", fake_sync) + case_factory(runs_dir, "case0001") + + main(["status", str(runs_dir)]) + main(["status", str(runs_dir)]) + + assert calls["count"] == 1, "the second call within the throttle window must be skipped" diff --git a/tests/web/test_api.py b/tests/web/test_api.py index 386e8ba..98eb8fd 100644 --- a/tests/web/test_api.py +++ b/tests/web/test_api.py @@ -1352,3 +1352,31 @@ def fake_sync(_runs_dir, **_kwargs): time.sleep(0.2) assert calls["count"] >= 1 + + +def test_kill_cancels_a_backend_task(runs_dir: Path, case_factory, monkeypatch) -> None: + from csauto.backends.fake import FakeBackend + from csauto.web_support import kill_case + + case_dir = case_factory(runs_dir, "case0001") + backend = FakeBackend(script=["RUNNING"]) + task_id = backend.submit(case_dir, ["run"], "img", 1, 1) + monkeypatch.setattr("csauto.backends.get_backend", lambda _name: backend) + save_registry( + runs_dir, + { + "case0001": { + "case_id": "case0001", + "path": str(case_dir), + "status": "RUNNING", + "backend": "fake", + "task_id": task_id, + "pid": None, + } + }, + ) + + kill_case(runs_dir, "case0001", actor=None, job_id_patterns=()) + + assert backend.poll(task_id).status == "FAILED" + assert load_registry(runs_dir)["case0001"]["status"] == "FAILED" diff --git a/tests/web/test_fastapi_server.py b/tests/web/test_fastapi_server.py index 63bafb8..baaffd9 100644 --- a/tests/web/test_fastapi_server.py +++ b/tests/web/test_fastapi_server.py @@ -302,7 +302,7 @@ def test_fastapi_server_cleanup_and_kill_error_paths(fastapi_server_env) -> None _http_post(f"{base_url}/api/kill_case", {"cases": ["case0001"]}) assert excinfo.value.code == 500 payload = json.loads(excinfo.value.read().decode("utf-8")) - assert payload["detail"] == "case0001: No PID, container_id or job_id for case0001" + assert payload["detail"] == "case0001: No PID, container_id, job_id or task_id for case0001" def test_fastapi_server_open_gui_without_display_returns_500(fastapi_server_env) -> None: From 055f0595ea5023649843694d1df18d4cfe35da48 Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Mon, 14 Sep 2026 15:41:39 +0200 Subject: [PATCH 08/42] test: cover the remote execution lifecycle end to end Submit, observe, finish on the fake backend, with no network. Documents the execution backend boundary and its two invariants. --- CHANGELOG.md | 3 ++ docs/architecture.md | 27 ++++++++++++ docs/config.md | 2 + tests/integration/test_fake_backend.py | 60 ++++++++++++++++++++++++++ 4 files changed, 92 insertions(+) create mode 100644 tests/integration/test_fake_backend.py diff --git a/CHANGELOG.md b/CHANGELOG.md index cf49917..446221b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this Make the dashboard follow what each solver can actually do: panels and action buttons are now derived from the solver adapter instead of being shown for every solver. +### Added +- `ExecutionBackend` boundary (`csauto/backends/`): a case can be launched on a remote execution service instead of a local process or a Slurm job. A backend implements five verbs (`submit`, `poll`, `sync`, `fetch_final`, `cancel`) and translates its own state names into csauto statuses; the core never sees a provider's API. The shipped `fake` backend covers the whole lifecycle in tests with no network, the way the `stub` solver does without a solver. Two new settings, `backend_poll_interval_s` and `backend_sync_interval_s` + ### Changed - Dashboard panels and action buttons are derived from what the solver adapter implements, rather than declared: a panel appears when the adapter provides what feeds it (`find_residuals_files`, `list_probe_files`, or a non-empty `compare_kinds` / `performance_columns` / `control_actions`), and the Restart, Stop, control and Open GUI controls follow the same rule. Solvers other than code_saturne lose the panels and buttons they could never feed: code_aster and the stub solver now show Status, Compare, Log Tail and Recent Errors only. code_saturne is unchanged. Adapters can no longer declare `dashboard_panels`, which now raises `TypeError` at import time - `csauto doctor` reports the panels and capabilities derived for the configured solver diff --git a/docs/architecture.md b/docs/architecture.md index a50bf97..c2927ca 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -109,6 +109,33 @@ Adding a solver means writing one adapter class and registering it in `csauto/solvers/__init__.py`; the core does not change. See [Adding a new solver](./adding-a-solver.md) for the step-by-step guide. +## Execution backend boundary + +Where `SolverAdapter` answers "what does this solver do", `ExecutionBackend` +(`csauto/backends/base.py`) answers "where does this case run". A backend +implements five verbs: `submit`, `poll`, `sync`, `fetch_final`, `cancel`. +`BackendState.status` already speaks csauto's vocabulary, so translating a +provider's own state names is the backend's job, never the runner's. + +A case a backend owns carries `backend` and `task_id` in the registry instead of +a `pid` or a `job_id`, and `_compute_refresh_result` does not finalise it: the +synchronisation pass in `csauto/backend_sync.py` owns its status. That pass runs +on a timer in `csauto serve` and once, throttled, in `csauto status`. + +Two invariants the implementation depends on: + +- **`DONE` means the results are on local disk.** Compare reads files, Clean + removes directories, `resu_size_mb` measures a size. The pass marks a case + `DONE` only after `fetch_final` succeeded. +- **A failed poll never changes a status.** A flaky network must not mark a + whole campaign as failed; consecutive failures are counted instead. + +`local` and Slurm are not behind this contract. They predate it and work; porting +them is a later, mechanical refactor. + +`FakeBackend` (`backend = "fake"`) is a shipped fake, what `StubAdapter` is to +solvers: it covers the whole lifecycle in tests with no network and no account. + ## Persistent data model - `RUNS/registry.json` - global case state diff --git a/docs/config.md b/docs/config.md index 20b8c60..9a3cafe 100644 --- a/docs/config.md +++ b/docs/config.md @@ -118,6 +118,8 @@ token = "your-secret-token" | `use_slurm` | (auto-detected) | `true` to force Slurm submission, `false` to force local | | `mpi_exec_options` | (none) | Options injected as `CS_MPIEXEC_OPTIONS` in Slurm jobs | | `max_parallel` | `1` | Maximum simultaneous case launches | +| `backend_poll_interval_s` | `15` | Seconds between two execution-backend polls | +| `backend_sync_interval_s` | `60` | Seconds between two observability file syncs | | `mesh_mode` | `symlink` | How `prepare` places the shared `MESH`/`POST` dirs into `RUNS/`: `symlink` or `copy` (see [concepts.md](./concepts.md#shared-meshpost-directories)) | | `host` | `127.0.0.1` | Bind address for `serve` | | `port` | `8000` | Bind port for `serve` | diff --git a/tests/integration/test_fake_backend.py b/tests/integration/test_fake_backend.py new file mode 100644 index 0000000..3ca239b --- /dev/null +++ b/tests/integration/test_fake_backend.py @@ -0,0 +1,60 @@ +"""The remote execution lifecycle, end to end, with no network. + +What tests/integration/test_stub_adapter.py is for solvers. +""" + +from __future__ import annotations + +from pathlib import Path + +from csauto.backend_sync import sync_backend_cases +from csauto.registry import STATUS_DONE, STATUS_RUNNING, load_registry +from csauto.runner import refresh_status, run_cases + + +def test_submit_observe_and_finish_on_the_fake_backend(monkeypatch, runs_dir: Path, case_factory) -> None: + from csauto.backends.fake import FakeBackend + + case_factory(runs_dir, "case0001") + backend = FakeBackend( + script=["RUNNING", "RUNNING", "DONE"], + stdout_lines=["iteration 1", "iteration 2"], + sync_files={"OUT/run_0001/progress.csv": "it,res\n1,1e-2\n"}, + final_files={"OUT/run_0001/result.dat": "done\n"}, + ) + monkeypatch.setattr("csauto.backends.get_backend", lambda _name: backend) + monkeypatch.setattr("shutil.which", lambda _name: "/bin/true") + + run_cases( + runs_dir, + nprocs=1, + nt=1, + max_parallel=1, + case_filter=["case0001"], + docker_image="image", + resume_only_failed=False, + source="test", + backend="fake", + ) + + case_dir = runs_dir / "case0001" + record = load_registry(runs_dir)["case0001"] + assert record["status"] == STATUS_RUNNING + assert record["task_id"].startswith("fake-") + + # refresh_status must not finalise a case the backend owns + assert refresh_status(runs_dir)[0]["status"] == STATUS_RUNNING + + sync_backend_cases(runs_dir, backend_factory=lambda _name: backend) + assert (case_dir / "OUT" / "run_0001" / "progress.csv").is_file() + assert "iteration 1" in (case_dir / "csauto.stdout").read_text() + assert load_registry(runs_dir)["case0001"]["status"] == STATUS_RUNNING + + sync_backend_cases(runs_dir, backend_factory=lambda _name: backend) + sync_backend_cases(runs_dir, backend_factory=lambda _name: backend) + + record = load_registry(runs_dir)["case0001"] + assert record["status"] == STATUS_DONE + assert record["results_fetched"] is True + assert (case_dir / "OUT" / "run_0001" / "result.dat").read_text() == "done\n" + assert refresh_status(runs_dir)[0]["status"] == STATUS_DONE From 324aabc329c8b8b7079702e8bc880ddf8aca0788 Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Mon, 14 Sep 2026 15:57:29 +0200 Subject: [PATCH 09/42] fix: do not report the reserved registry marker as a case The _backend throttle marker sits at the top of registry.json beside the cases, and refresh_status listed it as a phantom case in PREPARED. Registry iteration now goes through case_records. --- csauto/registry.py | 11 +++++++++++ csauto/runner.py | 3 ++- tests/runner/test_runner.py | 16 ++++++++++++++++ tests/unit/test_registry.py | 11 +++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/csauto/registry.py b/csauto/registry.py index 85e7442..9290c2c 100644 --- a/csauto/registry.py +++ b/csauto/registry.py @@ -18,6 +18,17 @@ STATUS_DONE = "DONE" STATUS_FAILED = "FAILED" +# Keys at the top of registry.json that hold campaign state rather than a case. +# Everything that iterates the registry as a list of cases must go through +# case_records, or a marker is reported as a phantom case. +RESERVED_REGISTRY_KEYS = frozenset({"_backend"}) + + +def case_records(registry: dict[str, Any]) -> dict[str, Any]: + """The registry entries that are cases, with the reserved markers removed.""" + return {key: value for key, value in registry.items() if key not in RESERVED_REGISTRY_KEYS} + + REGISTRY_FILENAME = "registry.json" REGISTRY_LOCKFILE = f"{REGISTRY_FILENAME}.lock" REGISTRY_THREAD_LOCK = threading.RLock() diff --git a/csauto/runner.py b/csauto/runner.py index 999418f..0609507 100644 --- a/csauto/runner.py +++ b/csauto/runner.py @@ -32,6 +32,7 @@ STATUS_PREPARED, STATUS_RUNNING, append_history, + case_records, load_registry, mutate_registry, registry_transaction, @@ -841,7 +842,7 @@ def refresh_status( results: list[RefreshResult] = [] doe_columns: list[str] = [] seen_doe_columns: set[str] = set() - for case_id in sorted(registry): + for case_id in sorted(case_records(registry)): result = _compute_refresh_result( runs_dir, case_id, diff --git a/tests/runner/test_runner.py b/tests/runner/test_runner.py index a26d870..edc1f81 100644 --- a/tests/runner/test_runner.py +++ b/tests/runner/test_runner.py @@ -1194,3 +1194,19 @@ def test_refresh_status_still_finalizes_a_local_case_whose_process_is_gone(runs_ rows = refresh_status(runs_dir) assert rows[0]["status"] == STATUS_FAILED + + +def test_refresh_status_ignores_the_reserved_backend_key(runs_dir, case_factory) -> None: + """The _backend marker is campaign state, not a case; it must not be listed.""" + case_factory(runs_dir, "case0001") + save_registry( + runs_dir, + { + "_backend": {"last_sync": "2026-09-14T10:00:00"}, + "case0001": {"case_id": "case0001", "path": str(runs_dir / "case0001"), "status": "PREPARED"}, + }, + ) + + rows = refresh_status(runs_dir) + + assert [row["case_id"] for row in rows] == ["case0001"] diff --git a/tests/unit/test_registry.py b/tests/unit/test_registry.py index 42b12c0..cb07508 100644 --- a/tests/unit/test_registry.py +++ b/tests/unit/test_registry.py @@ -107,3 +107,14 @@ def test_registry_transaction_saves_normally_without_an_exception(runs_dir: Path update_case(registry, "case0001", status="DONE") assert load_registry(runs_dir)["case0001"]["status"] == "DONE" + + +def test_case_records_drops_reserved_keys() -> None: + from csauto.registry import case_records + + registry = { + "_backend": {"last_sync": "2026-09-14T10:00:00"}, + "case0001": {"case_id": "case0001", "status": "PREPARED"}, + } + + assert list(case_records(registry)) == ["case0001"] From ed535367a7a0c1456d534b94a09b29517a665cb8 Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Mon, 14 Sep 2026 15:58:25 +0200 Subject: [PATCH 10/42] feat: let an adapter declare its observability files A remote backend cannot call find_residuals_files on a directory that only exists on the compute node, so the adapter declares the patterns instead. The argv submitted to a backend now uses a relative case path for the same reason. --- csauto/runner.py | 6 ++++-- csauto/solvers/base.py | 7 +++++++ csauto/solvers/code_aster.py | 7 +++++++ csauto/solvers/code_saturne.py | 8 ++++++++ csauto/solvers/stub.py | 1 + tests/runner/test_runner.py | 35 ++++++++++++++++++++++++++++++++++ tests/unit/test_solvers.py | 15 +++++++++++++++ 7 files changed, 77 insertions(+), 2 deletions(-) diff --git a/csauto/runner.py b/csauto/runner.py index 0609507..9aa2ae1 100644 --- a/csauto/runner.py +++ b/csauto/runner.py @@ -356,12 +356,14 @@ def _start_case( _launch_backend( case_dir, case_id, - adapter.run_argv(case_dir, nprocs, nt, restart_args), + # A relative path: the remote task's working directory is the + # case directory, and the host path means nothing over there. + adapter.run_argv(".", nprocs, nt, restart_args), selection.docker_image, nprocs, nt, backend_name, - getattr(adapter, "observability_globs", ()), + adapter.observability_globs, registry, base_update, ) diff --git a/csauto/solvers/base.py b/csauto/solvers/base.py index 8b7d83f..2ceecff 100644 --- a/csauto/solvers/base.py +++ b/csauto/solvers/base.py @@ -63,6 +63,7 @@ class SolverAdapter(Protocol): shared_dir_names: tuple[str, ...] template_input_names: frozenset[str] anomaly_file_names: tuple[str, ...] + observability_globs: tuple[str, ...] cleanup_log_names: frozenset[str] performance_fields: tuple[str, ...] performance_columns: tuple[PerfColumn, ...] @@ -182,6 +183,12 @@ class SolverAdapterBase(ABC): shared_dir_names: ClassVar[tuple[str, ...]] = () template_input_names: ClassVar[frozenset[str]] = frozenset() anomaly_file_names: ClassVar[tuple[str, ...]] = ("csauto.stderr", "csauto.stdout") + # Patterns, relative to the case directory, naming the few files a remote + # execution backend should pull back *while the run is in progress*. They + # exist because find_residuals_files and friends inspect a local directory + # and so cannot describe a task running on someone else's machine. + # Keep the list narrow: Qarnot advises a snapshot stays under 1 GB. + observability_globs: ClassVar[tuple[str, ...]] = () cleanup_log_names: ClassVar[frozenset[str]] = frozenset({"csauto.stdout", "csauto.stderr"}) performance_columns: ClassVar[tuple[PerfColumn, ...]] = () compare_kinds: ClassVar[tuple[CompareKind, ...]] = () diff --git a/csauto/solvers/code_aster.py b/csauto/solvers/code_aster.py index f06278d..1332496 100644 --- a/csauto/solvers/code_aster.py +++ b/csauto/solvers/code_aster.py @@ -34,6 +34,13 @@ class CodeAsterAdapter(SolverAdapterBase): "MESH", "RESU", ) + # Declared for completeness. code_aster does not run on an execution + # backend yet: run_argv returns [] because build_run_command composes the + # whole launch line itself, so there is no argv to hand to a remote task. + observability_globs: ClassVar[tuple[str, ...]] = ( + "RESU/LOGS/run_solver.log", + "RESU/LOGS/*.mess", + ) # doe_row.csv is written into every generated case by the generic DOE code, # so it is comparable whatever the solver. The .export file would be a better # candidate but its name varies per case (find_setup_file globs *.export) diff --git a/csauto/solvers/code_saturne.py b/csauto/solvers/code_saturne.py index db70989..f858e5f 100644 --- a/csauto/solvers/code_saturne.py +++ b/csauto/solvers/code_saturne.py @@ -82,6 +82,14 @@ class CodeSaturneAdapter(SolverAdapterBase): default_docker_image: ClassVar[str] = "simvia/code_saturne" results_dirname: ClassVar[str] = "RESU" shared_dir_names: ClassVar[tuple[str, ...]] = ("MESH", "POST") + observability_globs: ClassVar[tuple[str, ...]] = ( + "RESU/*/residuals.csv", + "RESU/*/listing", + "RESU/*/run_solver.log", + "RESU/*/performance.log", + "RESU/*/monitoring/*.csv", + "RESU/*/run_status.*", + ) template_input_names: ClassVar[frozenset[str]] = frozenset({"setup.xml", "run.cfg"}) anomaly_file_names: ClassVar[tuple[str, ...]] = ANOMALY_FILES_DEFAULT cleanup_log_names: ClassVar[frozenset[str]] = frozenset( diff --git a/csauto/solvers/stub.py b/csauto/solvers/stub.py index be5c581..ac7a122 100644 --- a/csauto/solvers/stub.py +++ b/csauto/solvers/stub.py @@ -40,6 +40,7 @@ class StubAdapter(SolverAdapterBase): container_root: ClassVar[str] = "/stub" default_docker_image: ClassVar[str] = "" results_dirname: ClassVar[str] = "OUT" + observability_globs: ClassVar[tuple[str, ...]] = (f"OUT/*/{STUB_LOG_FILENAME}",) template_input_names: ClassVar[frozenset[str]] = frozenset({STUB_SETUP_FILENAME}) anomaly_file_names: ClassVar[tuple[str, ...]] = ("csauto.stderr", "csauto.stdout", STUB_LOG_FILENAME) cleanup_log_names: ClassVar[frozenset[str]] = frozenset({STUB_LOG_FILENAME, "csauto.stdout", "csauto.stderr"}) diff --git a/tests/runner/test_runner.py b/tests/runner/test_runner.py index edc1f81..3357475 100644 --- a/tests/runner/test_runner.py +++ b/tests/runner/test_runner.py @@ -1210,3 +1210,38 @@ def test_refresh_status_ignores_the_reserved_backend_key(runs_dir, case_factory) rows = refresh_status(runs_dir) assert [row["case_id"] for row in rows] == ["case0001"] + + +def test_backend_argv_uses_a_relative_case_path(monkeypatch, runs_dir, case_factory) -> None: + """The remote container has no idea where the case lives on this machine.""" + from csauto.backends.fake import FakeBackend + + case_factory(runs_dir, "case0001") + backend = FakeBackend(script=["RUNNING"]) + submitted: dict[str, object] = {} + original_submit = backend.submit + + def record(case_dir, argv, image, nprocs, nt, observability_globs=()): + submitted["argv"] = list(argv) + submitted["globs"] = tuple(observability_globs) + return original_submit(case_dir, argv, image, nprocs, nt, observability_globs) + + backend.submit = record # type: ignore[method-assign] + monkeypatch.setattr("csauto.backends.get_backend", lambda _name: backend) + monkeypatch.setattr("shutil.which", lambda _name: "/bin/true") + + run_cases( + runs_dir, + nprocs=1, + nt=1, + max_parallel=1, + case_filter=["case0001"], + docker_image="image", + resume_only_failed=False, + source="test", + backend="fake", + ) + + assert str(runs_dir) not in " ".join(str(part) for part in submitted["argv"]) + assert "." in submitted["argv"] + assert submitted["globs"], "the adapter's observability patterns must reach the backend" diff --git a/tests/unit/test_solvers.py b/tests/unit/test_solvers.py index 6dd4b36..4ce4933 100644 --- a/tests/unit/test_solvers.py +++ b/tests/unit/test_solvers.py @@ -446,3 +446,18 @@ def test_code_saturne_detect_outcome_ignores_a_stale_failure_marker(tmp_path) -> start_time = datetime.fromtimestamp(time.time() - 60).isoformat(timespec="seconds") assert adapter.detect_outcome(case_dir, start_time) == STATUS_DONE + + +def test_code_saturne_declares_observability_globs() -> None: + from csauto.solvers.code_saturne import CodeSaturneAdapter + + globs = CodeSaturneAdapter().observability_globs + + assert "RESU/*/listing" in globs + assert all(not pattern.startswith("/") for pattern in globs), "patterns are relative to the case dir" + + +def test_an_adapter_that_declares_nothing_has_no_observability_globs() -> None: + from csauto.solvers.base import SolverAdapterBase + + assert SolverAdapterBase.observability_globs == () From e66bd48b8912e37e58964058a1cc0633befa0945 Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Mon, 14 Sep 2026 15:59:45 +0200 Subject: [PATCH 11/42] feat: add the pure Qarnot helpers Bucket naming, the glob to regex translation Qarnot's snapshot filter needs, the per-case upload plan and its cost guard, and the shared-directory fingerprint. No SDK, no network, no account. --- csauto/backends/qarnot_support.py | 157 ++++++++++++++++++++++++++++++ tests/unit/test_qarnot_support.py | 139 ++++++++++++++++++++++++++ 2 files changed, 296 insertions(+) create mode 100644 csauto/backends/qarnot_support.py create mode 100644 tests/unit/test_qarnot_support.py diff --git a/csauto/backends/qarnot_support.py b/csauto/backends/qarnot_support.py new file mode 100644 index 0000000..b2a624d --- /dev/null +++ b/csauto/backends/qarnot_support.py @@ -0,0 +1,157 @@ +"""Qarnot decisions that are not API calls. + +Kept apart from `qarnot.py` so every rule here is unit-testable with no SDK +installed, no token and no network, and so the backend module stays short +enough to read in one sitting. +""" + +from __future__ import annotations + +import hashlib +import re +from collections.abc import Sequence +from dataclasses import dataclass +from pathlib import Path + +# S3 bucket names: 3 to 63 characters, lowercase letters, digits, dots and +# hyphens, starting and ending with a letter or a digit. +_UNSAFE = re.compile(r"[^a-z0-9]+") +_SLUG_MAX = 20 + + +def bucket_name(runs_dir: Path, suffix: str = "") -> str: + """An S3-legal bucket name for a campaign, stable and collision free. + + The readable part comes from the campaign directory name, so a bucket is + recognisable in the Qarnot console; the digest comes from its absolute + path, so two campaigns that normalise to the same slug never collide. + """ + resolved = Path(runs_dir).resolve() + digest = hashlib.sha256(str(resolved).encode("utf-8")).hexdigest()[:8] + slug = _UNSAFE.sub("-", resolved.name.lower()).strip("-")[:_SLUG_MAX].strip("-") or "runs" + parts = ["csauto", slug, digest] + if suffix: + clean = _UNSAFE.sub("-", str(suffix).lower()).strip("-") + if clean: + parts.append(clean) + return "-".join(parts) + + +def snapshot_whitelist(globs: Sequence[str]) -> str: + """Translate adapter globs into the regex Qarnot's snapshot filter expects. + + Qarnot documents whitelist and blacklist as regular expressions, so the + patterns cannot be passed through unchanged. `*` stops at a slash, `**` + crosses one, and everything else is escaped. + """ + parts = [_translate(str(pattern).strip()) for pattern in globs if str(pattern).strip()] + return "|".join(parts) + + +def _translate(pattern: str) -> str: + out: list[str] = [] + index = 0 + while index < len(pattern): + char = pattern[index] + if char == "*": + if pattern[index + 1 : index + 2] == "*": + out.append(".*") + index += 2 + else: + out.append("[^/]*") + index += 1 + continue + if char == "?": + out.append("[^/]") + index += 1 + continue + out.append(re.escape(char)) + index += 1 + return "".join(out) + + +@dataclass(frozen=True) +class UploadPlan: + """What a single case would upload: relative paths, and how many bytes.""" + + paths: tuple[str, ...] + total_bytes: int + + +def upload_plan(case_dir: Path, *, shared_dir_names: Sequence[str], results_dirname: str) -> UploadPlan: + """The case's own input files: everything that is neither shared nor output. + + The shared directories are campaign-wide (and usually symlinks to a mesh of + several gigabytes), so they travel once per campaign in their own bucket, + not once per case. The results directory is what comes back, not what goes. + """ + case_dir = Path(case_dir) + skip = {str(name) for name in shared_dir_names} | {str(results_dirname)} + paths: list[str] = [] + total = 0 + for path in sorted(case_dir.rglob("*")): + if not path.is_file() or path.is_symlink(): + continue + relative = path.relative_to(case_dir) + if relative.parts[0] in skip or any(part.startswith(".") for part in relative.parts): + continue + paths.append(relative.as_posix()) + total += path.stat().st_size + return UploadPlan(paths=tuple(paths), total_bytes=total) + + +def ensure_upload_within(plan: UploadPlan, max_mb: int) -> None: + """Refuse a case whose inputs are too large to upload per case. + + A hundred-case campaign multiplies this by a hundred, so an accidental + gigabyte in DATA/ is worth stopping before it is paid for. + """ + limit = int(max_mb) * 1024 * 1024 + if plan.total_bytes <= limit: + return + raise ValueError( + f"Case inputs are {plan.total_bytes / 1024 / 1024:.1f} MB, over the " + f"{max_mb} MB limit ({len(plan.paths)} files). Move bulk data into a " + f"shared directory, or raise qarnot_max_upload_mb." + ) + + +def directory_signature(paths: Sequence[Path]) -> str: + """A cheap fingerprint of the shared directories: names, sizes, mtimes. + + Only stat calls, so fingerprinting a multi-gigabyte mesh costs nothing. + Used to skip re-uploading the shared bucket when nothing changed. + """ + digest = hashlib.sha256() + for root in sorted(Path(p) for p in paths): + for path in sorted(root.rglob("*")): + if not path.is_file(): + continue + stat = path.stat() + digest.update(f"{path.relative_to(root).as_posix()}:{stat.st_size}:{stat.st_mtime_ns}\n".encode()) + return digest.hexdigest() + + +def split_image(image: str) -> tuple[str, str]: + """Split `repo[:tag]` into the DOCKER_REPO and DOCKER_TAG constants.""" + text = str(image or "").strip() + if not text: + raise ValueError( + "Qarnot runs docker images, but no docker image is configured. " + "Set docker_image in csauto.toml even when the local runtime is native." + ) + repo, separator, tag = text.rpartition(":") + if not separator or "/" in tag: + return text, "latest" + return repo, tag or "latest" + + +__all__ = [ + "UploadPlan", + "bucket_name", + "directory_signature", + "ensure_upload_within", + "snapshot_whitelist", + "split_image", + "upload_plan", +] diff --git a/tests/unit/test_qarnot_support.py b/tests/unit/test_qarnot_support.py new file mode 100644 index 0000000..ac3c81f --- /dev/null +++ b/tests/unit/test_qarnot_support.py @@ -0,0 +1,139 @@ +"""Pure Qarnot logic: no SDK, no network, no account.""" + +from __future__ import annotations + +import re +from pathlib import Path + +import pytest + +from csauto.backends.qarnot_support import ( + bucket_name, + directory_signature, + ensure_upload_within, + snapshot_whitelist, + split_image, + upload_plan, +) + + +def test_bucket_name_is_s3_legal(tmp_path: Path) -> None: + campaign = tmp_path / "My_Study 2026" + campaign.mkdir() + + name = bucket_name(campaign) + + assert re.fullmatch(r"[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]", name), name + assert "_" not in name + assert 3 <= len(name) <= 63 + + +def test_bucket_name_is_stable_and_collision_free(tmp_path: Path) -> None: + """Two campaigns that normalise to the same slug must not share a bucket.""" + first = tmp_path / "a" / "My_Study" + second = tmp_path / "b" / "My-Study" + first.mkdir(parents=True) + second.mkdir(parents=True) + + assert bucket_name(first) == bucket_name(first) + assert bucket_name(first) != bucket_name(second) + + +def test_bucket_name_appends_a_suffix(tmp_path: Path) -> None: + assert bucket_name(tmp_path, "shared").endswith("-shared") + + +def test_snapshot_whitelist_is_a_regex_not_a_glob() -> None: + """Qarnot filters are regular expressions; the adapter declares globs.""" + pattern = snapshot_whitelist(("RESU/*/listing",)) + + assert re.match(pattern, "RESU/20260914-1200/listing") + assert not re.match(pattern, "RESU/a/b/listing"), "* must not cross a slash" + + +def test_snapshot_whitelist_escapes_regex_metacharacters() -> None: + pattern = snapshot_whitelist(("RESU/*/run_status.*",)) + + assert re.match(pattern, "RESU/r1/run_status.failed") + assert not re.match(pattern, "RESU/r1/run_statusXfailed") + + +def test_snapshot_whitelist_joins_several_patterns() -> None: + pattern = snapshot_whitelist(("a/*.csv", "b/*.log")) + + assert re.match(pattern, "a/x.csv") + assert re.match(pattern, "b/y.log") + assert not re.match(pattern, "c/z.txt") + + +def test_snapshot_whitelist_of_nothing_is_empty() -> None: + assert snapshot_whitelist(()) == "" + + +def test_upload_plan_skips_shared_and_results_directories(tmp_path: Path) -> None: + case_dir = tmp_path / "case0001" + (case_dir / "DATA").mkdir(parents=True) + (case_dir / "DATA" / "setup.xml").write_text("", encoding="utf-8") + (case_dir / "MESH").mkdir() + (case_dir / "MESH" / "huge.med").write_text("x" * 100, encoding="utf-8") + (case_dir / "RESU").mkdir() + (case_dir / "RESU" / "old.log").write_text("y", encoding="utf-8") + (case_dir / ".csauto.history.jsonl").write_text("{}", encoding="utf-8") + (case_dir / "doe_row.csv").write_text("a,b\n1,2\n", encoding="utf-8") + + plan = upload_plan(case_dir, shared_dir_names=("MESH", "POST"), results_dirname="RESU") + + assert set(plan.paths) == {"DATA/setup.xml", "doe_row.csv"} + assert plan.total_bytes == len("") + len("a,b\n1,2\n") + + +def test_ensure_upload_within_refuses_an_oversized_case() -> None: + from csauto.backends.qarnot_support import UploadPlan + + plan = UploadPlan(paths=("big.med",), total_bytes=3 * 1024 * 1024) + + with pytest.raises(ValueError, match=r"3\.0 MB"): + ensure_upload_within(plan, max_mb=2) + + +def test_ensure_upload_within_accepts_a_small_case() -> None: + from csauto.backends.qarnot_support import UploadPlan + + ensure_upload_within(UploadPlan(paths=("a",), total_bytes=10), max_mb=1) + + +def test_directory_signature_changes_when_a_file_changes(tmp_path: Path) -> None: + shared = tmp_path / "MESH" + shared.mkdir() + mesh = shared / "m.med" + mesh.write_text("one", encoding="utf-8") + + before = directory_signature([shared]) + mesh.write_text("one and more", encoding="utf-8") + + assert directory_signature([shared]) != before + + +def test_directory_signature_is_stable_when_nothing_changes(tmp_path: Path) -> None: + shared = tmp_path / "MESH" + shared.mkdir() + (shared / "m.med").write_text("one", encoding="utf-8") + + assert directory_signature([shared]) == directory_signature([shared]) + + +@pytest.mark.parametrize( + ("image", "expected"), + [ + ("simvia/code_saturne", ("simvia/code_saturne", "latest")), + ("simvia/code_saturne:9.0", ("simvia/code_saturne", "9.0")), + ("ubuntu", ("ubuntu", "latest")), + ], +) +def test_split_image(image: str, expected: tuple[str, str]) -> None: + assert split_image(image) == expected + + +def test_split_image_refuses_an_empty_image() -> None: + with pytest.raises(ValueError, match="no docker image"): + split_image("") From 590ba085350b0b841863e5d2a32a74a097f13c1f Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Mon, 14 Sep 2026 16:00:44 +0200 Subject: [PATCH 12/42] feat: add the [qarnot] settings table and the optional extra Profile, snapshot interval and per-case upload limit. A token in the config file is rejected outright: that file is shared, committed and archived, so the credential belongs in QARNOT_TOKEN. --- csauto/config.py | 35 +++++++++++++++++++++++++++++++++++ pyproject.toml | 1 + tests/unit/test_config.py | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/csauto/config.py b/csauto/config.py index 90dadba..4ded5d5 100644 --- a/csauto/config.py +++ b/csauto/config.py @@ -24,6 +24,9 @@ class Config: max_parallel: int = 1 backend_poll_interval_s: int = 15 backend_sync_interval_s: int = 60 + qarnot_profile: str = "docker-batch" + qarnot_snapshot_interval_s: int = 60 + qarnot_max_upload_mb: int = 512 mesh_mode: str = "symlink" host: str = "127.0.0.1" port: int = 8000 @@ -214,6 +217,38 @@ def load_config(path: Path | None = None) -> Config: token = api_section.get("token") if isinstance(api_section, dict) else None if token is not None: config.api_token = _coerce_str(token, "").strip() or None + + qarnot_section = data.get("qarnot") + if qarnot_section is not None and not isinstance(qarnot_section, dict): + raise ValueError(f"Invalid [qarnot] section in {config_path}: expected table") + if isinstance(qarnot_section, dict): + # The token is deliberately not a setting: this file is shared, + # committed and sometimes archived. Fail loudly rather than read it. + for secret_key in ("token", "client_token", "api_token"): + if secret_key in qarnot_section: + raise ValueError( + f"Invalid [qarnot] section in {config_path}: {secret_key!r} must not be " + f"stored in a config file. Set the QARNOT_TOKEN environment variable instead." + ) + if "profile" in qarnot_section: + profile = _coerce_str(qarnot_section.get("profile"), "").strip() + if not profile: + raise ValueError(f"Invalid [qarnot].profile in {config_path}: value must not be empty") + config.qarnot_profile = profile + if "snapshot_interval_s" in qarnot_section: + config.qarnot_snapshot_interval_s = _parse_required_int( + qarnot_section.get("snapshot_interval_s"), + field="[qarnot].snapshot_interval_s", + config_path=config_path, + min_value=1, + ) + if "max_upload_mb" in qarnot_section: + config.qarnot_max_upload_mb = _parse_required_int( + qarnot_section.get("max_upload_mb"), + field="[qarnot].max_upload_mb", + config_path=config_path, + min_value=1, + ) return config diff --git a/pyproject.toml b/pyproject.toml index 97b10fd..41c309d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ csauto = "csauto.cli:main" web = ["fastapi>=0.100", "uvicorn>=0.20", "pydantic>=2.0"] dev = ["pytest", "httpx", "ruff"] doe = ["scipy>=1.10"] +qarnot = ["qarnot>=2.23"] [tool.hatch.version] path = "VERSION" diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py index 73846a1..ee214a2 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -110,3 +110,38 @@ def test_backend_intervals_are_read_and_validated(tmp_path) -> None: config_path.write_text("backend_poll_interval_s = 0\n", encoding="utf-8") with pytest.raises(ValueError, match="backend_poll_interval_s"): load_config(config_path) + + +def test_qarnot_settings_have_defaults(tmp_path: Path) -> None: + (tmp_path / "csauto.toml").write_text("solver = 'stub'\n", encoding="utf-8") + config = load_config(tmp_path / "csauto.toml") + + assert config.qarnot_profile == "docker-batch" + assert config.qarnot_snapshot_interval_s == 60 + assert config.qarnot_max_upload_mb == 512 + + +def test_qarnot_settings_are_read_from_their_table(tmp_path: Path) -> None: + (tmp_path / "csauto.toml").write_text( + "[qarnot]\nprofile = 'docker-batch'\nsnapshot_interval_s = 120\nmax_upload_mb = 64\n", + encoding="utf-8", + ) + config = load_config(tmp_path / "csauto.toml") + + assert config.qarnot_snapshot_interval_s == 120 + assert config.qarnot_max_upload_mb == 64 + + +def test_a_qarnot_token_in_the_config_file_is_rejected(tmp_path: Path) -> None: + """The campaign file is shared, committed and archived. The token is not.""" + (tmp_path / "csauto.toml").write_text("[qarnot]\ntoken = 'secret'\n", encoding="utf-8") + + with pytest.raises(ValueError, match="QARNOT_TOKEN"): + load_config(tmp_path / "csauto.toml") + + +def test_a_bad_qarnot_interval_is_rejected(tmp_path: Path) -> None: + (tmp_path / "csauto.toml").write_text("[qarnot]\nsnapshot_interval_s = 0\n", encoding="utf-8") + + with pytest.raises(ValueError, match="snapshot_interval_s"): + load_config(tmp_path / "csauto.toml") From 5604d7a05336c1d53487828a56d05be3f57b313c Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Mon, 14 Sep 2026 16:02:12 +0200 Subject: [PATCH 13/42] feat: submit a case to Qarnot through the docker-batch profile Two input buckets per campaign: the shared mesh once, the case inputs per case. The snapshot whitelist comes from the adapter's observability globs, so only a few hundred kB come back while the run is in progress. --- csauto/backends/qarnot.py | 167 +++++++++++++++++++++++++ tests/unit/test_qarnot_backend.py | 199 ++++++++++++++++++++++++++++++ 2 files changed, 366 insertions(+) create mode 100644 csauto/backends/qarnot.py create mode 100644 tests/unit/test_qarnot_backend.py diff --git a/csauto/backends/qarnot.py b/csauto/backends/qarnot.py new file mode 100644 index 0000000..f689c79 --- /dev/null +++ b/csauto/backends/qarnot.py @@ -0,0 +1,167 @@ +"""The Qarnot execution backend. + +Everything Qarnot-specific lives here: the SDK, the buckets, the docker-batch +constants, the state translation and the snapshot policy. The core programs +against the five verbs in `base.py` and never sees a name from this file. + +The SDK is imported inside the methods that need it, so `[qarnot]` stays an +optional extra exactly as `[web]` does for FastAPI. + +Two facts about the SDK that the code below depends on, both checked against +qarnot 2.23.0 rather than assumed: + +- `Task.snapshot(interval, whitelist=...)` called before submission stores only + the interval and silently drops the whitelist, so the whitelist is set as an + attribute before submit (it is serialised into the creation payload) and the + periodic configuration is posted again afterwards, where the explicit path + runs. +- A Qarnot instance is a separate machine, not an MPI rank. `instancecount` is + always 1; the process count is inside `DOCKER_CMD`. +""" + +from __future__ import annotations + +import os +import shlex +from collections.abc import Sequence +from pathlib import Path +from typing import Any + +from .qarnot_support import ( + bucket_name, + directory_signature, + ensure_upload_within, + snapshot_whitelist, + split_image, + upload_plan, +) + +TOKEN_ENV = "QARNOT_TOKEN" + + +class QarnotBackend: + """Run a case on the Qarnot cloud through its generic docker-batch profile.""" + + name = "qarnot" + + def __init__(self, connection: Any = None) -> None: + self._connection = connection + + def _connect(self) -> Any: + """The SDK connection, built once, from the environment only.""" + if self._connection is not None: + return self._connection + token = os.environ.get(TOKEN_ENV, "").strip() + if not token: + raise RuntimeError( + f"{TOKEN_ENV} is not set. Qarnot runs on your own account: create a token in the " + f"Qarnot console and export it. It must never be written to csauto.toml." + ) + try: + from qarnot.connection import Connection + except ImportError as exc: # pragma: no cover - depends on the extra + raise RuntimeError("The qarnot SDK is not installed. Install csauto with the [qarnot] extra.") from exc + # Never let the token reach a traceback: the SDK raises on a bad token + # with its own message, which does not contain the value. + self._connection = Connection(client_token=token) + return self._connection + + def submit( + self, + case_dir: Path, + argv: Sequence[str], + image: str, + nprocs: int, + nt: int, + observability_globs: Sequence[str] = (), + ) -> str: + from ..solvers import get_solver_adapter + + case_dir = Path(case_dir).resolve() + runs_dir = case_dir.parent + config = _campaign_config(runs_dir) + adapter = get_solver_adapter(config.solver) + + command = [str(part) for part in argv if str(part) != ""] + if not command: + raise ValueError( + f"Solver {adapter.name} does not build a remote command: its run_argv is empty " + f"because it composes the whole launch line locally. It cannot run on {self.name} yet." + ) + + plan = upload_plan( + case_dir, + shared_dir_names=adapter.shared_dir_names, + results_dirname=adapter.results_dirname, + ) + ensure_upload_within(plan, config.qarnot_max_upload_mb) + + connection = self._connect() + campaign = bucket_name(runs_dir) + + case_bucket = connection.retrieve_or_create_bucket(f"{campaign}-{case_dir.name}") + for relative in plan.paths: + case_bucket.add_file(str(case_dir / relative), relative) + + resources = [case_bucket] + shared_bucket = self._shared_bucket(connection, runs_dir, adapter, campaign) + if shared_bucket is not None: + resources.append(shared_bucket) + + repo, tag = split_image(image or adapter.default_docker_image) + task = connection.create_task(f"csauto-{runs_dir.name}-{case_dir.name}", config.qarnot_profile, 1) + task.constants["DOCKER_REPO"] = repo + task.constants["DOCKER_TAG"] = tag + task.constants["DOCKER_CMD"] = shlex.join(command) + task.resources = resources + task.results = connection.retrieve_or_create_bucket(f"{campaign}-{case_dir.name}-out") + + whitelist = snapshot_whitelist(observability_globs) + if whitelist: + task.snapshot_whitelist = whitelist + task.submit() + if whitelist: + task.snapshot(config.qarnot_snapshot_interval_s, whitelist=whitelist) + return str(task.uuid) + + def _shared_bucket(self, connection: Any, runs_dir: Path, adapter: Any, campaign: str) -> Any: + """Upload MESH/POST once per campaign, not once per case. + + With mesh_mode = "symlink" these are shared directories of several + gigabytes. Re-uploading them per case would mean hundreds of gigabytes + for a hundred-case campaign, so a fingerprint in the registry records + what was last sent. + """ + from ..registry import load_registry, mutate_registry + + targets = [runs_dir / name for name in adapter.shared_dir_names] + targets = [path for path in targets if path.is_dir()] + if not targets: + return None + + bucket = connection.retrieve_or_create_bucket(f"{campaign}-shared") + signature = directory_signature(targets) + marker = load_registry(runs_dir).get("_backend", {}).get("shared_upload") or {} + if marker.get("bucket") == bucket.uuid and marker.get("signature") == signature: + return bucket + + for path in targets: + bucket.sync_directory(str(path), remote=path.name) + + def stamp(registry: dict[str, Any]) -> bool: + registry.setdefault("_backend", {})["shared_upload"] = { + "bucket": str(bucket.uuid), + "signature": signature, + } + return True + + mutate_registry(runs_dir, stamp) + return bucket + + +def _campaign_config(runs_dir: Path) -> Any: + """The campaign's own csauto.toml when it has one, the ambient config otherwise.""" + from ..config import load_config + + candidate = runs_dir / "csauto.toml" + return load_config(candidate if candidate.is_file() else None) diff --git a/tests/unit/test_qarnot_backend.py b/tests/unit/test_qarnot_backend.py new file mode 100644 index 0000000..b0f7949 --- /dev/null +++ b/tests/unit/test_qarnot_backend.py @@ -0,0 +1,199 @@ +"""The Qarnot backend, driven by a fake connection. + +No SDK, no token, no network: the tests assert what csauto asks Qarnot to do, +which is the part csauto owns. +""" + +from __future__ import annotations + +import re +from pathlib import Path +from typing import Any + +import pytest + +from csauto.backends.qarnot import QarnotBackend + + +class FakeBucket: + def __init__(self, name: str) -> None: + self.uuid = name + self.files: dict[str, str] = {} + self.directories: list[tuple[str, str]] = [] + + def add_file(self, local: str, remote: str) -> None: + self.files[remote] = local + + def sync_directory(self, directory: str, verbose: bool = False, remote: str | None = None) -> None: + self.directories.append((directory, remote or "")) + + +class FakeTask: + def __init__(self, name: str, profile: str, instancecount: int) -> None: + self.name = name + self.profile = profile + self.instancecount = instancecount + self.constants: dict[str, str] = {} + self.resources: list[FakeBucket] = [] + self.results: FakeBucket | None = None + self.snapshot_whitelist: str | None = None + self.snapshot_calls: list[tuple[int, str | None]] = [] + self.submitted = False + self.uuid = "task-0001" + + def submit(self) -> None: + self.submitted = True + + def snapshot(self, interval: int, whitelist: str | None = None, **_: Any) -> None: + self.snapshot_calls.append((interval, whitelist)) + + +class FakeConnection: + def __init__(self) -> None: + self.buckets: dict[str, FakeBucket] = {} + self.tasks: list[FakeTask] = [] + + def retrieve_or_create_bucket(self, name: str) -> FakeBucket: + return self.buckets.setdefault(name, FakeBucket(name)) + + def create_task(self, name: str, profile: str, instancecount: int = 1) -> FakeTask: + task = FakeTask(name, profile, instancecount) + self.tasks.append(task) + return task + + def retrieve_task(self, uuid: str) -> FakeTask: + for task in self.tasks: + if task.uuid == uuid: + return task + raise KeyError(uuid) + + +@pytest.fixture +def campaign(tmp_path: Path) -> Path: + runs_dir = tmp_path / "RUNS" + (runs_dir / "MESH").mkdir(parents=True) + (runs_dir / "MESH" / "m.med").write_text("mesh", encoding="utf-8") + case_dir = runs_dir / "case0001" + (case_dir / "DATA").mkdir(parents=True) + (case_dir / "DATA" / "setup.xml").write_text("", encoding="utf-8") + (case_dir / "doe_row.csv").write_text("a\n1\n", encoding="utf-8") + (runs_dir / "csauto.toml").write_text("solver = 'code_saturne'\n", encoding="utf-8") + return case_dir + + +def _submit(backend: QarnotBackend, case_dir: Path, **kwargs: Any) -> str: + defaults: dict[str, Any] = { + "argv": ["run", "--case", ".", "-n", "4", "--nt", "1"], + "image": "simvia/code_saturne:9.0", + "nprocs": 4, + "nt": 1, + "observability_globs": ("RESU/*/listing",), + } + defaults.update(kwargs) + return backend.submit(case_dir, **defaults) + + +def test_submit_returns_the_task_uuid(campaign: Path) -> None: + connection = FakeConnection() + + task_id = _submit(QarnotBackend(connection=connection), campaign) + + assert task_id == "task-0001" + assert connection.tasks[0].submitted is True + + +def test_submit_sets_the_docker_batch_constants(campaign: Path) -> None: + connection = FakeConnection() + + _submit(QarnotBackend(connection=connection), campaign) + + task = connection.tasks[0] + assert task.profile == "docker-batch" + assert task.constants["DOCKER_REPO"] == "simvia/code_saturne" + assert task.constants["DOCKER_TAG"] == "9.0" + assert task.constants["DOCKER_CMD"] == "run --case . -n 4 --nt 1" + + +def test_submit_asks_for_one_instance_not_one_per_process(campaign: Path) -> None: + """Qarnot instances are separate machines. nprocs would run four copies.""" + connection = FakeConnection() + + _submit(QarnotBackend(connection=connection), campaign, nprocs=4) + + assert connection.tasks[0].instancecount == 1 + + +def test_submit_uploads_the_case_inputs_but_not_the_mesh(campaign: Path) -> None: + connection = FakeConnection() + + _submit(QarnotBackend(connection=connection), campaign) + + case_bucket = next(b for b in connection.buckets.values() if b.uuid.endswith("-case0001")) + assert set(case_bucket.files) == {"DATA/setup.xml", "doe_row.csv"} + + +def test_submit_uploads_the_shared_directories_once_per_campaign(campaign: Path) -> None: + connection = FakeConnection() + backend = QarnotBackend(connection=connection) + + _submit(backend, campaign) + shared = next(b for b in connection.buckets.values() if b.uuid.endswith("-shared")) + assert shared.directories == [(str(campaign.parent / "MESH"), "MESH")] + + _submit(backend, campaign) + assert len(shared.directories) == 1, "unchanged shared dirs must not be re-uploaded" + + +def test_the_shared_bucket_is_re_uploaded_when_the_mesh_changes(campaign: Path) -> None: + connection = FakeConnection() + backend = QarnotBackend(connection=connection) + + _submit(backend, campaign) + (campaign.parent / "MESH" / "m.med").write_text("a different mesh", encoding="utf-8") + _submit(backend, campaign) + + shared = next(b for b in connection.buckets.values() if b.uuid.endswith("-shared")) + assert len(shared.directories) == 2 + + +def test_submit_whitelists_only_the_observability_files(campaign: Path) -> None: + connection = FakeConnection() + + _submit(QarnotBackend(connection=connection), campaign) + + task = connection.tasks[0] + assert re.match(task.snapshot_whitelist, "RESU/r1/listing") + assert task.snapshot_calls == [(60, task.snapshot_whitelist)] + + +def test_submit_does_not_snapshot_when_the_adapter_declares_nothing(campaign: Path) -> None: + connection = FakeConnection() + + _submit(QarnotBackend(connection=connection), campaign, observability_globs=()) + + assert connection.tasks[0].snapshot_calls == [] + + +def test_submit_refuses_a_solver_that_builds_no_remote_argv(campaign: Path) -> None: + connection = FakeConnection() + + with pytest.raises(ValueError, match="does not build a remote command"): + _submit(QarnotBackend(connection=connection), campaign, argv=[]) + + +def test_submit_refuses_an_oversized_case(campaign: Path) -> None: + (campaign.parent / "csauto.toml").write_text( + "solver = 'code_saturne'\n[qarnot]\nmax_upload_mb = 1\n", encoding="utf-8" + ) + (campaign / "DATA" / "big.dat").write_text("x" * (2 * 1024 * 1024), encoding="utf-8") + connection = FakeConnection() + + with pytest.raises(ValueError, match="over the 1 MB limit"): + _submit(QarnotBackend(connection=connection), campaign) + + +def test_a_missing_token_is_reported_without_leaking_anything(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.delenv("QARNOT_TOKEN", raising=False) + + with pytest.raises(RuntimeError, match="QARNOT_TOKEN"): + QarnotBackend()._connect() From ee54c8abe5fedd1856a6e2b735207025cedc6997 Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Mon, 14 Sep 2026 16:03:44 +0200 Subject: [PATCH 14/42] feat: observe, download and cancel a Qarnot task The state table lives in the backend, so runner.py never sees a Qarnot name, and an unknown state maps to PENDING rather than failing a live campaign. fetch_final pulls the results bucket unconditionally, because DONE means the results are on local disk. --- csauto/backend_sync.py | 4 + csauto/backends/__init__.py | 6 +- csauto/backends/base.py | 2 + csauto/backends/qarnot.py | 73 ++++++++++++++++++ tests/unit/test_backend_sync.py | 19 +++++ tests/unit/test_qarnot_backend.py | 124 ++++++++++++++++++++++++++++++ 6 files changed, 227 insertions(+), 1 deletion(-) diff --git a/csauto/backend_sync.py b/csauto/backend_sync.py index cf92679..067c7f2 100644 --- a/csauto/backend_sync.py +++ b/csauto/backend_sync.py @@ -93,6 +93,10 @@ def _observe( fields: dict[str, Any] = {"backend_poll_failures": 0} if state.progress is not None: fields["backend_progress"] = state.progress + if state.execution_time_s is not None: + fields["backend_execution_time_s"] = state.execution_time_s + if state.running_core_count is not None: + fields["backend_core_count"] = state.running_core_count _append(case_dir / "csauto.stdout", state.stdout_delta) _append(case_dir / "csauto.stderr", state.stderr_delta) diff --git a/csauto/backends/__init__.py b/csauto/backends/__init__.py index 2fb2391..624d9fe 100644 --- a/csauto/backends/__init__.py +++ b/csauto/backends/__init__.py @@ -11,7 +11,7 @@ from .base import BackendState, ExecutionBackend -_BACKEND_NAMES = ("fake",) +_BACKEND_NAMES = ("fake", "qarnot") def available_backends() -> tuple[str, ...]: @@ -29,6 +29,10 @@ def _backend_for(normalized: str) -> ExecutionBackend: from .fake import FakeBackend return FakeBackend() + if normalized == "qarnot": + from .qarnot import QarnotBackend + + return QarnotBackend() choices = ", ".join(available_backends()) raise ValueError(f"Unknown backend: {normalized!r}. Choices: {choices}") diff --git a/csauto/backends/base.py b/csauto/backends/base.py index 3078e8b..4e40b4d 100644 --- a/csauto/backends/base.py +++ b/csauto/backends/base.py @@ -23,6 +23,8 @@ class BackendState: progress: float | None = None stdout_delta: str = "" stderr_delta: str = "" + execution_time_s: float | None = None + running_core_count: int | None = None @runtime_checkable diff --git a/csauto/backends/qarnot.py b/csauto/backends/qarnot.py index f689c79..ac1e914 100644 --- a/csauto/backends/qarnot.py +++ b/csauto/backends/qarnot.py @@ -27,6 +27,7 @@ from pathlib import Path from typing import Any +from .base import BackendState from .qarnot_support import ( bucket_name, directory_signature, @@ -38,6 +39,26 @@ TOKEN_ENV = "QARNOT_TOKEN" +# Qarnot's own vocabulary, translated here and nowhere else. Taken from +# qarnot.task.Task.state, not guessed. An unlisted name maps to PENDING: a +# state csauto has never seen must not fail a running campaign. +_STATES = { + "submitted": "PENDING", + "partiallydispatched": "PENDING", + "fullydispatched": "PENDING", + "unsubmitted": "PENDING", + "partiallyexecuting": "RUNNING", + "fullyexecuting": "RUNNING", + "uploadingresults": "RUNNING", + "downloadingresults": "RUNNING", + "pendingcancel": "RUNNING", + "success": "DONE", + "failure": "FAILED", + "cancelled": "FAILED", + "pendingdelete": "FAILED", + "deleted": "FAILED", +} + class QarnotBackend: """Run a case on the Qarnot cloud through its generic docker-batch profile.""" @@ -158,6 +179,44 @@ def stamp(registry: dict[str, Any]) -> bool: mutate_registry(runs_dir, stamp) return bucket + def poll(self, task_id: str) -> BackendState: + task = self._task(task_id) + progress = getattr(task, "progress", None) + return BackendState( + status=_STATES.get(str(getattr(task, "state", "")).strip().lower(), "PENDING"), + progress=None if progress is None else float(progress) / 100.0, + stdout_delta=task.fresh_stdout() or "", + stderr_delta=task.fresh_stderr() or "", + execution_time_s=_as_float(getattr(task, "execution_time", None)), + running_core_count=_as_int(getattr(task, "running_core_count", None)), + ) + + def sync(self, task_id: str, case_dir: Path) -> None: + """Pull whatever the snapshot whitelist has captured so far. + + download_results only transfers when the task is dirty, so calling it + on a quiet task costs one request. The results bucket only ever holds + whitelisted paths under the results directory, so the case inputs are + never overwritten. + """ + Path(case_dir).mkdir(parents=True, exist_ok=True) + self._task(task_id).download_results(str(case_dir)) + + def fetch_final(self, task_id: str, case_dir: Path) -> None: + """Pull the complete results, once, unconditionally. + + Not download_results: that one skips the transfer when the SDK thinks + nothing changed, and DONE must mean the results really are on disk. + """ + Path(case_dir).mkdir(parents=True, exist_ok=True) + self._task(task_id).results.get_all_files(str(case_dir)) + + def cancel(self, task_id: str) -> None: + self._task(task_id).abort() + + def _task(self, task_id: str) -> Any: + return self._connect().retrieve_task(str(task_id)) + def _campaign_config(runs_dir: Path) -> Any: """The campaign's own csauto.toml when it has one, the ambient config otherwise.""" @@ -165,3 +224,17 @@ def _campaign_config(runs_dir: Path) -> Any: candidate = runs_dir / "csauto.toml" return load_config(candidate if candidate.is_file() else None) + + +def _as_float(value: Any) -> float | None: + try: + return None if value is None else float(value) + except (TypeError, ValueError): + return None + + +def _as_int(value: Any) -> int | None: + try: + return None if value is None else int(value) + except (TypeError, ValueError): + return None diff --git a/tests/unit/test_backend_sync.py b/tests/unit/test_backend_sync.py index 5f0f11e..1523979 100644 --- a/tests/unit/test_backend_sync.py +++ b/tests/unit/test_backend_sync.py @@ -163,3 +163,22 @@ def relaunch_then_poll(_name: str): record = load_registry(runs_dir)["case0001"] assert record["task_id"] == "fake-9999" assert record["status"] == STATUS_RUNNING + + +def test_sync_records_the_timing_figures(runs_dir: Path, case_factory) -> None: + """execution_time and running_core_count come free from the backend.""" + from csauto.backends.base import BackendState + + case_dir = case_factory(runs_dir, "case0001") + backend = FakeBackend(script=["RUNNING"]) + task_id = backend.submit(case_dir, ["run"], "img", 1, 1) + _register(runs_dir, case_dir, task_id) + backend.poll = lambda _task_id: BackendState( # type: ignore[method-assign] + status=STATUS_RUNNING, execution_time_s=42.0, running_core_count=8 + ) + + sync_backend_cases(runs_dir, backend_factory=lambda _name: backend) + + record = load_registry(runs_dir)["case0001"] + assert record["backend_execution_time_s"] == 42.0 + assert record["backend_core_count"] == 8 diff --git a/tests/unit/test_qarnot_backend.py b/tests/unit/test_qarnot_backend.py index b0f7949..791a6b7 100644 --- a/tests/unit/test_qarnot_backend.py +++ b/tests/unit/test_qarnot_backend.py @@ -197,3 +197,127 @@ def test_a_missing_token_is_reported_without_leaking_anything(monkeypatch: pytes with pytest.raises(RuntimeError, match="QARNOT_TOKEN"): QarnotBackend()._connect() + + +class PolledTask(FakeTask): + def __init__(self, state: str, progress: float = 50.0) -> None: + super().__init__("t", "docker-batch", 1) + self.state = state + self.progress = progress + self.execution_time = 12.5 + self.running_core_count = 4 + self.aborted = False + self.downloaded: list[str] = [] + self.results = FakeBucket("out") + self._stdout = "iteration 1\n" + self._stderr = "" + + def fresh_stdout(self, instanceId: int | None = None) -> str: + out, self._stdout = self._stdout, "" + return out + + def fresh_stderr(self, instanceId: int | None = None) -> str: + out, self._stderr = self._stderr, "" + return out + + def download_results(self, output_dir: str, progress: Any = None) -> None: + self.downloaded.append(output_dir) + + def abort(self) -> None: + self.aborted = True + + +def _backend_with(task: PolledTask) -> QarnotBackend: + connection = FakeConnection() + task.uuid = "task-0001" + connection.tasks.append(task) + return QarnotBackend(connection=connection) + + +@pytest.mark.parametrize( + ("qarnot_state", "expected"), + [ + ("Submitted", "PENDING"), + ("PartiallyDispatched", "PENDING"), + ("FullyDispatched", "PENDING"), + ("PartiallyExecuting", "RUNNING"), + ("FullyExecuting", "RUNNING"), + ("UploadingResults", "RUNNING"), + ("DownloadingResults", "RUNNING"), + ("PendingCancel", "RUNNING"), + ("Success", "DONE"), + ("Failure", "FAILED"), + ("Cancelled", "FAILED"), + ("PendingDelete", "FAILED"), + ], +) +def test_poll_translates_every_qarnot_state(qarnot_state: str, expected: str) -> None: + backend = _backend_with(PolledTask(qarnot_state)) + + assert backend.poll("task-0001").status == expected + + +def test_an_unknown_state_is_reported_pending_not_failed() -> None: + """A state name csauto has never seen must not fail a running campaign.""" + backend = _backend_with(PolledTask("SomeNewState")) + + assert backend.poll("task-0001").status == "PENDING" + + +def test_poll_returns_progress_as_a_fraction() -> None: + """Qarnot reports a percentage; BackendState.progress is 0..1.""" + backend = _backend_with(PolledTask("FullyExecuting", progress=50.0)) + + assert backend.poll("task-0001").progress == 0.5 + + +def test_poll_returns_only_the_new_output() -> None: + task = PolledTask("FullyExecuting") + backend = _backend_with(task) + + assert backend.poll("task-0001").stdout_delta == "iteration 1\n" + assert backend.poll("task-0001").stdout_delta == "" + + +def test_poll_reports_the_timing_figures() -> None: + backend = _backend_with(PolledTask("FullyExecuting")) + + state = backend.poll("task-0001") + + assert state.execution_time_s == 12.5 + assert state.running_core_count == 4 + + +def test_sync_downloads_into_the_case_directory(tmp_path: Path) -> None: + task = PolledTask("FullyExecuting") + backend = _backend_with(task) + + backend.sync("task-0001", tmp_path / "case0001") + + assert task.downloaded == [str(tmp_path / "case0001")] + + +def test_fetch_final_pulls_the_whole_results_bucket(tmp_path: Path) -> None: + task = PolledTask("Success") + pulled: list[str] = [] + task.results.get_all_files = lambda output_dir, progress=None: pulled.append(output_dir) # type: ignore[attr-defined] + backend = _backend_with(task) + + backend.fetch_final("task-0001", tmp_path / "case0001") + + assert pulled == [str(tmp_path / "case0001")] + + +def test_cancel_aborts_the_task() -> None: + task = PolledTask("FullyExecuting") + backend = _backend_with(task) + + backend.cancel("task-0001") + + assert task.aborted is True + + +def test_qarnot_is_a_registered_backend() -> None: + from csauto.backends import available_backends + + assert "qarnot" in available_backends() From f8f11a05a3159380177b032b1fe0256d0876b07b Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Mon, 14 Sep 2026 16:05:46 +0200 Subject: [PATCH 15/42] feat: report the Qarnot prerequisites in doctor, and document the backend doctor --backend qarnot checks the SDK, the token, the image and whether the configured solver can build a remote command, each separately, and never prints the token itself. --- CHANGELOG.md | 1 + README.md | 1 + csauto/cli.py | 6 +++ csauto/maintenance.py | 35 ++++++++++++++ docs/architecture.md | 10 ++++ docs/cli.md | 4 ++ docs/config.md | 3 ++ docs/qarnot.md | 86 ++++++++++++++++++++++++++++++++++ tests/unit/test_maintenance.py | 46 ++++++++++++++++++ 9 files changed, 192 insertions(+) create mode 100644 docs/qarnot.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 446221b..2b96cdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Make the dashboard follow what each solver can actually do: panels and action bu ### Added - `ExecutionBackend` boundary (`csauto/backends/`): a case can be launched on a remote execution service instead of a local process or a Slurm job. A backend implements five verbs (`submit`, `poll`, `sync`, `fetch_final`, `cancel`) and translates its own state names into csauto statuses; the core never sees a provider's API. The shipped `fake` backend covers the whole lifecycle in tests with no network, the way the `stub` solver does without a solver. Two new settings, `backend_poll_interval_s` and `backend_sync_interval_s` +- Qarnot cloud execution (`backend = "qarnot"`, optional `[qarnot]` extra): a campaign can run on your own Qarnot account through the generic `docker-batch` profile, with the same docker image used locally. The shared mesh directories travel once per campaign and the case inputs once per case; while a run is in progress only the files the solver adapter declares in `observability_globs` come back, so residuals, probes and logs are live in the dashboard without downloading a multi-gigabyte results directory. The token is read from `QARNOT_TOKEN` and is rejected if found in `csauto.toml`. `csauto doctor --backend qarnot` reports each prerequisite. code_saturne and the stub solver only; code_aster builds no remote command yet ### Changed - Dashboard panels and action buttons are derived from what the solver adapter implements, rather than declared: a panel appears when the adapter provides what feeds it (`find_residuals_files`, `list_probe_files`, or a non-empty `compare_kinds` / `performance_columns` / `control_actions`), and the Restart, Stop, control and Open GUI controls follow the same rule. Solvers other than code_saturne lose the panels and buttons they could never feed: code_aster and the stub solver now show Status, Compare, Log Tail and Recent Errors only. code_saturne is unchanged. Adapters can no longer declare `dashboard_panels`, which now raises `TypeError` at import time diff --git a/README.md b/README.md index 207c31e..fc7ba10 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,7 @@ To re-enable: `csauto enable-telemetry` - [Web UI guide](docs/web-ui.md) - [CLI reference](docs/cli.md) - [Configuration](docs/config.md) +- [Running on Qarnot](docs/qarnot.md) - [DOE format](docs/doe-format.md) - [Run lifecycle](docs/run-lifecycle.md) - [HTTP API](docs/api.md) diff --git a/csauto/cli.py b/csauto/cli.py index b2b53cd..a4bc9b3 100644 --- a/csauto/cli.py +++ b/csauto/cli.py @@ -331,6 +331,11 @@ def _add_export_args(parser: argparse.ArgumentParser) -> None: doctor_parser = subparsers.add_parser("doctor", help="Check configuration and cases.") doctor_parser.add_argument("runs_dir", type=Path, help="Directory containing generated cases") + doctor_parser.add_argument( + "--backend", + default=None, + help="Also check the prerequisites of an execution backend (for example: qarnot).", + ) cleanup_parser = subparsers.add_parser("cleanup", help="Clean up runs (results/logs/cache).") cleanup_parser.add_argument("runs_dir", type=Path, help="Directory containing generated cases") @@ -592,6 +597,7 @@ def main(argv: Sequence[str] | None = None) -> int: singularity_image=config.singularity_image, singularity_bin=config.singularity_bin, adapter=adapter, + backend=args.backend, ) if _print_doctor(items): return 1 diff --git a/csauto/maintenance.py b/csauto/maintenance.py index 5c20943..0f17e9a 100644 --- a/csauto/maintenance.py +++ b/csauto/maintenance.py @@ -41,6 +41,37 @@ def _check_web_deps(add: object) -> None: add("ok", "web dependencies available (fastapi, uvicorn, pydantic)") +def _check_qarnot(add: object, adapter: object) -> None: + """Report each Qarnot prerequisite separately, and never the token itself.""" + import importlib + + try: + importlib.import_module("qarnot") + except ImportError: + add("fail", "qarnot SDK missing: install csauto with the [qarnot] extra") + else: + add("ok", "qarnot SDK available") + + if os.environ.get("QARNOT_TOKEN", "").strip(): + add("ok", "QARNOT_TOKEN is set") + else: + add("fail", "QARNOT_TOKEN is not set (the token belongs in the environment, never in csauto.toml)") + + image = getattr(adapter, "default_docker_image", "") + if image: + add("ok", f"cloud image: {image}") + else: + add("fail", f"solver {adapter.name}: no docker image configured, and qarnot only runs docker images") + + if adapter.run_argv(".", 1, 1): + add("ok", f"solver {adapter.name}: builds a remote command") + else: + add( + "fail", + f"solver {adapter.name}: builds no remote command (run_argv is empty), so it cannot run on qarnot yet", + ) + + def run_doctor( runs_dir: Path, require_docker: bool = False, @@ -53,6 +84,7 @@ def run_doctor( singularity_image: str | None = None, singularity_bin: str | None = None, adapter=None, + backend: str | None = None, ) -> list[DoctorItem]: adapter = adapter or _default_adapter() items: list[DoctorItem] = [] @@ -162,6 +194,9 @@ def add(level: str, message: str) -> None: else: add("warn", "DISPLAY is set but /tmp/.X11-unix is missing") + if str(backend or "").strip().lower() == "qarnot": + _check_qarnot(add, adapter) + _check_web_deps(add) return items diff --git a/docs/architecture.md b/docs/architecture.md index c2927ca..f5c01bf 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -130,6 +130,16 @@ Two invariants the implementation depends on: - **A failed poll never changes a status.** A flaky network must not mark a whole campaign as failed; consecutive failures are counted instead. +`QarnotBackend` (`backend = "qarnot"`) is the first real implementation. It runs +any docker image through Qarnot's generic `docker-batch` profile, uploads the +shared directories once per campaign and the case inputs once per case, and +turns `adapter.observability_globs` into the regex whitelist Qarnot's periodic +snapshot expects. Everything Qarnot-specific (the SDK, the buckets, the +`DOCKER_*` constants, the state table) lives in `csauto/backends/qarnot.py`; +`csauto/backends/qarnot_support.py` holds the parts that are decisions rather +than API calls, so they are testable with no SDK. See +[Running a campaign on Qarnot](./qarnot.md). + `local` and Slurm are not behind this contract. They predate it and work; porting them is a later, mechanical refactor. diff --git a/docs/cli.md b/docs/cli.md index f87a61f..be2a3c3 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -374,6 +374,10 @@ Checks performed: - Runtime binary / image is accessible (reads `csauto.toml` — native binary, Singularity image, or Docker) - X11 display availability (for GUI launch) +`--backend ` also checks the prerequisites of an execution backend, for +example `csauto doctor RUNS --backend qarnot` (SDK installed, `QARNOT_TOKEN` +set, an image to run, and a solver that can produce a remote command). + Each check prints `[OK]` or `[FAIL]`. Fix all failures before running. --- diff --git a/docs/config.md b/docs/config.md index 9a3cafe..932d9f6 100644 --- a/docs/config.md +++ b/docs/config.md @@ -120,6 +120,9 @@ token = "your-secret-token" | `max_parallel` | `1` | Maximum simultaneous case launches | | `backend_poll_interval_s` | `15` | Seconds between two execution-backend polls | | `backend_sync_interval_s` | `60` | Seconds between two observability file syncs | +| `[qarnot].profile` | `docker-batch` | Qarnot profile used for cloud runs | +| `[qarnot].snapshot_interval_s` | `60` | Seconds between two remote captures of the observability files | +| `[qarnot].max_upload_mb` | `512` | Refuse to submit a case whose own inputs exceed this | | `mesh_mode` | `symlink` | How `prepare` places the shared `MESH`/`POST` dirs into `RUNS/`: `symlink` or `copy` (see [concepts.md](./concepts.md#shared-meshpost-directories)) | | `host` | `127.0.0.1` | Bind address for `serve` | | `port` | `8000` | Bind port for `serve` | diff --git a/docs/qarnot.md b/docs/qarnot.md new file mode 100644 index 0000000..b433e36 --- /dev/null +++ b/docs/qarnot.md @@ -0,0 +1,86 @@ +# Running a campaign on Qarnot + +csauto can send cases to the [Qarnot](https://qarnot.com) cloud instead of running +them on this machine. You run them on **your own Qarnot account**: csauto submits +on your behalf, and Simvia hosts nothing, bills nothing and sees nothing. + +Supported solvers: code_saturne and the `stub` test solver. code_aster is not +supported yet, because its adapter composes its whole launch line locally +instead of producing a command a remote container can run; `csauto doctor +--backend qarnot` says so. + +## Setup + +```bash +pip install "csauto[qarnot]" +export QARNOT_TOKEN="" +csauto doctor RUNS --backend qarnot +``` + +**The token goes in the environment, never in `csauto.toml`.** That file lives in +the campaign directory, which gets shared, committed and archived. csauto +refuses to start if it finds a token in there. + +## Settings + +`csauto.toml` carries the non-sensitive settings only: + +```toml +[qarnot] +profile = "docker-batch" # the Qarnot profile +snapshot_interval_s = 60 # how often the remote task captures the watched files +max_upload_mb = 512 # refuse a case whose own inputs exceed this +``` + +Two more settings govern how often csauto looks: + +```toml +backend_poll_interval_s = 15 # status, progress, stdout and stderr +backend_sync_interval_s = 60 # pull the captured files +``` + +Pulling more often than the task captures only wastes requests, so keep +`backend_sync_interval_s` at or above `snapshot_interval_s`. + +## What is uploaded, and what comes back + +| Bucket | Content | Uploaded | +|---|---|---| +| `csauto---shared` | the shared directories (`MESH`, `POST`) | once per campaign, and again only if they change | +| `csauto---` | the case's own inputs (`DATA/`, `SRC/`, `doe_row.csv`, ...) | once per case | +| `csauto----out` | snapshots and results | written by the task | + +While a case runs, only the files the solver adapter declares as observability +files come back (for code_saturne: the listing, the residuals, the monitoring +probes and the run status markers). That is a few hundred kB, which keeps the +snapshot well under the 1 GB Qarnot advises. The complete results directory is +downloaded once, when the task finishes. + +Because those files genuinely land in `RUNS/caseXXXX/`, the dashboard's +residuals, probes, log tail and error panels work during a cloud run exactly as +they do locally. + +## Monitoring + +Live monitoring goes through `csauto serve`: the polling loop lives in the +server. From the CLI, `csauto status RUNS` performs one poll and one sync, at +most once every ten seconds. + +## What the statuses mean + +- `PENDING`: submitted, waiting for machines. +- `RUNNING`: executing, or uploading and downloading its results. +- `DONE`: **the results are on local disk.** csauto never reports `DONE` before + the download succeeded, because Compare, Clean and the size figures all read + local files. +- `FAILED`: the task failed, or was cancelled. + +A failed poll never changes a status. A flaky network must not mark a hundred +cases as failed, so consecutive failures are counted and reported instead. + +## Costs + +csauto shows no price estimate: it has no pricing figure it can vouch for, and a +wrong number on a launch button would be worse than none. What it does do is +refuse an oversized upload before submission, and record the execution time and +core count of each case afterwards. diff --git a/tests/unit/test_maintenance.py b/tests/unit/test_maintenance.py index 746937c..5974458 100644 --- a/tests/unit/test_maintenance.py +++ b/tests/unit/test_maintenance.py @@ -516,3 +516,49 @@ def find_setup_file(self, template_dir): items = run_doctor(runs_dir, check_setup=False, check_display=False, adapter=BareAdapter()) assert _has_item(items, "ok", "solver bare: capabilities none") + + +def test_doctor_says_nothing_about_qarnot_by_default(tmp_path: Path) -> None: + from csauto.maintenance import run_doctor + + (tmp_path / "case0001").mkdir() + items = run_doctor(tmp_path, check_display=False, check_setup=False) + + assert not [item for item in items if "qarnot" in item.message.lower()] + + +def test_doctor_reports_a_missing_token(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: + from csauto.maintenance import run_doctor + + monkeypatch.delenv("QARNOT_TOKEN", raising=False) + (tmp_path / "case0001").mkdir() + + items = run_doctor(tmp_path, check_display=False, check_setup=False, backend="qarnot") + messages = [item.message for item in items] + + assert any("QARNOT_TOKEN" in message for message in messages) + assert any(item.level == "fail" for item in items if "QARNOT_TOKEN" in item.message) + + +def test_doctor_never_prints_the_token(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: + from csauto.maintenance import run_doctor + + monkeypatch.setenv("QARNOT_TOKEN", "super-secret-value") + (tmp_path / "case0001").mkdir() + + items = run_doctor(tmp_path, check_display=False, check_setup=False, backend="qarnot") + + assert not [item for item in items if "super-secret-value" in item.message] + assert any("QARNOT_TOKEN is set" in item.message for item in items) + + +def test_doctor_reports_a_solver_that_cannot_run_remotely(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: + from csauto.maintenance import run_doctor + from csauto.solvers.code_aster import CodeAsterAdapter + + monkeypatch.setenv("QARNOT_TOKEN", "x") + (tmp_path / "case0001").mkdir() + + items = run_doctor(tmp_path, check_display=False, check_setup=False, backend="qarnot", adapter=CodeAsterAdapter()) + + assert any(item.level == "fail" and "code_aster" in item.message for item in items) From ca949b39fd26b2fd0c200452811957973ea380e1 Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Tue, 15 Sep 2026 07:14:46 +0200 Subject: [PATCH 16/42] feat: list the execution backends in /api/app_config The dashboard asks the server what is on offer rather than hardcoding a provider name, the same way it already does for panels and capabilities. --- csauto/fastapi_routes/observability.py | 3 +++ tests/web/test_api.py | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/csauto/fastapi_routes/observability.py b/csauto/fastapi_routes/observability.py index d4ffab1..b3d190d 100644 --- a/csauto/fastapi_routes/observability.py +++ b/csauto/fastapi_routes/observability.py @@ -1,6 +1,7 @@ import math from typing import Annotated, Any +from ..backends import available_backends from ..logs import ( ANOMALY_CONTEXT_DEFAULT, ANOMALY_SEVERITY, @@ -87,6 +88,7 @@ class AppConfigModel(BaseModel): compare_kinds: list[CompareKindModel] error_files: list[str] control_actions: list[str] + backends: list[str] class RecentErrorItemModel(BaseModel): case_id: str @@ -150,6 +152,7 @@ def api_app_config( "compare_kinds": [{"value": kind.value, "label": kind.label} for kind in ctx.adapter.compare_kinds], "error_files": list(ctx.adapter.anomaly_file_names), "control_actions": sorted(ctx.adapter.control_actions), + "backends": list(available_backends()), } @app.get("/api/restart_origin", response_model=RestartOriginResponse) diff --git a/tests/web/test_api.py b/tests/web/test_api.py index 98eb8fd..629a833 100644 --- a/tests/web/test_api.py +++ b/tests/web/test_api.py @@ -1380,3 +1380,21 @@ def test_kill_cancels_a_backend_task(runs_dir: Path, case_factory, monkeypatch) assert backend.poll(task_id).status == "FAILED" assert load_registry(runs_dir)["case0001"]["status"] == "FAILED" + + +def test_app_config_lists_the_execution_backends(web_env) -> None: + """The dashboard asks what is on offer; it never hardcodes a provider name.""" + base_url, _case_dir = web_env + _status, body = _http_get(f"{base_url}/api/app_config") + data = json.loads(body) + + assert "qarnot" in data["backends"] + assert "fake" in data["backends"] + + +def test_app_config_never_returns_a_credential(web_env) -> None: + base_url, _case_dir = web_env + _status, body = _http_get(f"{base_url}/api/app_config") + + assert "token" not in body.lower() + assert "secret" not in body.lower() From 5a88864517dd17dd58e962026413a9cbc19bc9b5 Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Tue, 15 Sep 2026 07:21:09 +0200 Subject: [PATCH 17/42] feat: choose the execution backend per launch over HTTP /api/run_case accepts a backend name, validated against the registered ones, and the status row carries back what the backend reports: progress, execution time and core count. --- csauto/fastapi_routes/actions.py | 12 ++++++++ csauto/runner.py | 7 +++++ tests/runner/test_runner.py | 40 +++++++++++++++++++++++++++ tests/web/test_api.py | 47 ++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+) diff --git a/csauto/fastapi_routes/actions.py b/csauto/fastapi_routes/actions.py index 7f7d35b..ab829e5 100644 --- a/csauto/fastapi_routes/actions.py +++ b/csauto/fastapi_routes/actions.py @@ -63,6 +63,7 @@ class RunCasePayload(BaseModel): n: int | None = None nt: int | None = None max_parallel: int | None = None + backend: str | None = None restart: bool = False restart_mode: str = "" restart_value: int | float | None = None @@ -446,6 +447,16 @@ def api_run_case( else: max_parallel = payload.max_parallel + backend = (payload.backend or "").strip().lower() or None + if backend is not None: + from ..backends import available_backends + + if backend not in available_backends(): + raise ctx.http_exception_cls( + status_code=400, + detail=f"Unknown execution backend: {backend!r}. Choices: {', '.join(available_backends())}", + ) + restart, restart_mode, restart_value, restart_path = _parse_restart_params( payload, ctx.http_exception_cls, @@ -486,6 +497,7 @@ def api_run_case( mpi_exec_options=ctx.mpi_exec_options, source="web", adapter=ctx.adapter, + backend=backend, ) except ctx.http_exception_cls: raise diff --git a/csauto/runner.py b/csauto/runner.py index 9aa2ae1..19cf2f5 100644 --- a/csauto/runner.py +++ b/csauto/runner.py @@ -944,6 +944,13 @@ def _compute_refresh_result( "duration": duration, "last_mod": last_mod, "resu_size_mb": resu_size_mb, + # A case a remote backend owns has no local duration to speak of, so + # the dashboard shows what the backend reports instead. Empty and None + # for every local case, which is every case today. + "backend": backend_name, + "backend_progress": record.get("backend_progress"), + "backend_execution_time_s": record.get("backend_execution_time_s"), + "backend_core_count": record.get("backend_core_count"), } doe_cols: tuple[str, ...] = () diff --git a/tests/runner/test_runner.py b/tests/runner/test_runner.py index 3357475..a8139fd 100644 --- a/tests/runner/test_runner.py +++ b/tests/runner/test_runner.py @@ -1245,3 +1245,43 @@ def record(case_dir, argv, image, nprocs, nt, observability_globs=()): assert str(runs_dir) not in " ".join(str(part) for part in submitted["argv"]) assert "." in submitted["argv"] assert submitted["globs"], "the adapter's observability patterns must reach the backend" + + +def test_status_row_carries_the_backend_figures(runs_dir, case_factory) -> None: + """The dashboard shows what a cloud case costs; the figures live in the registry.""" + case_factory(runs_dir, "case0001") + save_registry( + runs_dir, + { + "case0001": { + "case_id": "case0001", + "path": str(runs_dir / "case0001"), + "status": STATUS_RUNNING, + "backend": "fake", + "task_id": "fake-0001", + "backend_progress": 0.25, + "backend_execution_time_s": 42.0, + "backend_core_count": 8, + } + }, + ) + + row = refresh_status(runs_dir)[0] + + assert row["backend"] == "fake" + assert row["backend_progress"] == 0.25 + assert row["backend_execution_time_s"] == 42.0 + assert row["backend_core_count"] == 8 + + +def test_status_row_of_a_local_case_has_no_backend_figures(runs_dir, case_factory) -> None: + case_factory(runs_dir, "case0001") + save_registry( + runs_dir, + {"case0001": {"case_id": "case0001", "path": str(runs_dir / "case0001"), "status": "PREPARED"}}, + ) + + row = refresh_status(runs_dir)[0] + + assert row["backend"] == "" + assert row["backend_execution_time_s"] is None diff --git a/tests/web/test_api.py b/tests/web/test_api.py index 629a833..8160a0f 100644 --- a/tests/web/test_api.py +++ b/tests/web/test_api.py @@ -1398,3 +1398,50 @@ def test_app_config_never_returns_a_credential(web_env) -> None: assert "token" not in body.lower() assert "secret" not in body.lower() + + +def test_run_case_passes_the_chosen_backend_through(monkeypatch, web_env) -> None: + """The runtime is chosen per launch, so the choice must reach run_cases.""" + base_url, _case_dir = web_env + seen: dict[str, object] = {} + + def fake_run_cases(*args, **kwargs): + seen.update(kwargs) + + monkeypatch.setattr("csauto.fastapi_routes.actions.run_cases", fake_run_cases) + + status, _body = _http_post( + f"{base_url}/api/run_case", + {"cases": ["case0001"], "n": 1, "nt": 1, "backend": "fake"}, + ) + + assert status == 200 + assert seen["backend"] == "fake" + + +def test_run_case_defaults_to_no_backend(monkeypatch, web_env) -> None: + """Omitting the field must keep the local behaviour exactly as before.""" + base_url, _case_dir = web_env + seen: dict[str, object] = {} + + def fake_run_cases(*args, **kwargs): + seen.update(kwargs) + + monkeypatch.setattr("csauto.fastapi_routes.actions.run_cases", fake_run_cases) + + _http_post(f"{base_url}/api/run_case", {"cases": ["case0001"], "n": 1, "nt": 1}) + + assert seen.get("backend") is None + + +def test_run_case_rejects_an_unknown_backend(web_env) -> None: + base_url, _case_dir = web_env + + with pytest.raises(HTTPError) as excinfo: + _http_post( + f"{base_url}/api/run_case", + {"cases": ["case0001"], "n": 1, "nt": 1, "backend": "not-a-backend"}, + ) + + assert excinfo.value.code == 400 + assert "not-a-backend" in json.loads(excinfo.value.read())["detail"] From cf2a696697decef04c17faaf017744e16b688e17 Mon Sep 17 00:00:00 2001 From: florian-simvia Date: Tue, 15 Sep 2026 07:22:33 +0200 Subject: [PATCH 18/42] feat: choose where a launch runs, from the Run dialog The execution list comes from /api/app_config, so the dialog never names a provider. Choosing one shows what is about to be submitted and billed before the button is pressed. --- frontend/src/lib/api/endpoints.ts | 2 + frontend/src/lib/api/types.ts | 7 ++++ .../lib/components/dialogs/RunDialog.svelte | 41 ++++++++++++++++++- .../lib/components/status/StatusCard.svelte | 1 + frontend/src/lib/stores/appConfig.svelte.ts | 11 +++++ frontend/src/lib/stores/settings.svelte.ts | 11 +++++ 6 files changed, 71 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/api/endpoints.ts b/frontend/src/lib/api/endpoints.ts index 25cdce4..c74a93d 100644 --- a/frontend/src/lib/api/endpoints.ts +++ b/frontend/src/lib/api/endpoints.ts @@ -194,6 +194,7 @@ export function runCase(params: { n: number; nt: number; maxParallel?: number | null; + backend?: string | null; restart?: boolean; restartMode?: string; restartValue?: number; @@ -203,6 +204,7 @@ export function runCase(params: { n: params.n, nt: params.nt, max_parallel: params.maxParallel ?? undefined, + backend: params.backend ?? undefined, restart: params.restart ?? false, restart_mode: params.restartMode ?? "", restart_value: params.restartValue ?? undefined, diff --git a/frontend/src/lib/api/types.ts b/frontend/src/lib/api/types.ts index e4c577c..de7d5fd 100644 --- a/frontend/src/lib/api/types.ts +++ b/frontend/src/lib/api/types.ts @@ -19,6 +19,10 @@ export interface StatusRow { duration: string | null; last_mod: string | null; resu_size_mb: number | null; + backend: string | null; + backend_progress: number | null; + backend_execution_time_s: number | null; + backend_core_count: number | null; doe: Record | null; [extra: string]: unknown; } @@ -59,6 +63,7 @@ export interface AppConfig { compare_kinds: CompareKindOption[]; error_files: string[]; control_actions: string[]; + backends: string[]; } /* Shared */ @@ -122,6 +127,8 @@ export interface RunParams { n: number; nt: number; maxParallel: number | null; + /** Execution backend name, or null/absent to run on this machine. */ + backend?: string | null; } export interface RestartParams extends RunParams { diff --git a/frontend/src/lib/components/dialogs/RunDialog.svelte b/frontend/src/lib/components/dialogs/RunDialog.svelte index 43a1128..266be9b 100644 --- a/frontend/src/lib/components/dialogs/RunDialog.svelte +++ b/frontend/src/lib/components/dialogs/RunDialog.svelte @@ -1,7 +1,9 @@ -{#if state.files.length > 0} + +{#if allCases.length > 0} files.filter((f) => { const stem = f.replace(/\.csv$/i, "").toLowerCase(); @@ -132,13 +127,18 @@ let emptyMessage = $derived( state.selectedCases.length === 0 ? "Please select at least one case." - : state.selectedColumns.length === 0 - ? "Please select at least one probe." - : "No data to display.", + : state.files.length === 0 + ? "No probe data for the selected cases yet." + : state.selectedColumns.length === 0 + ? "Please select at least one probe." + : "No data to display.", ); -{#if state.files.length > 0} + +{#if allCases.length > 0} Date: Tue, 15 Sep 2026 11:03:57 +0200 Subject: [PATCH 40/42] refactor: drop the Cloud Time and Cloud Cores columns Neither earned its place: Qarnot only reports an execution time once a task has finished, so the column stayed empty for the whole run, and the core count showed zero for every local case. The figures are still recorded per case in the registry, where they cost nothing and remain available to an audit. --- CHANGELOG.md | 2 +- docs/web-ui.md | 2 -- .../dist/_app/immutable/chunks/5xxJZag6.js | 1 + .../dist/_app/immutable/chunks/DTWAhvnO.js | 1 - .../{app.CBC_xGKp.js => app.BiXph2fc.js} | 4 +-- .../_app/immutable/entry/start.C6epiIK6.js | 1 + .../_app/immutable/entry/start.CbP2J41B.js | 1 - .../nodes/{1.D7xrQvTV.js => 1.CCoIP03N.js} | 2 +- .../nodes/{2.BGWNYlPN.js => 2.Db1dJWID.js} | 34 +++++++++---------- frontend/dist/_app/version.json | 2 +- frontend/dist/index.html | 12 +++---- .../lib/components/status/StatusTable.svelte | 14 -------- frontend/src/lib/stores/status.svelte.ts | 5 --- 13 files changed, 30 insertions(+), 51 deletions(-) create mode 100644 frontend/dist/_app/immutable/chunks/5xxJZag6.js delete mode 100644 frontend/dist/_app/immutable/chunks/DTWAhvnO.js rename frontend/dist/_app/immutable/entry/{app.CBC_xGKp.js => app.BiXph2fc.js} (94%) create mode 100644 frontend/dist/_app/immutable/entry/start.C6epiIK6.js delete mode 100644 frontend/dist/_app/immutable/entry/start.CbP2J41B.js rename frontend/dist/_app/immutable/nodes/{1.D7xrQvTV.js => 1.CCoIP03N.js} (78%) rename frontend/dist/_app/immutable/nodes/{2.BGWNYlPN.js => 2.Db1dJWID.js} (75%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89e2ac8..6186aae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ Make the dashboard follow what each solver can actually do: panels and action bu - `POST /api/sync_backends`, and a Refresh button that uses it: for a case an execution backend owns, Refresh now asks the server to pull fresh data before rereading. It used to reread local files that only change when the background sync pass runs, so the panels looked frozen between two passes. Throttled to one pass every ten seconds and never failing, so clicking repeatedly costs nothing and a provider outage does not break the button. `csauto status` shares the same throttle - `ExecutionBackend` boundary (`csauto/backends/`): a case can be launched on a remote execution service instead of a local process or a Slurm job. A backend implements five verbs (`submit`, `poll`, `sync`, `fetch_final`, `cancel`) and translates its own state names into csauto statuses; the core never sees a provider's API. The shipped `fake` backend covers the whole lifecycle in tests with no network, the way the `stub` solver does without a solver. Two new settings, `backend_poll_interval_s` and `backend_sync_interval_s` - Qarnot cloud execution (`backend = "qarnot"`, optional `[qarnot]` extra): a campaign can run on your own Qarnot account through the generic `docker-batch` profile, with the same docker image used locally. The shared mesh directories travel once per campaign and the case inputs once per case; while a run is in progress only the files the solver adapter declares in `observability_globs` come back, so residuals, probes and logs are live in the dashboard without downloading a multi-gigabyte results directory. The token is read from `QARNOT_TOKEN` and is rejected if found in `csauto.toml`. `csauto doctor --backend qarnot` reports each prerequisite. code_saturne and the stub solver only; code_aster builds no remote command yet -- A **Run on** selector in the dashboard's Run dialog: a launch can be sent to an execution backend instead of the machine hosting the server, chosen per launch rather than per campaign, so one case can be verified locally while the rest go to the cloud. Choosing a backend restates the case count and that the run is billed to your own account before the button is pressed. The list comes from `GET /api/app_config`, so the frontend names no provider; `POST /api/run_case` accepts a matching `backend` field. Two optional status columns, Cloud Time (s) and Cloud Cores, report what each remote case used +- A **Run on** selector in the dashboard's Run dialog: a launch can be sent to an execution backend instead of the machine hosting the server, chosen per launch rather than per campaign, so one case can be verified locally while the rest go to the cloud. Choosing a backend restates the case count and that the run is billed to your own account before the button is pressed. The list comes from `GET /api/app_config`, so the frontend names no provider; `POST /api/run_case` accepts a matching `backend` field. - Per-launch execution options: a backend declares what a user may choose and the Run dialog renders it, so choosing Qarnot now offers a scheduling **Priority** (`Flex`, `OnDemand`, `Reserved`) and a **Node type** read from your own account. The core carries the chosen values without interpreting them, which keeps a provider's vocabulary inside its own module, and records them in the case history. The catalogue comes from a dedicated `GET /api/launch_options`, called when the dialog opens rather than on every page load, cached for ten minutes, and degraded rather than failed when the provider is unreachable: a launch never depends on it ### Changed diff --git a/docs/web-ui.md b/docs/web-ui.md index 63c87bb..ff300d8 100644 --- a/docs/web-ui.md +++ b/docs/web-ui.md @@ -62,8 +62,6 @@ The toolbar provides: | `DURATION` | Elapsed time since launch | | `RESU SIZE (MB)` | Total RESU disk usage | | `LAST MODIFIED` | Timestamp of the last state change | -| `CLOUD TIME (S)` | Execution time reported by the execution backend; empty for a local run | -| `CLOUD CORES` | Cores the execution backend used; empty for a local run | Each row also has an **Open GUI** button to launch the code_saturne GUI for that case. diff --git a/frontend/dist/_app/immutable/chunks/5xxJZag6.js b/frontend/dist/_app/immutable/chunks/5xxJZag6.js new file mode 100644 index 0000000..6f962eb --- /dev/null +++ b/frontend/dist/_app/immutable/chunks/5xxJZag6.js @@ -0,0 +1 @@ +var te=t=>{throw TypeError(t)};var je=(t,e,n)=>e.has(t)||te("Cannot "+n);var v=(t,e,n)=>(je(t,e,"read from private field"),n?n.call(t):e.get(t)),U=(t,e,n)=>e.has(t)?te("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,n);import{bf as Ut,bg as Ne,aK as T,o as I,$ as O,aJ as mt,bh as qe}from"./VJhVTb1A.js";import{o as ee}from"./Cz6m6K8p.js";const B=[];function Dt(t,e=Ut){let n=null;const a=new Set;function r(o){if(Ne(t,o)&&(t=o,n)){const l=!B.length;for(const c of a)c[1](),B.push(c,t);if(l){for(let c=0;c{a.delete(c),a.size===0&&n&&(n(),n=null)}}return{set:r,update:i,subscribe:s}}class Vt{constructor(e,n){this.status=e,typeof n=="string"?this.body={message:n}:n?this.body=n:this.body={message:`Error: ${e}`}}toString(){return JSON.stringify(this.body)}}class Kt{constructor(e,n){this.status=e,this.location=n}}class Bt extends Error{constructor(e,n,a){super(a),this.status=e,this.text=n}}new URL("sveltekit-internal://");function De(t,e){return t==="/"||e==="ignore"?t:e==="never"?t.endsWith("/")?t.slice(0,-1):t:e==="always"&&!t.endsWith("/")?t+"/":t}function Ve(t){return t.split("%25").map(decodeURI).join("%25")}function Ke(t){for(const e in t)t[e]=decodeURIComponent(t[e]);return t}function Tt({href:t}){return t.split("#")[0]}function Be(...t){let e=5381;for(const n of t)if(typeof n=="string"){let a=n.length;for(;a;)e=e*33^n.charCodeAt(--a)}else if(ArrayBuffer.isView(n)){const a=new Uint8Array(n.buffer,n.byteOffset,n.byteLength);let r=a.length;for(;r;)e=e*33^a[--r]}else throw new TypeError("value must be a string or TypedArray");return(e>>>0).toString(36)}new TextEncoder;new TextDecoder;function Me(t){const e=atob(t),n=new Uint8Array(e.length);for(let a=0;a((t instanceof Request?t.method:(e==null?void 0:e.method)||"GET")!=="GET"&&H.delete(Mt(t)),ze(t,e));const H=new Map;function Fe(t,e){const n=Mt(t,e),a=document.querySelector(n);if(a!=null&&a.textContent){a.remove();let{body:r,...i}=JSON.parse(a.textContent);const s=a.getAttribute("data-ttl");return s&&H.set(n,{body:r,init:i,ttl:1e3*Number(s)}),a.getAttribute("data-b64")!==null&&(r=Me(r)),Promise.resolve(new Response(r,i))}return window.fetch(t,e)}function Ge(t,e,n){if(H.size>0){const a=Mt(t,n),r=H.get(a);if(r){if(performance.now(){const r=/^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(a);if(r)return e.push({name:r[1],matcher:r[2],optional:!1,rest:!0,chained:!0}),"(?:/([^]*))?";const i=/^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(a);if(i)return e.push({name:i[1],matcher:i[2],optional:!0,rest:!1,chained:!0}),"(?:/([^/]+))?";if(!a)return;const s=a.split(/\[(.+?)\](?!\])/);return"/"+s.map((l,c)=>{if(c%2){if(l.startsWith("x+"))return It(String.fromCharCode(parseInt(l.slice(2),16)));if(l.startsWith("u+"))return It(String.fromCharCode(...l.slice(2).split("-").map(m=>parseInt(m,16))));const d=We.exec(l),[,u,w,p,f]=d;return e.push({name:p,matcher:f,optional:!!u,rest:!!w,chained:w?c===1&&s[0]==="":!1}),w?"([^]*?)":u?"([^/]*)?":"([^/]+?)"}return It(l)}).join("")}).join("")}/?$`),params:e}}function Ye(t){return t!==""&&!/^\([^)]+\)$/.test(t)}function He(t){return t.slice(1).split("/").filter(Ye)}function Xe(t,e,n){const a={},r=t.slice(1),i=r.filter(o=>o!==void 0);let s=0;for(let o=0;od).join("/"),s=0),c===void 0)if(l.rest)c="";else continue;if(!l.matcher||n[l.matcher](c)){a[l.name]=c;const d=e[o+1],u=r[o+1];d&&!d.rest&&d.optional&&u&&l.chained&&(s=0),!d&&!u&&Object.keys(a).length===i.length&&(s=0);continue}if(l.optional&&l.chained){s++;continue}return}if(!s)return a}function It(t){return t.normalize().replace(/[[\]]/g,"\\$&").replace(/%/g,"%25").replace(/\//g,"%2[Ff]").replace(/\?/g,"%3[Ff]").replace(/#/g,"%23").replace(/[.*+?^${}()|\\]/g,"\\$&")}function Qe({nodes:t,server_loads:e,dictionary:n,matchers:a}){const r=new Set(e);return Object.entries(n).map(([o,[l,c,d]])=>{const{pattern:u,params:w}=Je(o),p={id:o,exec:f=>{const m=u.exec(f);if(m)return Xe(m,w,a)},errors:[1,...d||[]].map(f=>t[f]),layouts:[0,...c||[]].map(s),leaf:i(l)};return p.errors.length=p.layouts.length=Math.max(p.errors.length,p.layouts.length),p});function i(o){const l=o<0;return l&&(o=~o),[l,t[o]]}function s(o){return o===void 0?o:[r.has(o),t[o]]}}function ge(t,e=JSON.parse){try{return e(sessionStorage[t])}catch{}}function ne(t,e,n=JSON.stringify){const a=n(e);try{sessionStorage[t]=a}catch{}}var fe;const A=((fe=globalThis.__sveltekit_vrmx5m)==null?void 0:fe.base)??"";var ue;const Ze=((ue=globalThis.__sveltekit_vrmx5m)==null?void 0:ue.assets)??A??"",tn="1789462962656",me="sveltekit:snapshot",_e="sveltekit:scroll",we="sveltekit:states",en="sveltekit:pageurl",z="sveltekit:history",Q="sveltekit:navigation",q={tap:1,hover:2,viewport:3,eager:4,off:-1,false:-1},zt=location.origin;function ve(t){if(t instanceof URL)return t;let e=document.baseURI;if(!e){const n=document.getElementsByTagName("base");e=n.length?n[0].href:document.URL}return new URL(t,e)}function V(){return{x:pageXOffset,y:pageYOffset}}function M(t,e){return t.getAttribute(`data-sveltekit-${e}`)}const re={...q,"":q.hover};function ye(t){let e=t.assignedSlot??t.parentNode;return(e==null?void 0:e.nodeType)===11&&(e=e.host),e}function be(t,e){for(;t&&t!==e;){if(t.nodeName.toUpperCase()==="A"&&t.hasAttribute("href"))return t;t=ye(t)}}function $t(t,e,n){let a;try{if(a=new URL(t instanceof SVGAElement?t.href.baseVal:t.href,document.baseURI),n&&a.hash.match(/^#[^/]/)){const o=location.hash.split("#")[1]||"/";a.hash=`#${o}${a.hash}`}}catch{}const r=t instanceof SVGAElement?t.target.baseVal:t.target,i=!a||!!r||Rt(a,e,n)||(t.getAttribute("rel")||"").split(/\s+/).includes("external"),s=(a==null?void 0:a.origin)===zt&&t.hasAttribute("download");return{url:a,external:i,target:r,download:s}}function _t(t){let e=null,n=null,a=null,r=null,i=null,s=null,o=t;for(;o&&o!==document.documentElement;)a===null&&(a=M(o,"preload-code")),r===null&&(r=M(o,"preload-data")),e===null&&(e=M(o,"keepfocus")),n===null&&(n=M(o,"noscroll")),i===null&&(i=M(o,"reload")),s===null&&(s=M(o,"replacestate")),o=ye(o);function l(c){switch(c){case"":case"true":return!0;case"off":case"false":return!1;default:return}}return{preload_code:re[a??"off"],preload_data:re[r??"off"],keepfocus:l(e),noscroll:l(n),reload:l(i),replace_state:l(s)}}function ae(t){const e=Dt(t);let n=!0;function a(){n=!0,e.update(s=>s)}function r(s){n=!1,e.set(s)}function i(s){let o;return e.subscribe(l=>{(o===void 0||n&&l!==o)&&s(o=l)})}return{notify:a,set:r,subscribe:i}}const ke={v:()=>{}};function nn(){const{set:t,subscribe:e}=Dt(!1);let n;async function a(){clearTimeout(n);try{const r=await fetch(`${Ze}/_app/version.json`,{headers:{pragma:"no-cache","cache-control":"no-cache"}});if(!r.ok)return!1;const s=(await r.json()).version!==tn;return s&&(t(!0),ke.v(),clearTimeout(n)),s}catch{return!1}}return{subscribe:e,check:a}}function Rt(t,e,n){return t.origin!==zt||!t.pathname.startsWith(e)?!0:n?t.pathname!==location.pathname:!1}function On(t){}const Se=new Set(["load","prerender","csr","ssr","trailingSlash","config"]);[...Se];const rn=new Set([...Se]);[...rn];function an(t){return t.filter(e=>e!=null)}function Ft(t){return t instanceof Vt||t instanceof Bt?t.status:500}function on(t){return t instanceof Bt?t.text:"Internal Error"}let R,Z,Ot;const sn=ee.toString().includes("$$")||/function \w+\(\) \{\}/.test(ee.toString()),oe="a:";var at,ot,st,it,lt,ct,ft,ut,de,dt,he,ht,pe;sn?(R={data:{},form:null,error:null,params:{},route:{id:null},state:{},status:-1,url:new URL(oe)},Z={current:null},Ot={current:!1}):(R=new(de=class{constructor(){U(this,at,T({}));U(this,ot,T(null));U(this,st,T(null));U(this,it,T({}));U(this,lt,T({id:null}));U(this,ct,T({}));U(this,ft,T(-1));U(this,ut,T(new URL(oe)))}get data(){return I(v(this,at))}set data(e){O(v(this,at),e)}get form(){return I(v(this,ot))}set form(e){O(v(this,ot),e)}get error(){return I(v(this,st))}set error(e){O(v(this,st),e)}get params(){return I(v(this,it))}set params(e){O(v(this,it),e)}get route(){return I(v(this,lt))}set route(e){O(v(this,lt),e)}get state(){return I(v(this,ct))}set state(e){O(v(this,ct),e)}get status(){return I(v(this,ft))}set status(e){O(v(this,ft),e)}get url(){return I(v(this,ut))}set url(e){O(v(this,ut),e)}},at=new WeakMap,ot=new WeakMap,st=new WeakMap,it=new WeakMap,lt=new WeakMap,ct=new WeakMap,ft=new WeakMap,ut=new WeakMap,de),Z=new(he=class{constructor(){U(this,dt,T(null))}get current(){return I(v(this,dt))}set current(e){O(v(this,dt),e)}},dt=new WeakMap,he),Ot=new(pe=class{constructor(){U(this,ht,T(!1))}get current(){return I(v(this,ht))}set current(e){O(v(this,ht),e)}},ht=new WeakMap,pe),ke.v=()=>Ot.current=!0);function ln(t){Object.assign(R,t)}const cn=new Set(["icon","shortcut icon","apple-touch-icon"]);let J=null;const j=ge(_e)??{},tt=ge(me)??{},C={url:ae({}),page:ae({}),navigating:Dt(null),updated:nn()};function Gt(t){j[t]=V()}function fn(t,e){let n=t+1;for(;j[n];)delete j[n],n+=1;for(n=e+1;tt[n];)delete tt[n],n+=1}function et(t,e=!1){return e?location.replace(t.href):location.href=t.href,new Promise(()=>{})}async function Ee(){if("serviceWorker"in navigator){const t=await navigator.serviceWorker.getRegistration(A||"/");t&&await t.update()}}function se(){}let Wt,Ct,wt,P,jt,S;const vt=[],yt=[];let y=null;function Nt(){var t;(t=y==null?void 0:y.fork)==null||t.then(e=>e==null?void 0:e.discard()),y=null}const gt=new Map,Re=new Set,un=new Set,X=new Set;let _={branch:[],error:null,url:null},xe=!1,bt=!1,ie=!0,nt=!1,Y=!1,Le=!1,Jt=!1,Ae,k,L,D;const kt=new Set,le=new Map;async function jn(t,e,n){var i,s,o,l,c;(i=globalThis.__sveltekit_vrmx5m)!=null&&i.data&&globalThis.__sveltekit_vrmx5m.data,document.URL!==location.href&&(location.href=location.href),S=t,await((o=(s=t.hooks).init)==null?void 0:o.call(s)),Wt=Qe(t),P=document.documentElement,jt=e,Ct=t.nodes[0],wt=t.nodes[1],Ct(),wt(),k=(l=history.state)==null?void 0:l[z],L=(c=history.state)==null?void 0:c[Q],k||(k=L=Date.now(),history.replaceState({...history.state,[z]:k,[Q]:L},""));const a=j[k];function r(){a&&(history.scrollRestoration="manual",scrollTo(a.x,a.y))}n?(r(),await Rn(jt,n)):(await F({type:"enter",url:ve(S.hash?An(new URL(location.href)):location.href),replace_state:!0}),r()),En()}function dn(){vt.length=0,Jt=!1}function Ue(t){yt.some(e=>e==null?void 0:e.snapshot)&&(tt[t]=yt.map(e=>{var n;return(n=e==null?void 0:e.snapshot)==null?void 0:n.capture()}))}function Te(t){var e;(e=tt[t])==null||e.forEach((n,a)=>{var r,i;(i=(r=yt[a])==null?void 0:r.snapshot)==null||i.restore(n)})}function ce(){Gt(k),ne(_e,j),Ue(L),ne(me,tt)}async function hn(t,e,n,a){let r;e.invalidateAll&&Nt(),await F({type:"goto",url:ve(t),keepfocus:e.keepFocus,noscroll:e.noScroll,replace_state:e.replaceState,state:e.state,redirect_count:n,nav_token:a,accept:()=>{e.invalidateAll&&(Jt=!0,r=[...le.keys()]),e.invalidate&&e.invalidate.forEach(Sn)}}),e.invalidateAll&&mt().then(mt).then(()=>{le.forEach(({resource:i},s)=>{var o;r!=null&&r.includes(s)&&((o=i.refresh)==null||o.call(i))})})}async function pn(t){if(t.id!==(y==null?void 0:y.id)){Nt();const e={};kt.add(e),y={id:t.id,token:e,promise:Oe({...t,preload:e}).then(n=>(kt.delete(e),n.type==="loaded"&&n.state.error&&Nt(),n)),fork:null}}return y.promise}async function Pt(t){var n;const e=(n=await xt(t,!1))==null?void 0:n.route;e&&await Promise.all([...e.layouts,e.leaf].filter(Boolean).map(a=>a[1]()))}async function Ie(t,e,n){var i;const a={params:_.params,route:{id:((i=_.route)==null?void 0:i.id)??null},url:new URL(location.href)};_={...t.state,nav:a};const r=document.querySelector("style[data-sveltekit]");if(r&&r.remove(),Object.assign(R,t.props.page),Ae=new S.root({target:e,props:{...t.props,stores:C,components:yt},hydrate:n,sync:!1,transformError:void 0}),await Promise.resolve(),Te(L),n){const s={from:null,to:{...a,scroll:j[k]??V()},willUnload:!1,type:"enter",complete:Promise.resolve()};X.forEach(o=>o(s))}bt=!0}async function St({url:t,params:e,branch:n,errors:a,status:r,error:i,route:s,form:o}){let l="never";if(A&&(t.pathname===A||t.pathname===A+"/"))l="always";else for(const f of n)(f==null?void 0:f.slash)!==void 0&&(l=f.slash);t.pathname=De(t.pathname,l),t.search=t.search;const c={type:"loaded",state:{url:t,params:e,branch:n,error:i,route:s},props:{constructors:an(n).map(f=>f.node.component),page:Zt(R)}};o!==void 0&&(c.props.form=o);let d={},u=!R,w=0;for(let f=0;fo(new URL(s))))return!0;return!1}function Ht(t,e){return(t==null?void 0:t.type)==="data"?t:(t==null?void 0:t.type)==="skip"?e??null:null}function _n(t,e){if(!t)return new Set(e.searchParams.keys());const n=new Set([...t.searchParams.keys(),...e.searchParams.keys()]);for(const a of n){const r=t.searchParams.getAll(a),i=e.searchParams.getAll(a);r.every(s=>i.includes(s))&&i.every(s=>r.includes(s))&&n.delete(a)}return n}function wn({error:t,url:e,route:n,params:a}){return{type:"loaded",state:{error:t,url:e,route:n,params:a,branch:[]},props:{page:Zt(R),constructors:[]}}}async function Oe({id:t,invalidating:e,url:n,params:a,route:r,preload:i}){if((y==null?void 0:y.id)===t)return kt.delete(y.token),y.promise;const{errors:s,layouts:o,leaf:l}=r,c=[...o,l];s.forEach(h=>h==null?void 0:h().catch(()=>{})),c.forEach(h=>h==null?void 0:h[1]().catch(()=>{}));const d=_.url?t!==Et(_.url):!1,u=_.route?r.id!==_.route.id:!1,w=_n(_.url,n);let p=!1;const f=c.map(async(h,g)=>{var $;if(!h)return;const b=_.branch[g];return h[1]===(b==null?void 0:b.loader)&&!mn(p,u,d,w,($=b.universal)==null?void 0:$.uses,a)?b:(p=!0,Yt({loader:h[1],url:n,params:a,route:r,parent:async()=>{var pt;const N={};for(let K=0;K{});const m=[];for(let h=0;hPromise.resolve({}),server_data_node:Ht(i)}),o={node:await wt(),loader:wt,universal:null,server:null,data:null};return St({url:n,params:r,branch:[s,o],status:t,error:e,errors:[],route:null})}catch(s){if(s instanceof Kt)return hn(new URL(s.location,location.href),{},0);throw s}}async function yn(t){const e=t.href;if(gt.has(e))return gt.get(e);let n;try{const a=(async()=>{let r=await S.hooks.reroute({url:new URL(t),fetch:async(i,s)=>gn(i,s,t).promise})??t;if(typeof r=="string"){const i=new URL(t);S.hash?i.hash=r:i.pathname=r,r=i}return r})();gt.set(e,a),n=await a}catch{gt.delete(e);return}return n}async function xt(t,e){if(t&&!Rt(t,A,S.hash)){const n=await yn(t);if(!n)return;const a=bn(n);for(const r of Wt){const i=r.exec(a);if(i)return{id:Et(t),invalidating:e,route:r,params:Ke(i),url:t}}}}function bn(t){return Ve(S.hash?t.hash.replace(/^#/,"").replace(/[?#].+/,""):t.pathname.slice(A.length))||"/"}function Et(t){return(S.hash?t.hash.replace(/^#/,""):t.pathname)+t.search}function Pe({url:t,type:e,intent:n,delta:a,event:r,scroll:i}){let s=!1;const o=Qt(_,n,t,e,i??null);a!==void 0&&(o.navigation.delta=a),r!==void 0&&(o.navigation.event=r);const l={...o.navigation,cancel:()=>{s=!0,o.reject(new Error("navigation cancelled"))}};return nt||Re.forEach(c=>c(l)),s?null:o}async function F({type:t,url:e,popped:n,keepfocus:a,noscroll:r,replace_state:i,state:s={},redirect_count:o=0,nav_token:l={},accept:c=se,block:d=se,event:u}){var K;const w=D;D=l;const p=await xt(e,!1),f=t==="enter"?Qt(_,p,e,t):Pe({url:e,type:t,delta:n==null?void 0:n.delta,intent:p,scroll:n==null?void 0:n.scroll,event:u});if(!f){d(),D===l&&(D=w);return}const m=k,h=L;c(),nt=!0,bt&&f.navigation.type!=="enter"&&C.navigating.set(Z.current=f.navigation);let g=p&&await Oe(p);if(!g){if(Rt(e,A,S.hash))return await et(e,i);g=await $e(e,{id:null},await rt(new Bt(404,"Not Found",`Not found: ${e.pathname}`),{url:e,params:{},route:{id:null}}),404,i)}if(e=(p==null?void 0:p.url)||e,D!==l)return f.reject(new Error("navigation aborted")),!1;if(g.type==="redirect"){if(o<20){await F({type:t,url:new URL(g.location,e),popped:n,keepfocus:a,noscroll:r,replace_state:i,state:s,redirect_count:o+1,nav_token:l}),f.fulfil(void 0);return}g=await Xt({status:500,error:await rt(new Error("Redirect loop"),{url:e,params:{},route:{id:null}}),url:e,route:{id:null}})}else g.props.page.status>=400&&await C.updated.check()&&(await Ee(),await et(e,i));if(dn(),Gt(m),Ue(h),g.props.page.url.pathname!==e.pathname&&(e.pathname=g.props.page.url.pathname),s=n?n.state:s,!n){const E=i?0:1,G={[z]:k+=E,[Q]:L+=E,[we]:s};(i?history.replaceState:history.pushState).call(history,G,"",e),i||fn(k,L)}const b=p&&(y==null?void 0:y.id)===p.id?y.fork:null;y=null,g.props.page.state=s;let x;if(bt){const E=(await Promise.all(Array.from(un,W=>W(f.navigation)))).filter(W=>typeof W=="function");if(E.length>0){let W=function(){E.forEach(At=>{X.delete(At)})};E.push(W),E.forEach(At=>{X.add(At)})}const G=f.navigation.to;_={...g.state,nav:{params:G.params,route:G.route,url:G.url}},g.props.page&&(g.props.page.url=e);const Lt=b&&await b;Lt?x=Lt.commit():(J=null,Ae.$set(g.props),J&&Object.assign(g.props.page,J),ln(g.props.page),x=(K=qe)==null?void 0:K()),Le=!0}else await Ie(g,jt,!1);const{activeElement:$}=document;await x,await mt(),await mt();let N=null;if(ie){const E=n?n.scroll:r?V():null;E?scrollTo(E.x,E.y):(N=e.hash&&document.getElementById(Ce(e)))?N.scrollIntoView():scrollTo(0,0)}const pt=document.activeElement!==$&&document.activeElement!==document.body;!a&&!pt&&Ln(e,!N),ie=!0,g.props.page&&(J&&Object.assign(g.props.page,J),Object.assign(R,g.props.page)),nt=!1,t==="popstate"&&Te(L),f.fulfil(void 0),f.navigation.to&&(f.navigation.to.scroll=V()),X.forEach(E=>E(f.navigation)),C.navigating.set(Z.current=null)}async function $e(t,e,n,a,r){return t.origin===zt&&t.pathname===location.pathname&&!xe?await Xt({status:a,error:n,url:t,route:e}):await et(t,r)}function kn(){let t,e={element:void 0,href:void 0},n;P.addEventListener("mousemove",o=>{const l=o.target;clearTimeout(t),t=setTimeout(()=>{i(l,q.hover)},20)});function a(o){o.defaultPrevented||i(o.composedPath()[0],q.tap)}P.addEventListener("mousedown",a),P.addEventListener("touchstart",a,{passive:!0});const r=new IntersectionObserver(o=>{for(const l of o)l.isIntersecting&&(Pt(new URL(l.target.href)),r.unobserve(l.target))},{threshold:0});async function i(o,l){const c=be(o,P),d=c===e.element&&(c==null?void 0:c.href)===e.href&&l>=n;if(!c||d)return;const{url:u,external:w,download:p}=$t(c,A,S.hash);if(w||p)return;const f=_t(c),m=u&&Et(_.url)===Et(u);if(!(f.reload||m))if(l<=f.preload_data){e={element:c,href:c.href},n=q.tap;const h=await xt(u,!1);if(!h)return;pn(h)}else l<=f.preload_code&&(e={element:c,href:c.href},n=l,Pt(u))}function s(){r.disconnect();for(const o of P.querySelectorAll("a")){const{url:l,external:c,download:d}=$t(o,A,S.hash);if(c||d)continue;const u=_t(o);u.reload||(u.preload_code===q.viewport&&r.observe(o),u.preload_code===q.eager&&Pt(l))}}X.add(s),s()}function rt(t,e){if(t instanceof Vt)return t.body;const n=Ft(t),a=on(t);return S.hooks.handleError({error:t,event:e,status:n,message:a})??{message:a}}function Sn(t){if(typeof t=="function")vt.push(t);else{const{href:e}=new URL(t,location.href);vt.push(n=>n.href===e)}}function En(){var e;history.scrollRestoration="manual",addEventListener("beforeunload",n=>{let a=!1;if(ce(),!nt){const r=Qt(_,void 0,null,"leave"),i={...r.navigation,cancel:()=>{a=!0,r.reject(new Error("navigation cancelled"))}};Re.forEach(s=>s(i))}a?(n.preventDefault(),n.returnValue=""):history.scrollRestoration="auto"}),addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&ce()}),(e=navigator.connection)!=null&&e.saveData||kn(),P.addEventListener("click",async n=>{if(n.button||n.which!==1||n.metaKey||n.ctrlKey||n.shiftKey||n.altKey||n.defaultPrevented)return;const a=be(n.composedPath()[0],P);if(!a)return;const{url:r,external:i,target:s,download:o}=$t(a,A,S.hash);if(!r)return;if(s==="_parent"||s==="_top"){if(window.parent!==window)return}else if(s&&s!=="_self")return;const l=_t(a);if(!(a instanceof SVGAElement)&&r.protocol!==location.protocol&&!(r.protocol==="https:"||r.protocol==="http:")||o)return;const[d,u]=(S.hash?r.hash.replace(/^#/,""):r.href).split("#"),w=d===Tt(location);if(i||l.reload&&(!w||!u)){Pe({url:r,type:"link",event:n})?nt=!0:n.preventDefault();return}if(u!==void 0&&w){const[,p]=_.url.href.split("#");if(p===u){if(n.preventDefault(),u===""||u==="top"&&a.ownerDocument.getElementById("top")===null)scrollTo({top:0});else{const f=a.ownerDocument.getElementById(decodeURIComponent(u));f&&(f.scrollIntoView(),f.focus())}return}if(Y=!0,Gt(k),t(r),!l.replace_state)return;Y=!1}n.preventDefault(),await new Promise(p=>{requestAnimationFrame(()=>{setTimeout(p,0)}),setTimeout(p,100)}),await F({type:"link",url:r,keepfocus:l.keepfocus,noscroll:l.noscroll,replace_state:l.replace_state??r.href===location.href,event:n})}),P.addEventListener("submit",n=>{if(n.defaultPrevented)return;const a=HTMLFormElement.prototype.cloneNode.call(n.target),r=n.submitter;if(((r==null?void 0:r.formTarget)||a.target)==="_blank"||((r==null?void 0:r.formMethod)||a.method)!=="get")return;const o=new URL((r==null?void 0:r.hasAttribute("formaction"))&&(r==null?void 0:r.formAction)||a.action);if(Rt(o,A,!1))return;const l=n.target,c=_t(l);if(c.reload)return;n.preventDefault(),n.stopPropagation();const d=new FormData(l,r);o.search=new URLSearchParams(d).toString(),F({type:"form",url:o,keepfocus:c.keepfocus,noscroll:c.noscroll,replace_state:c.replace_state??o.href===location.href,event:n})}),addEventListener("popstate",async n=>{var a;if(!qt){if((a=n.state)!=null&&a[z]){const r=n.state[z];if(D={},r===k)return;const i=j[r],s=n.state[we]??{},o=new URL(n.state[en]??location.href),l=n.state[Q],c=_.url?Tt(location)===Tt(_.url):!1;if(l===L&&(Le||c)){s!==R.state&&(R.state=s),t(o),j[k]=V(),i&&scrollTo(i.x,i.y),k=r;return}const u=r-k;await F({type:"popstate",url:o,popped:{state:s,scroll:i,delta:u},accept:()=>{k=r,L=l},block:()=>{history.go(-u)},nav_token:D,event:n})}else if(!Y){const r=new URL(location.href);t(r),S.hash&&location.reload()}}}),addEventListener("hashchange",()=>{Y&&(Y=!1,history.replaceState({...history.state,[z]:++k,[Q]:L},"",location.href))});for(const n of document.querySelectorAll("link"))cn.has(n.rel)&&(n.href=n.href);addEventListener("pageshow",n=>{n.persisted&&C.navigating.set(Z.current=null)});function t(n){_.url=R.url=n,C.page.set(Zt(R)),C.page.notify()}}async function Rn(t,{status:e=200,error:n,node_ids:a,params:r,route:i,server_route:s,data:o,form:l}){xe=!0;const c=new URL(location.href);let d;({params:r={},route:i={id:null}}=await xt(c,!1)||{}),d=Wt.find(({id:p})=>p===i.id);let u,w=!0;try{const p=a.map(async(m,h)=>{const g=o[h];return g!=null&&g.uses&&(g.uses=xn(g.uses)),Yt({loader:S.nodes[m],url:c,params:r,route:i,parent:async()=>{const b={};for(let x=0;x{const o=history.state;qt=!0,location.replace(new URL(`#${a}`,location.href)),history.replaceState(o,"",t),e&&scrollTo(i,s),qt=!1})}else{const i=document.body,s=i.getAttribute("tabindex");i.tabIndex=-1,i.focus({preventScroll:!0,focusVisible:!1}),s!==null?i.setAttribute("tabindex",s):i.removeAttribute("tabindex")}const r=getSelection();if(r&&r.type!=="None"){const i=[];for(let s=0;s{if(r.rangeCount===i.length){for(let s=0;s{i=u,s=w});return o.catch(()=>{}),{navigation:{from:{params:t.params,route:{id:((c=t.route)==null?void 0:c.id)??null},url:t.url,scroll:V()},to:n&&{params:(e==null?void 0:e.params)??null,route:{id:((d=e==null?void 0:e.route)==null?void 0:d.id)??null},url:n,scroll:r},willUnload:!e,type:a,complete:o},fulfil:i,reject:s}}function Zt(t){return{data:t.data,error:t.error,form:t.form,params:t.params,route:t.route,state:t.state,status:t.status,url:t.url}}function An(t){const e=new URL(t);return e.hash=decodeURIComponent(t.hash),e}function Ce(t){let e;if(S.hash){const[,,n]=t.hash.split("#",3);e=n??""}else e=t.hash.slice(1);return decodeURIComponent(e)}export{jn as a,On as l,R as p,C as s}; diff --git a/frontend/dist/_app/immutable/chunks/DTWAhvnO.js b/frontend/dist/_app/immutable/chunks/DTWAhvnO.js deleted file mode 100644 index 601dbaa..0000000 --- a/frontend/dist/_app/immutable/chunks/DTWAhvnO.js +++ /dev/null @@ -1 +0,0 @@ -var te=t=>{throw TypeError(t)};var je=(t,e,n)=>e.has(t)||te("Cannot "+n);var v=(t,e,n)=>(je(t,e,"read from private field"),n?n.call(t):e.get(t)),U=(t,e,n)=>e.has(t)?te("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,n);import{bf as Ut,bg as Ne,aK as T,o as I,$ as O,aJ as mt,bh as qe}from"./VJhVTb1A.js";import{o as ee}from"./Cz6m6K8p.js";const B=[];function Dt(t,e=Ut){let n=null;const a=new Set;function r(o){if(Ne(t,o)&&(t=o,n)){const l=!B.length;for(const c of a)c[1](),B.push(c,t);if(l){for(let c=0;c{a.delete(c),a.size===0&&n&&(n(),n=null)}}return{set:r,update:i,subscribe:s}}class Vt{constructor(e,n){this.status=e,typeof n=="string"?this.body={message:n}:n?this.body=n:this.body={message:`Error: ${e}`}}toString(){return JSON.stringify(this.body)}}class Kt{constructor(e,n){this.status=e,this.location=n}}class Bt extends Error{constructor(e,n,a){super(a),this.status=e,this.text=n}}new URL("sveltekit-internal://");function De(t,e){return t==="/"||e==="ignore"?t:e==="never"?t.endsWith("/")?t.slice(0,-1):t:e==="always"&&!t.endsWith("/")?t+"/":t}function Ve(t){return t.split("%25").map(decodeURI).join("%25")}function Ke(t){for(const e in t)t[e]=decodeURIComponent(t[e]);return t}function Tt({href:t}){return t.split("#")[0]}function Be(...t){let e=5381;for(const n of t)if(typeof n=="string"){let a=n.length;for(;a;)e=e*33^n.charCodeAt(--a)}else if(ArrayBuffer.isView(n)){const a=new Uint8Array(n.buffer,n.byteOffset,n.byteLength);let r=a.length;for(;r;)e=e*33^a[--r]}else throw new TypeError("value must be a string or TypedArray");return(e>>>0).toString(36)}new TextEncoder;new TextDecoder;function Me(t){const e=atob(t),n=new Uint8Array(e.length);for(let a=0;a((t instanceof Request?t.method:(e==null?void 0:e.method)||"GET")!=="GET"&&H.delete(Mt(t)),ze(t,e));const H=new Map;function Fe(t,e){const n=Mt(t,e),a=document.querySelector(n);if(a!=null&&a.textContent){a.remove();let{body:r,...i}=JSON.parse(a.textContent);const s=a.getAttribute("data-ttl");return s&&H.set(n,{body:r,init:i,ttl:1e3*Number(s)}),a.getAttribute("data-b64")!==null&&(r=Me(r)),Promise.resolve(new Response(r,i))}return window.fetch(t,e)}function Ge(t,e,n){if(H.size>0){const a=Mt(t,n),r=H.get(a);if(r){if(performance.now(){const r=/^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(a);if(r)return e.push({name:r[1],matcher:r[2],optional:!1,rest:!0,chained:!0}),"(?:/([^]*))?";const i=/^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(a);if(i)return e.push({name:i[1],matcher:i[2],optional:!0,rest:!1,chained:!0}),"(?:/([^/]+))?";if(!a)return;const s=a.split(/\[(.+?)\](?!\])/);return"/"+s.map((l,c)=>{if(c%2){if(l.startsWith("x+"))return It(String.fromCharCode(parseInt(l.slice(2),16)));if(l.startsWith("u+"))return It(String.fromCharCode(...l.slice(2).split("-").map(m=>parseInt(m,16))));const d=We.exec(l),[,u,w,p,f]=d;return e.push({name:p,matcher:f,optional:!!u,rest:!!w,chained:w?c===1&&s[0]==="":!1}),w?"([^]*?)":u?"([^/]*)?":"([^/]+?)"}return It(l)}).join("")}).join("")}/?$`),params:e}}function Ye(t){return t!==""&&!/^\([^)]+\)$/.test(t)}function He(t){return t.slice(1).split("/").filter(Ye)}function Xe(t,e,n){const a={},r=t.slice(1),i=r.filter(o=>o!==void 0);let s=0;for(let o=0;od).join("/"),s=0),c===void 0)if(l.rest)c="";else continue;if(!l.matcher||n[l.matcher](c)){a[l.name]=c;const d=e[o+1],u=r[o+1];d&&!d.rest&&d.optional&&u&&l.chained&&(s=0),!d&&!u&&Object.keys(a).length===i.length&&(s=0);continue}if(l.optional&&l.chained){s++;continue}return}if(!s)return a}function It(t){return t.normalize().replace(/[[\]]/g,"\\$&").replace(/%/g,"%25").replace(/\//g,"%2[Ff]").replace(/\?/g,"%3[Ff]").replace(/#/g,"%23").replace(/[.*+?^${}()|\\]/g,"\\$&")}function Qe({nodes:t,server_loads:e,dictionary:n,matchers:a}){const r=new Set(e);return Object.entries(n).map(([o,[l,c,d]])=>{const{pattern:u,params:w}=Je(o),p={id:o,exec:f=>{const m=u.exec(f);if(m)return Xe(m,w,a)},errors:[1,...d||[]].map(f=>t[f]),layouts:[0,...c||[]].map(s),leaf:i(l)};return p.errors.length=p.layouts.length=Math.max(p.errors.length,p.layouts.length),p});function i(o){const l=o<0;return l&&(o=~o),[l,t[o]]}function s(o){return o===void 0?o:[r.has(o),t[o]]}}function ge(t,e=JSON.parse){try{return e(sessionStorage[t])}catch{}}function ne(t,e,n=JSON.stringify){const a=n(e);try{sessionStorage[t]=a}catch{}}var fe;const A=((fe=globalThis.__sveltekit_1q3elkp)==null?void 0:fe.base)??"";var ue;const Ze=((ue=globalThis.__sveltekit_1q3elkp)==null?void 0:ue.assets)??A??"",tn="1789462193110",me="sveltekit:snapshot",_e="sveltekit:scroll",we="sveltekit:states",en="sveltekit:pageurl",z="sveltekit:history",Q="sveltekit:navigation",q={tap:1,hover:2,viewport:3,eager:4,off:-1,false:-1},zt=location.origin;function ve(t){if(t instanceof URL)return t;let e=document.baseURI;if(!e){const n=document.getElementsByTagName("base");e=n.length?n[0].href:document.URL}return new URL(t,e)}function V(){return{x:pageXOffset,y:pageYOffset}}function M(t,e){return t.getAttribute(`data-sveltekit-${e}`)}const re={...q,"":q.hover};function ye(t){let e=t.assignedSlot??t.parentNode;return(e==null?void 0:e.nodeType)===11&&(e=e.host),e}function be(t,e){for(;t&&t!==e;){if(t.nodeName.toUpperCase()==="A"&&t.hasAttribute("href"))return t;t=ye(t)}}function $t(t,e,n){let a;try{if(a=new URL(t instanceof SVGAElement?t.href.baseVal:t.href,document.baseURI),n&&a.hash.match(/^#[^/]/)){const o=location.hash.split("#")[1]||"/";a.hash=`#${o}${a.hash}`}}catch{}const r=t instanceof SVGAElement?t.target.baseVal:t.target,i=!a||!!r||Rt(a,e,n)||(t.getAttribute("rel")||"").split(/\s+/).includes("external"),s=(a==null?void 0:a.origin)===zt&&t.hasAttribute("download");return{url:a,external:i,target:r,download:s}}function _t(t){let e=null,n=null,a=null,r=null,i=null,s=null,o=t;for(;o&&o!==document.documentElement;)a===null&&(a=M(o,"preload-code")),r===null&&(r=M(o,"preload-data")),e===null&&(e=M(o,"keepfocus")),n===null&&(n=M(o,"noscroll")),i===null&&(i=M(o,"reload")),s===null&&(s=M(o,"replacestate")),o=ye(o);function l(c){switch(c){case"":case"true":return!0;case"off":case"false":return!1;default:return}}return{preload_code:re[a??"off"],preload_data:re[r??"off"],keepfocus:l(e),noscroll:l(n),reload:l(i),replace_state:l(s)}}function ae(t){const e=Dt(t);let n=!0;function a(){n=!0,e.update(s=>s)}function r(s){n=!1,e.set(s)}function i(s){let o;return e.subscribe(l=>{(o===void 0||n&&l!==o)&&s(o=l)})}return{notify:a,set:r,subscribe:i}}const ke={v:()=>{}};function nn(){const{set:t,subscribe:e}=Dt(!1);let n;async function a(){clearTimeout(n);try{const r=await fetch(`${Ze}/_app/version.json`,{headers:{pragma:"no-cache","cache-control":"no-cache"}});if(!r.ok)return!1;const s=(await r.json()).version!==tn;return s&&(t(!0),ke.v(),clearTimeout(n)),s}catch{return!1}}return{subscribe:e,check:a}}function Rt(t,e,n){return t.origin!==zt||!t.pathname.startsWith(e)?!0:n?t.pathname!==location.pathname:!1}function On(t){}const Se=new Set(["load","prerender","csr","ssr","trailingSlash","config"]);[...Se];const rn=new Set([...Se]);[...rn];function an(t){return t.filter(e=>e!=null)}function Ft(t){return t instanceof Vt||t instanceof Bt?t.status:500}function on(t){return t instanceof Bt?t.text:"Internal Error"}let R,Z,Ot;const sn=ee.toString().includes("$$")||/function \w+\(\) \{\}/.test(ee.toString()),oe="a:";var at,ot,st,it,lt,ct,ft,ut,de,dt,he,ht,pe;sn?(R={data:{},form:null,error:null,params:{},route:{id:null},state:{},status:-1,url:new URL(oe)},Z={current:null},Ot={current:!1}):(R=new(de=class{constructor(){U(this,at,T({}));U(this,ot,T(null));U(this,st,T(null));U(this,it,T({}));U(this,lt,T({id:null}));U(this,ct,T({}));U(this,ft,T(-1));U(this,ut,T(new URL(oe)))}get data(){return I(v(this,at))}set data(e){O(v(this,at),e)}get form(){return I(v(this,ot))}set form(e){O(v(this,ot),e)}get error(){return I(v(this,st))}set error(e){O(v(this,st),e)}get params(){return I(v(this,it))}set params(e){O(v(this,it),e)}get route(){return I(v(this,lt))}set route(e){O(v(this,lt),e)}get state(){return I(v(this,ct))}set state(e){O(v(this,ct),e)}get status(){return I(v(this,ft))}set status(e){O(v(this,ft),e)}get url(){return I(v(this,ut))}set url(e){O(v(this,ut),e)}},at=new WeakMap,ot=new WeakMap,st=new WeakMap,it=new WeakMap,lt=new WeakMap,ct=new WeakMap,ft=new WeakMap,ut=new WeakMap,de),Z=new(he=class{constructor(){U(this,dt,T(null))}get current(){return I(v(this,dt))}set current(e){O(v(this,dt),e)}},dt=new WeakMap,he),Ot=new(pe=class{constructor(){U(this,ht,T(!1))}get current(){return I(v(this,ht))}set current(e){O(v(this,ht),e)}},ht=new WeakMap,pe),ke.v=()=>Ot.current=!0);function ln(t){Object.assign(R,t)}const cn=new Set(["icon","shortcut icon","apple-touch-icon"]);let J=null;const j=ge(_e)??{},tt=ge(me)??{},C={url:ae({}),page:ae({}),navigating:Dt(null),updated:nn()};function Gt(t){j[t]=V()}function fn(t,e){let n=t+1;for(;j[n];)delete j[n],n+=1;for(n=e+1;tt[n];)delete tt[n],n+=1}function et(t,e=!1){return e?location.replace(t.href):location.href=t.href,new Promise(()=>{})}async function Ee(){if("serviceWorker"in navigator){const t=await navigator.serviceWorker.getRegistration(A||"/");t&&await t.update()}}function se(){}let Wt,Ct,wt,P,jt,S;const vt=[],yt=[];let y=null;function Nt(){var t;(t=y==null?void 0:y.fork)==null||t.then(e=>e==null?void 0:e.discard()),y=null}const gt=new Map,Re=new Set,un=new Set,X=new Set;let _={branch:[],error:null,url:null},xe=!1,bt=!1,ie=!0,nt=!1,Y=!1,Le=!1,Jt=!1,Ae,k,L,D;const kt=new Set,le=new Map;async function jn(t,e,n){var i,s,o,l,c;(i=globalThis.__sveltekit_1q3elkp)!=null&&i.data&&globalThis.__sveltekit_1q3elkp.data,document.URL!==location.href&&(location.href=location.href),S=t,await((o=(s=t.hooks).init)==null?void 0:o.call(s)),Wt=Qe(t),P=document.documentElement,jt=e,Ct=t.nodes[0],wt=t.nodes[1],Ct(),wt(),k=(l=history.state)==null?void 0:l[z],L=(c=history.state)==null?void 0:c[Q],k||(k=L=Date.now(),history.replaceState({...history.state,[z]:k,[Q]:L},""));const a=j[k];function r(){a&&(history.scrollRestoration="manual",scrollTo(a.x,a.y))}n?(r(),await Rn(jt,n)):(await F({type:"enter",url:ve(S.hash?An(new URL(location.href)):location.href),replace_state:!0}),r()),En()}function dn(){vt.length=0,Jt=!1}function Ue(t){yt.some(e=>e==null?void 0:e.snapshot)&&(tt[t]=yt.map(e=>{var n;return(n=e==null?void 0:e.snapshot)==null?void 0:n.capture()}))}function Te(t){var e;(e=tt[t])==null||e.forEach((n,a)=>{var r,i;(i=(r=yt[a])==null?void 0:r.snapshot)==null||i.restore(n)})}function ce(){Gt(k),ne(_e,j),Ue(L),ne(me,tt)}async function hn(t,e,n,a){let r;e.invalidateAll&&Nt(),await F({type:"goto",url:ve(t),keepfocus:e.keepFocus,noscroll:e.noScroll,replace_state:e.replaceState,state:e.state,redirect_count:n,nav_token:a,accept:()=>{e.invalidateAll&&(Jt=!0,r=[...le.keys()]),e.invalidate&&e.invalidate.forEach(Sn)}}),e.invalidateAll&&mt().then(mt).then(()=>{le.forEach(({resource:i},s)=>{var o;r!=null&&r.includes(s)&&((o=i.refresh)==null||o.call(i))})})}async function pn(t){if(t.id!==(y==null?void 0:y.id)){Nt();const e={};kt.add(e),y={id:t.id,token:e,promise:Oe({...t,preload:e}).then(n=>(kt.delete(e),n.type==="loaded"&&n.state.error&&Nt(),n)),fork:null}}return y.promise}async function Pt(t){var n;const e=(n=await xt(t,!1))==null?void 0:n.route;e&&await Promise.all([...e.layouts,e.leaf].filter(Boolean).map(a=>a[1]()))}async function Ie(t,e,n){var i;const a={params:_.params,route:{id:((i=_.route)==null?void 0:i.id)??null},url:new URL(location.href)};_={...t.state,nav:a};const r=document.querySelector("style[data-sveltekit]");if(r&&r.remove(),Object.assign(R,t.props.page),Ae=new S.root({target:e,props:{...t.props,stores:C,components:yt},hydrate:n,sync:!1,transformError:void 0}),await Promise.resolve(),Te(L),n){const s={from:null,to:{...a,scroll:j[k]??V()},willUnload:!1,type:"enter",complete:Promise.resolve()};X.forEach(o=>o(s))}bt=!0}async function St({url:t,params:e,branch:n,errors:a,status:r,error:i,route:s,form:o}){let l="never";if(A&&(t.pathname===A||t.pathname===A+"/"))l="always";else for(const f of n)(f==null?void 0:f.slash)!==void 0&&(l=f.slash);t.pathname=De(t.pathname,l),t.search=t.search;const c={type:"loaded",state:{url:t,params:e,branch:n,error:i,route:s},props:{constructors:an(n).map(f=>f.node.component),page:Zt(R)}};o!==void 0&&(c.props.form=o);let d={},u=!R,w=0;for(let f=0;fo(new URL(s))))return!0;return!1}function Ht(t,e){return(t==null?void 0:t.type)==="data"?t:(t==null?void 0:t.type)==="skip"?e??null:null}function _n(t,e){if(!t)return new Set(e.searchParams.keys());const n=new Set([...t.searchParams.keys(),...e.searchParams.keys()]);for(const a of n){const r=t.searchParams.getAll(a),i=e.searchParams.getAll(a);r.every(s=>i.includes(s))&&i.every(s=>r.includes(s))&&n.delete(a)}return n}function wn({error:t,url:e,route:n,params:a}){return{type:"loaded",state:{error:t,url:e,route:n,params:a,branch:[]},props:{page:Zt(R),constructors:[]}}}async function Oe({id:t,invalidating:e,url:n,params:a,route:r,preload:i}){if((y==null?void 0:y.id)===t)return kt.delete(y.token),y.promise;const{errors:s,layouts:o,leaf:l}=r,c=[...o,l];s.forEach(h=>h==null?void 0:h().catch(()=>{})),c.forEach(h=>h==null?void 0:h[1]().catch(()=>{}));const d=_.url?t!==Et(_.url):!1,u=_.route?r.id!==_.route.id:!1,w=_n(_.url,n);let p=!1;const f=c.map(async(h,g)=>{var $;if(!h)return;const b=_.branch[g];return h[1]===(b==null?void 0:b.loader)&&!mn(p,u,d,w,($=b.universal)==null?void 0:$.uses,a)?b:(p=!0,Yt({loader:h[1],url:n,params:a,route:r,parent:async()=>{var pt;const N={};for(let K=0;K{});const m=[];for(let h=0;hPromise.resolve({}),server_data_node:Ht(i)}),o={node:await wt(),loader:wt,universal:null,server:null,data:null};return St({url:n,params:r,branch:[s,o],status:t,error:e,errors:[],route:null})}catch(s){if(s instanceof Kt)return hn(new URL(s.location,location.href),{},0);throw s}}async function yn(t){const e=t.href;if(gt.has(e))return gt.get(e);let n;try{const a=(async()=>{let r=await S.hooks.reroute({url:new URL(t),fetch:async(i,s)=>gn(i,s,t).promise})??t;if(typeof r=="string"){const i=new URL(t);S.hash?i.hash=r:i.pathname=r,r=i}return r})();gt.set(e,a),n=await a}catch{gt.delete(e);return}return n}async function xt(t,e){if(t&&!Rt(t,A,S.hash)){const n=await yn(t);if(!n)return;const a=bn(n);for(const r of Wt){const i=r.exec(a);if(i)return{id:Et(t),invalidating:e,route:r,params:Ke(i),url:t}}}}function bn(t){return Ve(S.hash?t.hash.replace(/^#/,"").replace(/[?#].+/,""):t.pathname.slice(A.length))||"/"}function Et(t){return(S.hash?t.hash.replace(/^#/,""):t.pathname)+t.search}function Pe({url:t,type:e,intent:n,delta:a,event:r,scroll:i}){let s=!1;const o=Qt(_,n,t,e,i??null);a!==void 0&&(o.navigation.delta=a),r!==void 0&&(o.navigation.event=r);const l={...o.navigation,cancel:()=>{s=!0,o.reject(new Error("navigation cancelled"))}};return nt||Re.forEach(c=>c(l)),s?null:o}async function F({type:t,url:e,popped:n,keepfocus:a,noscroll:r,replace_state:i,state:s={},redirect_count:o=0,nav_token:l={},accept:c=se,block:d=se,event:u}){var K;const w=D;D=l;const p=await xt(e,!1),f=t==="enter"?Qt(_,p,e,t):Pe({url:e,type:t,delta:n==null?void 0:n.delta,intent:p,scroll:n==null?void 0:n.scroll,event:u});if(!f){d(),D===l&&(D=w);return}const m=k,h=L;c(),nt=!0,bt&&f.navigation.type!=="enter"&&C.navigating.set(Z.current=f.navigation);let g=p&&await Oe(p);if(!g){if(Rt(e,A,S.hash))return await et(e,i);g=await $e(e,{id:null},await rt(new Bt(404,"Not Found",`Not found: ${e.pathname}`),{url:e,params:{},route:{id:null}}),404,i)}if(e=(p==null?void 0:p.url)||e,D!==l)return f.reject(new Error("navigation aborted")),!1;if(g.type==="redirect"){if(o<20){await F({type:t,url:new URL(g.location,e),popped:n,keepfocus:a,noscroll:r,replace_state:i,state:s,redirect_count:o+1,nav_token:l}),f.fulfil(void 0);return}g=await Xt({status:500,error:await rt(new Error("Redirect loop"),{url:e,params:{},route:{id:null}}),url:e,route:{id:null}})}else g.props.page.status>=400&&await C.updated.check()&&(await Ee(),await et(e,i));if(dn(),Gt(m),Ue(h),g.props.page.url.pathname!==e.pathname&&(e.pathname=g.props.page.url.pathname),s=n?n.state:s,!n){const E=i?0:1,G={[z]:k+=E,[Q]:L+=E,[we]:s};(i?history.replaceState:history.pushState).call(history,G,"",e),i||fn(k,L)}const b=p&&(y==null?void 0:y.id)===p.id?y.fork:null;y=null,g.props.page.state=s;let x;if(bt){const E=(await Promise.all(Array.from(un,W=>W(f.navigation)))).filter(W=>typeof W=="function");if(E.length>0){let W=function(){E.forEach(At=>{X.delete(At)})};E.push(W),E.forEach(At=>{X.add(At)})}const G=f.navigation.to;_={...g.state,nav:{params:G.params,route:G.route,url:G.url}},g.props.page&&(g.props.page.url=e);const Lt=b&&await b;Lt?x=Lt.commit():(J=null,Ae.$set(g.props),J&&Object.assign(g.props.page,J),ln(g.props.page),x=(K=qe)==null?void 0:K()),Le=!0}else await Ie(g,jt,!1);const{activeElement:$}=document;await x,await mt(),await mt();let N=null;if(ie){const E=n?n.scroll:r?V():null;E?scrollTo(E.x,E.y):(N=e.hash&&document.getElementById(Ce(e)))?N.scrollIntoView():scrollTo(0,0)}const pt=document.activeElement!==$&&document.activeElement!==document.body;!a&&!pt&&Ln(e,!N),ie=!0,g.props.page&&(J&&Object.assign(g.props.page,J),Object.assign(R,g.props.page)),nt=!1,t==="popstate"&&Te(L),f.fulfil(void 0),f.navigation.to&&(f.navigation.to.scroll=V()),X.forEach(E=>E(f.navigation)),C.navigating.set(Z.current=null)}async function $e(t,e,n,a,r){return t.origin===zt&&t.pathname===location.pathname&&!xe?await Xt({status:a,error:n,url:t,route:e}):await et(t,r)}function kn(){let t,e={element:void 0,href:void 0},n;P.addEventListener("mousemove",o=>{const l=o.target;clearTimeout(t),t=setTimeout(()=>{i(l,q.hover)},20)});function a(o){o.defaultPrevented||i(o.composedPath()[0],q.tap)}P.addEventListener("mousedown",a),P.addEventListener("touchstart",a,{passive:!0});const r=new IntersectionObserver(o=>{for(const l of o)l.isIntersecting&&(Pt(new URL(l.target.href)),r.unobserve(l.target))},{threshold:0});async function i(o,l){const c=be(o,P),d=c===e.element&&(c==null?void 0:c.href)===e.href&&l>=n;if(!c||d)return;const{url:u,external:w,download:p}=$t(c,A,S.hash);if(w||p)return;const f=_t(c),m=u&&Et(_.url)===Et(u);if(!(f.reload||m))if(l<=f.preload_data){e={element:c,href:c.href},n=q.tap;const h=await xt(u,!1);if(!h)return;pn(h)}else l<=f.preload_code&&(e={element:c,href:c.href},n=l,Pt(u))}function s(){r.disconnect();for(const o of P.querySelectorAll("a")){const{url:l,external:c,download:d}=$t(o,A,S.hash);if(c||d)continue;const u=_t(o);u.reload||(u.preload_code===q.viewport&&r.observe(o),u.preload_code===q.eager&&Pt(l))}}X.add(s),s()}function rt(t,e){if(t instanceof Vt)return t.body;const n=Ft(t),a=on(t);return S.hooks.handleError({error:t,event:e,status:n,message:a})??{message:a}}function Sn(t){if(typeof t=="function")vt.push(t);else{const{href:e}=new URL(t,location.href);vt.push(n=>n.href===e)}}function En(){var e;history.scrollRestoration="manual",addEventListener("beforeunload",n=>{let a=!1;if(ce(),!nt){const r=Qt(_,void 0,null,"leave"),i={...r.navigation,cancel:()=>{a=!0,r.reject(new Error("navigation cancelled"))}};Re.forEach(s=>s(i))}a?(n.preventDefault(),n.returnValue=""):history.scrollRestoration="auto"}),addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&ce()}),(e=navigator.connection)!=null&&e.saveData||kn(),P.addEventListener("click",async n=>{if(n.button||n.which!==1||n.metaKey||n.ctrlKey||n.shiftKey||n.altKey||n.defaultPrevented)return;const a=be(n.composedPath()[0],P);if(!a)return;const{url:r,external:i,target:s,download:o}=$t(a,A,S.hash);if(!r)return;if(s==="_parent"||s==="_top"){if(window.parent!==window)return}else if(s&&s!=="_self")return;const l=_t(a);if(!(a instanceof SVGAElement)&&r.protocol!==location.protocol&&!(r.protocol==="https:"||r.protocol==="http:")||o)return;const[d,u]=(S.hash?r.hash.replace(/^#/,""):r.href).split("#"),w=d===Tt(location);if(i||l.reload&&(!w||!u)){Pe({url:r,type:"link",event:n})?nt=!0:n.preventDefault();return}if(u!==void 0&&w){const[,p]=_.url.href.split("#");if(p===u){if(n.preventDefault(),u===""||u==="top"&&a.ownerDocument.getElementById("top")===null)scrollTo({top:0});else{const f=a.ownerDocument.getElementById(decodeURIComponent(u));f&&(f.scrollIntoView(),f.focus())}return}if(Y=!0,Gt(k),t(r),!l.replace_state)return;Y=!1}n.preventDefault(),await new Promise(p=>{requestAnimationFrame(()=>{setTimeout(p,0)}),setTimeout(p,100)}),await F({type:"link",url:r,keepfocus:l.keepfocus,noscroll:l.noscroll,replace_state:l.replace_state??r.href===location.href,event:n})}),P.addEventListener("submit",n=>{if(n.defaultPrevented)return;const a=HTMLFormElement.prototype.cloneNode.call(n.target),r=n.submitter;if(((r==null?void 0:r.formTarget)||a.target)==="_blank"||((r==null?void 0:r.formMethod)||a.method)!=="get")return;const o=new URL((r==null?void 0:r.hasAttribute("formaction"))&&(r==null?void 0:r.formAction)||a.action);if(Rt(o,A,!1))return;const l=n.target,c=_t(l);if(c.reload)return;n.preventDefault(),n.stopPropagation();const d=new FormData(l,r);o.search=new URLSearchParams(d).toString(),F({type:"form",url:o,keepfocus:c.keepfocus,noscroll:c.noscroll,replace_state:c.replace_state??o.href===location.href,event:n})}),addEventListener("popstate",async n=>{var a;if(!qt){if((a=n.state)!=null&&a[z]){const r=n.state[z];if(D={},r===k)return;const i=j[r],s=n.state[we]??{},o=new URL(n.state[en]??location.href),l=n.state[Q],c=_.url?Tt(location)===Tt(_.url):!1;if(l===L&&(Le||c)){s!==R.state&&(R.state=s),t(o),j[k]=V(),i&&scrollTo(i.x,i.y),k=r;return}const u=r-k;await F({type:"popstate",url:o,popped:{state:s,scroll:i,delta:u},accept:()=>{k=r,L=l},block:()=>{history.go(-u)},nav_token:D,event:n})}else if(!Y){const r=new URL(location.href);t(r),S.hash&&location.reload()}}}),addEventListener("hashchange",()=>{Y&&(Y=!1,history.replaceState({...history.state,[z]:++k,[Q]:L},"",location.href))});for(const n of document.querySelectorAll("link"))cn.has(n.rel)&&(n.href=n.href);addEventListener("pageshow",n=>{n.persisted&&C.navigating.set(Z.current=null)});function t(n){_.url=R.url=n,C.page.set(Zt(R)),C.page.notify()}}async function Rn(t,{status:e=200,error:n,node_ids:a,params:r,route:i,server_route:s,data:o,form:l}){xe=!0;const c=new URL(location.href);let d;({params:r={},route:i={id:null}}=await xt(c,!1)||{}),d=Wt.find(({id:p})=>p===i.id);let u,w=!0;try{const p=a.map(async(m,h)=>{const g=o[h];return g!=null&&g.uses&&(g.uses=xn(g.uses)),Yt({loader:S.nodes[m],url:c,params:r,route:i,parent:async()=>{const b={};for(let x=0;x{const o=history.state;qt=!0,location.replace(new URL(`#${a}`,location.href)),history.replaceState(o,"",t),e&&scrollTo(i,s),qt=!1})}else{const i=document.body,s=i.getAttribute("tabindex");i.tabIndex=-1,i.focus({preventScroll:!0,focusVisible:!1}),s!==null?i.setAttribute("tabindex",s):i.removeAttribute("tabindex")}const r=getSelection();if(r&&r.type!=="None"){const i=[];for(let s=0;s{if(r.rangeCount===i.length){for(let s=0;s{i=u,s=w});return o.catch(()=>{}),{navigation:{from:{params:t.params,route:{id:((c=t.route)==null?void 0:c.id)??null},url:t.url,scroll:V()},to:n&&{params:(e==null?void 0:e.params)??null,route:{id:((d=e==null?void 0:e.route)==null?void 0:d.id)??null},url:n,scroll:r},willUnload:!e,type:a,complete:o},fulfil:i,reject:s}}function Zt(t){return{data:t.data,error:t.error,form:t.form,params:t.params,route:t.route,state:t.state,status:t.status,url:t.url}}function An(t){const e=new URL(t);return e.hash=decodeURIComponent(t.hash),e}function Ce(t){let e;if(S.hash){const[,,n]=t.hash.split("#",3);e=n??""}else e=t.hash.slice(1);return decodeURIComponent(e)}export{jn as a,On as l,R as p,C as s}; diff --git a/frontend/dist/_app/immutable/entry/app.CBC_xGKp.js b/frontend/dist/_app/immutable/entry/app.BiXph2fc.js similarity index 94% rename from frontend/dist/_app/immutable/entry/app.CBC_xGKp.js rename to frontend/dist/_app/immutable/entry/app.BiXph2fc.js index 7788d9b..c46a97f 100644 --- a/frontend/dist/_app/immutable/entry/app.CBC_xGKp.js +++ b/frontend/dist/_app/immutable/entry/app.BiXph2fc.js @@ -1,2 +1,2 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["../nodes/0.DecrOZwe.js","../chunks/C14P8jaG.js","../chunks/VJhVTb1A.js","../chunks/CiUbu-1F.js","../chunks/U5yjBaoU.js","../nodes/1.D7xrQvTV.js","../chunks/DmjnTBOd.js","../chunks/BeON-0X8.js","../chunks/DTWAhvnO.js","../chunks/Cz6m6K8p.js","../nodes/2.BGWNYlPN.js","../chunks/zf4GFcxs.js","../assets/2.BvWkSkaN.css"])))=>i.map(i=>d[i]); -var H=t=>{throw TypeError(t)};var J=(t,e,s)=>e.has(t)||H("Cannot "+s);var l=(t,e,s)=>(J(t,e,"read from private field"),s?s.call(t):e.get(t)),S=(t,e,s)=>e.has(t)?H("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,s),C=(t,e,s,o)=>(J(t,e,"write to private field"),o?o.call(t,s):e.set(t,s),s);import{$ as L,ab as X,o as g,aH as Z,au as M,aI as N,x as $,u as ee,j as te,aJ as re,g as j,C as se,z as ae,aK as A,A as ne,B as oe,aL as B,y as ce}from"../chunks/VJhVTb1A.js";import{h as ie,m as le,u as ue,s as fe}from"../chunks/BeON-0X8.js";import{a as w,c as I,f as W,t as me}from"../chunks/C14P8jaG.js";import{o as de}from"../chunks/Cz6m6K8p.js";import{p as T,i as D,c as V,b as q}from"../chunks/zf4GFcxs.js";function he(t){return class extends _e{constructor(e){super({component:t,...e})}}}var y,m;class _e{constructor(e){S(this,y);S(this,m);var _;var s=new Map,o=(a,r)=>{var c=N(r,!1,!1);return s.set(a,c),c};const u=new Proxy({...e.props||{},$$events:{}},{get(a,r){return g(s.get(r)??o(r,Reflect.get(a,r)))},has(a,r){return r===X?!0:(g(s.get(r)??o(r,Reflect.get(a,r))),Reflect.has(a,r))},set(a,r,c){return L(s.get(r)??o(r,c),c),Reflect.set(a,r,c)}});C(this,m,(e.hydrate?ie:le)(e.component,{target:e.target,anchor:e.anchor,props:u,context:e.context,intro:e.intro??!1,recover:e.recover,transformError:e.transformError})),(!((_=e==null?void 0:e.props)!=null&&_.$$host)||e.sync===!1)&&Z(),C(this,y,u.$$events);for(const a of Object.keys(l(this,m)))a==="$set"||a==="$destroy"||a==="$on"||M(this,a,{get(){return l(this,m)[a]},set(r){l(this,m)[a]=r},enumerable:!0});l(this,m).$set=a=>{Object.assign(u,a)},l(this,m).$destroy=()=>{ue(l(this,m))}}$set(e){l(this,m).$set(e)}$on(e,s){l(this,y)[e]=l(this,y)[e]||[];const o=(...u)=>s.call(this,...u);return l(this,y)[e].push(o),()=>{l(this,y)[e]=l(this,y)[e].filter(u=>u!==o)}}$destroy(){l(this,m).$destroy()}}y=new WeakMap,m=new WeakMap;const ve="modulepreload",ge=function(t,e){return new URL(t,e).href},K={},U=function(e,s,o){let u=Promise.resolve();if(s&&s.length>0){let a=function(i){return Promise.all(i.map(v=>Promise.resolve(v).then(b=>({status:"fulfilled",value:b}),b=>({status:"rejected",reason:b}))))};const r=document.getElementsByTagName("link"),c=document.querySelector("meta[property=csp-nonce]"),k=(c==null?void 0:c.nonce)||(c==null?void 0:c.getAttribute("nonce"));u=a(s.map(i=>{if(i=ge(i,o),i in K)return;K[i]=!0;const v=i.endsWith(".css"),b=v?'[rel="stylesheet"]':"";if(!!o)for(let n=r.length-1;n>=0;n--){const f=r[n];if(f.href===i&&(!v||f.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${i}"]${b}`))return;const h=document.createElement("link");if(h.rel=v?"stylesheet":ve,v||(h.as="script"),h.crossOrigin="",h.href=i,k&&h.setAttribute("nonce",k),document.head.appendChild(h),v)return new Promise((n,f)=>{h.addEventListener("load",n),h.addEventListener("error",()=>f(new Error(`Unable to preload CSS for ${i}`)))})}))}function _(a){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=a,window.dispatchEvent(r),!r.defaultPrevented)throw a}return u.then(a=>{for(const r of a||[])r.status==="rejected"&&_(r.reason);return e().catch(_)})},Se={};var ye=W('
'),be=W(" ",1);function Ee(t,e){$(e,!0);let s=T(e,"components",23,()=>[]),o=T(e,"data_0",3,null),u=T(e,"data_1",3,null);ee(()=>e.stores.page.set(e.page)),te(()=>{e.stores,e.page,e.constructors,s(),e.form,o(),u(),e.stores.page.notify()});let _=A(!1),a=A(!1),r=A(null);de(()=>{const n=e.stores.page.subscribe(()=>{g(_)&&(L(a,!0),re().then(()=>{L(r,document.title||"untitled page",!0)}))});return L(_,!0),n});const c=B(()=>e.constructors[1]);var k=be(),i=j(k);{var v=n=>{const f=B(()=>e.constructors[0]);var E=I(),x=j(E);V(x,()=>g(f),(P,R)=>{q(R(P,{get data(){return o()},get form(){return e.form},get params(){return e.page.params},children:(d,Re)=>{var G=I(),p=j(G);V(p,()=>g(c),(F,Q)=>{q(Q(F,{get data(){return u()},get form(){return e.form},get params(){return e.page.params}}),O=>s()[1]=O,()=>{var O;return(O=s())==null?void 0:O[1]})}),w(d,G)},$$slots:{default:!0}}),d=>s()[0]=d,()=>{var d;return(d=s())==null?void 0:d[0]})}),w(n,E)},b=n=>{const f=B(()=>e.constructors[0]);var E=I(),x=j(E);V(x,()=>g(f),(P,R)=>{q(R(P,{get data(){return o()},get form(){return e.form},get params(){return e.page.params}}),d=>s()[0]=d,()=>{var d;return(d=s())==null?void 0:d[0]})}),w(n,E)};D(i,n=>{e.constructors[1]?n(v):n(b,-1)})}var z=se(i,2);{var h=n=>{var f=ye(),E=ne(f);{var x=P=>{var R=me();ce(()=>fe(R,g(r))),w(P,R)};D(E,P=>{g(a)&&P(x)})}oe(f),w(n,f)};D(z,n=>{g(_)&&n(h)})}w(t,k),ae()}const Ce=he(Ee),Ae=[()=>U(()=>import("../nodes/0.DecrOZwe.js"),__vite__mapDeps([0,1,2,3,4]),import.meta.url),()=>U(()=>import("../nodes/1.D7xrQvTV.js"),__vite__mapDeps([5,1,2,6,7,8,9]),import.meta.url),()=>U(()=>import("../nodes/2.BGWNYlPN.js"),__vite__mapDeps([10,1,2,9,11,4,7,3,6,12]),import.meta.url)],Be=[],Ie={"/":[2]},Y={handleError:(({error:t})=>{console.error(t)}),reroute:(()=>{}),transport:{}},Pe=Object.fromEntries(Object.entries(Y.transport).map(([t,e])=>[t,e.decode])),Te=Object.fromEntries(Object.entries(Y.transport).map(([t,e])=>[t,e.encode])),De=!1,Ve=(t,e)=>Pe[t](e);export{Ve as decode,Pe as decoders,Ie as dictionary,Te as encoders,De as hash,Y as hooks,Se as matchers,Ae as nodes,Ce as root,Be as server_loads}; +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["../nodes/0.DecrOZwe.js","../chunks/C14P8jaG.js","../chunks/VJhVTb1A.js","../chunks/CiUbu-1F.js","../chunks/U5yjBaoU.js","../nodes/1.CCoIP03N.js","../chunks/DmjnTBOd.js","../chunks/BeON-0X8.js","../chunks/5xxJZag6.js","../chunks/Cz6m6K8p.js","../nodes/2.Db1dJWID.js","../chunks/zf4GFcxs.js","../assets/2.BvWkSkaN.css"])))=>i.map(i=>d[i]); +var H=t=>{throw TypeError(t)};var J=(t,e,s)=>e.has(t)||H("Cannot "+s);var l=(t,e,s)=>(J(t,e,"read from private field"),s?s.call(t):e.get(t)),S=(t,e,s)=>e.has(t)?H("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,s),C=(t,e,s,o)=>(J(t,e,"write to private field"),o?o.call(t,s):e.set(t,s),s);import{$ as L,ab as X,o as g,aH as Z,au as M,aI as N,x as $,u as ee,j as te,aJ as re,g as j,C as se,z as ae,aK as A,A as ne,B as oe,aL as B,y as ce}from"../chunks/VJhVTb1A.js";import{h as ie,m as le,u as ue,s as fe}from"../chunks/BeON-0X8.js";import{a as w,c as I,f as W,t as me}from"../chunks/C14P8jaG.js";import{o as de}from"../chunks/Cz6m6K8p.js";import{p as T,i as D,c as V,b as q}from"../chunks/zf4GFcxs.js";function he(t){return class extends _e{constructor(e){super({component:t,...e})}}}var y,m;class _e{constructor(e){S(this,y);S(this,m);var _;var s=new Map,o=(a,r)=>{var c=N(r,!1,!1);return s.set(a,c),c};const u=new Proxy({...e.props||{},$$events:{}},{get(a,r){return g(s.get(r)??o(r,Reflect.get(a,r)))},has(a,r){return r===X?!0:(g(s.get(r)??o(r,Reflect.get(a,r))),Reflect.has(a,r))},set(a,r,c){return L(s.get(r)??o(r,c),c),Reflect.set(a,r,c)}});C(this,m,(e.hydrate?ie:le)(e.component,{target:e.target,anchor:e.anchor,props:u,context:e.context,intro:e.intro??!1,recover:e.recover,transformError:e.transformError})),(!((_=e==null?void 0:e.props)!=null&&_.$$host)||e.sync===!1)&&Z(),C(this,y,u.$$events);for(const a of Object.keys(l(this,m)))a==="$set"||a==="$destroy"||a==="$on"||M(this,a,{get(){return l(this,m)[a]},set(r){l(this,m)[a]=r},enumerable:!0});l(this,m).$set=a=>{Object.assign(u,a)},l(this,m).$destroy=()=>{ue(l(this,m))}}$set(e){l(this,m).$set(e)}$on(e,s){l(this,y)[e]=l(this,y)[e]||[];const o=(...u)=>s.call(this,...u);return l(this,y)[e].push(o),()=>{l(this,y)[e]=l(this,y)[e].filter(u=>u!==o)}}$destroy(){l(this,m).$destroy()}}y=new WeakMap,m=new WeakMap;const ve="modulepreload",ge=function(t,e){return new URL(t,e).href},K={},U=function(e,s,o){let u=Promise.resolve();if(s&&s.length>0){let a=function(i){return Promise.all(i.map(v=>Promise.resolve(v).then(b=>({status:"fulfilled",value:b}),b=>({status:"rejected",reason:b}))))};const r=document.getElementsByTagName("link"),c=document.querySelector("meta[property=csp-nonce]"),k=(c==null?void 0:c.nonce)||(c==null?void 0:c.getAttribute("nonce"));u=a(s.map(i=>{if(i=ge(i,o),i in K)return;K[i]=!0;const v=i.endsWith(".css"),b=v?'[rel="stylesheet"]':"";if(!!o)for(let n=r.length-1;n>=0;n--){const f=r[n];if(f.href===i&&(!v||f.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${i}"]${b}`))return;const h=document.createElement("link");if(h.rel=v?"stylesheet":ve,v||(h.as="script"),h.crossOrigin="",h.href=i,k&&h.setAttribute("nonce",k),document.head.appendChild(h),v)return new Promise((n,f)=>{h.addEventListener("load",n),h.addEventListener("error",()=>f(new Error(`Unable to preload CSS for ${i}`)))})}))}function _(a){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=a,window.dispatchEvent(r),!r.defaultPrevented)throw a}return u.then(a=>{for(const r of a||[])r.status==="rejected"&&_(r.reason);return e().catch(_)})},Se={};var ye=W('
'),be=W(" ",1);function Ee(t,e){$(e,!0);let s=T(e,"components",23,()=>[]),o=T(e,"data_0",3,null),u=T(e,"data_1",3,null);ee(()=>e.stores.page.set(e.page)),te(()=>{e.stores,e.page,e.constructors,s(),e.form,o(),u(),e.stores.page.notify()});let _=A(!1),a=A(!1),r=A(null);de(()=>{const n=e.stores.page.subscribe(()=>{g(_)&&(L(a,!0),re().then(()=>{L(r,document.title||"untitled page",!0)}))});return L(_,!0),n});const c=B(()=>e.constructors[1]);var k=be(),i=j(k);{var v=n=>{const f=B(()=>e.constructors[0]);var E=I(),x=j(E);V(x,()=>g(f),(P,R)=>{q(R(P,{get data(){return o()},get form(){return e.form},get params(){return e.page.params},children:(d,Re)=>{var G=I(),p=j(G);V(p,()=>g(c),(F,Q)=>{q(Q(F,{get data(){return u()},get form(){return e.form},get params(){return e.page.params}}),O=>s()[1]=O,()=>{var O;return(O=s())==null?void 0:O[1]})}),w(d,G)},$$slots:{default:!0}}),d=>s()[0]=d,()=>{var d;return(d=s())==null?void 0:d[0]})}),w(n,E)},b=n=>{const f=B(()=>e.constructors[0]);var E=I(),x=j(E);V(x,()=>g(f),(P,R)=>{q(R(P,{get data(){return o()},get form(){return e.form},get params(){return e.page.params}}),d=>s()[0]=d,()=>{var d;return(d=s())==null?void 0:d[0]})}),w(n,E)};D(i,n=>{e.constructors[1]?n(v):n(b,-1)})}var z=se(i,2);{var h=n=>{var f=ye(),E=ne(f);{var x=P=>{var R=me();ce(()=>fe(R,g(r))),w(P,R)};D(E,P=>{g(a)&&P(x)})}oe(f),w(n,f)};D(z,n=>{g(_)&&n(h)})}w(t,k),ae()}const Ce=he(Ee),Ae=[()=>U(()=>import("../nodes/0.DecrOZwe.js"),__vite__mapDeps([0,1,2,3,4]),import.meta.url),()=>U(()=>import("../nodes/1.CCoIP03N.js"),__vite__mapDeps([5,1,2,6,7,8,9]),import.meta.url),()=>U(()=>import("../nodes/2.Db1dJWID.js"),__vite__mapDeps([10,1,2,9,11,4,7,3,6,12]),import.meta.url)],Be=[],Ie={"/":[2]},Y={handleError:(({error:t})=>{console.error(t)}),reroute:(()=>{}),transport:{}},Pe=Object.fromEntries(Object.entries(Y.transport).map(([t,e])=>[t,e.decode])),Te=Object.fromEntries(Object.entries(Y.transport).map(([t,e])=>[t,e.encode])),De=!1,Ve=(t,e)=>Pe[t](e);export{Ve as decode,Pe as decoders,Ie as dictionary,Te as encoders,De as hash,Y as hooks,Se as matchers,Ae as nodes,Ce as root,Be as server_loads}; diff --git a/frontend/dist/_app/immutable/entry/start.C6epiIK6.js b/frontend/dist/_app/immutable/entry/start.C6epiIK6.js new file mode 100644 index 0000000..754951f --- /dev/null +++ b/frontend/dist/_app/immutable/entry/start.C6epiIK6.js @@ -0,0 +1 @@ +import{l as o,a as r}from"../chunks/5xxJZag6.js";export{o as load_css,r as start}; diff --git a/frontend/dist/_app/immutable/entry/start.CbP2J41B.js b/frontend/dist/_app/immutable/entry/start.CbP2J41B.js deleted file mode 100644 index 0ecd514..0000000 --- a/frontend/dist/_app/immutable/entry/start.CbP2J41B.js +++ /dev/null @@ -1 +0,0 @@ -import{l as o,a as r}from"../chunks/DTWAhvnO.js";export{o as load_css,r as start}; diff --git a/frontend/dist/_app/immutable/nodes/1.D7xrQvTV.js b/frontend/dist/_app/immutable/nodes/1.CCoIP03N.js similarity index 78% rename from frontend/dist/_app/immutable/nodes/1.D7xrQvTV.js rename to frontend/dist/_app/immutable/nodes/1.CCoIP03N.js index bc3c384..4a0947a 100644 --- a/frontend/dist/_app/immutable/nodes/1.D7xrQvTV.js +++ b/frontend/dist/_app/immutable/nodes/1.CCoIP03N.js @@ -1 +1 @@ -import{a as g,f as h}from"../chunks/C14P8jaG.js";import{i as l}from"../chunks/DmjnTBOd.js";import{x as v,g as d,y as x,z as _,A as e,B as o,C as $}from"../chunks/VJhVTb1A.js";import{s as p}from"../chunks/BeON-0X8.js";import{s as k,p as m}from"../chunks/DTWAhvnO.js";const b={get error(){return m.error},get status(){return m.status}};k.updated.check;const i=b;var y=h("

",1);function j(f,n){v(n,!1),l();var t=y(),r=d(t),c=e(r,!0);o(r);var s=$(r,2),u=e(s,!0);o(s),x(()=>{var a;p(c,i.status),p(u,(a=i.error)==null?void 0:a.message)}),g(f,t),_()}export{j as component}; +import{a as g,f as h}from"../chunks/C14P8jaG.js";import{i as l}from"../chunks/DmjnTBOd.js";import{x as v,g as d,y as x,z as _,A as e,B as o,C as $}from"../chunks/VJhVTb1A.js";import{s as p}from"../chunks/BeON-0X8.js";import{s as k,p as m}from"../chunks/5xxJZag6.js";const b={get error(){return m.error},get status(){return m.status}};k.updated.check;const i=b;var y=h("

",1);function j(f,n){v(n,!1),l();var t=y(),r=d(t),c=e(r,!0);o(r);var s=$(r,2),u=e(s,!0);o(s),x(()=>{var a;p(c,i.status),p(u,(a=i.error)==null?void 0:a.message)}),g(f,t),_()}export{j as component}; diff --git a/frontend/dist/_app/immutable/nodes/2.BGWNYlPN.js b/frontend/dist/_app/immutable/nodes/2.Db1dJWID.js similarity index 75% rename from frontend/dist/_app/immutable/nodes/2.BGWNYlPN.js rename to frontend/dist/_app/immutable/nodes/2.Db1dJWID.js index ba61940..5af38a8 100644 --- a/frontend/dist/_app/immutable/nodes/2.BGWNYlPN.js +++ b/frontend/dist/_app/immutable/nodes/2.Db1dJWID.js @@ -1,6 +1,6 @@ -import{b as Pr,a as u,f as P,c as be,t as Ve,d as Ht}from"../chunks/C14P8jaG.js";import{o as Jn,a as vr}from"../chunks/Cz6m6K8p.js";import{h as Fe,L as Pt,G as Tt,c as Er,J as Gt,b as ea,o as e,N as Ba,ag as qa,O as Pn,P as zt,f as _t,ay as ta,aF as Ka,ao as En,e as mt,a as dr,aM as Ga,s as Wa,a5 as Xa,aN as ra,aE as un,aO as Ya,a7 as Rn,aP as Qa,aQ as Za,aI as Ja,aR as ht,aS as na,a1 as es,r as aa,p as sa,aT as Ur,ae as oa,aU as ts,aV as rs,aC as ns,m as as,d as Nr,az as ia,y as ee,F as la,aW as ss,aG as os,aA as is,D as ca,aX as ua,aY as ls,aZ as cs,E as us,aw as da,S as Ir,k as Mr,a_ as fa,a$ as ds,b0 as fs,b1 as va,b2 as vs,b3 as gs,b4 as gr,b5 as hs,b6 as ps,b7 as ms,b8 as _s,b9 as bs,ba as xs,bb as ys,aJ as Et,U as ws,aK as D,_ as ce,$ as f,A as _,B as v,x as Me,g as G,z as Ae,C as E,j as Ne,ap as we,aL as V,bc as ks,bd as ga,q as Ut,be as it,bf as Cs}from"../chunks/VJhVTb1A.js";import{p as me,i as Y,b as nt,c as Ss,l as Oe,s as Ue}from"../chunks/zf4GFcxs.js";import{i as $s,a as Ps,d as ue,b as Ge,c as Es,n as Rs,e as Ns,s as ae,f as rt}from"../chunks/BeON-0X8.js";import{s as ft}from"../chunks/CiUbu-1F.js";import{i as Ms}from"../chunks/DmjnTBOd.js";import{B as As}from"../chunks/U5yjBaoU.js";function dn(t,r){return r}function Ts(t,r,a){for(var n=[],s=r.length,i,o=r.length,l=0;l{if(i){if(i.pending.delete(k),i.done.add(k),i.pending.size===0){var b=t.outrogroups;Qr(t,un(i.done)),b.delete(i),b.size===0&&(t.outrogroups=null)}}else o-=1},!1)}if(o===0){var d=n.length===0&&a!==null;if(d){var m=a,x=m.parentNode;ns(x),x.append(m),t.items.clear()}Qr(t,r,!d)}else i={pending:new Set(r),done:new Set},(t.outrogroups??(t.outrogroups=new Set)).add(i)}function Qr(t,r,a=!0){var n;if(t.pending.size>0){n=new Set;for(const o of t.pending.values())for(const l of o)n.add(t.items.get(l).e)}for(var s=0;s{var p=a();return ra(p)?p:p==null?[]:un(p)}),b,z=new Map,A=!0;function h(p){(R.effect.f&es)===0&&(R.pending.delete(p),R.fallback=x,Ls(R,b,o,r,n),x!==null&&(b.length===0?(x.f&ht)===0?aa(x):(x.f^=ht,lr(x,null,o)):sa(x,()=>{x=null})))}function c(p){R.pending.delete(p)}var C=ea(()=>{b=e(k);var p=b.length;let w=!1;if(Fe){var $=Ba(o)===qa;$!==(p===0)&&(o=Pn(),Pt(o),zt(!1),w=!0)}for(var g=new Set,N=mt,I=Wa(),T=0;Ti(o)):(x=dr(()=>i(Nn??(Nn=Er()))),x.f|=ht)),p>g.size&&Ga(),Fe&&p>0&&Pt(Pn()),!A)if(z.set(N,g),I){for(const[U,X]of l)g.has(U)||N.skip_effect(X.e);N.oncommit(h),N.ondiscard(c)}else h(N);w&&zt(!0),e(k)}),R={effect:C,items:l,pending:z,outrogroups:null,fallback:x};A=!1,Fe&&(o=_t)}function ar(t){for(;t!==null&&(t.f&ts)===0;)t=t.next;return t}function Ls(t,r,a,n,s){var q,H,j,U,X,K,re,y,S;var i=(n&rs)!==0,o=r.length,l=t.items,d=ar(t.effect.first),m,x=null,k,b=[],z=[],A,h,c,C;if(i)for(C=0;C0){var T=(n&na)!==0&&o===0?a:null;if(i){for(C=0;C{var O,F;if(k!==void 0)for(c of k)(F=(O=c.nodes)==null?void 0:O.a)==null||F.apply()})}function Fs(t,r,a,n,s,i,o,l){var d=(o&Qa)!==0?(o&Za)===0?Ja(a,!1,!1):Rn(a):null,m=(o&Ya)!==0?Rn(s):null;return{v:d,i:m,e:dr(()=>(i(r,d??a,m??s,l),()=>{t.delete(n)}))}}function lr(t,r,a){if(t.nodes)for(var n=t.nodes.start,s=t.nodes.end,i=r&&(r.f&ht)===0?r.nodes.start:a;n!==null;){var o=ia(n);if(i.before(n),n===s)return;n=o}}function kt(t,r,a){r===null?t.effect.first=a:r.next=a,a===null?t.effect.last=r:a.prev=r}function zr(t,r,a=!1,n=!1,s=!1,i=!1){var o=t,l="";if(a){var d=t;Fe&&(o=Pt(Tt(d)))}ee(()=>{var m=la;if(l===(l=r()??"")){Fe&&Gt();return}if(a&&!Fe){m.nodes=null,d.innerHTML=l,l!==""&&Pr(Tt(d),d.lastChild);return}if(m.nodes!==null&&(ss(m.nodes.start,m.nodes.end),m.nodes=null),l!==""){if(Fe){_t.data;for(var x=Gt(),k=x;x!==null&&(x.nodeType!==ta||x.data!=="");)k=x,x=ia(x);if(x===null)throw os(),is;Pr(_t,k),o=Pt(x);return}var b=n?ua:s?ls:void 0,z=ca(n?"svg":s?"math":"template",b);z.innerHTML=l;var A=n||s?z:z.content;if(Pr(Tt(A),A.lastChild),n||s)for(;Tt(A);)o.before(Tt(A));else o.before(A)}})}function je(t,r,a,n,s){var l;Fe&&Gt();var i=(l=r.$$slots)==null?void 0:l[a],o=!1;i===!0&&(i=r.children,o=!0),i===void 0||i(t,o?()=>n:n)}function Is(t,r,a,n,s,i){let o=Fe;Fe&&Gt();var l=null;Fe&&_t.nodeType===cs&&(l=_t,Gt());var d=Fe?_t:t,m=new As(d,!1);ea(()=>{const x=r()||null;var k=ua;if(x===null){m.ensure(null,null);return}return m.ensure(x,b=>{if(x){if(l=Fe?l:ca(x,k),Pr(l,l),n){Fe&&$s(x)&&l.append(document.createComment(""));var z=Fe?Tt(l):l.appendChild(Er());Fe&&(z===null?zt(!1):Pt(z)),n(l,z)}la.nodes.end=l,b.before(l)}Fe&&Pt(b)}),()=>{}},us),da(()=>{}),o&&(zt(!0),Pt(d))}function zs(t,r,a){Ir(()=>{var n=Mr(()=>r(t,a==null?void 0:a())||{});if(n!=null&&n.destroy)return()=>n.destroy()})}function Ds(t,r){var a=void 0,n;fa(()=>{a!==(a=r())&&(n&&(Nr(n),n=null),a&&(n=dr(()=>{Ir(()=>a(t))})))})}function ha(t){var r,a,n="";if(typeof t=="string"||typeof t=="number")n+=t;else if(typeof t=="object")if(Array.isArray(t)){var s=t.length;for(r=0;r=0;){var l=o+i;(o===0||Mn.includes(n[o-1]))&&(l===n.length||Mn.includes(n[l]))?n=(o===0?"":n.substring(0,o))+n.substring(l+1):o=l}}return n===""?null:n}function An(t,r=!1){var a=r?" !important;":";",n="";for(var s of Object.keys(t)){var i=t[s];i!=null&&i!==""&&(n+=" "+s+": "+i+a)}return n}function Br(t){return t[0]!=="-"||t[1]!=="-"?t.toLowerCase():t}function js(t,r){if(r){var a="",n,s;if(Array.isArray(r)?(n=r[0],s=r[1]):n=r,t){t=String(t).replaceAll(/\s*\/\*.*?\*\/\s*/g,"").trim();var i=!1,o=0,l=!1,d=[];n&&d.push(...Object.keys(n).map(Br)),s&&d.push(...Object.keys(s).map(Br));var m=0,x=-1;const h=t.length;for(var k=0;k{Ar(t,t.__value)});r.observe(t,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["value"]}),da(()=>{r.disconnect()})}function Zr(t,r,a=r){var n=new WeakSet,s=!0;va(t,"change",i=>{var o=i?"[selected]":":checked",l;if(t.multiple)l=[].map.call(t.querySelectorAll(o),ur);else{var d=t.querySelector(o)??t.querySelector("option:not([disabled])");l=d&&ur(d)}a(l),t.__value=l,mt!==null&&n.add(mt)}),Ir(()=>{var i=r();if(t===document.activeElement){var o=mt;if(n.has(o))return}if(Ar(t,i,s),s&&i===void 0){var l=t.querySelector(":checked");l!==null&&(i=ur(l),a(i))}t.__value=i,s=!1}),pa(t)}function ur(t){return"__value"in t?t.__value:t.value}const sr=Symbol("class"),or=Symbol("style"),ma=Symbol("is custom element"),_a=Symbol("is html"),Vs=gr?"link":"LINK",Us=gr?"input":"INPUT",Bs=gr?"option":"OPTION",qs=gr?"select":"SELECT",Ks=gr?"progress":"PROGRESS";function Ye(t){if(Fe){var r=!1,a=()=>{if(!r){if(r=!0,t.hasAttribute("value")){var n=t.value;ke(t,"value",null),t.value=n}if(t.hasAttribute("checked")){var s=t.checked;ke(t,"checked",null),t.checked=s}}};t.__on_r=a,oa(a),ms()}}function fn(t,r){var a=vn(t);a.value===(a.value=r??void 0)||t.value===r&&(r!==0||t.nodeName!==Ks)||(t.value=r??"")}function Gs(t,r){r?t.hasAttribute("selected")||t.setAttribute("selected",""):t.removeAttribute("selected")}function ke(t,r,a,n){var s=vn(t);Fe&&(s[r]=t.getAttribute(r),r==="src"||r==="srcset"||r==="href"&&t.nodeName===Vs)||s[r]!==(s[r]=a)&&(r==="loading"&&(t[xs]=a),a==null?t.removeAttribute(r):typeof a!="string"&&ba(t).includes(r)?t[r]=a:t.setAttribute(r,a))}function Ws(t,r,a,n,s=!1,i=!1){if(Fe&&s&&t.nodeName===Us){var o=t,l=o.type==="checkbox"?"defaultChecked":"defaultValue";l in a||Ye(o)}var d=vn(t),m=d[ma],x=!d[_a];let k=Fe&&m;k&&zt(!1);var b=r||{},z=t.nodeName===Bs;for(var A in r)A in a||(a[A]=null);a.class?a.class=dt(a.class):a[sr]&&(a.class=null),a[or]&&(a.style??(a.style=null));var h=ba(t);for(const g in a){let N=a[g];if(z&&g==="value"&&N==null){t.value=t.__value="",b[g]=N;continue}if(g==="class"){var c=t.namespaceURI==="http://www.w3.org/1999/xhtml";Se(t,c,N,n,r==null?void 0:r[sr],a[sr]),b[g]=N,b[sr]=a[sr];continue}if(g==="style"){Ze(t,N,r==null?void 0:r[or],a[or]),b[g]=N,b[or]=a[or];continue}var C=b[g];if(!(N===C&&!(N===void 0&&t.hasAttribute(g)))){b[g]=N;var R=g[0]+g[1];if(R!=="$$")if(R==="on"){const I={},T="$$"+g;let q=g.slice(2);var p=Ns(q);if(Ps(q)&&(q=q.slice(0,-7),I.capture=!0),!p&&C){if(N!=null)continue;t.removeEventListener(q,b[T],I),b[T]=null}if(p)ue(q,t,N),Ge([q]);else if(N!=null){let H=function(j){b[g].call(this,j)};b[T]=Es(q,t,H,I)}}else if(g==="style")ke(t,g,N);else if(g==="autofocus")hs(t,!!N);else if(!m&&(g==="__value"||g==="value"&&N!=null))t.value=t.__value=N;else if(g==="selected"&&z)Gs(t,N);else{var w=g;x||(w=Rs(w));var $=w==="defaultValue"||w==="defaultChecked";if(N==null&&!m&&!$)if(d[g]=null,w==="value"||w==="checked"){let I=t;const T=r===void 0;if(w==="value"){let q=I.defaultValue;I.removeAttribute(w),I.defaultValue=q,I.value=I.__value=T?q:null}else{let q=I.defaultChecked;I.removeAttribute(w),I.defaultChecked=q,I.checked=T?q:!1}}else t.removeAttribute(g);else $||h.includes(w)&&(m||typeof N!="string")?(t[w]=N,w in d&&(d[w]=ps)):typeof N!="function"&&ke(t,w,N)}}}return k&&zt(!0),b}function Tn(t,r,a=[],n=[],s=[],i,o=!1,l=!1){vs(s,a,n,d=>{var m=void 0,x={},k=t.nodeName===qs,b=!1;if(fa(()=>{var A=r(...d.map(e)),h=Ws(t,m,A,i,o,l);b&&k&&"value"in A&&Ar(t,A.value);for(let C of Object.getOwnPropertySymbols(x))A[C]||Nr(x[C]);for(let C of Object.getOwnPropertySymbols(A)){var c=A[C];C.description===gs&&(!m||c!==m[C])&&(x[C]&&Nr(x[C]),x[C]=dr(()=>Ds(t,()=>c))),h[C]=c}m=h}),k){var z=t;Ir(()=>{Ar(z,m.value,!0),pa(z)})}b=!0})}function vn(t){return t.__attributes??(t.__attributes={[ma]:t.nodeName.includes("-"),[_a]:t.namespaceURI===_s})}var Ln=new Map;function ba(t){var r=t.getAttribute("is")||t.nodeName,a=Ln.get(r);if(a)return a;Ln.set(r,a=[]);for(var n,s=t,i=Element.prototype;i!==s;){n=ys(s);for(var o in n)n[o].set&&a.push(o);s=bs(s)}return a}function tt(t,r,a=r){var n=new WeakSet;va(t,"input",async s=>{var i=s?t.defaultValue:t.value;if(i=Kr(t)?Gr(i):i,a(i),mt!==null&&n.add(mt),await Et(),i!==(i=r())){var o=t.selectionStart,l=t.selectionEnd,d=t.value.length;if(t.value=i??"",l!==null){var m=t.value.length;o===l&&l===d&&m>d?(t.selectionStart=m,t.selectionEnd=m):(t.selectionStart=o,t.selectionEnd=Math.min(l,m))}}}),(Fe&&t.defaultValue!==t.value||Mr(r)==null&&t.value)&&(a(Kr(t)?Gr(t.value):t.value),mt!==null&&n.add(mt)),ws(()=>{var s=r();if(t===document.activeElement){var i=mt;if(n.has(i))return}Kr(t)&&s===Gr(t.value)||t.type==="date"&&!s&&!t.value||s!==t.value&&(t.value=s??"")})}function Kr(t){var r=t.type;return r==="number"||r==="range"}function Gr(t){return t===""?null:+t}let xt=D(ce({type:"none",props:{},resolve:null}));function Xs(){return e(xt)}function ot(t=void 0){const{resolve:r}=e(xt);f(xt,{type:"none",props:{},resolve:null},!0),r==null||r(t)}function Ys(t){return new Promise(r=>{f(xt,{type:"run",props:{cases:t},resolve:r},!0)})}function Qs(t){return new Promise(r=>{f(xt,{type:"restart",props:{cases:t},resolve:r},!0)})}function Zs(t){return new Promise(r=>{f(xt,{type:"clean",props:{cases:t},resolve:r},!0)})}function Xe(t,r="Alert"){return new Promise(a=>{f(xt,{type:"alert",props:{title:r,message:t},resolve:()=>a()},!0)})}function Fn(t,r="Confirm",a="OK",n="primary"){return new Promise(s=>{f(xt,{type:"confirm",props:{title:r,message:t,confirmLabel:a,confirmVariant:n},resolve:s},!0)})}function gn(t,r="",a="Input",n="",s=!1){return new Promise(i=>{f(xt,{type:"prompt",props:{title:a,message:t,value:r,placeholder:n,multiline:s},resolve:i},!0)})}var Js=P('
');function Jt(t,r){function a(i){return document.body.appendChild(i),{destroy(){i.remove()}}}var n=Js(),s=_(n);ft(s,()=>r.children),v(n),zs(n,i=>a==null?void 0:a(i)),u(t,n)}var eo=P('
'),to=P('
');function hr(t,r){Me(r,!0);const a=A=>{var h=to(),c=_(h),C=_(c),R=_(C),p=_(R,!0);v(R);var w=E(R,2);{var $=T=>{var q=eo(),H=_(q,!0);v(q),ee(()=>ae(H,r.subtitle)),u(T,q)};Y(w,T=>{r.subtitle&&T($)})}v(C);var g=E(C,2);ft(g,()=>r.children);var N=E(g,2),I=_(N);ft(I,()=>r.footer),v(N),v(c),v(h),ee(()=>{Se(c,1,`w-[min(${n()??""},96vw)] bg-white border border-border rounded-[10px] p-4.5`),ke(c,"aria-labelledby",r.titleId),ke(R,"id",r.titleId),ae(p,r.title)}),ue("keydown",h,o),ue("mousedown",h,d),ue("click",h,m),u(A,h)};let n=me(r,"maxWidth",3,"520px"),s=me(r,"portal",3,!1);function i(){r.onCancel?r.onCancel():ot(null)}function o(A){A.key==="Escape"?i():A.key==="Enter"&&r.onConfirm&&r.onConfirm()}let l=!1;function d(A){l=A.target.dataset.backdrop!==void 0}function m(A){const h=A.target.dataset.backdrop!==void 0;l&&h&&i(),l=!1}var x=be(),k=G(x);{var b=A=>{Jt(A,{children:(h,c)=>{a(h)}})},z=A=>{a(A)};Y(k,A=>{s()?A(b):A(z,-1)})}u(t,x),Ae()}Ge(["keydown","mousedown","click"]);var ro=P("");function Pe(t,r){let a=me(r,"variant",3,"primary"),n=me(r,"size",3,"default"),s=me(r,"disabled",3,!1);const i={default:"h-[34px] rounded-md px-3.5 leading-none text-[13px] tracking-wide",sm:"h-[30px] rounded-md px-2.5 leading-none text-xs"},o={primary:"bg-edf-bleu-moyen text-white border-none hover:bg-[rgb(12,72,170)]",secondary:"bg-white text-ink border border-border hover:bg-edf-gris-clair",run:"bg-edf-vert-fonce text-white border-none hover:bg-[rgb(38,98,12)]",warning:"bg-edf-orange-moyen text-white border-none hover:bg-[rgb(230,118,20)]",danger:"bg-edf-orange-fonce text-white border-none hover:bg-[rgb(180,56,8)]"};var l=ro(),d=_(l);ft(d,()=>r.children),v(l),ee(()=>{Se(l,1,`inline-flex items-center justify-center gap-1.5 font-bold cursor-pointer transition-colors duration-150 disabled:opacity-40 disabled:pointer-events-none ${i[n()]??""} ${o[a()]??""}`),l.disabled=s()}),ue("click",l,function(...m){var x;(x=r.onclick)==null||x.apply(this,m)}),u(t,l)}Ge(["click"]);var no=P(" ",1),ao=P(''),so=P(''),oo=P('
'),io=P('

',1);function lo(t,r){Me(r,!0);let a=me(r,"confirmLabel",3,"OK"),n=me(r,"confirmVariant",3,"primary"),s=me(r,"value",3,""),i=me(r,"placeholder",3,""),o=me(r,"multiline",3,!1);const l=s();let d=D(ce(l)),m=D(void 0),x=D(void 0);Ne(()=>{var z;r.mode==="prompt"&&e(m)?(e(m).focus(),"select"in e(m)&&e(m).select()):e(x)&&((z=e(x).querySelector("button"))==null||z.focus())});function k(){r.mode==="prompt"?ot(e(d)):r.mode==="confirm"?ot(!0):ot()}function b(){r.mode==="confirm"?ot(!1):r.mode==="prompt"?ot(null):ot()}{const z=h=>{var c=no(),C=G(c);{var R=$=>{Pe($,{variant:"secondary",onclick:b,children:(g,N)=>{we();var I=Ve("Cancel");u(g,I)},$$slots:{default:!0}})};Y(C,$=>{r.mode!=="alert"&&$(R)})}var p=E(C,2),w=_(p);Pe(w,{get variant(){return n()},onclick:k,children:($,g)=>{we();var N=Ve();ee(()=>ae(N,a())),u($,N)},$$slots:{default:!0}}),v(p),nt(p,$=>f(x,$),()=>e(x)),u(h,c)};let A=V(()=>o()?void 0:k);hr(t,{get title(){return r.title},titleId:"app-dialog-title",get onConfirm(){return e(A)},onCancel:b,footer:z,children:(h,c)=>{var C=io(),R=G(C),p=_(R,!0);v(R);var w=E(R,2);{var $=g=>{var N=oo(),I=_(N);{var T=H=>{var j=ao();ks(j),nt(j,U=>f(m,U),()=>e(m)),ee(()=>ke(j,"placeholder",i())),tt(j,()=>e(d),U=>f(d,U)),u(H,j)},q=H=>{var j=so();Ye(j),nt(j,U=>f(m,U),()=>e(m)),ee(()=>ke(j,"placeholder",i())),tt(j,()=>e(d),U=>f(d,U)),u(H,j)};Y(I,H=>{o()?H(T):H(q,-1)})}v(N),u(g,N)};Y(w,g=>{r.mode==="prompt"&&g($)})}ee(()=>ae(p,r.message)),u(h,C)},$$slots:{footer:!0,default:!0}})}Ae()}var co=P('');function Re(t,r){var a=co(),n=_(a),s=E(n);ft(s,()=>r.children),v(a),ee(()=>ae(n,`${r.text??""} `)),u(t,a)}function St(t,r){const a=localStorage.getItem(t);if(a===null)return r;const n=Number(a);return Number.isFinite(n)?n:r}function $t(t,r){Number.isFinite(r)&&localStorage.setItem(t,String(r))}function xa(t,r){return localStorage.getItem(t)??r}function uo(t,r){r?localStorage.setItem(t,r):localStorage.removeItem(t)}function fo(){return{n:St("csauto_run_n",1),nt:St("csauto_run_nt",1),maxParallel:St("csauto_run_max_parallel",0)||null,backend:xa("csauto_run_backend","")||null}}function vo(t){$t("csauto_run_n",t.n),$t("csauto_run_nt",t.nt),t.maxParallel&&$t("csauto_run_max_parallel",t.maxParallel),uo("csauto_run_backend",t.backend??"")}function go(){return{n:St("csauto_restart_n",1),nt:St("csauto_restart_nt",1),maxParallel:St("csauto_restart_max_parallel",0)||null,mode:xa("csauto_restart_mode","iterations"),value:St("csauto_restart_value",100)}}function ho(t){$t("csauto_restart_n",t.n),$t("csauto_restart_nt",t.nt),t.maxParallel&&$t("csauto_restart_max_parallel",t.maxParallel),localStorage.setItem("csauto_restart_mode",t.mode),$t("csauto_restart_value",t.value)}function po(){return{keepLast:St("csauto_clean_keep_last",1)}}function mo(t){$t("csauto_clean_keep_last",t.keepLast)}let Dt=D(null);function _o(t){f(Dt,t,!0)}function Ft(){return e(Dt)}function cr(t){return e(Dt)===null||e(Dt).capabilities.includes(t)}function bo(t){return e(Dt)===null||e(Dt).control_actions.includes(t)}function xo(){var t;return((t=e(Dt))==null?void 0:t.backends)??[]}const Jr="csauto_token";let ya=D(ce(localStorage.getItem(Jr)??""));function wa(){return e(ya)}function ka(t){f(ya,t,!0),t?localStorage.setItem(Jr,t):localStorage.removeItem(Jr)}let wr=null;async function hn(t,r={}){const a=new Headers(r.headers),n=wa();n&&a.set("X-CSAUTO-TOKEN",n);const s=await fetch(t,{...r,headers:a});if(s.status===401){wr||(wr=gn("API token required:",n,"Authentication"));const i=await wr;return wr=null,i===null?s:(ka(i),a.set("X-CSAUTO-TOKEN",i),fetch(t,{...r,headers:a}))}return s}async function ct(t){const r=await hn(t);if(!r.ok)throw new Error(`GET ${t} failed: ${r.status}`);return r.json()}async function Dr(t){const r=await hn(t);if(!r.ok)throw new Error(`GET ${t} failed: ${r.status}`);return r.text()}async function yt(t,r){const a=await hn(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)});if(!a.ok){const n=await a.text().catch(()=>a.statusText);throw new Error(`POST ${t} failed: ${a.status} — ${n}`)}return a.json()}function Or(t){return t.map(r=>`case=${encodeURIComponent(r)}`).join("&")}function yo(t){return ct(`/api/launch_options?backend=${encodeURIComponent(t)}`)}function wo(t=!1){return ct(`/api/status${t?"?log=1":""}`)}function In(t){return ct(`/api/perf?${Or(t)}`)}function ko(){return ct("/api/app_config")}function Co(t){return ct(`/api/residual_columns?${Or(t)}`).then(r=>r.columns??[])}function So(t,r,a={}){const n=new URLSearchParams;return t.forEach(s=>n.append("case",s)),n.set("columns",r.join(",")),a.width&&n.set("width",String(a.width)),a.height&&n.set("height",String(a.height)),a.xMin!==void 0&&n.set("x_min",String(a.xMin)),a.includeHistory&&n.set("include_history","true"),Dr(`/api/residuals_svg?${n}`)}function Ca(t){return ct(`/api/restart_origin?${Or(t)}`)}function $o(t){var a;const r=new URLSearchParams;return t.cases.forEach(n=>r.append("case",n)),(a=t.files)!=null&&a.length&&r.set("files",t.files.join(",")),t.maxHits&&r.set("max_hits",String(t.maxHits)),t.context!==void 0&&r.set("context",String(t.context)),t.sev&&r.set("sev",t.sev),t.q&&r.set("q",t.q),ct(`/api/recent_errors?${r}`)}function Po(t,r,a){const n=new URLSearchParams({case:t,file:r,n:String(a)});return Dr(`/api/tail?${n}`)}function Eo(t){return ct(`/api/resu_files?case=${encodeURIComponent(t)}`).then(r=>r.files??[])}function Ro(t){return ct(`/api/resu_dirs?${Or(t)}`).then(r=>r.dirs??[])}function en(t,r){const a=new URLSearchParams({scope:r});return t.forEach(n=>a.append("case",n)),ct(`/api/probes?${a}`).then(n=>n.files??[])}function No(t,r){const a=new URLSearchParams;return t.forEach(n=>a.append("case",n)),r.forEach(n=>a.append("probe",n)),ct(`/api/probe_columns?${a}`).then(n=>n.columns??[])}function Mo(t,r,a){const n=new URLSearchParams({case:t,probe:r});return a.forEach(s=>n.append("column",s)),ct(`/api/probe_position?${n}`)}function Ao(t){const r=new URLSearchParams;return t.cases.forEach(a=>r.append("case",a)),t.probes.forEach(a=>r.append("probe",a)),r.set("columns",t.columns.join(",")),t.axis&&r.set("axis",t.axis),t.xMin!==void 0&&r.set("x_min",String(t.xMin)),t.timeMin!==void 0&&r.set("time_min",String(t.timeMin)),r.set("include_history","true"),t.width&&r.set("width",String(t.width)),t.height&&r.set("height",String(t.height)),Dr(`/api/probe_svg?${r}`)}function To(t){const r=new URLSearchParams;return t.cases.forEach(a=>r.append("case",a)),t.base&&r.set("base",t.base),t.kind&&r.set("kind",t.kind),t.filter&&r.set("filter",t.filter),Dr(`/api/compare_runs?${r}`)}function Lo(){return yt("/api/sync_backends",{}).then(()=>{}).catch(()=>{})}function zn(t){return yt("/api/run_case",{cases:t.cases,n:t.n,nt:t.nt,max_parallel:t.maxParallel??void 0,backend:t.backend??void 0,options:t.options??void 0,restart:t.restart??!1,restart_mode:t.restartMode??"",restart_value:t.restartValue??void 0}).then(()=>{})}function Fo(t){return yt("/api/kill_case",{cases:t}).then(()=>{})}function kr(t){return yt("/api/control_case",{cases:t.cases,action:t.action,value:t.value??void 0}).then(()=>{})}function Io(t){return yt("/api/cleanup_cases",{cases:t.cases,keep_last:t.keepLast??1,prune_resu:t.pruneResu??!0,keep_resu:t.keepResu??[],delete_resu:t.deleteResu??[],max_log_mb:t.maxLogMb??50,clear_cid:!0,clear_pyc:!1})}function zo(t,r){return yt("/api/case_note",{case:t,note:r}).then(()=>{})}function Do(t,r){return yt("/api/case_convergence",{case:t,convergence:r}).then(()=>{})}function Oo(t){return yt("/api/open_gui",{case:t}).then(()=>{})}function Ho(){return ct("/api/settings/telemetry")}function jo(t){return yt("/api/settings/telemetry",{enabled:t})}var Vo=P(" ",1),Uo=P(''),Bo=P(''),qo=P(''),Ko=P(""),Go=P(""),Wo=P('
'),Xo=P(`

About to submit , which runs on your own account and - bills you for the compute. Results come back automatically.

`),Yo=P(""),Qo=P(""),Zo=P('
'),Jo=P('

'),ei=P('
',1);function ti(t,r){Me(r,!0);const a=fo(),n=a.maxParallel??(r.cases.length>1?r.cases.length:0);let s=D(ce(a.n)),i=D(ce(a.nt)),o=D(ce(n));const l=xo().filter(z=>z!=="fake");let d=D(ce(l.includes(a.backend??"")?a.backend:"")),m=D(ce([])),x=D(!1),k=D(ce({}));Ne(()=>{const z=e(d);if(!z){f(m,[],!0),f(x,!1),f(k,{},!0);return}let A=!1;return yo(z).then(h=>{A||(f(m,h.options,!0),f(x,h.degraded,!0),f(k,Object.fromEntries(h.options.map(c=>[c.key,c.default])),!0))}).catch(()=>{A||(f(m,[],!0),f(x,!0),f(k,{},!0))}),()=>{A=!0}});async function b(){if(!Number.isFinite(e(s))||e(s)<=0){await Xe("MPI ranks must be an integer > 0.","Invalid value");return}if(!Number.isFinite(e(i))||e(i)<=0){await Xe("Threads must be an integer > 0.","Invalid value");return}if(e(o)&&(!Number.isFinite(e(o))||e(o)<=0)){await Xe("Max parallel must be empty or > 0.","Invalid value");return}const z={n:e(s),nt:e(i),maxParallel:e(o)||null,backend:e(d)||null,options:e(d)?e(k):void 0};vo(z),ot(z)}{const z=c=>{var C=Vo(),R=G(C);Pe(R,{variant:"secondary",onclick:()=>ot(null),children:(w,$)=>{we();var g=Ve("Cancel");u(w,g)},$$slots:{default:!0}});var p=E(R,2);Pe(p,{variant:"run",onclick:b,children:(w,$)=>{we();var g=Ve();ee(()=>ae(g,e(d)?`Run on ${e(d)}`:"Run")),u(w,g)},$$slots:{default:!0}}),u(c,C)};let A=V(()=>r.cases.length),h=V(()=>r.cases.length>1?"s":"");hr(t,{title:"Run Cases",titleId:"run-dialog-title",get subtitle(){return`${e(A)??""} case${e(h)??""} selected`},onConfirm:b,footer:z,children:(c,C)=>{var R=ei(),p=G(R),w=_(p);Re(w,{text:"MPI Ranks (n)",children:(K,re)=>{var y=Uo();Ye(y),tt(y,()=>e(s),S=>f(s,S)),u(K,y)}});var $=E(w,2);Re($,{text:"OMP Threads (nt)",children:(K,re)=>{var y=Bo();Ye(y),tt(y,()=>e(i),S=>f(i,S)),u(K,y)}});var g=E($,2);Re(g,{text:"Max Parallel",children:(K,re)=>{var y=qo();Ye(y),tt(y,()=>e(o),S=>f(o,S)),u(K,y)}}),v(p);var N=E(p,2);{var I=K=>{var re=Wo(),y=_(re);Re(y,{text:"Run on",children:(S,O)=>{var F=Go(),M=_(F);M.value=M.__value="";var L=E(M);He(L,16,()=>l,B=>B,(B,te)=>{var ie=Ko(),W=_(ie,!0);v(ie);var ne={};ee(()=>{ae(W,te),ne!==(ne=te)&&(ie.value=(ie.__value=te)??"")}),u(B,ie)}),v(F),Zr(F,()=>e(d),B=>f(d,B)),u(S,F)}}),v(re),u(K,re)};Y(N,K=>{l.length&&K(I)})}var T=E(N,2);{var q=K=>{var re=Xo(),y=E(_(re)),S=_(y,!0);v(y);var O=E(y),F=E(O),M=_(F,!0);v(F),we(),v(re),ee(()=>{ae(S,r.cases.length),ae(O,` case${r.cases.length>1?"s":""} to `),ae(M,e(d))}),u(K,re)};Y(T,K=>{e(d)&&K(q)})}var H=E(T,2);{var j=K=>{var re=Zo();He(re,21,()=>e(m),y=>y.key,(y,S)=>{Re(y,{get text(){return e(S).label},children:(O,F)=>{var M=Qo();He(M,21,()=>e(S).choices,([L,B])=>L,(L,B)=>{var te=V(()=>ga(e(B),2));let ie=()=>e(te)[0],W=()=>e(te)[1];var ne=Yo(),oe=_(ne,!0);v(ne);var de={};ee(()=>{ae(oe,W()),de!==(de=ie())&&(ne.value=(ne.__value=ie())??"")}),u(L,ne)}),v(M),Zr(M,()=>e(k)[e(S).key],L=>e(k)[e(S).key]=L),u(O,M)}})}),v(re),u(K,re)};Y(H,K=>{e(d)&&e(m).length&&K(j)})}var U=E(H,2);{var X=K=>{var re=Jo(),y=_(re);v(re),ee(()=>ae(y,`Could not reach ${e(d)??""} to list what is available. The run will use the defaults.`)),u(K,re)};Y(U,K=>{e(d)&&e(x)&&K(X)})}u(c,R)},$$slots:{footer:!0,default:!0}})}Ae()}var ri=P(" ",1),ni=P(''),ai=P(''),si=P(''),oi=P(""),ii=P(''),li=P('
',1);function ci(t,r){Me(r,!0);const a=go(),n=a.maxParallel??(r.cases.length>1?r.cases.length:0);let s=D(ce(a.n)),i=D(ce(a.nt)),o=D(ce(n)),l=D(ce(a.mode)),d=D(ce(a.value)),m=V(()=>e(l)==="iterations"?"Additional iterations":"Additional physical time"),x=V(()=>e(l)==="iterations"?"1":"any"),k=V(()=>e(l)==="iterations"?"1":"0");async function b(){if(!Number.isFinite(e(s))||e(s)<=0){await Xe("MPI ranks must be an integer > 0.","Invalid value");return}if(!Number.isFinite(e(i))||e(i)<=0){await Xe("Threads must be an integer > 0.","Invalid value");return}if(e(o)&&(!Number.isFinite(e(o))||e(o)<=0)){await Xe("Max parallel must be empty or > 0.","Invalid value");return}if(!Number.isFinite(e(d))||e(d)<=0){await Xe("Value must be > 0.","Invalid value");return}if(e(l)==="iterations"&&!Number.isInteger(e(d))){await Xe("Iterations must be an integer.","Invalid value");return}const z={n:e(s),nt:e(i),maxParallel:e(o)||null,restartMode:e(l),restartValue:e(d)};ho({n:e(s),nt:e(i),maxParallel:e(o)||null,mode:e(l),value:e(d)}),ot(z)}{const z=c=>{var C=ri(),R=G(C);Pe(R,{variant:"secondary",onclick:()=>ot(null),children:(w,$)=>{we();var g=Ve("Cancel");u(w,g)},$$slots:{default:!0}});var p=E(R,2);Pe(p,{variant:"warning",onclick:b,children:(w,$)=>{we();var g=Ve("Restart");u(w,g)},$$slots:{default:!0}}),u(c,C)};let A=V(()=>r.cases.length),h=V(()=>r.cases.length>1?"s":"");hr(t,{title:"Restart Cases",titleId:"restart-dialog-title",get subtitle(){return`${e(A)??""} case${e(h)??""} selected`},onConfirm:b,footer:z,children:(c,C)=>{var R=li(),p=G(R),w=_(p);Re(w,{text:"MPI Ranks (n)",children:(q,H)=>{var j=ni();Ye(j),tt(j,()=>e(s),U=>f(s,U)),u(q,j)}});var $=E(w,2);Re($,{text:"OMP Threads (nt)",children:(q,H)=>{var j=ai();Ye(j),tt(j,()=>e(i),U=>f(i,U)),u(q,j)}});var g=E($,2);Re(g,{text:"Max Parallel",children:(q,H)=>{var j=si();Ye(j),tt(j,()=>e(o),U=>f(o,U)),u(q,j)}}),v(p);var N=E(p,2),I=_(N);Re(I,{text:"Stop criterion",children:(q,H)=>{var j=oi(),U=_(j);U.value=U.__value="iterations";var X=E(U);X.value=X.__value="physical_time",v(j),Zr(j,()=>e(l),K=>f(l,K)),u(q,j)}});var T=E(I,2);Re(T,{get text(){return e(m)},children:(q,H)=>{var j=ii();Ye(j),ee(()=>{ke(j,"min",e(k)),ke(j,"step",e(x))}),tt(j,()=>e(d),U=>f(d,U)),u(q,j)}}),v(N),u(c,R)},$$slots:{footer:!0,default:!0}})}Ae()}function Le(t,r){let a=me(r,"size",3,14);var n=be(),s=G(n);Ss(s,()=>r.icon,(i,o)=>{o(i,{get size(){return a()},class:"icon"})}),u(t,n)}/** +import{b as Pr,a as u,f as P,c as be,t as Ve,d as Ht}from"../chunks/C14P8jaG.js";import{o as Jn,a as vr}from"../chunks/Cz6m6K8p.js";import{h as Fe,L as Pt,G as Lt,c as Er,J as Gt,b as ea,o as e,N as Ba,ag as qa,O as Pn,P as zt,f as _t,ay as ta,aF as Ka,ao as En,e as mt,a as dr,aM as Ga,s as Wa,a5 as Xa,aN as ra,aE as un,aO as Ya,a7 as Rn,aP as Qa,aQ as Za,aI as Ja,aR as ht,aS as na,a1 as es,r as aa,p as sa,aT as Ur,ae as oa,aU as ts,aV as rs,aC as ns,m as as,d as Nr,az as ia,y as ee,F as la,aW as ss,aG as os,aA as is,D as ca,aX as ua,aY as ls,aZ as cs,E as us,aw as da,S as Ir,k as Mr,a_ as fa,a$ as ds,b0 as fs,b1 as va,b2 as vs,b3 as gs,b4 as gr,b5 as hs,b6 as ps,b7 as ms,b8 as _s,b9 as bs,ba as xs,bb as ys,aJ as Et,U as ws,aK as D,_ as ce,$ as f,A as _,B as v,x as Me,g as G,z as Ae,C as E,j as Ne,ap as we,aL as V,bc as ks,bd as ga,q as Ut,be as it,bf as Cs}from"../chunks/VJhVTb1A.js";import{p as me,i as Y,b as nt,c as Ss,l as Oe,s as Ue}from"../chunks/zf4GFcxs.js";import{i as $s,a as Ps,d as ue,b as Ge,c as Es,n as Rs,e as Ns,s as ae,f as rt}from"../chunks/BeON-0X8.js";import{s as ft}from"../chunks/CiUbu-1F.js";import{i as Ms}from"../chunks/DmjnTBOd.js";import{B as As}from"../chunks/U5yjBaoU.js";function dn(t,r){return r}function Ls(t,r,a){for(var n=[],s=r.length,i,o=r.length,l=0;l{if(i){if(i.pending.delete(k),i.done.add(k),i.pending.size===0){var b=t.outrogroups;Qr(t,un(i.done)),b.delete(i),b.size===0&&(t.outrogroups=null)}}else o-=1},!1)}if(o===0){var d=n.length===0&&a!==null;if(d){var m=a,x=m.parentNode;ns(x),x.append(m),t.items.clear()}Qr(t,r,!d)}else i={pending:new Set(r),done:new Set},(t.outrogroups??(t.outrogroups=new Set)).add(i)}function Qr(t,r,a=!0){var n;if(t.pending.size>0){n=new Set;for(const o of t.pending.values())for(const l of o)n.add(t.items.get(l).e)}for(var s=0;s{var p=a();return ra(p)?p:p==null?[]:un(p)}),b,z=new Map,A=!0;function h(p){(R.effect.f&es)===0&&(R.pending.delete(p),R.fallback=x,Ts(R,b,o,r,n),x!==null&&(b.length===0?(x.f&ht)===0?aa(x):(x.f^=ht,lr(x,null,o)):sa(x,()=>{x=null})))}function c(p){R.pending.delete(p)}var C=ea(()=>{b=e(k);var p=b.length;let y=!1;if(Fe){var $=Ba(o)===qa;$!==(p===0)&&(o=Pn(),Pt(o),zt(!1),y=!0)}for(var g=new Set,N=mt,I=Wa(),L=0;Li(o)):(x=dr(()=>i(Nn??(Nn=Er()))),x.f|=ht)),p>g.size&&Ga(),Fe&&p>0&&Pt(Pn()),!A)if(z.set(N,g),I){for(const[U,X]of l)g.has(U)||N.skip_effect(X.e);N.oncommit(h),N.ondiscard(c)}else h(N);y&&zt(!0),e(k)}),R={effect:C,items:l,pending:z,outrogroups:null,fallback:x};A=!1,Fe&&(o=_t)}function ar(t){for(;t!==null&&(t.f&ts)===0;)t=t.next;return t}function Ts(t,r,a,n,s){var q,H,j,U,X,K,re,w,S;var i=(n&rs)!==0,o=r.length,l=t.items,d=ar(t.effect.first),m,x=null,k,b=[],z=[],A,h,c,C;if(i)for(C=0;C0){var L=(n&na)!==0&&o===0?a:null;if(i){for(C=0;C{var O,F;if(k!==void 0)for(c of k)(F=(O=c.nodes)==null?void 0:O.a)==null||F.apply()})}function Fs(t,r,a,n,s,i,o,l){var d=(o&Qa)!==0?(o&Za)===0?Ja(a,!1,!1):Rn(a):null,m=(o&Ya)!==0?Rn(s):null;return{v:d,i:m,e:dr(()=>(i(r,d??a,m??s,l),()=>{t.delete(n)}))}}function lr(t,r,a){if(t.nodes)for(var n=t.nodes.start,s=t.nodes.end,i=r&&(r.f&ht)===0?r.nodes.start:a;n!==null;){var o=ia(n);if(i.before(n),n===s)return;n=o}}function kt(t,r,a){r===null?t.effect.first=a:r.next=a,a===null?t.effect.last=r:a.prev=r}function zr(t,r,a=!1,n=!1,s=!1,i=!1){var o=t,l="";if(a){var d=t;Fe&&(o=Pt(Lt(d)))}ee(()=>{var m=la;if(l===(l=r()??"")){Fe&&Gt();return}if(a&&!Fe){m.nodes=null,d.innerHTML=l,l!==""&&Pr(Lt(d),d.lastChild);return}if(m.nodes!==null&&(ss(m.nodes.start,m.nodes.end),m.nodes=null),l!==""){if(Fe){_t.data;for(var x=Gt(),k=x;x!==null&&(x.nodeType!==ta||x.data!=="");)k=x,x=ia(x);if(x===null)throw os(),is;Pr(_t,k),o=Pt(x);return}var b=n?ua:s?ls:void 0,z=ca(n?"svg":s?"math":"template",b);z.innerHTML=l;var A=n||s?z:z.content;if(Pr(Lt(A),A.lastChild),n||s)for(;Lt(A);)o.before(Lt(A));else o.before(A)}})}function je(t,r,a,n,s){var l;Fe&&Gt();var i=(l=r.$$slots)==null?void 0:l[a],o=!1;i===!0&&(i=r.children,o=!0),i===void 0||i(t,o?()=>n:n)}function Is(t,r,a,n,s,i){let o=Fe;Fe&&Gt();var l=null;Fe&&_t.nodeType===cs&&(l=_t,Gt());var d=Fe?_t:t,m=new As(d,!1);ea(()=>{const x=r()||null;var k=ua;if(x===null){m.ensure(null,null);return}return m.ensure(x,b=>{if(x){if(l=Fe?l:ca(x,k),Pr(l,l),n){Fe&&$s(x)&&l.append(document.createComment(""));var z=Fe?Lt(l):l.appendChild(Er());Fe&&(z===null?zt(!1):Pt(z)),n(l,z)}la.nodes.end=l,b.before(l)}Fe&&Pt(b)}),()=>{}},us),da(()=>{}),o&&(zt(!0),Pt(d))}function zs(t,r,a){Ir(()=>{var n=Mr(()=>r(t,a==null?void 0:a())||{});if(n!=null&&n.destroy)return()=>n.destroy()})}function Ds(t,r){var a=void 0,n;fa(()=>{a!==(a=r())&&(n&&(Nr(n),n=null),a&&(n=dr(()=>{Ir(()=>a(t))})))})}function ha(t){var r,a,n="";if(typeof t=="string"||typeof t=="number")n+=t;else if(typeof t=="object")if(Array.isArray(t)){var s=t.length;for(r=0;r=0;){var l=o+i;(o===0||Mn.includes(n[o-1]))&&(l===n.length||Mn.includes(n[l]))?n=(o===0?"":n.substring(0,o))+n.substring(l+1):o=l}}return n===""?null:n}function An(t,r=!1){var a=r?" !important;":";",n="";for(var s of Object.keys(t)){var i=t[s];i!=null&&i!==""&&(n+=" "+s+": "+i+a)}return n}function Br(t){return t[0]!=="-"||t[1]!=="-"?t.toLowerCase():t}function js(t,r){if(r){var a="",n,s;if(Array.isArray(r)?(n=r[0],s=r[1]):n=r,t){t=String(t).replaceAll(/\s*\/\*.*?\*\/\s*/g,"").trim();var i=!1,o=0,l=!1,d=[];n&&d.push(...Object.keys(n).map(Br)),s&&d.push(...Object.keys(s).map(Br));var m=0,x=-1;const h=t.length;for(var k=0;k{Ar(t,t.__value)});r.observe(t,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["value"]}),da(()=>{r.disconnect()})}function Zr(t,r,a=r){var n=new WeakSet,s=!0;va(t,"change",i=>{var o=i?"[selected]":":checked",l;if(t.multiple)l=[].map.call(t.querySelectorAll(o),ur);else{var d=t.querySelector(o)??t.querySelector("option:not([disabled])");l=d&&ur(d)}a(l),t.__value=l,mt!==null&&n.add(mt)}),Ir(()=>{var i=r();if(t===document.activeElement){var o=mt;if(n.has(o))return}if(Ar(t,i,s),s&&i===void 0){var l=t.querySelector(":checked");l!==null&&(i=ur(l),a(i))}t.__value=i,s=!1}),pa(t)}function ur(t){return"__value"in t?t.__value:t.value}const sr=Symbol("class"),or=Symbol("style"),ma=Symbol("is custom element"),_a=Symbol("is html"),Vs=gr?"link":"LINK",Us=gr?"input":"INPUT",Bs=gr?"option":"OPTION",qs=gr?"select":"SELECT",Ks=gr?"progress":"PROGRESS";function Ye(t){if(Fe){var r=!1,a=()=>{if(!r){if(r=!0,t.hasAttribute("value")){var n=t.value;ke(t,"value",null),t.value=n}if(t.hasAttribute("checked")){var s=t.checked;ke(t,"checked",null),t.checked=s}}};t.__on_r=a,oa(a),ms()}}function fn(t,r){var a=vn(t);a.value===(a.value=r??void 0)||t.value===r&&(r!==0||t.nodeName!==Ks)||(t.value=r??"")}function Gs(t,r){r?t.hasAttribute("selected")||t.setAttribute("selected",""):t.removeAttribute("selected")}function ke(t,r,a,n){var s=vn(t);Fe&&(s[r]=t.getAttribute(r),r==="src"||r==="srcset"||r==="href"&&t.nodeName===Vs)||s[r]!==(s[r]=a)&&(r==="loading"&&(t[xs]=a),a==null?t.removeAttribute(r):typeof a!="string"&&ba(t).includes(r)?t[r]=a:t.setAttribute(r,a))}function Ws(t,r,a,n,s=!1,i=!1){if(Fe&&s&&t.nodeName===Us){var o=t,l=o.type==="checkbox"?"defaultChecked":"defaultValue";l in a||Ye(o)}var d=vn(t),m=d[ma],x=!d[_a];let k=Fe&&m;k&&zt(!1);var b=r||{},z=t.nodeName===Bs;for(var A in r)A in a||(a[A]=null);a.class?a.class=dt(a.class):a[sr]&&(a.class=null),a[or]&&(a.style??(a.style=null));var h=ba(t);for(const g in a){let N=a[g];if(z&&g==="value"&&N==null){t.value=t.__value="",b[g]=N;continue}if(g==="class"){var c=t.namespaceURI==="http://www.w3.org/1999/xhtml";Se(t,c,N,n,r==null?void 0:r[sr],a[sr]),b[g]=N,b[sr]=a[sr];continue}if(g==="style"){Ze(t,N,r==null?void 0:r[or],a[or]),b[g]=N,b[or]=a[or];continue}var C=b[g];if(!(N===C&&!(N===void 0&&t.hasAttribute(g)))){b[g]=N;var R=g[0]+g[1];if(R!=="$$")if(R==="on"){const I={},L="$$"+g;let q=g.slice(2);var p=Ns(q);if(Ps(q)&&(q=q.slice(0,-7),I.capture=!0),!p&&C){if(N!=null)continue;t.removeEventListener(q,b[L],I),b[L]=null}if(p)ue(q,t,N),Ge([q]);else if(N!=null){let H=function(j){b[g].call(this,j)};b[L]=Es(q,t,H,I)}}else if(g==="style")ke(t,g,N);else if(g==="autofocus")hs(t,!!N);else if(!m&&(g==="__value"||g==="value"&&N!=null))t.value=t.__value=N;else if(g==="selected"&&z)Gs(t,N);else{var y=g;x||(y=Rs(y));var $=y==="defaultValue"||y==="defaultChecked";if(N==null&&!m&&!$)if(d[g]=null,y==="value"||y==="checked"){let I=t;const L=r===void 0;if(y==="value"){let q=I.defaultValue;I.removeAttribute(y),I.defaultValue=q,I.value=I.__value=L?q:null}else{let q=I.defaultChecked;I.removeAttribute(y),I.defaultChecked=q,I.checked=L?q:!1}}else t.removeAttribute(g);else $||h.includes(y)&&(m||typeof N!="string")?(t[y]=N,y in d&&(d[y]=ps)):typeof N!="function"&&ke(t,y,N)}}}return k&&zt(!0),b}function Ln(t,r,a=[],n=[],s=[],i,o=!1,l=!1){vs(s,a,n,d=>{var m=void 0,x={},k=t.nodeName===qs,b=!1;if(fa(()=>{var A=r(...d.map(e)),h=Ws(t,m,A,i,o,l);b&&k&&"value"in A&&Ar(t,A.value);for(let C of Object.getOwnPropertySymbols(x))A[C]||Nr(x[C]);for(let C of Object.getOwnPropertySymbols(A)){var c=A[C];C.description===gs&&(!m||c!==m[C])&&(x[C]&&Nr(x[C]),x[C]=dr(()=>Ds(t,()=>c))),h[C]=c}m=h}),k){var z=t;Ir(()=>{Ar(z,m.value,!0),pa(z)})}b=!0})}function vn(t){return t.__attributes??(t.__attributes={[ma]:t.nodeName.includes("-"),[_a]:t.namespaceURI===_s})}var Tn=new Map;function ba(t){var r=t.getAttribute("is")||t.nodeName,a=Tn.get(r);if(a)return a;Tn.set(r,a=[]);for(var n,s=t,i=Element.prototype;i!==s;){n=ys(s);for(var o in n)n[o].set&&a.push(o);s=bs(s)}return a}function tt(t,r,a=r){var n=new WeakSet;va(t,"input",async s=>{var i=s?t.defaultValue:t.value;if(i=Kr(t)?Gr(i):i,a(i),mt!==null&&n.add(mt),await Et(),i!==(i=r())){var o=t.selectionStart,l=t.selectionEnd,d=t.value.length;if(t.value=i??"",l!==null){var m=t.value.length;o===l&&l===d&&m>d?(t.selectionStart=m,t.selectionEnd=m):(t.selectionStart=o,t.selectionEnd=Math.min(l,m))}}}),(Fe&&t.defaultValue!==t.value||Mr(r)==null&&t.value)&&(a(Kr(t)?Gr(t.value):t.value),mt!==null&&n.add(mt)),ws(()=>{var s=r();if(t===document.activeElement){var i=mt;if(n.has(i))return}Kr(t)&&s===Gr(t.value)||t.type==="date"&&!s&&!t.value||s!==t.value&&(t.value=s??"")})}function Kr(t){var r=t.type;return r==="number"||r==="range"}function Gr(t){return t===""?null:+t}let xt=D(ce({type:"none",props:{},resolve:null}));function Xs(){return e(xt)}function ot(t=void 0){const{resolve:r}=e(xt);f(xt,{type:"none",props:{},resolve:null},!0),r==null||r(t)}function Ys(t){return new Promise(r=>{f(xt,{type:"run",props:{cases:t},resolve:r},!0)})}function Qs(t){return new Promise(r=>{f(xt,{type:"restart",props:{cases:t},resolve:r},!0)})}function Zs(t){return new Promise(r=>{f(xt,{type:"clean",props:{cases:t},resolve:r},!0)})}function Xe(t,r="Alert"){return new Promise(a=>{f(xt,{type:"alert",props:{title:r,message:t},resolve:()=>a()},!0)})}function Fn(t,r="Confirm",a="OK",n="primary"){return new Promise(s=>{f(xt,{type:"confirm",props:{title:r,message:t,confirmLabel:a,confirmVariant:n},resolve:s},!0)})}function gn(t,r="",a="Input",n="",s=!1){return new Promise(i=>{f(xt,{type:"prompt",props:{title:a,message:t,value:r,placeholder:n,multiline:s},resolve:i},!0)})}var Js=P('
');function Jt(t,r){function a(i){return document.body.appendChild(i),{destroy(){i.remove()}}}var n=Js(),s=_(n);ft(s,()=>r.children),v(n),zs(n,i=>a==null?void 0:a(i)),u(t,n)}var eo=P('
'),to=P('
');function hr(t,r){Me(r,!0);const a=A=>{var h=to(),c=_(h),C=_(c),R=_(C),p=_(R,!0);v(R);var y=E(R,2);{var $=L=>{var q=eo(),H=_(q,!0);v(q),ee(()=>ae(H,r.subtitle)),u(L,q)};Y(y,L=>{r.subtitle&&L($)})}v(C);var g=E(C,2);ft(g,()=>r.children);var N=E(g,2),I=_(N);ft(I,()=>r.footer),v(N),v(c),v(h),ee(()=>{Se(c,1,`w-[min(${n()??""},96vw)] bg-white border border-border rounded-[10px] p-4.5`),ke(c,"aria-labelledby",r.titleId),ke(R,"id",r.titleId),ae(p,r.title)}),ue("keydown",h,o),ue("mousedown",h,d),ue("click",h,m),u(A,h)};let n=me(r,"maxWidth",3,"520px"),s=me(r,"portal",3,!1);function i(){r.onCancel?r.onCancel():ot(null)}function o(A){A.key==="Escape"?i():A.key==="Enter"&&r.onConfirm&&r.onConfirm()}let l=!1;function d(A){l=A.target.dataset.backdrop!==void 0}function m(A){const h=A.target.dataset.backdrop!==void 0;l&&h&&i(),l=!1}var x=be(),k=G(x);{var b=A=>{Jt(A,{children:(h,c)=>{a(h)}})},z=A=>{a(A)};Y(k,A=>{s()?A(b):A(z,-1)})}u(t,x),Ae()}Ge(["keydown","mousedown","click"]);var ro=P("");function Pe(t,r){let a=me(r,"variant",3,"primary"),n=me(r,"size",3,"default"),s=me(r,"disabled",3,!1);const i={default:"h-[34px] rounded-md px-3.5 leading-none text-[13px] tracking-wide",sm:"h-[30px] rounded-md px-2.5 leading-none text-xs"},o={primary:"bg-edf-bleu-moyen text-white border-none hover:bg-[rgb(12,72,170)]",secondary:"bg-white text-ink border border-border hover:bg-edf-gris-clair",run:"bg-edf-vert-fonce text-white border-none hover:bg-[rgb(38,98,12)]",warning:"bg-edf-orange-moyen text-white border-none hover:bg-[rgb(230,118,20)]",danger:"bg-edf-orange-fonce text-white border-none hover:bg-[rgb(180,56,8)]"};var l=ro(),d=_(l);ft(d,()=>r.children),v(l),ee(()=>{Se(l,1,`inline-flex items-center justify-center gap-1.5 font-bold cursor-pointer transition-colors duration-150 disabled:opacity-40 disabled:pointer-events-none ${i[n()]??""} ${o[a()]??""}`),l.disabled=s()}),ue("click",l,function(...m){var x;(x=r.onclick)==null||x.apply(this,m)}),u(t,l)}Ge(["click"]);var no=P(" ",1),ao=P(''),so=P(''),oo=P('
'),io=P('

',1);function lo(t,r){Me(r,!0);let a=me(r,"confirmLabel",3,"OK"),n=me(r,"confirmVariant",3,"primary"),s=me(r,"value",3,""),i=me(r,"placeholder",3,""),o=me(r,"multiline",3,!1);const l=s();let d=D(ce(l)),m=D(void 0),x=D(void 0);Ne(()=>{var z;r.mode==="prompt"&&e(m)?(e(m).focus(),"select"in e(m)&&e(m).select()):e(x)&&((z=e(x).querySelector("button"))==null||z.focus())});function k(){r.mode==="prompt"?ot(e(d)):r.mode==="confirm"?ot(!0):ot()}function b(){r.mode==="confirm"?ot(!1):r.mode==="prompt"?ot(null):ot()}{const z=h=>{var c=no(),C=G(c);{var R=$=>{Pe($,{variant:"secondary",onclick:b,children:(g,N)=>{we();var I=Ve("Cancel");u(g,I)},$$slots:{default:!0}})};Y(C,$=>{r.mode!=="alert"&&$(R)})}var p=E(C,2),y=_(p);Pe(y,{get variant(){return n()},onclick:k,children:($,g)=>{we();var N=Ve();ee(()=>ae(N,a())),u($,N)},$$slots:{default:!0}}),v(p),nt(p,$=>f(x,$),()=>e(x)),u(h,c)};let A=V(()=>o()?void 0:k);hr(t,{get title(){return r.title},titleId:"app-dialog-title",get onConfirm(){return e(A)},onCancel:b,footer:z,children:(h,c)=>{var C=io(),R=G(C),p=_(R,!0);v(R);var y=E(R,2);{var $=g=>{var N=oo(),I=_(N);{var L=H=>{var j=ao();ks(j),nt(j,U=>f(m,U),()=>e(m)),ee(()=>ke(j,"placeholder",i())),tt(j,()=>e(d),U=>f(d,U)),u(H,j)},q=H=>{var j=so();Ye(j),nt(j,U=>f(m,U),()=>e(m)),ee(()=>ke(j,"placeholder",i())),tt(j,()=>e(d),U=>f(d,U)),u(H,j)};Y(I,H=>{o()?H(L):H(q,-1)})}v(N),u(g,N)};Y(y,g=>{r.mode==="prompt"&&g($)})}ee(()=>ae(p,r.message)),u(h,C)},$$slots:{footer:!0,default:!0}})}Ae()}var co=P('');function Re(t,r){var a=co(),n=_(a),s=E(n);ft(s,()=>r.children),v(a),ee(()=>ae(n,`${r.text??""} `)),u(t,a)}function St(t,r){const a=localStorage.getItem(t);if(a===null)return r;const n=Number(a);return Number.isFinite(n)?n:r}function $t(t,r){Number.isFinite(r)&&localStorage.setItem(t,String(r))}function xa(t,r){return localStorage.getItem(t)??r}function uo(t,r){r?localStorage.setItem(t,r):localStorage.removeItem(t)}function fo(){return{n:St("csauto_run_n",1),nt:St("csauto_run_nt",1),maxParallel:St("csauto_run_max_parallel",0)||null,backend:xa("csauto_run_backend","")||null}}function vo(t){$t("csauto_run_n",t.n),$t("csauto_run_nt",t.nt),t.maxParallel&&$t("csauto_run_max_parallel",t.maxParallel),uo("csauto_run_backend",t.backend??"")}function go(){return{n:St("csauto_restart_n",1),nt:St("csauto_restart_nt",1),maxParallel:St("csauto_restart_max_parallel",0)||null,mode:xa("csauto_restart_mode","iterations"),value:St("csauto_restart_value",100)}}function ho(t){$t("csauto_restart_n",t.n),$t("csauto_restart_nt",t.nt),t.maxParallel&&$t("csauto_restart_max_parallel",t.maxParallel),localStorage.setItem("csauto_restart_mode",t.mode),$t("csauto_restart_value",t.value)}function po(){return{keepLast:St("csauto_clean_keep_last",1)}}function mo(t){$t("csauto_clean_keep_last",t.keepLast)}let Dt=D(null);function _o(t){f(Dt,t,!0)}function Ft(){return e(Dt)}function cr(t){return e(Dt)===null||e(Dt).capabilities.includes(t)}function bo(t){return e(Dt)===null||e(Dt).control_actions.includes(t)}function xo(){var t;return((t=e(Dt))==null?void 0:t.backends)??[]}const Jr="csauto_token";let ya=D(ce(localStorage.getItem(Jr)??""));function wa(){return e(ya)}function ka(t){f(ya,t,!0),t?localStorage.setItem(Jr,t):localStorage.removeItem(Jr)}let wr=null;async function hn(t,r={}){const a=new Headers(r.headers),n=wa();n&&a.set("X-CSAUTO-TOKEN",n);const s=await fetch(t,{...r,headers:a});if(s.status===401){wr||(wr=gn("API token required:",n,"Authentication"));const i=await wr;return wr=null,i===null?s:(ka(i),a.set("X-CSAUTO-TOKEN",i),fetch(t,{...r,headers:a}))}return s}async function ct(t){const r=await hn(t);if(!r.ok)throw new Error(`GET ${t} failed: ${r.status}`);return r.json()}async function Dr(t){const r=await hn(t);if(!r.ok)throw new Error(`GET ${t} failed: ${r.status}`);return r.text()}async function yt(t,r){const a=await hn(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)});if(!a.ok){const n=await a.text().catch(()=>a.statusText);throw new Error(`POST ${t} failed: ${a.status} — ${n}`)}return a.json()}function Or(t){return t.map(r=>`case=${encodeURIComponent(r)}`).join("&")}function yo(t){return ct(`/api/launch_options?backend=${encodeURIComponent(t)}`)}function wo(t=!1){return ct(`/api/status${t?"?log=1":""}`)}function In(t){return ct(`/api/perf?${Or(t)}`)}function ko(){return ct("/api/app_config")}function Co(t){return ct(`/api/residual_columns?${Or(t)}`).then(r=>r.columns??[])}function So(t,r,a={}){const n=new URLSearchParams;return t.forEach(s=>n.append("case",s)),n.set("columns",r.join(",")),a.width&&n.set("width",String(a.width)),a.height&&n.set("height",String(a.height)),a.xMin!==void 0&&n.set("x_min",String(a.xMin)),a.includeHistory&&n.set("include_history","true"),Dr(`/api/residuals_svg?${n}`)}function Ca(t){return ct(`/api/restart_origin?${Or(t)}`)}function $o(t){var a;const r=new URLSearchParams;return t.cases.forEach(n=>r.append("case",n)),(a=t.files)!=null&&a.length&&r.set("files",t.files.join(",")),t.maxHits&&r.set("max_hits",String(t.maxHits)),t.context!==void 0&&r.set("context",String(t.context)),t.sev&&r.set("sev",t.sev),t.q&&r.set("q",t.q),ct(`/api/recent_errors?${r}`)}function Po(t,r,a){const n=new URLSearchParams({case:t,file:r,n:String(a)});return Dr(`/api/tail?${n}`)}function Eo(t){return ct(`/api/resu_files?case=${encodeURIComponent(t)}`).then(r=>r.files??[])}function Ro(t){return ct(`/api/resu_dirs?${Or(t)}`).then(r=>r.dirs??[])}function en(t,r){const a=new URLSearchParams({scope:r});return t.forEach(n=>a.append("case",n)),ct(`/api/probes?${a}`).then(n=>n.files??[])}function No(t,r){const a=new URLSearchParams;return t.forEach(n=>a.append("case",n)),r.forEach(n=>a.append("probe",n)),ct(`/api/probe_columns?${a}`).then(n=>n.columns??[])}function Mo(t,r,a){const n=new URLSearchParams({case:t,probe:r});return a.forEach(s=>n.append("column",s)),ct(`/api/probe_position?${n}`)}function Ao(t){const r=new URLSearchParams;return t.cases.forEach(a=>r.append("case",a)),t.probes.forEach(a=>r.append("probe",a)),r.set("columns",t.columns.join(",")),t.axis&&r.set("axis",t.axis),t.xMin!==void 0&&r.set("x_min",String(t.xMin)),t.timeMin!==void 0&&r.set("time_min",String(t.timeMin)),r.set("include_history","true"),t.width&&r.set("width",String(t.width)),t.height&&r.set("height",String(t.height)),Dr(`/api/probe_svg?${r}`)}function Lo(t){const r=new URLSearchParams;return t.cases.forEach(a=>r.append("case",a)),t.base&&r.set("base",t.base),t.kind&&r.set("kind",t.kind),t.filter&&r.set("filter",t.filter),Dr(`/api/compare_runs?${r}`)}function To(){return yt("/api/sync_backends",{}).then(()=>{}).catch(()=>{})}function zn(t){return yt("/api/run_case",{cases:t.cases,n:t.n,nt:t.nt,max_parallel:t.maxParallel??void 0,backend:t.backend??void 0,options:t.options??void 0,restart:t.restart??!1,restart_mode:t.restartMode??"",restart_value:t.restartValue??void 0}).then(()=>{})}function Fo(t){return yt("/api/kill_case",{cases:t}).then(()=>{})}function kr(t){return yt("/api/control_case",{cases:t.cases,action:t.action,value:t.value??void 0}).then(()=>{})}function Io(t){return yt("/api/cleanup_cases",{cases:t.cases,keep_last:t.keepLast??1,prune_resu:t.pruneResu??!0,keep_resu:t.keepResu??[],delete_resu:t.deleteResu??[],max_log_mb:t.maxLogMb??50,clear_cid:!0,clear_pyc:!1})}function zo(t,r){return yt("/api/case_note",{case:t,note:r}).then(()=>{})}function Do(t,r){return yt("/api/case_convergence",{case:t,convergence:r}).then(()=>{})}function Oo(t){return yt("/api/open_gui",{case:t}).then(()=>{})}function Ho(){return ct("/api/settings/telemetry")}function jo(t){return yt("/api/settings/telemetry",{enabled:t})}var Vo=P(" ",1),Uo=P(''),Bo=P(''),qo=P(''),Ko=P(""),Go=P(""),Wo=P('
'),Xo=P(`

About to submit , which runs on your own account and + bills you for the compute. Results come back automatically.

`),Yo=P(""),Qo=P(""),Zo=P('
'),Jo=P('

'),ei=P('
',1);function ti(t,r){Me(r,!0);const a=fo(),n=a.maxParallel??(r.cases.length>1?r.cases.length:0);let s=D(ce(a.n)),i=D(ce(a.nt)),o=D(ce(n));const l=xo().filter(z=>z!=="fake");let d=D(ce(l.includes(a.backend??"")?a.backend:"")),m=D(ce([])),x=D(!1),k=D(ce({}));Ne(()=>{const z=e(d);if(!z){f(m,[],!0),f(x,!1),f(k,{},!0);return}let A=!1;return yo(z).then(h=>{A||(f(m,h.options,!0),f(x,h.degraded,!0),f(k,Object.fromEntries(h.options.map(c=>[c.key,c.default])),!0))}).catch(()=>{A||(f(m,[],!0),f(x,!0),f(k,{},!0))}),()=>{A=!0}});async function b(){if(!Number.isFinite(e(s))||e(s)<=0){await Xe("MPI ranks must be an integer > 0.","Invalid value");return}if(!Number.isFinite(e(i))||e(i)<=0){await Xe("Threads must be an integer > 0.","Invalid value");return}if(e(o)&&(!Number.isFinite(e(o))||e(o)<=0)){await Xe("Max parallel must be empty or > 0.","Invalid value");return}const z={n:e(s),nt:e(i),maxParallel:e(o)||null,backend:e(d)||null,options:e(d)?e(k):void 0};vo(z),ot(z)}{const z=c=>{var C=Vo(),R=G(C);Pe(R,{variant:"secondary",onclick:()=>ot(null),children:(y,$)=>{we();var g=Ve("Cancel");u(y,g)},$$slots:{default:!0}});var p=E(R,2);Pe(p,{variant:"run",onclick:b,children:(y,$)=>{we();var g=Ve();ee(()=>ae(g,e(d)?`Run on ${e(d)}`:"Run")),u(y,g)},$$slots:{default:!0}}),u(c,C)};let A=V(()=>r.cases.length),h=V(()=>r.cases.length>1?"s":"");hr(t,{title:"Run Cases",titleId:"run-dialog-title",get subtitle(){return`${e(A)??""} case${e(h)??""} selected`},onConfirm:b,footer:z,children:(c,C)=>{var R=ei(),p=G(R),y=_(p);Re(y,{text:"MPI Ranks (n)",children:(K,re)=>{var w=Uo();Ye(w),tt(w,()=>e(s),S=>f(s,S)),u(K,w)}});var $=E(y,2);Re($,{text:"OMP Threads (nt)",children:(K,re)=>{var w=Bo();Ye(w),tt(w,()=>e(i),S=>f(i,S)),u(K,w)}});var g=E($,2);Re(g,{text:"Max Parallel",children:(K,re)=>{var w=qo();Ye(w),tt(w,()=>e(o),S=>f(o,S)),u(K,w)}}),v(p);var N=E(p,2);{var I=K=>{var re=Wo(),w=_(re);Re(w,{text:"Run on",children:(S,O)=>{var F=Go(),M=_(F);M.value=M.__value="";var T=E(M);He(T,16,()=>l,B=>B,(B,te)=>{var ie=Ko(),W=_(ie,!0);v(ie);var ne={};ee(()=>{ae(W,te),ne!==(ne=te)&&(ie.value=(ie.__value=te)??"")}),u(B,ie)}),v(F),Zr(F,()=>e(d),B=>f(d,B)),u(S,F)}}),v(re),u(K,re)};Y(N,K=>{l.length&&K(I)})}var L=E(N,2);{var q=K=>{var re=Xo(),w=E(_(re)),S=_(w,!0);v(w);var O=E(w),F=E(O),M=_(F,!0);v(F),we(),v(re),ee(()=>{ae(S,r.cases.length),ae(O,` case${r.cases.length>1?"s":""} to `),ae(M,e(d))}),u(K,re)};Y(L,K=>{e(d)&&K(q)})}var H=E(L,2);{var j=K=>{var re=Zo();He(re,21,()=>e(m),w=>w.key,(w,S)=>{Re(w,{get text(){return e(S).label},children:(O,F)=>{var M=Qo();He(M,21,()=>e(S).choices,([T,B])=>T,(T,B)=>{var te=V(()=>ga(e(B),2));let ie=()=>e(te)[0],W=()=>e(te)[1];var ne=Yo(),oe=_(ne,!0);v(ne);var de={};ee(()=>{ae(oe,W()),de!==(de=ie())&&(ne.value=(ne.__value=ie())??"")}),u(T,ne)}),v(M),Zr(M,()=>e(k)[e(S).key],T=>e(k)[e(S).key]=T),u(O,M)}})}),v(re),u(K,re)};Y(H,K=>{e(d)&&e(m).length&&K(j)})}var U=E(H,2);{var X=K=>{var re=Jo(),w=_(re);v(re),ee(()=>ae(w,`Could not reach ${e(d)??""} to list what is available. The run will use the defaults.`)),u(K,re)};Y(U,K=>{e(d)&&e(x)&&K(X)})}u(c,R)},$$slots:{footer:!0,default:!0}})}Ae()}var ri=P(" ",1),ni=P(''),ai=P(''),si=P(''),oi=P(""),ii=P(''),li=P('
',1);function ci(t,r){Me(r,!0);const a=go(),n=a.maxParallel??(r.cases.length>1?r.cases.length:0);let s=D(ce(a.n)),i=D(ce(a.nt)),o=D(ce(n)),l=D(ce(a.mode)),d=D(ce(a.value)),m=V(()=>e(l)==="iterations"?"Additional iterations":"Additional physical time"),x=V(()=>e(l)==="iterations"?"1":"any"),k=V(()=>e(l)==="iterations"?"1":"0");async function b(){if(!Number.isFinite(e(s))||e(s)<=0){await Xe("MPI ranks must be an integer > 0.","Invalid value");return}if(!Number.isFinite(e(i))||e(i)<=0){await Xe("Threads must be an integer > 0.","Invalid value");return}if(e(o)&&(!Number.isFinite(e(o))||e(o)<=0)){await Xe("Max parallel must be empty or > 0.","Invalid value");return}if(!Number.isFinite(e(d))||e(d)<=0){await Xe("Value must be > 0.","Invalid value");return}if(e(l)==="iterations"&&!Number.isInteger(e(d))){await Xe("Iterations must be an integer.","Invalid value");return}const z={n:e(s),nt:e(i),maxParallel:e(o)||null,restartMode:e(l),restartValue:e(d)};ho({n:e(s),nt:e(i),maxParallel:e(o)||null,mode:e(l),value:e(d)}),ot(z)}{const z=c=>{var C=ri(),R=G(C);Pe(R,{variant:"secondary",onclick:()=>ot(null),children:(y,$)=>{we();var g=Ve("Cancel");u(y,g)},$$slots:{default:!0}});var p=E(R,2);Pe(p,{variant:"warning",onclick:b,children:(y,$)=>{we();var g=Ve("Restart");u(y,g)},$$slots:{default:!0}}),u(c,C)};let A=V(()=>r.cases.length),h=V(()=>r.cases.length>1?"s":"");hr(t,{title:"Restart Cases",titleId:"restart-dialog-title",get subtitle(){return`${e(A)??""} case${e(h)??""} selected`},onConfirm:b,footer:z,children:(c,C)=>{var R=li(),p=G(R),y=_(p);Re(y,{text:"MPI Ranks (n)",children:(q,H)=>{var j=ni();Ye(j),tt(j,()=>e(s),U=>f(s,U)),u(q,j)}});var $=E(y,2);Re($,{text:"OMP Threads (nt)",children:(q,H)=>{var j=ai();Ye(j),tt(j,()=>e(i),U=>f(i,U)),u(q,j)}});var g=E($,2);Re(g,{text:"Max Parallel",children:(q,H)=>{var j=si();Ye(j),tt(j,()=>e(o),U=>f(o,U)),u(q,j)}}),v(p);var N=E(p,2),I=_(N);Re(I,{text:"Stop criterion",children:(q,H)=>{var j=oi(),U=_(j);U.value=U.__value="iterations";var X=E(U);X.value=X.__value="physical_time",v(j),Zr(j,()=>e(l),K=>f(l,K)),u(q,j)}});var L=E(I,2);Re(L,{get text(){return e(m)},children:(q,H)=>{var j=ii();Ye(j),ee(()=>{ke(j,"min",e(k)),ke(j,"step",e(x))}),tt(j,()=>e(d),U=>f(d,U)),u(q,j)}}),v(N),u(c,R)},$$slots:{footer:!0,default:!0}})}Ae()}function Te(t,r){let a=me(r,"size",3,14);var n=be(),s=G(n);Ss(s,()=>r.icon,(i,o)=>{o(i,{get size(){return a()},class:"icon"})}),u(t,n)}/** * @license lucide-svelte v1.0.1 - ISC * * ISC License @@ -141,7 +141,7 @@ import{b as Pr,a as u,f as P,c as be,t as Ve,d as Ht}from"../chunks/C14P8jaG.js" * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * - */const Dn=(...t)=>t.filter((r,a,n)=>!!r&&r.trim()!==""&&n.indexOf(r)===a).join(" ").trim();var fi=Ht("");function Be(t,r){const a=Oe(r,["children","$$slots","$$events","$$legacy"]),n=Oe(a,["name","color","size","strokeWidth","absoluteStrokeWidth","iconNode"]);Me(r,!1);let s=me(r,"name",8,void 0),i=me(r,"color",8,"currentColor"),o=me(r,"size",8,24),l=me(r,"strokeWidth",8,2),d=me(r,"absoluteStrokeWidth",8,!1),m=me(r,"iconNode",24,()=>[]);Ms();var x=fi();Tn(x,(z,A,h)=>({...ui,...z,...n,width:o(),height:o(),stroke:i(),"stroke-width":A,class:h}),[()=>di(n)?void 0:{"aria-hidden":"true"},()=>(Ut(d()),Ut(l()),Ut(o()),Mr(()=>d()?Number(l())*24/Number(o()):l())),()=>(Ut(Dn),Ut(s()),Ut(a),Mr(()=>Dn("lucide-icon","lucide",s()?`lucide-${s()}`:"",a.class)))]);var k=_(x);He(k,1,m,dn,(z,A)=>{var h=V(()=>ga(e(A),2));let c=()=>e(h)[0],C=()=>e(h)[1];var R=be(),p=G(R);Is(p,c,!0,(w,$)=>{Tn(w,()=>({...C()}))}),u(z,R)});var b=E(k);je(b,r,"default",{}),v(x),u(t,x),Ae()}function vi(t,r){const a=Oe(r,["children","$$slots","$$events","$$legacy"]);/** + */const Dn=(...t)=>t.filter((r,a,n)=>!!r&&r.trim()!==""&&n.indexOf(r)===a).join(" ").trim();var fi=Ht("");function Be(t,r){const a=Oe(r,["children","$$slots","$$events","$$legacy"]),n=Oe(a,["name","color","size","strokeWidth","absoluteStrokeWidth","iconNode"]);Me(r,!1);let s=me(r,"name",8,void 0),i=me(r,"color",8,"currentColor"),o=me(r,"size",8,24),l=me(r,"strokeWidth",8,2),d=me(r,"absoluteStrokeWidth",8,!1),m=me(r,"iconNode",24,()=>[]);Ms();var x=fi();Ln(x,(z,A,h)=>({...ui,...z,...n,width:o(),height:o(),stroke:i(),"stroke-width":A,class:h}),[()=>di(n)?void 0:{"aria-hidden":"true"},()=>(Ut(d()),Ut(l()),Ut(o()),Mr(()=>d()?Number(l())*24/Number(o()):l())),()=>(Ut(Dn),Ut(s()),Ut(a),Mr(()=>Dn("lucide-icon","lucide",s()?`lucide-${s()}`:"",a.class)))]);var k=_(x);He(k,1,m,dn,(z,A)=>{var h=V(()=>ga(e(A),2));let c=()=>e(h)[0],C=()=>e(h)[1];var R=be(),p=G(R);Is(p,c,!0,(y,$)=>{Ln(y,()=>({...C()}))}),u(z,R)});var b=E(k);je(b,r,"default",{}),v(x),u(t,x),Ae()}function vi(t,r){const a=Oe(r,["children","$$slots","$$events","$$legacy"]);/** * @license lucide-svelte v1.0.1 - ISC * * ISC License @@ -1175,17 +1175,17 @@ import{b as Pr,a as u,f as P,c as be,t as Ve,d as Ht}from"../chunks/C14P8jaG.js" * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * - */const n=[["path",{d:"M10 11v6"}],["path",{d:"M14 11v6"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"}],["path",{d:"M3 6h18"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"}]];Be(t,Ue({name:"trash-2"},()=>a,{get iconNode(){return n},children:(s,i)=>{var o=be(),l=G(o);je(l,r,"default",{}),u(s,o)},$$slots:{default:!0}}))}var Mi=P('
  • '),Ai=P('
      '),Ti=P('
      ',1);function lt(t,r){Me(r,!0);let a=me(r,"value",3,""),n=me(r,"placeholder",3,"Select..."),s=me(r,"class",3,""),i=me(r,"buttonClass",3,""),o=D(!1),l=D(void 0),d=D(void 0),m=D(""),x=V(()=>{var T;return((T=r.options.find(q=>q.value===a()))==null?void 0:T.label)??n()});function k(T){var q;f(o,!1),(q=r.onchange)==null||q.call(r,T)}function b(){if(!e(l)||!e(d))return;const T=e(l).getBoundingClientRect(),q=e(d).offsetHeight,H=4,j=window.innerHeight-T.bottom-H,X=jj?T.top-H-q:T.bottom+H,K=Math.min(T.left,window.innerWidth-T.width);f(m,`position:fixed; top:${X}px; left:${K}px; width:${T.width}px; z-index:9999;`)}async function z(){f(o,!e(o)),e(o)&&(await Et(),b())}function A(T){T.key==="Escape"&&f(o,!1)}function h(T){e(o)&&e(l)&&!e(l).contains(T.target)&&e(d)&&!e(d).contains(T.target)&&f(o,!1)}var c=Ti();rt("mousedown",it,h),rt("keydown",it,A),rt("scroll",it,()=>{e(o)&&f(o,!1)});var C=G(c),R=_(C),p=_(R),w=_(p,!0);v(p);var $=E(p,2),g=_($);Le(g,{get icon(){return pn},size:14}),v($),v(R),nt(R,T=>f(l,T),()=>e(l)),v(C);var N=E(C,2);{var I=T=>{Jt(T,{children:(q,H)=>{var j=Ai();He(j,21,()=>r.options,U=>U.value,(U,X)=>{var K=Mi();ke(K,"tabindex",0);var re=_(K,!0);v(K),ee(()=>{ke(K,"aria-selected",e(X).value===a()),Se(K,1,`px-2.5 py-1.5 text-[13px] cursor-pointer transition-colors duration-100 - ${e(X).value===a()?"text-edf-bleu-fonce font-bold bg-[rgba(16,87,200,0.06)]":"text-ink hover:bg-edf-gris-clair"}`),ae(re,e(X).label)}),ue("mousedown",K,y=>{y.stopPropagation(),k(e(X).value)}),u(U,K)}),v(j),nt(j,U=>f(d,U),()=>e(d)),ee(()=>Ze(j,e(m))),u(q,j)}})};Y(N,T=>{e(o)&&T(I)})}ee(()=>{Se(C,1,`relative inline-flex ${s()??""}`),Se(R,1,dt(i()||"flex items-center justify-between gap-1.5 w-full h-[30px] cursor-pointer bg-white border border-border rounded-md text-[13px] text-ink font-normal pl-2.5 pr-2 transition-colors duration-150 hover:bg-edf-gris-clair focus:outline-none focus:border-edf-bleu-moyen")),ke(R,"aria-expanded",e(o)),ae(w,e(x)),Se($,1,`text-muted shrink-0 transition-transform duration-150 ${e(o)?"rotate-180":""}`)}),ue("mousedown",R,T=>{T.stopPropagation(),z()}),u(t,c),Ae()}Ge(["mousedown"]);var Li=Ht(''),Fi=P('
      '),Ii=P('
      '),zi=P('
      ',1);function Wt(t,r){Me(r,!0);let a=me(r,"selected",19,()=>[]),n=me(r,"placeholder",3,"Select..."),s=me(r,"class",3,""),i=D(!1),o=D(void 0),l=D(void 0),d=D(""),m=V(()=>new Set(a())),x=V(()=>a().length===0?n():a().length===r.options.length?`All (${r.options.length})`:a().length<=2?a().map(H=>{var j;return((j=r.options.find(U=>U.value===H))==null?void 0:j.label)??H}).join(", "):`${a().length} selected`);function k(H){var U;const j=new Set(e(m));j.has(H)?j.delete(H):j.add(H),(U=r.onchange)==null||U.call(r,[...j])}function b(){var H;(H=r.onchange)==null||H.call(r,r.options.map(j=>j.value))}function z(){var H;(H=r.onchange)==null||H.call(r,[])}function A(){if(!e(o)||!e(l))return;const H=e(o).getBoundingClientRect(),j=e(l).offsetHeight,U=4,X=window.innerHeight-H.bottom-U,re=XX?H.top-U-j:H.bottom+U,y=Math.min(H.left,window.innerWidth-H.width);f(d,`position:fixed; top:${re}px; left:${y}px; min-width:${H.width}px; z-index:9999;`)}async function h(){f(i,!e(i)),e(i)&&(await Et(),A())}function c(H){e(i)&&e(o)&&!e(o).contains(H.target)&&e(l)&&!e(l).contains(H.target)&&f(i,!1)}function C(H){H.key==="Escape"&&f(i,!1)}var R=zi();rt("mousedown",it,c),rt("keydown",it,C),rt("scroll",it,()=>{e(i)&&f(i,!1)});var p=G(R),w=_(p),$=_(w),g=_($,!0);v($);var N=E($,2),I=_(N);Le(I,{get icon(){return pn},size:14}),v(N),v(w),nt(w,H=>f(o,H),()=>e(o)),v(p);var T=E(p,2);{var q=H=>{Jt(H,{children:(j,U)=>{var X=Ii(),K=_(X),re=_(K),y=E(re,2);v(K);var S=E(K,2);He(S,17,()=>r.options,O=>O.value,(O,F)=>{var M=Fi(),L=_(M),B=_(L);{var te=oe=>{var de=Li();u(oe,de)},ie=V(()=>e(m).has(e(F).value));Y(B,oe=>{e(ie)&&oe(te)})}v(L);var W=E(L,2),ne=_(W,!0);v(W),v(M),ee(oe=>{Se(L,1,`inline-flex items-center justify-center shrink-0 w-[14px] h-[14px] rounded border-2 transition-colors duration-100 - ${oe??""}`),ae(ne,e(F).label)},[()=>e(m).has(e(F).value)?"bg-edf-bleu-moyen border-edf-bleu-moyen text-white":"bg-white border-edf-gris-moyen"]),ue("mousedown",M,oe=>{oe.stopPropagation(),k(e(F).value)}),u(O,M)}),v(X),nt(X,O=>f(l,O),()=>e(l)),ee(()=>Ze(X,e(d))),ue("mousedown",re,O=>{O.stopPropagation(),b()}),ue("mousedown",y,O=>{O.stopPropagation(),z()}),u(j,X)}})};Y(T,H=>{e(i)&&H(q)})}ee(()=>{Se(p,1,`relative inline-flex ${s()??""}`),ke(w,"aria-expanded",e(i)),ae(g,e(x)),Se(N,1,`text-muted shrink-0 transition-transform duration-150 ${e(i)?"rotate-180":""}`)}),ue("mousedown",w,H=>{H.stopPropagation(),h()}),u(t,R),Ae()}Ge(["mousedown"]);var Di=P(" ",1),Oi=P(''),Hi=P('Loading...'),ji=P('No folders found'),Vi=P('
      '),Ui=P('
      ',1);function Bi(t,r){Me(r,!0);const a=po();let n=D("keep_latest"),s=D(ce(a.keepLast)),i=D(ce([])),o=D(ce([])),l=D(!0),d=V(()=>e(n)==="keep_folder"||e(n)==="delete_folder");const m=[{value:"keep_latest",label:"Keep latest N"},{value:"delete_all",label:"Delete all RESU"},{value:"keep_folder",label:"Keep specific folders"},{value:"delete_folder",label:"Delete specific folders"}];let x=V(()=>e(i).map(b=>({value:b,label:b})));Jn(async()=>{try{f(i,await Ro(r.cases),!0)}catch{f(i,[],!0)}f(l,!1)});async function k(){if(e(d)&&e(o).length===0){await Xe("Please select at least one RESU folder.","Missing selection");return}const b={action:e(n)};e(n)==="keep_latest"?(b.keepLast=e(s),mo({keepLast:e(s)})):e(n)==="delete_all"?b.keepLast=0:e(n)==="keep_folder"?b.keepResu=e(o):e(n)==="delete_folder"&&(b.deleteResu=e(o)),ot(b)}{const b=h=>{var c=Di(),C=G(c);Pe(C,{variant:"secondary",onclick:()=>ot(null),children:(p,w)=>{we();var $=Ve("Cancel");u(p,$)},$$slots:{default:!0}});var R=E(C,2);Pe(R,{variant:"primary",onclick:k,children:(p,w)=>{we();var $=Ve("Clean");u(p,$)},$$slots:{default:!0}}),u(h,c)};let z=V(()=>r.cases.length),A=V(()=>r.cases.length>1?"s":"");hr(t,{title:"Cleanup Cases",titleId:"clean-dialog-title",get subtitle(){return`${e(z)??""} case${e(A)??""} selected`},footer:b,children:(h,c)=>{var C=Ui(),R=G(C),p=_(R);Re(p,{text:"Action",children:(I,T)=>{lt(I,{class:"w-[200px]",get options(){return m},get value(){return e(n)},onchange:q=>f(n,q,!0)})}});var w=E(p,2);{var $=I=>{Re(I,{text:"Keep last",children:(T,q)=>{var H=Oi();Ye(H),tt(H,()=>e(s),j=>f(s,j)),u(T,H)}})};Y(w,I=>{e(n)==="keep_latest"&&I($)})}v(R);var g=E(R,2);{var N=I=>{var T=Vi(),q=_(T);Re(q,{text:"RESU folders",children:(H,j)=>{var U=be(),X=G(U);{var K=S=>{var O=Hi();u(S,O)},re=S=>{var O=ji();u(S,O)},y=S=>{Wt(S,{class:"w-full",get options(){return e(x)},get selected(){return e(o)},onchange:O=>f(o,O,!0),placeholder:"Select folders..."})};Y(X,S=>{e(l)?S(K):e(i).length===0?S(re,1):S(y,-1)})}u(H,U)}}),v(T),u(I,T)};Y(g,I=>{e(d)&&I(N)})}u(h,C)},$$slots:{footer:!0,default:!0}})}Ae()}function qi(t,r){Me(r,!0);let a=V(Xs);var n=be(),s=G(n);{var i=m=>{ti(m,{get cases(){return e(a).props.cases}})},o=m=>{ci(m,{get cases(){return e(a).props.cases}})},l=m=>{Bi(m,{get cases(){return e(a).props.cases}})},d=m=>{lo(m,{get mode(){return e(a).type},get title(){return e(a).props.title},get message(){return e(a).props.message},get confirmLabel(){return e(a).props.confirmLabel},get confirmVariant(){return e(a).props.confirmVariant},get value(){return e(a).props.value},get placeholder(){return e(a).props.placeholder},get multiline(){return e(a).props.multiline}})};Y(s,m=>{e(a).type==="run"?m(i):e(a).type==="restart"?m(o,1):e(a).type==="clean"?m(l,2):(e(a).type==="alert"||e(a).type==="confirm"||e(a).type==="prompt")&&m(d,3)})}u(t,n),Ae()}let fr=D(ce([])),Ki=0;function Gi(){return e(fr)}function Cr(t,r="success",a=3500){const n=++Ki;f(fr,[...e(fr),{id:n,message:t,variant:r}],!0),setTimeout(()=>$a(n),a)}function $a(t){f(fr,e(fr).filter(r=>r.id!==t),!0)}var Wi=P('
      '),Xi=P('
      ');function Yi(t,r){Me(r,!0);let a=V(Gi);Jt(t,{children:(n,s)=>{var i=Xi();He(i,21,()=>e(a),o=>o.id,(o,l)=>{var d=Wi(),m=_(d,!0);v(d),ee(()=>{Se(d,1,`pointer-events-auto max-w-[320px] px-4 py-2.5 rounded-md shadow-lg text-sm font-semibold text-white cursor-pointer ${e(l).variant==="success"?"bg-edf-vert-fonce":"bg-edf-orange-fonce"}`),ae(m,e(l).message)}),ue("click",d,()=>$a(e(l).id)),u(o,d)}),v(i),u(n,i)}}),Ae()}Ge(["click"]);var Qi=Ht(''),Zi=Ht(''),Ji=P(''),el=P(" ",1),tl=P(" ",1),rl=P('');function Ot(t,r){Me(r,!0);const a=h=>{var c=Ji(),C=_(c);{var R=w=>{var $=Qi();ee(()=>Ze($,`width: ${o()-4}px; height: ${o()-4}px;`)),u(w,$)},p=w=>{var $=Zi();ee(()=>Ze($,`width: ${o()-4}px; height: ${o()-4}px;`)),u(w,$)};Y(C,w=>{n()&&!s()?w(R):s()&&w(p,1)})}v(c),ee(()=>{Se(c,1,`inline-flex items-center justify-center rounded border-2 cursor-pointer transition-colors duration-100 shrink-0 + */const n=[["path",{d:"M10 11v6"}],["path",{d:"M14 11v6"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"}],["path",{d:"M3 6h18"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"}]];Be(t,Ue({name:"trash-2"},()=>a,{get iconNode(){return n},children:(s,i)=>{var o=be(),l=G(o);je(l,r,"default",{}),u(s,o)},$$slots:{default:!0}}))}var Mi=P('
    • '),Ai=P('
        '),Li=P('
        ',1);function lt(t,r){Me(r,!0);let a=me(r,"value",3,""),n=me(r,"placeholder",3,"Select..."),s=me(r,"class",3,""),i=me(r,"buttonClass",3,""),o=D(!1),l=D(void 0),d=D(void 0),m=D(""),x=V(()=>{var L;return((L=r.options.find(q=>q.value===a()))==null?void 0:L.label)??n()});function k(L){var q;f(o,!1),(q=r.onchange)==null||q.call(r,L)}function b(){if(!e(l)||!e(d))return;const L=e(l).getBoundingClientRect(),q=e(d).offsetHeight,H=4,j=window.innerHeight-L.bottom-H,X=jj?L.top-H-q:L.bottom+H,K=Math.min(L.left,window.innerWidth-L.width);f(m,`position:fixed; top:${X}px; left:${K}px; width:${L.width}px; z-index:9999;`)}async function z(){f(o,!e(o)),e(o)&&(await Et(),b())}function A(L){L.key==="Escape"&&f(o,!1)}function h(L){e(o)&&e(l)&&!e(l).contains(L.target)&&e(d)&&!e(d).contains(L.target)&&f(o,!1)}var c=Li();rt("mousedown",it,h),rt("keydown",it,A),rt("scroll",it,()=>{e(o)&&f(o,!1)});var C=G(c),R=_(C),p=_(R),y=_(p,!0);v(p);var $=E(p,2),g=_($);Te(g,{get icon(){return pn},size:14}),v($),v(R),nt(R,L=>f(l,L),()=>e(l)),v(C);var N=E(C,2);{var I=L=>{Jt(L,{children:(q,H)=>{var j=Ai();He(j,21,()=>r.options,U=>U.value,(U,X)=>{var K=Mi();ke(K,"tabindex",0);var re=_(K,!0);v(K),ee(()=>{ke(K,"aria-selected",e(X).value===a()),Se(K,1,`px-2.5 py-1.5 text-[13px] cursor-pointer transition-colors duration-100 + ${e(X).value===a()?"text-edf-bleu-fonce font-bold bg-[rgba(16,87,200,0.06)]":"text-ink hover:bg-edf-gris-clair"}`),ae(re,e(X).label)}),ue("mousedown",K,w=>{w.stopPropagation(),k(e(X).value)}),u(U,K)}),v(j),nt(j,U=>f(d,U),()=>e(d)),ee(()=>Ze(j,e(m))),u(q,j)}})};Y(N,L=>{e(o)&&L(I)})}ee(()=>{Se(C,1,`relative inline-flex ${s()??""}`),Se(R,1,dt(i()||"flex items-center justify-between gap-1.5 w-full h-[30px] cursor-pointer bg-white border border-border rounded-md text-[13px] text-ink font-normal pl-2.5 pr-2 transition-colors duration-150 hover:bg-edf-gris-clair focus:outline-none focus:border-edf-bleu-moyen")),ke(R,"aria-expanded",e(o)),ae(y,e(x)),Se($,1,`text-muted shrink-0 transition-transform duration-150 ${e(o)?"rotate-180":""}`)}),ue("mousedown",R,L=>{L.stopPropagation(),z()}),u(t,c),Ae()}Ge(["mousedown"]);var Ti=Ht(''),Fi=P('
        '),Ii=P('
        '),zi=P('
        ',1);function Wt(t,r){Me(r,!0);let a=me(r,"selected",19,()=>[]),n=me(r,"placeholder",3,"Select..."),s=me(r,"class",3,""),i=D(!1),o=D(void 0),l=D(void 0),d=D(""),m=V(()=>new Set(a())),x=V(()=>a().length===0?n():a().length===r.options.length?`All (${r.options.length})`:a().length<=2?a().map(H=>{var j;return((j=r.options.find(U=>U.value===H))==null?void 0:j.label)??H}).join(", "):`${a().length} selected`);function k(H){var U;const j=new Set(e(m));j.has(H)?j.delete(H):j.add(H),(U=r.onchange)==null||U.call(r,[...j])}function b(){var H;(H=r.onchange)==null||H.call(r,r.options.map(j=>j.value))}function z(){var H;(H=r.onchange)==null||H.call(r,[])}function A(){if(!e(o)||!e(l))return;const H=e(o).getBoundingClientRect(),j=e(l).offsetHeight,U=4,X=window.innerHeight-H.bottom-U,re=XX?H.top-U-j:H.bottom+U,w=Math.min(H.left,window.innerWidth-H.width);f(d,`position:fixed; top:${re}px; left:${w}px; min-width:${H.width}px; z-index:9999;`)}async function h(){f(i,!e(i)),e(i)&&(await Et(),A())}function c(H){e(i)&&e(o)&&!e(o).contains(H.target)&&e(l)&&!e(l).contains(H.target)&&f(i,!1)}function C(H){H.key==="Escape"&&f(i,!1)}var R=zi();rt("mousedown",it,c),rt("keydown",it,C),rt("scroll",it,()=>{e(i)&&f(i,!1)});var p=G(R),y=_(p),$=_(y),g=_($,!0);v($);var N=E($,2),I=_(N);Te(I,{get icon(){return pn},size:14}),v(N),v(y),nt(y,H=>f(o,H),()=>e(o)),v(p);var L=E(p,2);{var q=H=>{Jt(H,{children:(j,U)=>{var X=Ii(),K=_(X),re=_(K),w=E(re,2);v(K);var S=E(K,2);He(S,17,()=>r.options,O=>O.value,(O,F)=>{var M=Fi(),T=_(M),B=_(T);{var te=oe=>{var de=Ti();u(oe,de)},ie=V(()=>e(m).has(e(F).value));Y(B,oe=>{e(ie)&&oe(te)})}v(T);var W=E(T,2),ne=_(W,!0);v(W),v(M),ee(oe=>{Se(T,1,`inline-flex items-center justify-center shrink-0 w-[14px] h-[14px] rounded border-2 transition-colors duration-100 + ${oe??""}`),ae(ne,e(F).label)},[()=>e(m).has(e(F).value)?"bg-edf-bleu-moyen border-edf-bleu-moyen text-white":"bg-white border-edf-gris-moyen"]),ue("mousedown",M,oe=>{oe.stopPropagation(),k(e(F).value)}),u(O,M)}),v(X),nt(X,O=>f(l,O),()=>e(l)),ee(()=>Ze(X,e(d))),ue("mousedown",re,O=>{O.stopPropagation(),b()}),ue("mousedown",w,O=>{O.stopPropagation(),z()}),u(j,X)}})};Y(L,H=>{e(i)&&H(q)})}ee(()=>{Se(p,1,`relative inline-flex ${s()??""}`),ke(y,"aria-expanded",e(i)),ae(g,e(x)),Se(N,1,`text-muted shrink-0 transition-transform duration-150 ${e(i)?"rotate-180":""}`)}),ue("mousedown",y,H=>{H.stopPropagation(),h()}),u(t,R),Ae()}Ge(["mousedown"]);var Di=P(" ",1),Oi=P(''),Hi=P('Loading...'),ji=P('No folders found'),Vi=P('
        '),Ui=P('
        ',1);function Bi(t,r){Me(r,!0);const a=po();let n=D("keep_latest"),s=D(ce(a.keepLast)),i=D(ce([])),o=D(ce([])),l=D(!0),d=V(()=>e(n)==="keep_folder"||e(n)==="delete_folder");const m=[{value:"keep_latest",label:"Keep latest N"},{value:"delete_all",label:"Delete all RESU"},{value:"keep_folder",label:"Keep specific folders"},{value:"delete_folder",label:"Delete specific folders"}];let x=V(()=>e(i).map(b=>({value:b,label:b})));Jn(async()=>{try{f(i,await Ro(r.cases),!0)}catch{f(i,[],!0)}f(l,!1)});async function k(){if(e(d)&&e(o).length===0){await Xe("Please select at least one RESU folder.","Missing selection");return}const b={action:e(n)};e(n)==="keep_latest"?(b.keepLast=e(s),mo({keepLast:e(s)})):e(n)==="delete_all"?b.keepLast=0:e(n)==="keep_folder"?b.keepResu=e(o):e(n)==="delete_folder"&&(b.deleteResu=e(o)),ot(b)}{const b=h=>{var c=Di(),C=G(c);Pe(C,{variant:"secondary",onclick:()=>ot(null),children:(p,y)=>{we();var $=Ve("Cancel");u(p,$)},$$slots:{default:!0}});var R=E(C,2);Pe(R,{variant:"primary",onclick:k,children:(p,y)=>{we();var $=Ve("Clean");u(p,$)},$$slots:{default:!0}}),u(h,c)};let z=V(()=>r.cases.length),A=V(()=>r.cases.length>1?"s":"");hr(t,{title:"Cleanup Cases",titleId:"clean-dialog-title",get subtitle(){return`${e(z)??""} case${e(A)??""} selected`},footer:b,children:(h,c)=>{var C=Ui(),R=G(C),p=_(R);Re(p,{text:"Action",children:(I,L)=>{lt(I,{class:"w-[200px]",get options(){return m},get value(){return e(n)},onchange:q=>f(n,q,!0)})}});var y=E(p,2);{var $=I=>{Re(I,{text:"Keep last",children:(L,q)=>{var H=Oi();Ye(H),tt(H,()=>e(s),j=>f(s,j)),u(L,H)}})};Y(y,I=>{e(n)==="keep_latest"&&I($)})}v(R);var g=E(R,2);{var N=I=>{var L=Vi(),q=_(L);Re(q,{text:"RESU folders",children:(H,j)=>{var U=be(),X=G(U);{var K=S=>{var O=Hi();u(S,O)},re=S=>{var O=ji();u(S,O)},w=S=>{Wt(S,{class:"w-full",get options(){return e(x)},get selected(){return e(o)},onchange:O=>f(o,O,!0),placeholder:"Select folders..."})};Y(X,S=>{e(l)?S(K):e(i).length===0?S(re,1):S(w,-1)})}u(H,U)}}),v(L),u(I,L)};Y(g,I=>{e(d)&&I(N)})}u(h,C)},$$slots:{footer:!0,default:!0}})}Ae()}function qi(t,r){Me(r,!0);let a=V(Xs);var n=be(),s=G(n);{var i=m=>{ti(m,{get cases(){return e(a).props.cases}})},o=m=>{ci(m,{get cases(){return e(a).props.cases}})},l=m=>{Bi(m,{get cases(){return e(a).props.cases}})},d=m=>{lo(m,{get mode(){return e(a).type},get title(){return e(a).props.title},get message(){return e(a).props.message},get confirmLabel(){return e(a).props.confirmLabel},get confirmVariant(){return e(a).props.confirmVariant},get value(){return e(a).props.value},get placeholder(){return e(a).props.placeholder},get multiline(){return e(a).props.multiline}})};Y(s,m=>{e(a).type==="run"?m(i):e(a).type==="restart"?m(o,1):e(a).type==="clean"?m(l,2):(e(a).type==="alert"||e(a).type==="confirm"||e(a).type==="prompt")&&m(d,3)})}u(t,n),Ae()}let fr=D(ce([])),Ki=0;function Gi(){return e(fr)}function Cr(t,r="success",a=3500){const n=++Ki;f(fr,[...e(fr),{id:n,message:t,variant:r}],!0),setTimeout(()=>$a(n),a)}function $a(t){f(fr,e(fr).filter(r=>r.id!==t),!0)}var Wi=P('
        '),Xi=P('
        ');function Yi(t,r){Me(r,!0);let a=V(Gi);Jt(t,{children:(n,s)=>{var i=Xi();He(i,21,()=>e(a),o=>o.id,(o,l)=>{var d=Wi(),m=_(d,!0);v(d),ee(()=>{Se(d,1,`pointer-events-auto max-w-[320px] px-4 py-2.5 rounded-md shadow-lg text-sm font-semibold text-white cursor-pointer ${e(l).variant==="success"?"bg-edf-vert-fonce":"bg-edf-orange-fonce"}`),ae(m,e(l).message)}),ue("click",d,()=>$a(e(l).id)),u(o,d)}),v(i),u(n,i)}}),Ae()}Ge(["click"]);var Qi=Ht(''),Zi=Ht(''),Ji=P(''),el=P(" ",1),tl=P(" ",1),rl=P('');function Ot(t,r){Me(r,!0);const a=h=>{var c=Ji(),C=_(c);{var R=y=>{var $=Qi();ee(()=>Ze($,`width: ${o()-4}px; height: ${o()-4}px;`)),u(y,$)},p=y=>{var $=Zi();ee(()=>Ze($,`width: ${o()-4}px; height: ${o()-4}px;`)),u(y,$)};Y(C,y=>{n()&&!s()?y(R):s()&&y(p,1)})}v(c),ee(()=>{Se(c,1,`inline-flex items-center justify-center rounded border-2 cursor-pointer transition-colors duration-100 shrink-0 ${i()?"opacity-40 cursor-not-allowed":""} - ${n()||s()?"bg-edf-bleu-moyen border-edf-bleu-moyen text-white":"bg-white border-edf-gris-moyen hover:border-edf-bleu-clair"}`),Ze(c,`width: ${o()??""}px; height: ${o()??""}px;`),ke(c,"aria-checked",s()?"mixed":n()),ke(c,"aria-disabled",i()),ke(c,"tabindex",i()?-1:0)}),ue("click",c,m),ue("keydown",c,x),u(h,c)};let n=me(r,"checked",3,!1),s=me(r,"indeterminate",3,!1),i=me(r,"disabled",3,!1),o=me(r,"size",3,16),l=me(r,"label",3,""),d=me(r,"labelFirst",3,!1);function m(h){var C;if(h.stopPropagation(),i())return;const c=s()?!0:!n();(C=r.onchange)==null||C.call(r,c)}function x(h){(h.key===" "||h.key==="Enter")&&(h.preventDefault(),h.stopPropagation(),m(h))}var k=be(),b=G(k);{var z=h=>{var c=rl(),C=_(c);{var R=w=>{var $=el(),g=G($),N=_(g,!0);v(g);var I=E(g);a(I),ee(()=>ae(N,l())),u(w,$)},p=w=>{var $=tl(),g=G($);a(g);var N=E(g),I=_(N,!0);v(N),ee(()=>ae(I,l())),u(w,$)};Y(C,w=>{d()?w(R):w(p,-1)})}v(c),ue("click",c,m),ue("keydown",c,x),u(h,c)},A=h=>{a(h)};Y(b,h=>{l()?h(z):h(A,-1)})}u(t,k),Ae()}Ge(["click","keydown"]);function mn(t,r){try{const a=localStorage.getItem(t);return a===null?r:JSON.parse(a)}catch{return r}}function Hr(t,r){localStorage.setItem(t,JSON.stringify(r))}const tn=new Map;function mr(t,r,a){vt(t),tn.set(t,setInterval(r,a))}function vt(t){const r=tn.get(t);r!==void 0&&(clearInterval(r),tn.delete(t))}const Lt={status:1e3,tail:1e3,plot:3e3,probe:3e3,errors:5e3},nl=["status","plot","probe","tail","errors"],Wr={status:{label:"Status table",min:500},plot:{label:"Residual plots",min:1e3},probe:{label:"Probe plots",min:1e3},tail:{label:"Log tail",min:500},errors:{label:"Recent errors",min:2e3}},ir=mn("csauto_refresh_rates",{});let bt=D(ce({status:ir.status??Lt.status,tail:ir.tail??Lt.tail,plot:ir.plot??Lt.plot,probe:ir.probe??Lt.probe,errors:ir.errors??Lt.errors}));function al(){return e(bt)}function sl(t,r){f(bt,{...e(bt),[t]:r},!0),Hr("csauto_refresh_rates",e(bt))}function ol(){return e(bt).status}function On(){return e(bt).tail}function Hn(){return e(bt).plot}function il(){return e(bt).probe}function jn(){return e(bt).errors}const rn=new Set;function _r(t){return rn.add(t),()=>rn.delete(t)}function ll(){rn.forEach(t=>t())}const nn=mn("csauto_autorefresh_enabled",{});function tr(t){return nn[t]??!0}function rr(t,r){nn[t]=r,Hr("csauto_autorefresh_enabled",nn)}var cl=P(" ",1),ul=P('
        ms
        '),dl=P('
        Telemetry
        '),fl=P('
        API token
        Auto-refresh intervals
        ',1);function vl(t,r){Me(r,!0);let a=D(ce(wa())),n=D(ce({...al()})),s=D(!0),i=D(!1);Ne(()=>{Ho().then(m=>{f(s,m.enabled,!0),f(i,!0)}).catch(()=>{f(i,!1)})});function o(m){const{min:x}=Wr[m];let k=e(n)[m];(typeof k!="number"||isNaN(k))&&(k=Lt[m]),f(n,{...e(n),[m]:Math.max(x,Math.round(k))},!0)}function l(m,x){const k=x.target,b=k.value.replace(/[^0-9]/g,""),z=parseInt(b,10);isNaN(z)?b===""&&f(n,{...e(n),[m]:Wr[m].min},!0):f(n,{...e(n),[m]:z},!0),k.value=String(e(n)[m])}function d(){ka(e(a));for(const m of Object.keys(e(n)))o(m),sl(m,e(n)[m]);e(i)&&jo(e(s)).catch(()=>{}),r.onClose()}hr(t,{title:"Settings",titleId:"settings-dialog-title",get onCancel(){return r.onClose},onConfirm:d,maxWidth:"400px",portal:!0,footer:x=>{var k=cl(),b=G(k);Pe(b,{variant:"secondary",get onclick(){return r.onClose},children:(A,h)=>{we();var c=Ve("Cancel");u(A,c)},$$slots:{default:!0}});var z=E(b,2);Pe(z,{variant:"primary",onclick:d,children:(A,h)=>{we();var c=Ve("Save");u(A,c)},$$slots:{default:!0}}),u(x,k)},children:(x,k)=>{var b=fl(),z=G(b),A=E(_(z),2);Ye(A),v(z);var h=E(z,2),c=_(h),C=E(_(c),2);v(c);var R=E(c,2);He(R,20,()=>nl,$=>$,($,g)=>{const N=V(()=>Wr[g]);var I=ul(),T=_(I),q=_(T),H=_(q,!0);v(q);var j=E(q,2),U=_(j);v(j),v(T);var X=E(T,2),K=_(X);Ye(K),we(2),v(X),v(I),ee(()=>{ae(H,e(N).label),ae(U,`min ${e(N).min??""}ms`),fn(K,e(n)[g])}),ue("input",K,re=>l(g,re)),rt("blur",K,()=>o(g)),u($,I)}),v(R),v(h);var p=E(h,2);{var w=$=>{var g=dl(),N=E(_(g),2);Ot(N,{get checked(){return e(s)},onchange:I=>f(s,I,!0),label:"Send anonymous usage statistics"}),v(g),u($,g)};Y(p,$=>{e(i)&&$(w)})}tt(A,()=>e(a),$=>f(a,$)),ue("click",C,()=>f(n,{...Lt},!0)),u(x,b)},$$slots:{footer:!0,default:!0}}),Ae()}Ge(["click","input"]);const gl=""+new URL("../assets/code-saturne.BHojVttu.svg",import.meta.url).href,hl="data:image/svg+xml,%3c?xml%20version='1.0'%20encoding='UTF-8'?%3e%3csvg%20id='Calque_1'%20xmlns='http://www.w3.org/2000/svg'%20version='1.1'%20height='400px'%20viewBox='0%200%20397.89%2095.85'%3e%3c!--%20Generator:%20Adobe%20Illustrator%2029.8.1,%20SVG%20Export%20Plug-In%20.%20SVG%20Version:%202.1.1%20Build%202)%20--%3e%3cdefs%3e%3cstyle%3e%20.st0%20{%20fill:%20%23ff861d;%20}%20.st1%20{%20fill:%20%23ffb210;%20}%20.st2%20{%20fill:%20%231057c8;%20}%20.st3%20{%20fill:%20%23d6430a;%20}%20%3c/style%3e%3c/defs%3e%3cpath%20class='st2'%20d='M26.25,61.73c-12.02,0-20.11-8.16-20.11-21.37s8.01-21.37,20.33-21.37c8.83,0,15.06,3.78,17.36,11.28l-5.64,2.52c-1.48-5.94-4.97-8.69-11.87-8.69-8.46,0-14.25,6.01-14.25,16.25s5.93,16.25,13.95,16.25,11.73-2.82,12.84-8.61l5.71,1.93c-2,7.79-9.13,11.8-18.33,11.8'/%3e%3cpath%20class='st2'%20d='M89.96,40.36c0,13.21-7.87,21.37-20.48,21.37s-20.48-8.16-20.48-21.37,7.86-21.37,20.48-21.37,20.48,8.16,20.48,21.37M54.94,40.36c0,10.24,5.64,16.25,14.54,16.25s14.54-6.01,14.54-16.25-5.64-16.25-14.54-16.25-14.54,6.01-14.54,16.25'/%3e%3cpath%20class='st2'%20d='M134.3,40.29c0,13.43-8.01,20.85-21.81,20.85h-14.84V19.58h14.84c13.8,0,21.81,7.42,21.81,20.7M128.36,40.29c0-9.94-5.57-15.58-15.44-15.58h-9.35v31.32h9.35c9.87,0,15.44-5.71,15.44-15.73'/%3e%3cpolygon%20class='st2'%20points='174.11%2056.02%20174.11%2061.14%20141.98%2061.14%20141.98%2019.58%20173.14%2019.58%20173.14%2024.7%20147.91%2024.7%20147.91%2037.54%20167.65%2037.54%20167.65%2042.66%20147.91%2042.66%20147.91%2056.02%20174.11%2056.02'/%3e%3cpath%20class='st2'%20d='M213.81,22.13c4.16-1.92,9.75-3.11,14.3-3.11,12.55,0,17.74,5.19,17.74,17.34v5.27c0,4.16.08,7.28.16,10.31.08,3.12.24,5.99.48,9.19h-9.43c-.4-2.16-.4-4.88-.48-6.15h-.16c-2.48,4.56-7.83,7.11-12.71,7.11-7.28,0-14.39-4.4-14.39-12.23,0-6.15,2.96-9.75,7.03-11.75,4.08-2,9.35-2.4,13.83-2.4h5.91c0-6.63-2.96-8.87-9.27-8.87-4.56,0-9.11,1.76-12.71,4.48l-.32-9.19ZM226.36,54.26c3.27,0,5.83-1.44,7.51-3.68,1.76-2.32,2.24-5.27,2.24-8.47h-4.63c-4.8,0-11.91.8-11.91,7.11,0,3.52,2.96,5.04,6.79,5.04'/%3e%3cpath%20class='st2'%20d='M280.98,28.61c-3.28-1.12-5.67-1.76-9.59-1.76-2.88,0-6.31,1.04-6.31,4.56,0,6.56,18.62,2.4,18.62,17.27,0,9.59-8.55,13.43-17.27,13.43-4.08,0-8.23-.72-12.15-1.76l.64-8.79c3.36,1.68,6.87,2.72,10.55,2.72,2.72,0,7.03-1.04,7.03-5.04,0-8.07-18.62-2.56-18.62-17.42,0-8.87,7.75-12.79,16.14-12.79,5.03,0,8.31.8,11.67,1.52l-.72,8.07Z'/%3e%3cpath%20class='st2'%20d='M295.25,27.81h-7.91v-7.83h7.91v-8.23l10.71-3.44v11.67h9.51v7.83h-9.51v19.18c0,3.52.96,6.79,5.04,6.79,1.92,0,3.76-.4,4.88-1.12l.32,8.47c-2.24.64-4.72.96-7.91.96-8.39,0-13.03-5.2-13.03-13.35v-20.94Z'/%3e%3cpath%20class='st2'%20d='M355.22,58.82c-3.92,2.16-8.39,3.28-14.15,3.28-13.59,0-21.5-7.83-21.5-21.34,0-11.91,6.31-21.74,19.1-21.74,15.27,0,19.58,10.47,19.58,24.85h-28.45c.48,6.63,5.11,10.39,11.75,10.39,5.2,0,9.67-1.92,13.67-4.15v8.71ZM348.03,36.52c-.32-5.19-2.72-9.67-8.71-9.67s-9.03,4.16-9.51,9.67h18.22Z'/%3e%3cpath%20class='st2'%20d='M366.57,19.98h9.51v9.35h.16c.48-3.84,4.87-10.31,11.27-10.31,1.04,0,2.16,0,3.27.32v10.79c-.96-.56-2.88-.88-4.79-.88-8.71,0-8.71,10.87-8.71,16.78v15.11h-10.71V19.98Z'/%3e%3cpath%20class='st0'%20d='M229.73,80.9c0,3.67-2.97,6.64-6.64,6.64s-6.64-2.97-6.64-6.64,2.97-6.64,6.64-6.64,6.64,2.97,6.64,6.64'/%3e%3cpath%20class='st0'%20d='M203.17,80.9c0,3.67-2.97,6.64-6.64,6.64s-6.64-2.97-6.64-6.64,2.97-6.64,6.64-6.64,6.64,2.97,6.64,6.64'/%3e%3cpath%20class='st1'%20d='M296.14,80.9c0,3.67-2.97,6.64-6.64,6.64s-6.64-2.97-6.64-6.64,2.97-6.64,6.64-6.64,6.64,2.97,6.64,6.64'/%3e%3cpath%20class='st1'%20d='M378.47,80.9c0,3.67-2.97,6.64-6.64,6.64s-6.64-2.97-6.64-6.64,2.97-6.64,6.64-6.64,6.64,2.97,6.64,6.64'/%3e%3cpath%20class='st1'%20d='M269.57,80.9c0,3.67-2.97,6.64-6.64,6.64s-6.64-2.97-6.64-6.64,2.97-6.64,6.64-6.64,6.64,2.97,6.64,6.64'/%3e%3cpath%20class='st3'%20d='M107.55,80.9c0,3.67-2.97,6.64-6.64,6.64s-6.64-2.97-6.64-6.64,2.97-6.64,6.64-6.64,6.64,2.97,6.64,6.64'/%3e%3crect%20class='st0'%20x='134.12'%20y='75.59'%20width='42.5'%20height='10.62'/%3e%3crect%20class='st1'%20x='309.42'%20y='75.59'%20width='42.5'%20height='10.62'/%3e%3c/svg%3e";var pl=P('CODE_SATURNE'),ml=P('CODE_ASTER'),_l=P(' '),bl=P('
        ',1);function xl(t,r){Me(r,!0);let a=V(()=>r.shownCases!==r.totalCases),n=D(!1);var s=bl(),i=G(s),o=_(i),l=_(o);{var d=F=>{var M=pl();ee(()=>ke(M,"src",gl)),u(F,M)},m=V(()=>{var F;return!Ft()||((F=Ft())==null?void 0:F.solver)==="code_saturne"}),x=F=>{var M=ml();ee(()=>ke(M,"src",hl)),u(F,M)},k=V(()=>{var F;return((F=Ft())==null?void 0:F.solver)==="code_aster"}),b=F=>{var M=_l(),L=_(M,!0);v(M),ee(B=>ae(L,B),[()=>{var B;return(B=Ft())==null?void 0:B.solver}]),u(F,M)};Y(l,F=>{e(m)?F(d):e(k)?F(x,1):F(b,-1)})}v(o);var z=E(o,2),A=_(z),h=_(A),c=_(h);{var C=F=>{var M=Ve();ee(()=>ae(M,`${r.shownCases??""}/`)),u(F,M)};Y(c,F=>{e(a)&&F(C)})}var R=E(c,1,!0);v(h);var p=E(h,2),w=_(p,!0);v(p),v(A);var $=E(A,2),g=_($),N=_(g);{var I=F=>{var M=Ve();ee(()=>ae(M,`${r.shownRunning??""}/`)),u(F,M)};Y(N,F=>{e(a)&&F(I)})}var T=E(N,1,!0);v(g),we(2),v($);var q=E($,2),H=_(q),j=_(H);{var U=F=>{var M=Ve();ee(()=>ae(M,`${r.shownConverged??""}/`)),u(F,M)};Y(j,F=>{e(a)&&F(U)})}var X=E(j,1,!0);v(H),we(2),v(q),v(z);var K=E(z,2),re=_(K),y=_(re);Le(y,{get icon(){return Ri},size:18}),v(re),v(K),v(i);var S=E(i,2);{var O=F=>{vl(F,{onClose:()=>f(n,!1)})};Y(S,F=>{e(n)&&F(O)})}ee(()=>{ae(R,r.totalCases),ae(w,r.totalCases===1?"case":"cases"),ae(T,r.totalRunning),ae(X,r.totalConverged)}),ue("click",re,()=>f(n,!0)),u(t,s),Ae()}Ge(["click"]);const yl=""+new URL("../assets/simvia-logo.kLDq7Uoj.svg",import.meta.url).href;var wl=P('
        '),kl=P('

        '),Cl=P("
        "),Sl=P('
        '),$l=P('
        '),Pl=P('
        ');function jt(t,r){let a=me(r,"eyebrow",3,""),n=me(r,"title",3,""),s=me(r,"wide",3,!1);var i=Pl(),o=E(_(i),2);{var l=m=>{var x=$l(),k=_(x);{var b=c=>{var C=be(),R=G(C);ft(R,()=>r.titleSlot),u(c,C)},z=c=>{var C=Cl(),R=_(C);{var p=g=>{var N=wl(),I=_(N,!0);v(N),ee(()=>ae(I,a())),u(g,N)};Y(R,g=>{a()&&g(p)})}var w=E(R,2);{var $=g=>{var N=kl(),I=_(N,!0);v(N),ee(()=>ae(I,n())),u(g,N)};Y(w,g=>{n()&&g($)})}v(C),u(c,C)};Y(k,c=>{r.titleSlot?c(b):c(z,-1)})}var A=E(k,2);{var h=c=>{var C=Sl(),R=_(C);ft(R,()=>r.actions),v(C),u(c,C)};Y(A,c=>{r.actions&&c(h)})}v(x),u(m,x)};Y(o,m=>{(a()||n()||r.titleSlot||r.actions)&&m(l)})}var d=E(o,2);ft(d,()=>r.children),v(i),ee(()=>{Se(i,1,`${s()?"col-span-12":"col-span-6"} bg-card border border-border rounded-[10px] p-[16px_18px_18px] relative overflow-clip animate-rise`),ke(i,"id",r.id)}),u(t,i)}function nr(t,r){Me(r,!0);let a=me(r,"checked",15,!0);Ne(()=>{a()?mr(r.name,r.onRefresh,r.intervalMs):vt(r.name)}),vr(()=>vt(r.name)),Ot(t,{get checked(){return a()},onchange:n=>a(n),size:14,label:"Auto-refresh",labelFirst:!0}),Ae()}var El=P(" ",1),Rl=P('
      • '),Nl=P(''),Ml=P(' ',1);function Al(t,r){Me(r,!0);let a=me(r,"label",3,"More"),n=D(!1),s=D(void 0),i=D(void 0),o=D("");function l(){if(!e(s)||!e(i))return;const c=e(s).getBoundingClientRect(),C=e(i).offsetHeight,R=4,p=window.innerHeight-c.bottom-R,$=pp?c.top-R-C:c.bottom+R,g=Math.min(c.left,window.innerWidth-180);f(o,`position:fixed; top:${$}px; left:${g}px; min-width:170px; z-index:9999;`)}async function d(){f(n,!e(n)),e(n)&&(await Et(),l())}function m(c){c.disabled||(f(n,!1),c.onClick())}function x(c){e(n)&&e(s)&&!e(s).contains(c.target)&&e(i)&&!e(i).contains(c.target)&&f(n,!1)}var k=Ml();rt("mousedown",it,x),rt("keydown",it,c=>{c.key==="Escape"&&f(n,!1)}),rt("scroll",it,()=>{e(n)&&f(n,!1)});var b=G(k),z=_(b);Pe(z,{variant:"secondary",size:"sm",onclick:d,children:(c,C)=>{var R=El(),p=G(R);Le(p,{get icon(){return xi}});var w=E(p);ee(()=>ae(w,` ${a()??""}`)),u(c,R)},$$slots:{default:!0}}),v(b),nt(b,c=>f(s,c),()=>e(s));var A=E(b,2);{var h=c=>{Jt(c,{children:(C,R)=>{var p=Nl();He(p,21,()=>r.items,w=>w.label,(w,$)=>{var g=Rl(),N=_(g),I=_(N);Le(I,{get icon(){return e($).icon},size:14});var T=E(I);v(N),v(g),ee(()=>{N.disabled=e($).disabled,ke(N,"aria-disabled",e($).disabled),Se(N,1,`w-full flex items-center gap-2 text-left py-[7px] px-[10px] bg-transparent border border-transparent rounded-md text-[12.5px] text-ink transition-[background] duration-[120ms] ease-in-out disabled:opacity-40 disabled:pointer-events-none ${e($).disabled?"":"cursor-pointer hover:bg-edf-gris-clair hover:border-border"}`),ae(T,` ${e($).label??""}`)}),ue("mousedown",N,q=>{q.stopPropagation(),m(e($))}),u(w,g)}),v(p),nt(p,w=>f(i,w),()=>e(i)),ee(()=>Ze(p,e(o))),u(C,p)}})};Y(A,c=>{e(n)&&c(h)})}u(t,k),Ae()}Ge(["mousedown"]);const It=[{key:"note",label:"Note"},{key:"nprocs",label:"MPI Ranks"},{key:"nt",label:"Thread Count"},{key:"last_iter",label:"Last Iter"},{key:"duration",label:"Duration"},{key:"last_mod",label:"Last Modified"},{key:"resu_size_mb",label:"RESU Size (MB)"},{key:"backend_execution_time_s",label:"Cloud Time (s)"},{key:"backend_core_count",label:"Cloud Cores"}];let Xt=D(ce([])),Tr=D(ce([])),pt=D(ce(new Set)),br=D(""),_n=D(""),Rt=D(ce([])),st=D(ce([])),Yt=D(""),Pa=D(!0),Tl=D(!0),Kt=D(ce(mn("csauto_status_views",{}))),bn=D(ce(localStorage.getItem("csauto_status_view_selected")??""));function xn(){return e(Xt)}function yn(){return e(Tr)}function Bt(){return e(pt)}function Vn(){return e(br)}function an(){return e(_n)}function Ll(){return e(Rt)}function Fl(){return e(Rt).filter(t=>e(Tr).includes(t))}function Il(){return e(Rt).filter(t=>It.some(r=>r.key===t))}function zl(){return e(st)}function Dl(){return e(Yt)}function Ol(){return e(Pa)}function Un(){return e(Kt)}function Hl(){return e(bn)}function jl(t){f(Xt,t,!0)}function Vl(t){const r=e(Tr).length===0&&t.length>0;f(Tr,t,!0),r&&e(Rt).length===0&&f(Rt,[...t,...It.map(a=>a.key)],!0)}function Ul(t){f(br,t,!0)}function Bn(t){f(Rt,t,!0)}function Bl(t){f(Yt,t,!0)}function ql(t){f(Pa,t,!0)}function Kl(t){f(Tl,t,!0)}function wn(t){f(bn,t,!0),localStorage.setItem("csauto_status_view_selected",t)}function sn(t){const r=new Set(e(pt));r.has(t)?r.delete(t):r.add(t),f(pt,r,!0),f(br,t,!0),f(_n,t,!0)}function Ea(t){f(pt,new Set([t]),!0),f(br,t,!0),f(_n,t,!0)}function on(t,r,a){const n=a.indexOf(t),s=a.indexOf(r);if(n<0||s<0)return;const[i,o]=n{const a=Gl(r);return t.every(n=>a.includes(n))})}function Wl(t){if(t==null||t==="")return"";const r=Number(t);return Number.isFinite(r)?r:String(t).toLowerCase()}function Gn(t,r){var n;if(r==="case_id")return t.case_id;if(r==="status")return t.status??"";if(r==="note")return t.note??"";if(r==="nprocs")return t.nprocs??0;if(r==="nt")return t.nt??0;if(r==="last_iter")return t.last_iter??0;if(r==="duration")return t.duration_s??0;if(r==="last_mod")return t.last_mod??"";if(r==="resu_size_mb")return t.resu_size_mb??0;if(r==="backend_execution_time_s")return t.backend_execution_time_s??0;if(r==="backend_core_count")return t.backend_core_count??0;const a=(n=t.doe)==null?void 0:n[r];return a!==void 0?Wl(a):""}function Xl(t,r){return t===""&&r===""?0:t===""?1:r===""?-1:typeof t=="number"&&typeof r=="number"?t-r:String(t).localeCompare(String(r))}function Na(){const t=Ra();return e(st).length===0?t:[...t].sort((r,a)=>{for(const n of e(st)){const s=Gn(r,n.key),i=Gn(a,n.key),o=Xl(s,i);if(o!==0)return n.dir==="asc"?o:-o}return 0})}function Rr(){return Na().map(t=>t.case_id)}function Yl(t,r){const a=e(st).findIndex(n=>n.key===t);if(r)if(a>=0){const n=[...e(st)];n[a]={key:t,dir:n[a].dir==="asc"?"desc":"asc"},f(st,n,!0)}else f(st,[...e(st),{key:t,dir:"asc"}],!0);else a>=0&&e(st).length===1?f(st,[{key:t,dir:e(st)[0].dir==="asc"?"desc":"asc"}],!0):f(st,[{key:t,dir:"asc"}],!0)}function Ql(){return{visibleColumns:[...e(Rt)],search:e(Yt),sorts:e(st).map(t=>({...t}))}}function Zl(t){f(Rt,[...t.visibleColumns],!0),f(Yt,t.search,!0),f(st,t.sorts.map(r=>({...r})),!0)}function Jl(t){f(Kt,{...e(Kt),[t]:Ql()},!0),Hr("csauto_status_views",e(Kt)),wn(t)}function ec(t){const r={...e(Kt)};delete r[t],f(Kt,r,!0),Hr("csauto_status_views",r),e(bn)===t&&wn("")}function Sr(){const t=new Set;for(const r of e(Xt))e(pt).has(r.case_id)&&t.add((r.status??"").toUpperCase());return t}function Wn(){for(const t of e(Xt))if(e(pt).has(t.case_id)&&t.resu_size_mb!=null&&t.resu_size_mb>0)return!0;return!1}function tc(t,r){const a=r.length,n=t.length,s=r.filter(d=>d.status==="RUNNING").length,i=t.filter(d=>d.status==="RUNNING").length,o=r.filter(d=>d.convergence==="converged").length,l=t.filter(d=>d.convergence==="converged").length;return{totalCases:a,shownCases:n,totalRunning:s,shownRunning:i,totalConverged:o,shownConverged:l}}var rc=P('
        DOE columns
        '),nc=P('
        ');function ac(t,r){Me(r,!0);let a=me(r,"initialName",3,""),n=V(yn),s=D(ce(a())),i=D(ce(new Set(r.initialColumns??[...e(n),...It.map(U=>U.key)])));function o(U){const X=new Set(e(i));X.has(U)?X.delete(U):X.add(U),f(i,X,!0)}function l(U,X){const K=new Set(e(i));U.forEach(re=>X?K.add(re):K.delete(re)),f(i,K,!0)}function d(){const U=e(s).trim();U&&r.onSave(U,[...e(i)])}function m(U){U.key==="Escape"&&r.onCancel()}function x(U){U.target.dataset.backdrop!==void 0&&r.onCancel()}let k=D(void 0);Ne(()=>{var U,X;(U=e(k))==null||U.focus(),(X=e(k))==null||X.select()});var b=nc(),z=_(b),A=_(z),h=_(A,!0);v(A);var c=E(A,2),C=E(_(c),2);Ye(C),nt(C,U=>f(k,U),()=>e(k)),v(c);var R=E(c,2);{var p=U=>{var X=rc(),K=_(X),re=E(_(K),2),y=_(re),S=E(y,2);v(re),v(K);var O=E(K,2);He(O,20,()=>e(n),F=>F,(F,M)=>{{let L=V(()=>e(i).has(M));Ot(F,{get checked(){return e(L)},onchange:()=>o(M),size:14,get label(){return M}})}}),v(O),v(X),ue("click",y,()=>l(e(n),!0)),ue("click",S,()=>l(e(n),!1)),u(U,X)};Y(R,U=>{e(n).length>0&&U(p)})}var w=E(R,2),$=_(w),g=E(_($),2),N=_(g),I=E(N,2);v(g),v($);var T=E($,2);He(T,21,()=>It,U=>U.key,(U,X)=>{{let K=V(()=>e(i).has(e(X).key));Ot(U,{get checked(){return e(K)},onchange:()=>o(e(X).key),size:14,get label(){return e(X).label}})}}),v(T),v(w);var q=E(w,2),H=_(q);Pe(H,{variant:"secondary",get onclick(){return r.onCancel},children:(U,X)=>{we();var K=Ve("Cancel");u(U,K)},$$slots:{default:!0}});var j=E(H,2);{let U=V(()=>!e(s).trim());Pe(j,{variant:"primary",onclick:d,get disabled(){return e(U)},children:(X,K)=>{we();var re=Ve("Save");u(X,re)},$$slots:{default:!0}})}v(q),v(z),v(b),ee(()=>ae(h,r.mode==="create"?"New view":"Edit view")),ue("keydown",b,m),ue("click",b,x),tt(C,()=>e(s),U=>f(s,U)),ue("click",N,()=>l(It.map(U=>U.key),!0)),ue("click",I,()=>l(It.map(U=>U.key),!1)),u(t,b),Ae()}Ge(["keydown","click"]);var sc=P('
        ',1);function oc(t,r){Me(r,!0);let a=V(()=>Object.keys(Un())),n=D(ce(Hl())),s=D(!1),i=D("create"),o=V(()=>[{value:"",label:"All columns"},...e(a).map($=>({value:$,label:$}))]);function l($){if(f(n,$,!0),wn($),$==="")Bn([...yn(),...It.map(g=>g.key)]);else{const g=Un();g[$]&&Zl(g[$])}}function d(){f(i,"edit"),f(s,!0)}function m(){f(i,"create"),f(s,!0)}function x($,g){Bn(g),e(i)==="edit"&&e(n)&&$!==e(n)&&ec(e(n)),Jl($),f(n,$,!0),f(s,!1)}function k(){f(s,!1)}var b=sc(),z=G(b),A=_(z);lt(A,{class:"w-[140px]",get options(){return e(o)},get value(){return e(n)},onchange:l});var h=E(A,2),c=_(h);Le(c,{get icon(){return Si},size:14}),v(h);var C=E(h,2),R=_(C);Le(R,{get icon(){return $i},size:14}),v(C),v(z);var p=E(z,2);{var w=$=>{Jt($,{children:(g,N)=>{{let I=V(()=>e(i)==="edit"?e(n):""),T=V(Ll);ac(g,{get mode(){return e(i)},get initialName(){return e(I)},get initialColumns(){return e(T)},onSave:x,onCancel:k})}}})};Y(p,$=>{e(s)&&$(w)})}ue("click",h,d),ue("click",C,m),u(t,b),Ae()}Ge(["click"]);var ic=P('
        '),lc=P(""),cc=P(' '),uc=P(' '),dc=P('
        '),fc=P(" "),vc=P(''),gc=P(''),hc=P(" Open GUI",1),pc=P(""),mc=P(" "),_c=P(""),bc=P('
        ');function xc(t,r){Me(r,!0);let a=V(()=>r.rows.length>0&&r.rows.every(y=>e(m).has(y.case_id))),n=V(()=>r.rows.some(y=>e(m).has(y.case_id))),s=V(()=>e(n)&&!e(a)),i=V(Fl),o=V(()=>new Set(Il())),l=V(zl),d=V(Ol),m=V(Bt),x=V(()=>{const y=[{key:"_select",label:"",kind:"meta",sticky:"left"},{key:"case_id",label:"Case",kind:"meta",sticky:"left"},{key:"status",label:"Status",kind:"meta",sticky:"left"},...e(o).has("note")?[{key:"note",label:"Note",kind:"meta"}]:[]],S=e(i).map(L=>({key:L,label:L,kind:"doe"})),F=[{key:"nprocs",label:"MPI Ranks",kind:"calc"},{key:"nt",label:"Thread Count",kind:"calc"},{key:"last_iter",label:"Last Iter",kind:"calc"},{key:"duration",label:"Duration",kind:"calc"},{key:"last_mod",label:"Last Modified",kind:"calc"},{key:"resu_size_mb",label:"RESU Size (MB)",kind:"calc"},{key:"backend_execution_time_s",label:"Cloud Time (s)",kind:"calc"},{key:"backend_core_count",label:"Cloud Cores",kind:"calc"}].filter(L=>e(o).has(L.key)),M=cr("gui")?[{key:"_actions",label:"",kind:"meta",sticky:"right"}]:[];return[...y,...S,...F,...M]});function k(y){const S=e(l).findIndex(F=>F.key===y);if(S<0)return"";const O=e(l)[S].dir==="asc"?"▲":"▼";return e(l).length>1?`${O}${S+1}`:O}function b(y,S){y!=="_actions"&&Yl(y,S.ctrlKey||S.metaKey)}function z(y,S){S.shiftKey?on(an()||y,y,Rr()):S.ctrlKey||S.metaKey?sn(y):Ea(y)}function A(y,S){var F;const O=((F=y.status)==null?void 0:F.toUpperCase())??"";O!=="DONE"&&O!=="FAILED"||(S.preventDefault(),r.onContextMenu(y.case_id,S.clientX,S.clientY,y.convergence??""))}async function h(y){const S=y.note??"",O=await gn("Case note:",S,`Note — ${y.case_id}`,"",!0);if(O!==null)try{await zo(y.case_id,O)}catch(F){await Xe(`Failed to set note: ${F instanceof Error?F.message:F}`,"Error")}}async function c(y){try{await Oo(y)}catch(S){await Xe(`Failed to open GUI: ${S instanceof Error?S.message:S}`,"Error")}}function C(y,S){const O=(y==null?void 0:y.toUpperCase())??"";return(O==="DONE"||O==="FAILED")&&S==="converged"?"CONVERGED":(O==="DONE"||O==="FAILED")&&S==="not_converged"?"NOT CONVERGED":O}function R(y,S){const O=(y==null?void 0:y.toUpperCase())??"";return(O==="DONE"||O==="FAILED")&&S==="converged"?"status-converged":(O==="DONE"||O==="FAILED")&&S==="not_converged"?"status-not-converged":O==="RUNNING"?"status-running":O==="DONE"?"status-done":O==="FAILED"?"status-failed":O==="PREPARED"?"status-prepared":"status-unknown"}function p(y){return e(m).has(y.case_id)?"row-selected":""}function w(y,S){var O;return S.key==="case_id"?y.case_id:S.key==="nprocs"?y.nprocs!=null?String(y.nprocs):"":S.key==="nt"?y.nt!=null?String(y.nt):"":S.key==="last_iter"?y.last_iter!=null?String(y.last_iter):"":S.key==="duration"?y.duration??"":S.key==="last_mod"?$(y.last_mod):S.key==="resu_size_mb"?y.resu_size_mb!=null?String(y.resu_size_mb):"":S.key==="backend_execution_time_s"?y.backend_execution_time_s!=null?y.backend_execution_time_s.toFixed(1):"":S.key==="backend_core_count"?y.backend_core_count!=null?String(y.backend_core_count):"":S.kind==="doe"?((O=y.doe)==null?void 0:O[S.key])!=null&&String(y.doe[S.key])!==""?String(y.doe[S.key]):"—":""}function $(y){if(!y)return"";try{const S=new Date(y),O=new Date,F=S.toDateString()===O.toDateString(),M=new Date(O);M.setDate(M.getDate()-1);const L=S.toDateString()===M.toDateString(),B=S.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"});return F?`Today ${B}`:L?`Yesterday ${B}`:S.toLocaleDateString([],{month:"short",day:"numeric"})+` ${B}`}catch{return y}}let g=D(ce({})),N=D(void 0);Ne(()=>{e(d),e(x),r.rows,!(!e(N)||!e(d))&&Et().then(()=>{if(!e(N))return;const y=Array.from(e(N).querySelectorAll("thead th.sticky-left")),S={};let O=0;y.forEach(F=>{const M=F.dataset.colKey??"";S[M]=O,O+=F.getBoundingClientRect().width}),f(g,S,!0)})});function I(y,S,O){const F=O&&y.key!=="_select"&&y.key!=="_actions"?["status-sortable"]:[];if(!O&&y.key==="case_id"&&F.push("case-id"),y.kind==="doe"&&F.push("status-col-doe"),!O&&y.key==="note"&&F.push("max-w-[140px] whitespace-nowrap overflow-hidden text-ellipsis cursor-pointer hover:underline hover:decoration-dotted hover:underline-offset-2"),e(d)&&y.sticky==="left"&&F.push("sticky-left"),e(d)&&y.sticky==="right"&&F.push("sticky-right"),e(d)&&y.sticky==="left"){const M=e(x)[S+1];(!M||M.sticky!=="left")&&F.push("sticky-divider")}if(e(d)&&y.sticky==="right"){const M=e(x)[S-1];(!M||M.sticky!=="right")&&F.push("sticky-divider")}return F.join(" ")}function T(y,S){return I(y,S,!0)}function q(y,S){return I(y,S,!1)}function H(y){return!e(d)||!y.sticky?"":y.sticky==="right"?"position:sticky;right:0;":y.sticky==="left"&&y.key in e(g)?`position:sticky;left:${e(g)[y.key]}px;`:""}var j=bc(),U=_(j),X=_(U),K=_(X);He(K,23,()=>e(x),y=>y.key,(y,S,O)=>{var F=be(),M=G(F);{var L=ie=>{var W=ic(),ne=_(W),oe=_(ne);Ot(oe,{get checked(){return e(a)},get indeterminate(){return e(s)},get onchange(){return r.onToggleAll},size:14}),v(ne),v(W),ee((de,ve)=>{Se(W,1,`${de??""} !px-2.5 !py-0 w-9`),ke(W,"data-col-key",e(S).key),Ze(W,ve)},[()=>T(e(S),e(O)),()=>H(e(S))]),u(ie,W)},B=ie=>{var W=lc();ee((ne,oe)=>{Se(W,1,ne),ke(W,"data-col-key",e(S).key),Ze(W,oe)},[()=>dt(T(e(S),e(O))),()=>H(e(S))]),u(ie,W)},te=ie=>{var W=uc(),ne=_(W),oe=E(ne);{var de=se=>{var _e=cc(),he=_(_e,!0);v(_e),ee(Ee=>ae(he,Ee),[()=>k(e(S).key)]),u(se,_e)},ve=V(()=>k(e(S).key));Y(oe,se=>{e(ve)&&se(de)})}v(W),ee((se,_e)=>{Se(W,1,se),ke(W,"data-col-key",e(S).key),Ze(W,_e),ae(ne,`${e(S).label??""} `)},[()=>dt(T(e(S),e(O))),()=>H(e(S))]),ue("click",W,se=>b(e(S).key,se)),ue("keydown",W,se=>{(se.key==="Enter"||se.key===" ")&&(se.preventDefault(),b(e(S).key,se))}),u(ie,W)};Y(M,ie=>{e(S).key==="_select"?ie(L):e(S).key==="_actions"?ie(B,1):ie(te,-1)})}u(y,F)}),v(K),v(X);var re=E(X);He(re,21,()=>r.rows,y=>y.case_id,(y,S)=>{var O=_c();He(O,23,()=>e(x),F=>F.key,(F,M,L)=>{var B=be(),te=G(B);{var ie=ve=>{var se=dc(),_e=_(se),he=_(_e);{let Ee=V(()=>e(m).has(e(S).case_id));Ot(he,{get checked(){return e(Ee)},onchange:()=>r.onToggleRow(e(S).case_id),size:14})}v(_e),v(se),ee((Ee,pe)=>{Se(se,1,`${Ee??""} !px-2.5 !py-0 w-9`),ke(se,"data-col-key",e(M).key),Ze(se,pe)},[()=>q(e(M),e(L)),()=>H(e(M))]),u(ve,se)},W=ve=>{var se=fc(),_e=_(se),he=_(_e,!0);v(_e),v(se),ee((Ee,pe,Ie,ze)=>{Se(se,1,Ee),ke(se,"data-col-key",e(M).key),Ze(se,pe),Se(_e,1,`status-pill ${Ie??""}`),ae(he,ze)},[()=>dt(q(e(M),e(L))),()=>H(e(M)),()=>R(e(S).status,e(S).convergence),()=>C(e(S).status,e(S).convergence)]),u(ve,se)},ne=ve=>{var se=gc(),_e=_(se);{var he=pe=>{var Ie=Ve();ee(()=>ae(Ie,e(S).note)),u(pe,Ie)},Ee=pe=>{var Ie=vc(),ze=_(Ie);Le(ze,{get icon(){return Ci},size:12}),v(Ie),u(pe,Ie)};Y(_e,pe=>{e(S).note?pe(he):pe(Ee,-1)})}v(se),ee((pe,Ie)=>{Se(se,1,pe),ke(se,"data-col-key",e(M).key),Ze(se,Ie),ke(se,"title",e(S).note||"Add note")},[()=>dt(q(e(M),e(L))),()=>H(e(M))]),ue("click",se,pe=>{pe.stopPropagation(),h(e(S))}),ue("keydown",se,pe=>{(pe.key==="Enter"||pe.key===" ")&&(pe.preventDefault(),pe.stopPropagation(),h(e(S)))}),u(ve,se)},oe=ve=>{var se=pc(),_e=_(se);Pe(_e,{variant:"primary",size:"sm",onclick:he=>{he.stopPropagation(),c(e(S).case_id)},children:(he,Ee)=>{var pe=hc(),Ie=G(pe);Le(Ie,{get icon(){return yi},size:12}),we(),u(he,pe)},$$slots:{default:!0}}),v(se),ee((he,Ee)=>{Se(se,1,he),ke(se,"data-col-key",e(M).key),Ze(se,Ee)},[()=>dt(q(e(M),e(L))),()=>H(e(M))]),u(ve,se)},de=ve=>{var se=mc(),_e=_(se,!0);v(se),ee((he,Ee,pe)=>{Se(se,1,he),ke(se,"data-col-key",e(M).key),Ze(se,Ee),ae(_e,pe)},[()=>dt(q(e(M),e(L))),()=>H(e(M)),()=>w(e(S),e(M))]),u(ve,se)};Y(te,ve=>{e(M).key==="_select"?ve(ie):e(M).key==="status"?ve(W,1):e(M).key==="note"?ve(ne,2):e(M).key==="_actions"?ve(oe,3):ve(de,-1)})}u(F,B)}),v(O),ee(F=>Se(O,1,F),[()=>dt(p(e(S)))]),ue("click",O,F=>z(e(S).case_id,F)),ue("contextmenu",O,F=>A(e(S),F)),u(y,O)}),v(re),v(U),nt(U,y=>f(N,y),()=>e(N)),v(j),ee(()=>Se(j,1,`table-wrap ${e(d)?"sticky-enabled":""}`)),u(t,j),Ae()}Ge(["click","keydown","contextmenu"]);var yc=P('
        ');function wc(t,r){Me(r,!0);let a=V(()=>{const b=Math.min(r.x,window.innerWidth-180),z=Math.min(r.y,window.innerHeight-120);return`left: ${b}px; top: ${z}px;`});async function n(b){try{for(const z of r.cases)await Do(z,b)}catch(z){console.error("Failed to set convergence:",z)}r.onClose()}const s=r.cases.length>1?` (${r.cases.length})`:"";var i=yc();rt("keydown",it,b=>{b.key==="Escape"&&r.onClose()});var o=_(i),l=_(o);v(o);var d=E(o,2),m=_(d);v(d);var x=E(d,2),k=_(x);v(x),v(i),ee(()=>{Ze(i,e(a)),Se(o,1,`w-full text-left py-[7px] px-[10px] bg-transparent border border-transparent rounded-md text-[12.5px] cursor-pointer transition-[background] duration-[120ms] ease-in-out hover:bg-edf-gris-clair hover:border-border ${r.currentValue==="converged"?"is-active":""}`),ae(l,`Mark Converged${s}`),Se(d,1,`w-full text-left py-[7px] px-[10px] bg-transparent border border-transparent rounded-md text-[12.5px] cursor-pointer transition-[background] duration-[120ms] ease-in-out hover:bg-edf-gris-clair hover:border-border ${r.currentValue==="not_converged"?"is-active":""}`),ae(m,`Mark Not Converged${s}`),Se(x,1,`w-full text-left py-[7px] px-[10px] bg-transparent border border-transparent rounded-md text-[12.5px] cursor-pointer transition-[background] duration-[120ms] ease-in-out hover:bg-edf-gris-clair hover:border-border ${r.currentValue===""?"is-active":""}`),ae(k,`Clear Mark${s}`)}),ue("click",o,()=>n("converged")),ue("click",d,()=>n("not_converged")),ue("click",x,()=>n("")),u(t,i),Ae()}Ge(["click"]);var kc=P(" Refresh",1),Cc=P(" ",1),Sc=P(" Run",1),$c=P(" Restart",1),Pc=P(" Stop",1),Ec=P(" Kill",1),Rc=P(" Clean",1),Nc=P('
        ',1),Mc=P('
        ',1);function Ac(t,r){Me(r,!0);async function a(){await Lo(),r.onRefresh()}function n(){return[...Bt()]}async function s(){const L=n();if(!L.length)return;const B=await Ys(L);if(B)try{await zn({cases:L,n:B.n,nt:B.nt,maxParallel:B.maxParallel,backend:B.backend,options:B.options}),r.onRefresh()}catch(te){await Xe(`Run failed: ${te instanceof Error?te.message:te}`,"Error")}}async function i(){const L=n();if(!L.length)return;const B=await Qs(L);if(B)try{await zn({cases:L,n:B.n,nt:B.nt,maxParallel:B.maxParallel,restart:!0,restartMode:B.restartMode,restartValue:B.restartValue}),r.onRefresh()}catch(te){await Xe(`Restart failed: ${te instanceof Error?te.message:te}`,"Error")}}async function o(){const L=n();if(!(!L.length||!await Fn(`Kill ${L.length} case${L.length>1?"s":""}?`,"Confirm Kill","Kill","danger")))try{await Fo(L),r.onRefresh()}catch(te){await Xe(`Kill failed: ${te instanceof Error?te.message:te}`,"Error")}}async function l(){const L=n();if(!(!L.length||!await Fn(`Stop ${L.length} case${L.length>1?"s":""} gracefully (checkpoint + exit)?`,"Confirm Stop","Stop","danger")))try{await kr({cases:L,action:"stop"}),r.onRefresh(),Cr(`Stop requested for ${L.length} case${L.length>1?"s":""} — will checkpoint and exit`)}catch(te){await Xe(`Stop failed: ${te instanceof Error?te.message:te}`,"Error")}}async function d(){const L=n();if(L.length)try{await kr({cases:L,action:"checkpoint"}),r.onRefresh(),Cr(`Checkpoint requested for ${L.length} case${L.length>1?"s":""}`)}catch(B){await Xe(`Checkpoint failed: ${B instanceof Error?B.message:B}`,"Error")}}async function m(){const L=n();if(!L.length)return;const B=await gn("Extend by how many additional time steps?","500","Extend Run");if(B===null)return;const te=parseInt(B,10);if(!Number.isFinite(te)||te<=0){await Xe("Enter a positive integer number of time steps.","Error");return}try{await kr({cases:L,action:"extend",value:te}),r.onRefresh(),Cr(`Extended ${L.length} case${L.length>1?"s":""} by ${te} time steps`)}catch(ie){await Xe(`Extend failed: ${ie instanceof Error?ie.message:ie}`,"Error")}}async function x(){const L=n();if(L.length)try{await kr({cases:L,action:"flush"}),Cr(`Flush requested for ${L.length} case${L.length>1?"s":""}`)}catch(B){await Xe(`Flush failed: ${B instanceof Error?B.message:B}`,"Error")}}async function k(){const L=n();if(!L.length)return;const B=await Zs(L);if(B)try{await Io({cases:L,keepLast:B.keepLast,keepResu:B.keepResu,deleteResu:B.deleteResu,pruneResu:!0}),r.onRefresh(),ll()}catch(te){await Xe(`Cleanup failed: ${te instanceof Error?te.message:te}`,"Error")}}function b(){const L=Rr();L.length>0&&L.every(te=>Bt().has(te))?Kn():qn(L)}function z(L){sn(L)}let A=D(ce(tr("status")));Ne(()=>{rr("status",e(A))}),ql(!0);let h=V(()=>{const L=Sr();return L.size>0&&[...L].some(B=>B==="PREPARED"||B==="DONE"||B==="FAILED")}),c=V(()=>{if(!cr("restart"))return!1;const L=Sr();return L.size>0&&[...L].some(B=>B==="DONE"||B==="FAILED")&&Wn()}),C=V(()=>{const L=Sr();return L.size>0&&L.has("RUNNING")}),R=V(()=>{if(!cr("control"))return!1;const L=Sr();return L.size>0&&L.has("RUNNING")});const p=[{label:"Extend",icon:wi,onClick:m,action:"extend"},{label:"Checkpoint",icon:Ei,onClick:d,action:"checkpoint"},{label:"Flush",icon:bi,onClick:x,action:"flush"}];let w=V(()=>p.filter(L=>bo(L.action)).map(L=>({label:L.label,icon:L.icon,onClick:L.onClick,disabled:!e(R)}))),$=V(Wn),g=D(ce(Dl())),N=null;function I(){N&&clearTimeout(N),N=setTimeout(()=>{Bl(e(g))},200)}Ne(()=>Kl(e(A)));let T=D(!1),q=D(0),H=D(0),j=D(ce([])),U=D("");function X(L,B,te,ie){f(j,Bt().has(L)?[...Bt()]:[L],!0),f(U,ie,!0),f(q,B,!0),f(H,te,!0),f(T,!0)}function K(){f(T,!1)}function re(L){const B=Rr();if(B.length){if(L.key==="Escape"){Kn(),K();return}if((L.ctrlKey||L.metaKey)&&L.key==="a"){L.preventDefault(),qn(B);return}if(L.key==="ArrowDown"||L.key==="ArrowUp"){L.preventDefault();const te=L.key==="ArrowDown"?1:-1,ie=Vn(),W=B.indexOf(ie),ne=Math.max(0,Math.min(B.length-1,W+te)),oe=B[ne];L.shiftKey?on(an()||B[0],oe,B):Ea(oe),Ul(oe);return}if(L.key===" "){L.preventDefault();const te=Vn();te&&(L.shiftKey?on(an()||te,te,Rr()):sn(te))}}}var y=Mc();rt("click",it,K),rt("scroll",it,K);var S=G(y),O=_(S);jt(O,{eyebrow:"Overview",title:"Status",wide:!0,id:"status-card-shell",actions:B=>{var te=Cc(),ie=G(te);{let oe=V(ol);nr(ie,{name:"status",get intervalMs(){return e(oe)},get onRefresh(){return r.onRefresh},get checked(){return e(A)},set checked(de){f(A,de,!0)}})}var W=E(ie,2);{var ne=oe=>{Pe(oe,{variant:"primary",onclick:a,children:(de,ve)=>{var se=kc(),_e=G(se);Le(_e,{get icon(){return er}}),we(),u(de,se)},$$slots:{default:!0}})};Y(W,oe=>{e(A)||oe(ne)})}u(B,te)},children:(B,te)=>{var ie=Nc(),W=G(ie),ne=_(W),oe=_(ne);Ye(oe);var de=E(oe,2);oc(de,{}),v(ne);var ve=E(ne,2),se=_(ve),_e=_(se);v(se);var he=E(se,2);{let J=V(()=>!e(h));Pe(he,{variant:"run",size:"sm",onclick:s,get disabled(){return e(J)},children:(le,xe)=>{var ye=Sc(),fe=G(ye);Le(fe,{get icon(){return Sa}}),we(),u(le,ye)},$$slots:{default:!0}})}var Ee=E(he,2);{var pe=J=>{{let le=V(()=>!e(c));Pe(J,{variant:"warning",size:"sm",onclick:i,get disabled(){return e(le)},children:(xe,ye)=>{var fe=$c(),ge=G(fe);Le(ge,{get icon(){return Pi}}),we(),u(xe,fe)},$$slots:{default:!0}})}},Ie=V(()=>cr("restart"));Y(Ee,J=>{e(Ie)&&J(pe)})}var ze=E(Ee,2);{var Qe=J=>{{let le=V(()=>!e(R));Pe(J,{variant:"warning",size:"sm",onclick:l,get disabled(){return e(le)},children:(xe,ye)=>{var fe=Pc(),ge=G(fe);Le(ge,{get icon(){return mi}}),we(),u(xe,fe)},$$slots:{default:!0}})}},qe=V(()=>cr("control"));Y(ze,J=>{e(qe)&&J(Qe)})}var Je=E(ze,2);{var Te=J=>{Al(J,{get items(){return e(w)}})};Y(Je,J=>{e(w).length>0&&J(Te)})}var $e=E(Je,2);{let J=V(()=>!e(C));Pe($e,{variant:"danger",size:"sm",onclick:o,get disabled(){return e(J)},children:(le,xe)=>{var ye=Ec(),fe=G(ye);Le(fe,{get icon(){return _i}}),we(),u(le,ye)},$$slots:{default:!0}})}var Q=E($e,2);{let J=V(()=>!e($));Pe(Q,{variant:"secondary",size:"sm",onclick:k,get disabled(){return e(J)},children:(le,xe)=>{var ye=Rc(),fe=G(ye);Le(fe,{get icon(){return Ni}}),we(),u(le,ye)},$$slots:{default:!0}})}v(ve),v(W);var Z=E(W,2);{let J=V(Na);xc(Z,{get rows(){return e(J)},onContextMenu:X,onToggleAll:b,onToggleRow:z})}ee(J=>ae(_e,`${J??""} selected`),[()=>Bt().size]),ue("input",oe,I),tt(oe,()=>e(g),J=>f(g,J)),u(B,ie)},$$slots:{actions:!0,default:!0}}),v(S);var F=E(S,2);{var M=L=>{wc(L,{get cases(){return e(j)},get x(){return e(q)},get y(){return e(H)},get currentValue(){return e(U)},onClose:K})};Y(F,L=>{e(T)&&L(M)})}ue("keydown",S,re),u(t,y),Ae()}Ge(["keydown","input"]);var Tc=P('
        ');function Qt(t,r){var a=Tc(),n=_(a);ft(n,()=>r.children),v(a),u(t,a)}function kn(t){return t.map(r=>({value:r,label:r}))}var Lc=P(''),Fc=P(" ",1);function Cn(t,r){Me(r,!0);let a=me(r,"columnLabel",3,"Columns"),n=me(r,"xMinLabel",3,"Iter min"),s=V(()=>kn(r.allCases)),i=V(()=>r.columns.map(d=>({value:d,label:d}))),o=[{value:"zero",label:"Zero"},{value:"restart",label:"Restart start"},{value:"custom",label:"Custom"}];function l(d){r.onXMinChange(Number(d.target.value)||0)}Qt(t,{children:(d,m)=>{var x=Fc(),k=G(x);Re(k,{text:"Cases",children:(w,$)=>{Wt(w,{class:"w-[160px]",get options(){return e(s)},get selected(){return r.selectedCases},get onchange(){return r.onCasesChange},placeholder:"Select cases..."})}});var b=E(k,2);{var z=w=>{var $=be(),g=G($);ft(g,()=>r.middleSlot),u(w,$)};Y(b,w=>{r.middleSlot&&w(z)})}var A=E(b,2);Re(A,{get text(){return a()},children:(w,$)=>{Wt(w,{class:"w-[160px]",get options(){return e(i)},get selected(){return r.selectedColumns},get onchange(){return r.onColumnsChange},placeholder:"Select..."})}});var h=E(A,2);Re(h,{text:"Start from",children:(w,$)=>{lt(w,{class:"w-[140px]",get options(){return o},get value(){return r.startFrom},onchange:g=>r.onStartFromChange(g)})}});var c=E(h,2);{var C=w=>{Re(w,{get text(){return n()},children:($,g)=>{var N=Lc();Ye(N),ee(()=>fn(N,r.xMin)),ue("input",N,l),u($,N)}})};Y(c,w=>{r.startFrom==="custom"&&w(C)})}var R=E(c,2);{var p=w=>{var $=be(),g=G($);ft(g,()=>r.extraSlot),u(w,$)};Y(R,w=>{r.extraSlot&&w(p)})}u(d,x)}}),Ae()}Ge(["input"]);var Ic=P('
        '),zc=P('
        ');function Zt(t,r){let a=me(r,"emptyMessage",3,"");var n=zc(),s=_(n);{var i=l=>{var d=be(),m=G(d);zr(m,()=>r.svgHtml),u(l,d)},o=l=>{var d=Ic(),m=_(d),x=_(m,!0);v(m),v(d),ee(()=>ae(x,a()||"No data to display.")),u(l,d)};Y(s,l=>{r.svgHtml?l(i):l(o,-1)})}v(n),ee(()=>ke(n,"id",r.id)),u(t,n)}async function Sn(t,r="plot.png"){const a=document.getElementById(t),n=a==null?void 0:a.querySelector("svg");if(!n)return;const{width:s,height:i}=Dc(n),o=window.devicePixelRatio||1,l=document.createElement("canvas");l.width=s*o,l.height=i*o;const d=l.getContext("2d");if(!d)return;d.scale(o,o);const m=new XMLSerializer().serializeToString(n),x=new Blob([m],{type:"image/svg+xml;charset=utf-8"}),k=URL.createObjectURL(x),b=new Image;b.width=s,b.height=i,await new Promise((A,h)=>{b.onload=()=>{d.drawImage(b,0,0,s,i),URL.revokeObjectURL(k),A()},b.onerror=h,b.src=k});const z=await new Promise(A=>l.toBlob(A,"image/png"));z&&await Aa(z,Oc(r))}function xr(t,r,a="png"){const n=r.length<=3?r.join("_"):`${r.length}_cases`,s=new Date().toISOString().replace(/[:.]/g,"-").slice(0,19);return Hc(`${t}_${n}_${s}.${a}`)}async function Ma(t,r){const a=new Blob([t],{type:"text/csv;charset=utf-8"});await Aa(a,r)}function Dc(t){const r=t.getAttribute("viewBox");if(r){const a=r.split(/[\s,]+/).map(Number);if(a.length===4)return{width:a[2],height:a[3]}}return{width:t.width.baseVal.value||900,height:t.height.baseVal.value||500}}async function Aa(t,r){if("showSaveFilePicker"in window)try{const i=await(await window.showSaveFilePicker({suggestedName:r,types:[{description:t.type.startsWith("image/")?"Image":"File",accept:{[t.type]:[`.${r.split(".").pop()}`]}}]})).createWritable();await i.write(t),await i.close();return}catch(s){if((s==null?void 0:s.name)==="AbortError")return}const a=URL.createObjectURL(t),n=document.createElement("a");n.href=a,n.download=r,document.body.appendChild(n),n.click(),document.body.removeChild(n),URL.revokeObjectURL(a)}function Oc(t){return t.endsWith(".png")?t:`${t}.png`}function Hc(t){return t.replace(/[^a-zA-Z0-9._-]/g,"_")}let Ta=D(ce([])),La=D(ce([])),Fa=D(ce([])),Ia=D("zero"),za=D(0),Da=D(""),jc=D(!0);function Vc(){return{selectedCases:e(Ta),columns:e(La),selectedColumns:e(Fa),startFrom:e(Ia),iterMin:e(za),svgHtml:e(Da),autoRefresh:e(jc)}}function Xn(t){f(Ta,t,!0)}function Uc(t){f(La,t,!0)}function Xr(t){f(Fa,t,!0)}function Bc(t){f(Ia,t,!0)}function qc(t){f(za,t,!0)}function $r(t){f(Da,t,!0)}var Kc=P(" Refresh",1),Gc=P(" ",1),Wc=P(" Download as PNG",1),Xc=P('
        '),Yc=P('
        ',1),Qc=P('
        '),Zc=P('

        '),Jc=P(" ",1);function eu(t,r){Me(r,!0);let a=V(Vc),n=D("");const s=new Set(["iteration","wall_distance","walldistance"]),i=["velocity","pressure"];async function o(){if(e(a).selectedCases.length)try{const h=await Co(e(a).selectedCases);f(n,"");const c=h.filter(C=>!s.has(C.toLowerCase().replace(/\s+/g,"_")));if(Uc(c),c.length===0){Xr([]),$r("");return}if(e(a).selectedColumns.length===0){const C=c.filter(R=>i.some(p=>R.toLowerCase().includes(p)));Xr(C.length>0?C:[c[0]])}}catch(h){f(n,"Failed to load residual columns"),console.error("Failed to load residual columns:",h)}}async function l(){if(e(a).selectedCases.length&&!((e(a).columns.length===0||e(a).selectedColumns.length===0)&&(await o(),!e(a).selectedColumns.length)))try{let h=0,c=!0;if(e(a).startFrom==="restart"){const R=await Ca(e(a).selectedCases),p=Object.values(R.origins).map(w=>w.iteration).filter(w=>w!==void 0&&Number.isFinite(w));h=p.length>0?Math.min(...p):0}else e(a).startFrom==="custom"&&(h=e(a).iterMin);const C=await So(e(a).selectedCases,e(a).selectedColumns,{xMin:h,includeHistory:c});f(n,""),C&&$r(C)}catch(h){f(n,"Failed to load residual plot"),console.error("Failed to load residual plot:",h)}}function d(h){if(Xn(h),h.length===0){$r("");return}o(),l()}function m(h){if(Xr(h),h.length===0){$r("");return}l()}function x(h){Bc(h),l()}function k(h){qc(h),l()}let b=D(ce(tr("plot")));Ne(()=>{rr("plot",e(b))}),Ne(()=>{e(b)?mr("residualPlot",l,Hn()):vt("residualPlot")});let z=!1;Ne(()=>{r.allCases.length>0&&!z&&(z=!0,Xn([...r.allCases]),o().then(()=>l()))});const A=_r(()=>{z&&o().then(()=>l())});vr(()=>{vt("residualPlot"),A()}),jt(t,{eyebrow:"Diagnostics",title:"Residuals Plot",wide:!0,actions:c=>{var C=Gc(),R=G(C);{let $=V(Hn);nr(R,{name:"residualPlot",get intervalMs(){return e($)},onRefresh:l,get checked(){return e(b)},set checked(g){f(b,g,!0)}})}var p=E(R,2);{var w=$=>{Pe($,{variant:"primary",onclick:l,children:(g,N)=>{var I=Kc(),T=G(I);Le(T,{get icon(){return er}}),we(),u(g,I)},$$slots:{default:!0}})};Y(p,$=>{e(b)||$(w)})}u(c,C)},children:(c,C)=>{var R=Jc(),p=G(R);{var w=I=>{const T=X=>{var K=Xc(),re=_(K);{let y=V(()=>!e(a).svgHtml);Pe(re,{variant:"secondary",size:"sm",onclick:()=>Sn("plot-holder",xr("residuals",e(a).selectedCases)),get disabled(){return e(y)},children:(S,O)=>{var F=Wc(),M=G(F);Le(M,{get icon(){return pr}}),we(),u(S,F)},$$slots:{default:!0}})}v(K),u(X,K)};var q=Yc(),H=G(q);Cn(H,{prefix:"plot",get allCases(){return r.allCases},get selectedCases(){return e(a).selectedCases},onCasesChange:d,columnLabel:"Variables",get columns(){return e(a).columns},get selectedColumns(){return e(a).selectedColumns},onColumnsChange:m,get startFrom(){return e(a).startFrom},onStartFromChange:x,xMinLabel:"Iter min",get xMin(){return e(a).iterMin},onXMinChange:k,get extraSlot(){return T}});var j=E(H,2),U=_(j);{let X=V(()=>e(a).selectedCases.length===0?"Please select at least one case.":e(a).columns.length===0?"No residuals for the selected cases yet.":e(a).selectedColumns.length===0?"Please select at least one variable.":"No data to display.");Zt(U,{id:"plot-holder",get svgHtml(){return e(a).svgHtml},get emptyMessage(){return e(X)}})}v(j),u(I,q)},$=I=>{var T=Qc(),q=_(T);Zt(q,{id:"plot-holder",svgHtml:"",emptyMessage:"No data available. Please run a simulation first."}),v(T),u(I,T)};Y(p,I=>{r.allCases.length>0?I(w):I($,-1)})}var g=E(p,2);{var N=I=>{var T=Zc(),q=_(T,!0);v(T),ee(()=>ae(q,e(n))),u(I,T)};Y(g,I=>{e(n)&&I(N)})}u(c,R)},$$slots:{actions:!0,default:!0}}),Ae()}function Oa(t){const{scope:r,getState:a,setState:n,getAxis:s}=t;async function i(){const k=a();if(k.selectedCases.length)try{let b=await en(k.selectedCases,r);if(t.filterFiles&&(b=t.filterFiles(b)),n({files:b}),b.length===0){n({file:"",columns:[],selectedColumns:[],positionText:"",svgHtml:""});return}(!k.file||!b.includes(k.file))&&(n({file:b[0]}),await o())}catch(b){console.error(`Failed to load ${r} files:`,b)}}async function o(){const k=a();if(!(!k.selectedCases.length||!k.file))try{const b=await No(k.selectedCases,[k.file]),z=t.filterColumns?t.filterColumns(b):b;t.onColumnsLoaded?t.onColumnsLoaded(b):n({columns:z});const A=a();if(A.columns.length===0){n({selectedColumns:[],positionText:"",svgHtml:""});return}A.selectedColumns.length===0&&n({selectedColumns:[A.columns[0]]})}catch(b){console.error(`Failed to load ${r} columns:`,b)}}async function l(){let k=a();if(k.selectedCases.length){if(await i(),k=a(),!k.file){n({positionText:"",svgHtml:""});return}if((k.columns.length===0||k.selectedColumns.length===0)&&(await o(),k=a(),!k.selectedColumns.length)){n({positionText:"",svgHtml:""});return}try{let b;const z=a();if(z.startFrom==="restart"){const h=await Ca(z.selectedCases),c=s().toLowerCase(),C=c==="t"||c==="time"?"time":"iteration",R=Object.values(h.origins).map(p=>p[C]).filter(p=>p!==void 0&&Number.isFinite(p));b=R.length>0?Math.min(...R):void 0}else z.startFrom==="custom"&&(b=z.xMin);const A=await Ao({cases:z.selectedCases,probes:[z.file],columns:z.selectedColumns,axis:s(),timeMin:typeof b=="number"?b:void 0,xMin:b,includeHistory:!0});A&&n({svgHtml:A})}catch(b){console.error(`Failed to load ${r} plot:`,b)}}}async function d(k){if(n({selectedCases:k}),k.length===0){n({svgHtml:""});return}await i(),r==="monitoring"&&await l()}function m(k){if(n({selectedColumns:k}),k.length===0){n({svgHtml:""});return}l()}async function x(k){n({file:k}),await o(),await l()}return{loadFiles:i,loadColumns:o,loadPlot:l,handleCasesChange:d,handleColumnsChange:m,handleFileChange:x}}function Ha(){return{selectedCases:[],file:"",files:[],columns:[],selectedColumns:[],axis:"time",startFrom:"zero",xMin:0,positionText:"",svgHtml:"",autoRefresh:!0}}let qt=D("time"),ln=D(ce(Ha())),cn=D(ce({...Ha(),axis:""})),tu=D(!0),Lr=D(!1),Fr=D(!1);function ru(){return e(qt)}function nu(t){f(qt,t,!0)}function au(){return e(Lr)}function Yn(t){f(Lr,t,!0)}function su(){return e(Fr)}function Qn(t){f(Fr,t,!0)}function ou(){e(qt)==="time"&&!e(Lr)&&e(Fr)?f(qt,"profile"):e(qt)==="profile"&&!e(Fr)&&e(Lr)&&f(qt,"time")}function Zn(){return e(ln)}function Yr(){return e(cn)}function Ct(t){f(ln,{...e(ln),...t},!0)}function At(t){f(cn,{...e(cn),...t},!0)}function iu(){return e(tu)}var lu=P(" Download as PNG",1),cu=P('
        '),uu=P("
        "),du=P('
        '),fu=P('
        ',1),vu=P('
        ');function gu(t,r){Me(r,!0);let a=me(r,"onRefresh",15),n=V(Zn);const s=new Set(["time","t","iteration","iter"]),i=Oa({scope:"monitoring",getState:Zn,setState:Ct,filterFiles:h=>h.filter(c=>{const C=c.replace(/\.csv$/i,"").toLowerCase();return C!=="coords"&&!C.includes("coordinates")}),filterColumns:h=>h.filter(c=>!s.has(c.toLowerCase())),onColumnsLoaded:h=>{const c=h.filter(C=>!s.has(C.toLowerCase()));Ct({columns:c}),o()},getAxis:()=>"time"});async function o(){if(!(!e(n).selectedCases.length||!e(n).file||!e(n).selectedColumns.length))try{const h=[];for(const $ of e(n).selectedColumns){const g=await Mo(e(n).selectedCases[0],e(n).file,[$]);g.found&&h.push({col:$,x:g.x,y:g.y,z:g.z})}if(!h.length){Ct({positionText:""});return}const c=$=>{let g=0;for(const N of $){if(N===void 0)continue;const I=String(N),T=I.indexOf(".");T>=0&&(g=Math.max(g,I.length-T-1))}return g},C=c(h.map($=>$.x)),R=c(h.map($=>$.y)),p=c(h.map($=>$.z)),w=h.map($=>{const g=[];return $.x!==void 0&&g.push(`x=${$.x.toFixed(C)}`),$.y!==void 0&&g.push(`y=${$.y.toFixed(R)}`),$.z!==void 0&&g.push(`z=${$.z.toFixed(p)}`),`Probe position [${$.col}] : ${g.join(" · ")}`});Ct({positionText:w.join(` -`)})}catch{Ct({positionText:""})}}function l(h){i.handleColumnsChange(h),h.length>0&&o()}let d=V(()=>e(n).files.map(h=>({value:h,label:h.replace(/\.csv$/i,"").replace(/^probes?_/i,"")}))),m=!1;Ne(()=>{r.allCases.length>0&&!m&&(m=!0,Ct({selectedCases:[...r.allCases]}),i.loadFiles().then(()=>i.loadPlot()))}),Ne(()=>{a(i.loadPlot)});let x=V(()=>e(n).selectedCases.length===0?"Please select at least one case.":e(n).files.length===0?"No probe data for the selected cases yet.":e(n).selectedColumns.length===0?"Please select at least one probe.":"No data to display.");var k=be(),b=G(k);{var z=h=>{const c=I=>{Re(I,{text:"Quantity",children:(T,q)=>{lt(T,{class:"w-[160px]",get options(){return e(d)},get value(){return e(n).file},get onchange(){return i.handleFileChange}})}})},C=I=>{var T=cu(),q=_(T);{let H=V(()=>!e(n).svgHtml);Pe(q,{variant:"secondary",size:"sm",onclick:()=>Sn("probe-plot-holder",xr("probe",e(n).selectedCases)),get disabled(){return e(H)},children:(j,U)=>{var X=lu(),K=G(X);Le(K,{get icon(){return pr}}),we(),u(j,X)},$$slots:{default:!0}})}v(T),u(I,T)};var R=fu(),p=G(R);Cn(p,{prefix:"probe",get allCases(){return r.allCases},get selectedCases(){return e(n).selectedCases},get onCasesChange(){return i.handleCasesChange},get middleSlot(){return c},columnLabel:"Probes",get columns(){return e(n).columns},get selectedColumns(){return e(n).selectedColumns},onColumnsChange:l,get startFrom(){return e(n).startFrom},onStartFromChange:I=>{Ct({startFrom:I}),i.loadPlot()},xMinLabel:"Time min",get xMin(){return e(n).xMin},onXMinChange:I=>{Ct({xMin:I}),i.loadPlot()},get extraSlot(){return C}});var w=E(p,2);{var $=I=>{var T=du();He(T,21,()=>e(n).positionText.split(` -`).sort(),dn,(q,H)=>{var j=uu(),U=_(j,!0);v(j),ee(()=>ae(U,e(H))),u(q,j)}),v(T),u(I,T)};Y(w,I=>{e(n).positionText&&I($)})}var g=E(w,2),N=_(g);Zt(N,{id:"probe-plot-holder",get svgHtml(){return e(n).svgHtml},get emptyMessage(){return e(x)}}),v(g),u(h,R)},A=h=>{var c=vu(),C=_(c);Zt(C,{id:"probe-plot-holder",svgHtml:"",emptyMessage:"No probe data available. Please run a simulation first."}),v(c),u(h,c)};Y(b,h=>{r.allCases.length>0?h(z):h(A,-1)})}u(t,k),Ae()}var hu=P(" ",1),pu=P(" Download as PNG",1),mu=P('
        '),_u=P('
        ',1),bu=P('
        ');function xu(t,r){Me(r,!0);let a=me(r,"onRefresh",15),n=V(Yr);const s=["s","x","abscissa","distance","arclength","arc_length","curvilinear","length","r","y","z","coord","position"];function i(c){const C=c.map(R=>R.toLowerCase());for(const R of s){const p=C.indexOf(R);if(p>=0)return c[p]}return c[0]??""}const o=Oa({scope:"profiles",getState:Yr,setState:At,onColumnsLoaded:c=>{const C=e(n).axis||i(c),R=c.filter(p=>p!==C).toSorted((p,w)=>p.localeCompare(w));At({columns:R,axis:C,selectedColumns:e(n).selectedColumns.length?e(n).selectedColumns:R.length?[R[0]]:[]})},getAxis:()=>Yr().axis});function l(c){const C=e(n).axis;At({axis:c});const p=[...e(n).columns,...C?[C]:[]].filter(w=>w!==c).toSorted((w,$)=>w.localeCompare($));At({columns:p}),o.loadPlot()}let d=V(()=>e(n).files.map(c=>({value:c,label:c.replace(/\.csv$/i,"").replace(/^profiles\//i,"").replace(/_/g," ")}))),m=V(()=>[...e(n).axis?[e(n).axis]:[],...e(n).columns].toSorted((c,C)=>c.localeCompare(C)).map(c=>({value:c,label:c}))),x=!1;Ne(()=>{r.allCases.length>0&&!x&&(x=!0,At({selectedCases:[...r.allCases]}),o.loadFiles().then(()=>o.loadPlot()))}),Ne(()=>{a(o.loadPlot)});let k=V(()=>e(n).selectedCases.length===0?"Please select at least one case.":e(n).files.length===0?"No profile data for the selected cases yet.":e(n).selectedColumns.length===0?"Please select at least one value.":"No data to display.");var b=be(),z=G(b);{var A=c=>{const C=N=>{var I=hu(),T=G(I);Re(T,{text:"Profile",children:(H,j)=>{lt(H,{class:"w-[160px]",get options(){return e(d)},get value(){return e(n).file},get onchange(){return o.handleFileChange}})}});var q=E(T,2);Re(q,{text:"X axis",children:(H,j)=>{lt(H,{class:"w-[140px]",get options(){return e(m)},get value(){return e(n).axis},onchange:l})}}),u(N,I)},R=N=>{var I=mu(),T=_(I);{let q=V(()=>!e(n).svgHtml);Pe(T,{variant:"secondary",size:"sm",onclick:()=>Sn("profile-plot-holder",xr("profile",e(n).selectedCases)),get disabled(){return e(q)},children:(H,j)=>{var U=pu(),X=G(U);Le(X,{get icon(){return pr}}),we(),u(H,U)},$$slots:{default:!0}})}v(I),u(N,I)};var p=_u(),w=G(p);Cn(w,{prefix:"profile",get allCases(){return r.allCases},get selectedCases(){return e(n).selectedCases},get onCasesChange(){return o.handleCasesChange},get middleSlot(){return C},columnLabel:"Values",get columns(){return e(n).columns},get selectedColumns(){return e(n).selectedColumns},get onColumnsChange(){return o.handleColumnsChange},get startFrom(){return e(n).startFrom},onStartFromChange:N=>{At({startFrom:N}),o.loadPlot()},xMinLabel:"X min",get xMin(){return e(n).xMin},onXMinChange:N=>{At({xMin:N}),o.loadPlot()},get extraSlot(){return R}});var $=E(w,2),g=_($);Zt(g,{id:"profile-plot-holder",get svgHtml(){return e(n).svgHtml},get emptyMessage(){return e(k)}}),v($),u(c,p)},h=c=>{var C=bu(),R=_(C);Zt(R,{id:"profile-plot-holder",svgHtml:"",emptyMessage:"No profile data available. Please run a simulation first."}),v(C),u(c,C)};Y(z,c=>{r.allCases.length>0?c(A):c(h,-1)})}u(t,b),Ae()}var yu=P('
        Diagnostics
        '),wu=P(" Refresh",1),ku=P(" ",1);function Cu(t,r){Me(r,!0);let a=V(ru),n=V(iu),s=V(au),i=V(su),o=D(ce(tr("probe")));Ne(()=>{rr("probe",e(o))});let l=D(void 0);function d(){var A;(A=e(l))==null||A()}async function m(){if(r.allCases.length){try{const h=(await en(r.allCases,"monitoring")).filter(c=>{const C=c.replace(/\.csv$/i,"").toLowerCase();return C!=="coords"&&!C.includes("coordinates")});Yn(h.length>0)}catch{Yn(!1)}try{const A=await en(r.allCases,"profiles");Qn(A.length>0)}catch{Qn(!1)}}}Ne(()=>{r.allCases.length>0&&m()}),_r(()=>{m(),d()}),Ne(()=>{e(s),e(i),ou()});function x(A){A==="time"&&!e(s)||A==="profile"&&!e(i)||nu(A)}var k=be(),b=G(k);{var z=A=>{{const h=C=>{var R=yu(),p=E(_(R),2),w=_(p),$=E(w,2);v(p),v(R),ee(()=>{Se(w,1,`bg-transparent border-none p-0 m-0 text-lg font-bold tracking-tight transition-colors duration-150 - ${e(s)?e(a)==="time"?"text-edf-bleu-fonce cursor-pointer":"text-edf-gris-moyen cursor-pointer hover:text-muted":"text-edf-gris-moyen cursor-not-allowed opacity-50"}`),w.disabled=!e(s),ke(w,"title",e(s)?"":"No probe data available. Run a simulation first."),Se($,1,`bg-transparent border-none p-0 m-0 text-lg font-bold tracking-tight transition-colors duration-150 - ${e(i)?e(a)==="profile"?"text-edf-bleu-fonce cursor-pointer":"text-edf-gris-moyen cursor-pointer hover:text-muted":"text-edf-gris-moyen cursor-not-allowed opacity-50"}`),$.disabled=!e(i),ke($,"title",e(i)?"":"No profile data available. Run a simulation first.")}),ue("click",w,()=>x("time")),ue("click",$,()=>x("profile")),u(C,R)};jt(A,{wide:!0,titleSlot:h,tabs:h,actions:C=>{var R=ku(),p=G(R);{let g=V(()=>e(a)==="time"?"probePlot":"profilePlot"),N=V(il);nr(p,{get name(){return e(g)},get intervalMs(){return e(N)},onRefresh:d,get checked(){return e(o)},set checked(I){f(o,I,!0)}})}var w=E(p,2);{var $=g=>{Pe(g,{variant:"primary",onclick:d,children:(N,I)=>{var T=wu(),q=G(T);Le(q,{get icon(){return er}}),we(),u(N,T)},$$slots:{default:!0}})};Y(w,g=>{e(o)||g($)})}u(C,R)},children:(C,R)=>{var p=be(),w=G(p);{var $=N=>{gu(N,{get allCases(){return r.allCases},get onRefresh(){return e(l)},set onRefresh(I){f(l,I,!0)}})},g=N=>{xu(N,{get allCases(){return r.allCases},get onRefresh(){return e(l)},set onRefresh(I){f(l,I,!0)}})};Y(w,N=>{e(a)==="time"?N($):N(g,-1)})}u(C,p)},$$slots:{tabs:!0,actions:!0,default:!0}})}};Y(b,A=>{e(n)&&A(z)})}u(t,k),Ae()}Ge(["click"]);var Su=P(" Refresh",1),$u=P(" ",1),Pu=P(" Download as CSV",1),Eu=P('
        ',1),Ru=P(" "),Nu=P(' '),Mu=P(' '),Au=P('
        Case
        '),Tu=P('

        '),Lu=P(" ",1);function Fu(t,r){Me(r,!0);let a=D(ce([])),n=D(ce([])),s=D(!1),i=D(!1),o=D(ce(tr("perf")));Ne(()=>{rr("perf",e(o))});const l=5e3;let m=D(ce([{key:"elapsed_time",label:"Elapsed (s)",kind:"time"},{key:"io_time",label:"I/O (s)",kind:"time"},{key:"linear_solver_time",label:"Linear Solver (s)",kind:"time"},{key:"gradients_time",label:"Gradients (s)",kind:"time"},{key:"balances_time",label:"Balances (s)",kind:"time"},{key:"mpi_ranks",label:"MPI Ranks",kind:"int"},{key:"threads",label:"Threads",kind:"int"}])),x=V(()=>r.allCases.map(R=>({value:R,label:R}))),k=V(()=>e(n).length>0);async function b(){var R;if(e(a).length){f(i,!0);try{const p=await In(e(a));(R=p.columns)!=null&&R.length&&f(m,p.columns,!0),f(n,p.records,!0),e(n).length>0&&f(s,!0)}catch(p){console.error("Failed to load perf:",p)}f(i,!1)}}function z(R){if(f(a,R,!0),R.length===0){f(n,[],!0);return}b()}function A(R,p){if(p==null||p==="")return"-";if(R==="text")return p;const w=Number(p);return Number.isFinite(w)?R==="int"?String(Math.round(w)):w.toFixed(3):p}async function h(){if(!e(n).length)return;const R=["case_id",...e(m).map(g=>g.key)],p=e(n).map(g=>[g.case_id,...e(m).map(N=>g[N.key]??"")].join(",")),w=[R.join(","),...p].join(` -`),$=xr("timing_snapshot",e(a),"csv");await Ma(w,$)}let c=!1;Ne(()=>{r.allCases.length>0&&!c&&(c=!0,f(a,[...r.allCases],!0),In(r.allCases).then(R=>{var p;(p=R.columns)!=null&&p.length&&f(m,R.columns,!0),f(s,R.records.length>0),f(n,R.records,!0)}).catch(()=>{}))}),Ne(()=>{e(o)?mr("perf",b,l):vt("perf")});const C=_r(()=>{c&&b()});vr(()=>{vt("perf"),C()}),jt(t,{eyebrow:"Performance",title:"Timing Snapshot",wide:!0,actions:p=>{var w=$u(),$=G(w);nr($,{name:"perf",intervalMs:l,onRefresh:b,get checked(){return e(o)},set checked(I){f(o,I,!0)}});var g=E($,2);{var N=I=>{Pe(I,{variant:"primary",onclick:b,children:(T,q)=>{var H=Su(),j=G(H);Le(j,{get icon(){return er}}),we(),u(T,H)},$$slots:{default:!0}})};Y(g,I=>{e(o)||I(N)})}u(p,w)},children:(p,w)=>{var $=Lu(),g=G($);{var N=H=>{Qt(H,{children:(j,U)=>{var X=Eu(),K=G(X);Re(K,{text:"Cases",children:(S,O)=>{Wt(S,{class:"w-[160px]",get options(){return e(x)},get selected(){return e(a)},onchange:z,placeholder:"Select cases..."})}});var re=E(K,2),y=_(re);{let S=V(()=>!e(k));Pe(y,{variant:"secondary",size:"sm",onclick:h,get disabled(){return e(S)},children:(O,F)=>{var M=Pu(),L=G(M);Le(L,{get icon(){return pr}}),we(),u(O,M)},$$slots:{default:!0}})}v(re),u(j,X)}})};Y(g,H=>{e(s)&&H(N)})}var I=E(g,2);{var T=H=>{var j=Au(),U=_(j),X=_(U),K=_(X),re=E(_(K));He(re,17,()=>e(m),S=>S.key,(S,O)=>{var F=Ru(),M=_(F,!0);v(F),ee(()=>ae(M,e(O).label)),u(S,F)}),v(K),v(X);var y=E(X);He(y,21,()=>e(n),S=>S.case_id,(S,O)=>{var F=Mu(),M=_(F),L=_(M,!0);v(M);var B=E(M);He(B,17,()=>e(m),te=>te.key,(te,ie)=>{var W=Nu(),ne=_(W,!0);v(W),ee(oe=>ae(ne,oe),[()=>A(e(ie).kind,e(O)[e(ie).key])]),u(te,W)}),v(F),ee(()=>ae(L,e(O).case_id)),u(S,F)}),v(y),v(U),v(j),u(H,j)},q=H=>{var j=Tu(),U=_(j);{var X=re=>{var y=Ve("Select cases above to view timing information.");u(re,y)},K=re=>{var y=Ve("No performance data available. Please run a simulation first.");u(re,y)};Y(U,re=>{e(s)?re(X):re(K,-1)})}v(j),u(H,j)};Y(I,H=>{e(k)?H(T):H(q,-1)})}u(p,$)},$$slots:{actions:!0,default:!0}}),Ae()}const Iu={"&":"&","<":"<",">":">",'"':""","'":"'"};function ja(t){return t.replace(/[&<>"']/g,r=>Iu[r]??r)}var zu=P("
        "),Du=P('
        ');function Ou(t,r){Me(r,!0);let a=D(void 0);function n(m){const x=ja(m);if(!r.searchQuery.trim())return x;try{const k=new RegExp(`(${r.searchQuery})`,"gi");return x.replace(k,'$1')}catch{return x}}function s(m){return m==="error"?"tail-sev-error":m==="warn"?"tail-sev-warn":m==="info"?"tail-sev-info":""}function i(){return e(a)?e(a).scrollHeight-e(a).scrollTop-e(a).clientHeight<=24:!0}let o=D(!0);function l(){f(o,i(),!0)}Ne(()=>{r.autoScroll&&r.lines.length>0&&e(a)&&e(o)&&Et().then(()=>{e(a)&&(e(a).scrollTop=e(a).scrollHeight)})});var d=Du();He(d,21,()=>r.lines,m=>m.index,(m,x)=>{var k=zu();zr(k,()=>n(e(x).text),!0),v(k),ee(b=>Se(k,1,`tail-line ${b??""} ${e(x).isNew?"tail-new":""}`),[()=>s(e(x).severity)]),u(m,k)}),v(d),nt(d,m=>f(a,m),()=>e(a)),rt("scroll",d,l),u(t,d),Ae()}const Hu=/(fatal error|error detected|error reading|error writing|segmentation fault|sigterm|sigsegv|sigkill|abort|killed|terminated|core dumped|traceback|exception|errno)/i,ju=/(warning|divergence|non-convergence|clipping|negative|nan detected|overflow|underflow)/i;function Vu(t){return Hu.test(t)?"error":ju.test(t)?"warn":"none"}function Uu(t,r){return r==="all"||r===""?!0:r==="info"?t!=="none":t===r}var Bu=P(" Refresh",1),qu=P(" ",1),Ku=P(''),Gu=P(''),Wu=P("    ",1),Xu=Ht(''),Yu=Ht(''),Qu=Ht(''),Zu=P(" Resume",1),Ju=P(" Pause",1),ed=P('Case not running'),td=P(' 
        ',1),rd=P('

        No log data available. Please run a simulation first.

        '),nd=P('

        '),ad=P(" ",1);function sd(t,r){Me(r,!0);const a=80;let n=D(""),s=D(""),i=D(!1),o=D(ce([])),l=D(a),d=D(ce([])),m=D(ce(new Set)),x=D(!1),k=D(ce(tr("tail")));Ne(()=>{rr("tail",e(k))});let b=D(!0),z=D("all"),A=D(""),h=D(""),c=D(void 0),C=V(()=>kn(r.allCases)),R=V(()=>e(o).map(y=>{const S=y.split("/").pop()??y;return{value:y,label:S}})),p=[{value:"all",label:"All"},{value:"error",label:"Error"},{value:"warn",label:"Warn"}],w=V(()=>e(o).length>0),$=V(()=>xn().some(y=>{var S;return y.case_id===e(n)&&((S=y.status)==null?void 0:S.toUpperCase())==="RUNNING"}));const g={"run_solver.log":1,listing:2,"run_status.running":3,"csauto.stdout":4,"csauto.stderr":5,"performance.log":6};function N(y){const S=y.split("/").pop()??y;return g[S]??99}async function I(){if(e(n))try{const O=(await Eo(e(n))).filter(F=>{const M=F.split("/").pop()??F;return F.endsWith(".log")||F.endsWith("/summary")||M in g}).sort((F,M)=>N(F)-N(M));if(f(o,O,!0),O.length>0){const F=O[0];!e(s)||!O.includes(e(s))?(f(s,F,!0),f(i,!1)):!e(i)&&N(F)0&&O[O.length-1]===""?O.slice(0,-1):O;if(e(d).length>0){const M=q(e(d),F),L=new Set;for(let B=M;B{let y=e(d).map((S,O)=>({text:S,index:O,severity:Vu(S),isNew:e(m).has(O)}));if(e(z)!=="all"&&(y=y.filter(S=>Uu(S.severity,e(z)))),e(A).trim())try{const S=new RegExp(e(A),"i");y=y.filter(O=>S.test(O.text))}catch{}return y});function j(y){f(n,y,!0),f(s,""),f(i,!1),f(d,[],!0),I().then(()=>T(!0))}function U(y){f(s,y,!0),f(i,!0),f(d,[],!0),T(!0)}function X(){f(x,!e(x)),e(x)||T(!0)}function K(y){(y.ctrlKey||y.metaKey)&&y.key==="f"&&e(c)&&(y.preventDefault(),e(c).focus())}Ne(()=>{r.allCases.length>0&&!e(n)&&(f(n,r.allCases[0],!0),I().then(()=>T(!0)))}),Ne(()=>{e(k)&&!e(x)&&e(n)?mr("tail",()=>T(!1),On()):vt("tail")});const re=_r(()=>{e(n)&&I().then(()=>T(!0))});vr(()=>{vt("tail"),re()}),rt("keydown",it,K),jt(t,{eyebrow:"Live",title:"Log Tail",wide:!0,actions:S=>{var O=qu(),F=G(O);{let B=V(On);nr(F,{name:"tail",get intervalMs(){return e(B)},onRefresh:()=>T(!1),get checked(){return e(k)},set checked(te){f(k,te,!0)}})}var M=E(F,2);{var L=B=>{Pe(B,{variant:"primary",onclick:()=>T(!0),children:(te,ie)=>{var W=Bu(),ne=G(W);Le(ne,{get icon(){return er}}),we(),u(te,W)},$$slots:{default:!0}})};Y(M,B=>{e(k)||B(L)})}u(S,O)},children:(S,O)=>{var F=ad(),M=G(F);{var L=oe=>{Qt(oe,{children:(de,ve)=>{var se=Wu(),_e=G(se);Re(_e,{text:"Case",children:(ze,Qe)=>{lt(ze,{class:"w-[130px]",get options(){return e(C)},get value(){return e(n)},onchange:j})}});var he=E(_e,2);Re(he,{text:"File",children:(ze,Qe)=>{lt(ze,{class:"w-[150px]",get options(){return e(R)},get value(){return e(s)},onchange:U})}});var Ee=E(he,2);Re(Ee,{text:"Lines",children:(ze,Qe)=>{var qe=Ku();Ye(qe),ue("change",qe,()=>T(!0)),tt(qe,()=>e(l),Je=>f(l,Je)),u(ze,qe)}});var pe=E(Ee,2);Re(pe,{text:"Filter",children:(ze,Qe)=>{var qe=Gu();Ye(qe),nt(qe,Je=>f(c,Je),()=>e(c)),tt(qe,()=>e(A),Je=>f(A,Je)),u(ze,qe)}});var Ie=E(pe,2);Re(Ie,{text:"Severity",children:(ze,Qe)=>{lt(ze,{class:"w-[90px]",get options(){return p},get value(){return e(z)},onchange:qe=>f(z,qe,!0)})}}),u(de,se)}})};Y(M,oe=>{e(w)&&oe(L)})}var B=E(M,2);{var te=oe=>{var de=td(),ve=G(de);Ou(ve,{get lines(){return e(H)},get searchQuery(){return e(A)},get autoScroll(){return e(b)}});var se=E(ve,2),_e=_(se),he=_(_e);{var Ee=Q=>{var Z=Xu();u(Q,Z)},pe=Q=>{var Z=Yu();u(Q,Z)},Ie=Q=>{var Z=Qu();u(Q,Z)};Y(he,Q=>{e(x)?Q(Ee):e($)?Q(pe,1):Q(Ie,-1)})}var ze=E(he);v(_e);var Qe=E(_e,2);{var qe=Q=>{Pe(Q,{variant:"secondary",size:"sm",onclick:X,children:(Z,J)=>{var le=be(),xe=G(le);{var ye=ge=>{var Ce=Zu(),De=G(Ce);Le(De,{get icon(){return Sa}}),we(),u(ge,Ce)},fe=ge=>{var Ce=Ju(),De=G(Ce);Le(De,{get icon(){return ki}}),we(),u(ge,Ce)};Y(xe,ge=>{e(x)?ge(ye):ge(fe,-1)})}u(Z,le)},$$slots:{default:!0}})},Je=Q=>{var Z=ed();u(Q,Z)};Y(Qe,Q=>{e($)?Q(qe):Q(Je,-1)})}var Te=E(Qe,2),$e=_(Te);Ot($e,{get checked(){return e(b)},onchange:Q=>f(b,Q,!0),size:14,label:"Auto-scroll",labelFirst:!0}),v(Te),v(se),ee(()=>ae(ze,` ${e(H).length??""} / ${e(d).length??""}`)),u(oe,de)},ie=oe=>{var de=rd();u(oe,de)};Y(B,oe=>{e(d).length>0?oe(te):oe(ie,-1)})}var W=E(B,2);{var ne=oe=>{var de=nd(),ve=_(de,!0);v(de),ee(()=>ae(ve,e(h))),u(oe,de)};Y(W,oe=>{e(h)&&oe(ne)})}u(S,F)},$$slots:{actions:!0,default:!0}}),Ae()}Ge(["change"]);function od(t,r=3){var z,A;const a=t.split(` -`),n=[];let s=0,i=0,o=!1;for(const h of a){if(h.startsWith("===")||h.startsWith("---")||h.startsWith("+++"))continue;const c=h.match(/^@@ -(\d+),?\d* \+(\d+),?\d* @@/);if(c){s=parseInt(c[1],10)-1,i=parseInt(c[2],10)-1,o=!0;continue}if(o)if(h.startsWith("-"))s++,n.push({type:"del",leftNum:s,rightNum:null,leftContent:h.slice(1),rightContent:""});else if(h.startsWith("+"))i++,n.push({type:"add",leftNum:null,rightNum:i,leftContent:"",rightContent:h.slice(1)});else{s++,i++;const C=h.startsWith(" ")?h.slice(1):h;n.push({type:"equal",leftNum:s,rightNum:i,leftContent:C,rightContent:C})}}const l=[];let d=0;for(;d=0&&h-k>1&&x.push({kind:"separator",skipped:h-k-1});const c=l[h].type!=="equal",C=c&&!b;x.push({kind:"line",row:l[h],hunkStart:C}),b=c,k=h}if(k=0&&x.push({kind:"separator",skipped:l.length-1-k}),x.length>0&&x[0].kind==="line"){const h=((z=x[0].row)==null?void 0:z.leftNum)??0,c=((A=x[0].row)==null?void 0:A.rightNum)??0,C=Math.max(h,c)-1;C>0&&x.unshift({kind:"separator",skipped:C})}return x}var id=P('
        '),ld=P('
        '),cd=P(' ',1),ud=P('No matches'),dd=P('
        '),fd=P('

        Files are identical.

        '),vd=P('
        Loading...
        '),gd=P('
        ');function hd(t,r){Me(r,!0);let a=me(r,"loading",3,!1),n=D(void 0),s=V(()=>od(r.diffText)),i=V(()=>{const Q=[];return e(s).forEach((Z,J)=>{Z.hunkStart&&Q.push(J)}),Q}),o=V(()=>e(i).length>0),l=V(()=>{var Z;const Q=new Array(e(s).length).fill(-1);for(let J=0;J{r.diffText!==m&&(m=r.diffText,f(d,-1))});let x=V(()=>{if(!r.search.trim())return[];try{const Q=new RegExp(r.search,"i"),Z=[];return e(s).forEach((J,le)=>{J.kind==="line"&&J.row&&(Q.test(J.row.leftContent)||Q.test(J.row.rightContent))&&Z.push(le)}),Z}catch{return[]}}),k=D(-1),b="";Ne(()=>{r.search!==b&&(b=r.search,f(k,e(x).length>0?0:-1,!0))});async function z(Q){var le;f(k,Q,!0),await Et();const Z=e(x)[Q],J=(le=e(p))==null?void 0:le.querySelector(`[data-item-idx="${Z}"]`);if(J&&e(p)&&e(w)){const xe=e(p).getBoundingClientRect(),fe=J.getBoundingClientRect().top-xe.top+e(p).scrollTop-e(p).clientHeight/3;g=!0,e(p).scrollTop=fe,e(w).scrollTop=fe,requestAnimationFrame(()=>{g=!1})}}function A(){e(k)>0&&z(e(k)-1)}function h(){e(k)<0&&e(x).length>0?z(0):e(k)$1`)}catch{return J}}function C(Q,Z){return Q==="equal"?"":Q==="del"&&Z==="left"?"bg-[rgba(214,67,10,0.08)] text-edf-orange-fonce":Q==="del"&&Z==="right"?"bg-[rgba(48,122,16,0.08)] text-edf-vert-fonce":Q==="add"&&Z==="left"?"bg-edf-gris-clair":Q==="add"&&Z==="right"?"bg-[rgba(48,122,16,0.08)] text-edf-vert-fonce":""}function R(Q,Z){return Q==="del"&&Z==="left"?"rgb(251,237,233)":Q==="del"&&Z==="right"?"rgb(238,247,236)":Q==="add"&&Z==="left"?"var(--color-edf-gris-clair)":Q==="add"&&Z==="right"?"rgb(238,247,236)":"white"}let p=D(void 0),w=D(void 0),$=!1,g=!1;function N(Q){$||g||!e(p)||!e(w)||($=!0,Q==="left"?(e(w).scrollTop=e(p).scrollTop,e(w).scrollLeft=e(p).scrollLeft):(e(p).scrollTop=e(w).scrollTop,e(p).scrollLeft=e(w).scrollLeft),requestAnimationFrame(()=>{$=!1}))}async function I(Q){var J;f(d,Q,!0),await Et();const Z=(J=e(p))==null?void 0:J.querySelector(`[data-hunk-start="${Q}"]`);if(Z&&e(p)&&e(w)){g=!0;const le=e(p).getBoundingClientRect(),ye=Z.getBoundingClientRect().top-le.top+e(p).scrollTop-e(p).clientHeight/3;e(p).scrollTop=ye,e(w).scrollTop=ye,requestAnimationFrame(()=>{g=!1})}}function T(){e(d)>0&&I(e(d)-1)}function q(){e(d)<0?I(0):e(d)=0&&f(d,Z,!0)}function j(Q){Q.target instanceof HTMLInputElement||(Q.key==="n"&&!Q.shiftKey?(Q.preventDefault(),q()):Q.key==="N"||Q.key==="n"&&Q.shiftKey?(Q.preventDefault(),T()):Q.key==="j"?(Q.preventDefault(),h()):(Q.key==="J"||Q.key==="j"&&Q.shiftKey)&&(Q.preventDefault(),A()))}var U=gd();{const Q=(Z,J=Cs)=>{var le=be(),xe=G(le);He(xe,17,()=>e(s),dn,(ye,fe,ge)=>{const Ce=V(()=>e(i).indexOf(ge)),De=V(()=>e(l)[ge]>=0&&e(l)[ge]===e(d)),We=V(()=>{var at,Ke;return J()==="left"?(at=e(fe).row)==null?void 0:at.leftNum:(Ke=e(fe).row)==null?void 0:Ke.rightNum}),gt=V(()=>{var at,Ke;return J()==="left"?((at=e(fe).row)==null?void 0:at.leftContent)??"":((Ke=e(fe).row)==null?void 0:Ke.rightContent)??""});var Vt=be(),jr=G(Vt);{var et=at=>{var Ke=id(),Nt=E(_(Ke),2),Vr=_(Nt);v(Nt),v(Ke),ee(()=>ae(Vr,`... ${e(fe).skipped??""} lines hidden ...`)),u(at,Ke)},ut=at=>{var Ke=ld(),Nt=_(Ke),Vr=_(Nt,!0);v(Nt);var $n=E(Nt,2);zr($n,()=>c(e(gt),e(k)>=0&&e(x)[e(k)]===ge),!0),v($n),v(Ke),ee((wt,Mt,yr,Va,Ua)=>{Se(Ke,1,`flex w-full ${wt??""} ${Mt??""}`),Ze(Ke,e(De)?"background-color: rgba(16,87,200,0.12); color: rgb(16,87,200);":""),ke(Ke,"role",yr),ke(Ke,"tabindex",Va),ke(Ke,"data-hunk-start",J()==="left"&&e(Ce)>=0?e(Ce):void 0),ke(Ke,"data-item-idx",J()==="left"?ge:void 0),Ze(Nt,`background: ${Ua??""}; color: ${e(De)?"white":"var(--color-muted)"};`),ae(Vr,e(We)??"")},[()=>C(e(fe).row.type,J()),()=>e(fe).row.type!=="equal"||e(x).includes(ge)?"cursor-pointer":"",()=>e(fe).row.type!=="equal"||e(x).includes(ge)?"button":void 0,()=>e(fe).row.type!=="equal"||e(x).includes(ge)?0:void 0,()=>e(De)?"rgb(16,87,200)":R(e(fe).row.type,J())]),ue("click",Ke,()=>{var Mt;((Mt=e(fe).row)==null?void 0:Mt.type)!=="equal"&&H(ge);const wt=e(x).indexOf(ge);wt>=0&&f(k,wt,!0)}),ue("keydown",Ke,wt=>{var Mt;if(wt.key==="Enter"||wt.key===" "){wt.preventDefault(),((Mt=e(fe).row)==null?void 0:Mt.type)!=="equal"&&H(ge);const yr=e(x).indexOf(ge);yr>=0&&f(k,yr,!0)}}),u(at,Ke)};Y(jr,at=>{e(fe).kind==="separator"?at(et):e(fe).row&&at(ut,1)})}u(ye,Vt)}),u(Z,le)};var X=_(U),K=_(X),re=_(K),y=_(re,!0);v(re);var S=E(re,2),O=_(S,!0);v(S),v(K);var F=E(K,2),M=_(F),L=_(M,!0);v(M);var B=E(M,2),te=_(B,!0);v(B),v(F),v(X);var ie=E(X,2),W=_(ie),ne=_(W);Ye(ne);var oe=E(ne,2);{var de=Z=>{var J=cd(),le=G(J);{let ge=V(()=>e(k)<=0);Pe(le,{variant:"secondary",size:"sm",onclick:A,get disabled(){return e(ge)},children:(Ce,De)=>{we();var We=Ve("Prev");u(Ce,We)},$$slots:{default:!0}})}var xe=E(le,2),ye=_(xe);v(xe);var fe=E(xe,2);{let ge=V(()=>e(k)>=e(x).length-1);Pe(fe,{variant:"secondary",size:"sm",onclick:h,get disabled(){return e(ge)},children:(Ce,De)=>{we();var We=Ve("Next");u(Ce,We)},$$slots:{default:!0}})}ee(()=>ae(ye,`${e(k)>=0?e(k)+1:"–"} / ${e(x).length??""} occurrence${e(x).length!==1?"s":""}`)),u(Z,J)},ve=V(()=>r.search.trim()&&e(x).length>0),se=Z=>{var J=ud();u(Z,J)},_e=V(()=>r.search.trim());Y(oe,Z=>{e(ve)?Z(de):e(_e)&&Z(se,1)})}v(W);var he=E(W,2),Ee=_(he);{let Z=V(()=>e(i).length===0||e(d)<=0);Pe(Ee,{variant:"secondary",size:"sm",onclick:T,get disabled(){return e(Z)},children:(J,le)=>{we();var xe=Ve("Prev");u(J,xe)},$$slots:{default:!0}})}var pe=E(Ee,2),Ie=_(pe);v(pe);var ze=E(pe,2);{let Z=V(()=>e(i).length===0||e(d)>=e(i).length-1);Pe(ze,{variant:"secondary",size:"sm",onclick:q,get disabled(){return e(Z)},children:(J,le)=>{we();var xe=Ve("Next");u(J,xe)},$$slots:{default:!0}})}v(he),v(ie);var Qe=E(ie,2);{var qe=Z=>{var J=dd(),le=_(J),xe=_(le),ye=_(xe);Q(ye,()=>"left"),v(xe),v(le),nt(le,De=>f(p,De),()=>e(p));var fe=E(le,2),ge=_(fe),Ce=_(ge);Q(Ce,()=>"right"),v(ge),v(fe),nt(fe,De=>f(w,De),()=>e(w)),v(J),rt("scroll",le,()=>N("left")),rt("scroll",fe,()=>N("right")),u(Z,J)},Je=Z=>{var J=fd();u(Z,J)};Y(Qe,Z=>{e(o)?Z(qe):Z(Je,-1)})}var Te=E(Qe,2);{var $e=Z=>{var J=vd();u(Z,J)};Y(Te,Z=>{a()&&Z($e)})}v(U),nt(U,Z=>f(n,Z),()=>e(n)),ee(()=>{ae(y,r.leftLabel),ae(O,r.kind),ae(L,r.rightLabel),ae(te,r.kind),fn(ne,r.search),ae(Ie,`${e(d)>=0?e(d)+1:"–"} / ${e(i).length??""} diff${e(i).length!==1?"s":""}`)}),ue("input",ne,Z=>r.onSearchChange(Z.target.value)),ue("keydown",ne,Z=>{Z.key==="Enter"&&(Z.preventDefault(),h())})}ue("keydown",U,j),u(t,U),Ae()}Ge(["keydown","click","input"]);var pd=P(" ",1),md=P('

        Please select two different cases to compare.

        '),_d=P('

        Select two cases to compare.

        '),bd=P(' '),xd=P('
        Parameter
        '),yd=P('

        All parameters are identical.

        '),wd=P('
        ',1),kd=P('

        '),Cd=P('

        Loading...

        '),Sd=P(" ",1),$d=P(" ",1);function Pd(t,r){Me(r,!0);let a=D(""),n=D(""),s=D(""),i=D(""),o=D(""),l=D(!1),d=D(!1),m=D(""),x=V(()=>r.allCases.map(g=>({value:g,label:g})));const k=[{value:"setup.xml",label:"setup.xml"},{value:"doe_row.csv",label:"doe_row.csv"},{value:"run_solver.log",label:"run_solver.log"},{value:"performance.log",label:"performance.log"}];let b=V(()=>{var g;return((g=Ft())==null?void 0:g.compare_kinds)??k});Ne(()=>{var g;e(b).some(N=>N.value===e(s))||f(s,((g=e(b)[0])==null?void 0:g.value)??"",!0)});let z=D(!1),A=V(()=>e(a)&&e(n)&&e(a)!==e(n));function h(){const g=e(a);f(a,e(n),!0),f(n,g,!0)}async function c(){if(e(A)){f(l,!0),f(m,"");try{const g=await To({cases:[e(a),e(n)],base:e(a),kind:e(s)});f(o,g,!0),f(d,!0)}catch(g){console.error("Failed to load diff:",g),f(o,""),f(d,!1);const N=g instanceof Error?g.message:String(g);f(m,N.includes("404")?`File "${e(s)}" not found for one of the selected cases. It may not have been run yet.`:"Failed to load comparison.",!0)}f(l,!1)}}Ne(()=>{e(a)&&e(n)&&e(a)!==e(n)&&e(s)?c():e(a)&&e(n)&&e(a)===e(n)&&(f(o,""),f(d,!0))});let C=!1;Ne(()=>{r.allCases.length>=2&&!C?(C=!0,f(a,r.allCases[0],!0),f(n,r.allCases[1],!0)):r.allCases.length===1&&!C&&(C=!0,f(a,r.allCases[0],!0))});let R=V(yn),p=V(()=>{if(!e(a)||!e(n)||e(a)===e(n)||e(R).length===0)return[];const g=xn(),N=g.find(T=>T.case_id===e(a)),I=g.find(T=>T.case_id===e(n));return!N&&!I?[]:e(R).map(T=>{var j,U;const q=((j=N==null?void 0:N.doe)==null?void 0:j[T])!=null&&String(N.doe[T])!==""?String(N.doe[T]):"—",H=((U=I==null?void 0:I.doe)==null?void 0:U[T])!=null&&String(I.doe[T])!==""?String(I.doe[T]):"—";return{param:T,left:q,right:H,differs:q!==H}})}),w=V(()=>e(z)?e(p):e(p).filter(g=>g.differs)),$=V(()=>e(p).filter(g=>g.differs).length);jt(t,{eyebrow:"Compare",title:"Side-by-Side Comparison",wide:!0,children:(g,N)=>{var I=$d(),T=G(I);Qt(T,{children:(X,K)=>{var re=pd(),y=G(re);Re(y,{text:"First case",children:(F,M)=>{lt(F,{class:"w-32.5",get options(){return e(x)},get value(){return e(a)},onchange:L=>f(a,L,!0),placeholder:"Select..."})}});var S=E(y,2);{let F=V(()=>!e(a)&&!e(n));Pe(S,{variant:"secondary",size:"sm",onclick:h,get disabled(){return e(F)},children:(M,L)=>{Le(M,{get icon(){return gi}})},$$slots:{default:!0}})}var O=E(S,2);Re(O,{text:"Second case",children:(F,M)=>{lt(F,{class:"w-32.5",get options(){return e(x)},get value(){return e(n)},onchange:L=>f(n,L,!0),placeholder:"Select..."})}}),u(X,re)}});var q=E(T,2);{var H=X=>{var K=md();u(X,K)},j=X=>{var K=_d();u(X,K)},U=X=>{var K=Sd(),re=G(K);{var y=B=>{var te=wd(),ie=G(te),W=_(ie),ne=_(W);v(W);var oe=E(W,2),de=_(oe,!0);v(oe),v(ie);var ve=E(ie,2);{var se=he=>{var Ee=xd(),pe=_(Ee),Ie=_(pe),ze=_(Ie),Qe=_(ze),qe=_(Qe,!0);v(Qe);var Je=E(Qe,2),Te=_(Je,!0);v(Je),v(ze),v(Ie);var $e=E(Ie);He($e,21,()=>e(w),Q=>Q.param,(Q,Z)=>{var J=bd(),le=_(J),xe=_(le,!0);v(le);var ye=E(le),fe=_(ye,!0);v(ye);var ge=E(ye),Ce=_(ge,!0);v(ge),v(J),ee(()=>{Se(le,1,dt(e(Z).differs?"text-edf-orange-fonce bg-[rgba(214,67,10,0.04)]":"")),ae(xe,e(Z).left),ae(fe,e(Z).param),Se(ge,1,dt(e(Z).differs?"text-edf-vert-fonce bg-[rgba(48,122,16,0.04)]":"")),ae(Ce,e(Z).right)}),u(Q,J)}),v($e),v(pe),v(Ee),ee(()=>{ae(qe,e(a)),ae(Te,e(n))}),u(he,Ee)},_e=he=>{var Ee=yd();u(he,Ee)};Y(ve,he=>{e(w).length>0?he(se):he(_e,-1)})}ee(()=>{ae(ne,`${e($)??""} difference${e($)!==1?"s":""} out of ${e(p).length??""} - parameters`),ae(de,e(z)?"Show differences only":"Show all parameters")}),ue("click",oe,()=>f(z,!e(z))),u(B,te)};Y(re,B=>{e(p).length>0&&B(y)})}var S=E(re,2);Qt(S,{children:(B,te)=>{Re(B,{text:"File",children:(ie,W)=>{lt(ie,{class:"w-37.5",get options(){return e(b)},get value(){return e(s)},onchange:ne=>f(s,ne,!0)})}})}});var O=E(S,2);{var F=B=>{var te=kd(),ie=_(te,!0);v(te),ee(()=>ae(ie,e(m))),u(B,te)},M=B=>{hd(B,{get diffText(){return e(o)},get leftLabel(){return e(a)},get rightLabel(){return e(n)},get kind(){return e(s)},get search(){return e(i)},get loading(){return e(l)},onSearchChange:te=>f(i,te,!0)})},L=B=>{var te=Cd();u(B,te)};Y(O,B=>{e(m)?B(F):e(d)?B(M,1):e(l)&&B(L,2)})}u(X,K)};Y(q,X=>{e(a)&&e(n)&&e(a)===e(n)?X(H):!e(a)||!e(n)?X(j,1):X(U,-1)})}u(g,I)},$$slots:{default:!0}}),Ae()}Ge(["click"]);var Ed=P(" Refresh",1),Rd=P(" ",1),Nd=P(''),Md=P(''),Ad=P(" Download as CSV",1),Td=P('
        ',1),Ld=P(" ",1),Fd=P(""),Id=P('
        Sort by:
        '),zd=P('

        No errors found. Select cases and adjust filters above.

        '),Dd=P(' '),Od=P('NEW'),Hd=P('
        '),jd=P('
        '),Vd=P(' '),Ud=P('
        '),Bd=P('

        '),qd=P("
        ",1);function Kd(t,r){Me(r,!0);const a=6,n=["csauto.stderr","run_solver.log","listing"];let s=D(ce([])),i=D(ce([...n])),o=!1;Ne(()=>{var ne;const W=(ne=Ft())==null?void 0:ne.error_files;W!=null&&W.length&&!o&&f(i,[...W],!0)});let l=D("all"),d=D(""),m=D(a),x=D(!1),k=D(""),b=D(ce(tr("errors")));Ne(()=>{rr("errors",e(b))});let z=D(ce([])),A=D(ce(new Set));function h(W){return W.toLowerCase().replace(/[0-9]+/g,"#").replace(/0x[0-9a-f]+/gi,"#").replace(/\s+/g," ").trim()}function c(W){const ne=W.line_html.replace(/<[^>]*>/g,"").slice(0,120);return`${W.case_id}|${W.file}|${W.severity}|${h(ne)}`}function C(W){const ne=new Map;for(const oe of W){const de=c(oe),ve=ne.get(de);ve?ve.count++:ne.set(de,{...oe,count:1,fingerprint:de,isNew:!e(A).has(de)})}return Array.from(ne.values())}async function R(){if(!(!e(s).length||!e(i).length)){f(x,!0);try{const W=await $o({cases:e(s),files:e(i),context:e(m),sev:e(l)==="all"?"":e(l),q:e(d)});f(k,"");const ne=C(W.items);f(z,W.items,!0);const oe=new Set(ne.map(de=>de.fingerprint));f(A,oe,!0),f(p,ne,!0)}catch(W){f(k,"Failed to load errors"),console.error("Failed to load errors:",W)}finally{f(x,!1)}}}let p=D(ce([])),w=null;function $(){w&&clearTimeout(w),w=setTimeout(R,250)}function g(W){return W==="error"?"error-sev-error":W==="warn"?"error-sev-warn":"error-sev-info"}let N=V(()=>kn(r.allCases));const I=["csauto.stderr","run_solver.log","listing","csauto.stdout"];let T=V(()=>{var W;return(((W=Ft())==null?void 0:W.error_files)??I).map(ne=>({value:ne,label:ne}))}),q=[{value:"all",label:"All"},{value:"error",label:"Error"},{value:"warn",label:"Warn"},{value:"info",label:"Info"}];function H(W){if(f(s,W,!0),W.length===0){f(p,[],!0),f(z,[],!0);return}R()}function j(W){if(o=!0,f(i,W,!0),W.length===0){f(p,[],!0),f(z,[],!0);return}R()}function U(W){f(l,W,!0),R()}function X(){R()}let K=!1;Ne(()=>{r.allCases.length>0&&!K&&(K=!0,f(s,[...r.allCases],!0),R())}),Ne(()=>{e(b)?mr("errors",R,jn()):vt("errors")});const re=_r(()=>{K&&R()});vr(()=>{vt("errors"),re()});let y=D(ce(new Set));function S(W){const ne=new Set(e(y));ne.has(W)?ne.delete(W):ne.add(W),f(y,ne,!0)}let O=D("severity"),F=D("desc");const M={info:0,warn:1,error:2};let L=V(()=>{const W=[...e(p)];return W.sort((ne,oe)=>{let de=0;return e(O)==="severity"?de=(M[ne.severity]??9)-(M[oe.severity]??9):e(O)==="count"?de=ne.count-oe.count:de=ne[e(O)].localeCompare(oe[e(O)]),e(F)==="asc"?de:-de}),W});const B={severity:"desc",count:"desc",case_id:"asc",file:"asc"};function te(W){e(O)===W?f(F,e(F)==="asc"?"desc":"asc",!0):(f(O,W,!0),f(F,B[W],!0))}function ie(){if(!e(p).length)return;const W=["case_id","file","severity","count","is_new","line_text"],ne=e(L).map(ve=>[ve.case_id,ve.file,ve.severity,ve.count,ve.isNew?"yes":"no",`"${ve.line_html.replace(/<[^>]*>/g,"").replace(/"/g,'""')}"`].join(",")),oe=[W.join(","),...ne].join(` -`),de=xr("errors",e(s),"csv");Ma(oe,de)}jt(t,{eyebrow:"Diagnostics",title:"Recent Errors",wide:!0,actions:ne=>{var oe=Rd(),de=G(oe);{let _e=V(jn);nr(de,{name:"errors",get intervalMs(){return e(_e)},onRefresh:R,get checked(){return e(b)},set checked(he){f(b,he,!0)}})}var ve=E(de,2);{var se=_e=>{Pe(_e,{variant:"primary",onclick:R,children:(he,Ee)=>{var pe=Ed(),Ie=G(pe);Le(Ie,{get icon(){return er}}),we(),u(he,pe)},$$slots:{default:!0}})};Y(ve,_e=>{e(b)||_e(se)})}u(ne,oe)},children:(ne,oe)=>{var de=qd(),ve=G(de);Qt(ve,{children:(Te,$e)=>{var Q=Td(),Z=G(Q);Re(Z,{text:"Cases",children:(Ce,De)=>{Wt(Ce,{class:"w-[160px]",get options(){return e(N)},get selected(){return e(s)},onchange:H,placeholder:"Select cases..."})}});var J=E(Z,2);Re(J,{text:"Files",children:(Ce,De)=>{Wt(Ce,{class:"w-[160px]",get options(){return e(T)},get selected(){return e(i)},onchange:j,placeholder:"Select files..."})}});var le=E(J,2);Re(le,{text:"Severity",children:(Ce,De)=>{lt(Ce,{class:"w-[90px]",get options(){return q},get value(){return e(l)},onchange:U})}});var xe=E(le,2);Re(xe,{text:"Search",children:(Ce,De)=>{var We=Nd();Ye(We),ue("input",We,$),tt(We,()=>e(d),gt=>f(d,gt)),u(Ce,We)}});var ye=E(xe,2);Re(ye,{text:"Context",children:(Ce,De)=>{var We=Md();Ye(We),ue("change",We,X),tt(We,()=>e(m),gt=>f(m,gt)),u(Ce,We)}});var fe=E(ye,2),ge=_(fe);{let Ce=V(()=>e(p).length===0);Pe(ge,{variant:"secondary",size:"sm",onclick:ie,get disabled(){return e(Ce)},children:(De,We)=>{var gt=Ad(),Vt=G(gt);Le(Vt,{get icon(){return pr}}),we(),u(De,gt)},$$slots:{default:!0}})}v(fe),u(Te,Q)}});var se=E(ve,2);{var _e=Te=>{var $e=Id(),Q=E(_($e),2);He(Q,16,()=>[{key:"severity",label:"Severity"},{key:"count",label:"Count"},{key:"case_id",label:"Case"},{key:"file",label:"File"}],Z=>Z.key,(Z,J)=>{var le=Fd(),xe=_(le,!0),ye=E(xe);{var fe=ge=>{var Ce=Ld(),De=E(G(Ce));{let We=V(()=>e(F)==="asc"?hi:vi);Le(De,{get icon(){return e(We)},size:12})}u(ge,Ce)};Y(ye,ge=>{e(O)===J.key&&ge(fe)})}v(le),ee(()=>{Se(le,1,`cursor-pointer bg-transparent border-none text-xs hover:underline ${e(O)===J.key?"text-edf-bleu-fonce font-bold":"text-muted"}`),ae(xe,J.label)}),ue("click",le,()=>te(J.key)),u(Z,le)}),v($e),u(Te,$e)};Y(se,Te=>{e(L).length>0&&Te(_e)})}var he=E(se,2),Ee=_(he);{var pe=Te=>{var $e=zd();u(Te,$e)};Y(Ee,Te=>{e(L).length===0&&Te(pe)})}var Ie=E(Ee,2);He(Ie,17,()=>e(L),Te=>Te.fingerprint,(Te,$e)=>{var Q=jd(),Z=_(Q),J=_(Z),le=_(J);{let et=V(()=>e(y).has(e($e).fingerprint)?pn:pi);Le(le,{get icon(){return e(et)},size:14})}v(J);var xe=E(J,2),ye=_(xe);v(xe);var fe=E(xe,2),ge=_(fe);{var Ce=et=>{var ut=Dd(),at=_(ut);v(ut),ee(()=>ae(at,`${e($e).count??""}x`)),u(et,ut)};Y(ge,et=>{e($e).count>1&&et(Ce)})}var De=E(ge,2);{var We=et=>{var ut=Od();u(et,ut)};Y(De,et=>{e($e).isNew&&et(We)})}v(fe),v(Z);var gt=E(Z,2);{var Vt=et=>{var ut=Hd();zr(ut,()=>e($e).line_html,!0),v(ut),u(et,ut)},jr=V(()=>e(y).has(e($e).fingerprint));Y(gt,et=>{e(jr)&&et(Vt)})}v(Q),ee(et=>{Se(Q,1,`error-item ${et??""}`),ae(ye,`${e($e).case_id??""} / ${e($e).file??""} : ${e($e).severity??""}`)},[()=>g(e($e).severity)]),ue("click",Z,()=>S(e($e).fingerprint)),u(Te,Q)}),v(he);var ze=E(he,2);{var Qe=Te=>{var $e=Ud(),Q=_($e),Z=_(Q);v(Q);var J=E(Q,2);{var le=ye=>{var fe=Vd(),ge=_(fe);v(fe),ee(Ce=>ae(ge,`${Ce??""} new`),[()=>e(p).filter(Ce=>Ce.isNew).length]),u(ye,fe)},xe=V(()=>e(p).some(ye=>ye.isNew));Y(J,ye=>{e(xe)&&ye(le)})}v($e),ee(()=>ae(Z,`${e(p).length??""} unique / ${e(z).length??""} total`)),u(Te,$e)};Y(ze,Te=>{e(L).length>0&&Te(Qe)})}var qe=E(ze,2);{var Je=Te=>{var $e=Bd(),Q=_($e,!0);v($e),ee(()=>ae(Q,e(k))),u(Te,$e)};Y(qe,Te=>{e(k)&&Te(Je)})}ee(()=>Se(he,1,`error-list ${e(x)?"opacity-50 pointer-events-none":""}`)),u(ne,de)},$$slots:{actions:!0,default:!0}}),Ae()}Ge(["input","change","click"]);var Gd=P('
        Developed by Simvia
        ',1);function rf(t,r){Me(r,!0);let a=D(ce({totalCases:0,shownCases:0,totalRunning:0,shownRunning:0,totalConverged:0,shownConverged:0})),n=D(ce([])),s=D(null);const i=M=>e(s)===null||e(s).includes(M);async function o(M){var te;const L=`/favicon-${M}.svg`;try{const ie=await fetch(L);if(!ie.ok||!((te=ie.headers.get("content-type"))!=null&&te.includes("svg")))return}catch{return}const B=document.querySelector('link[rel="icon"]');B&&(B.href=L)}async function l(){try{const M=await wo();jl(M.rows),Vl(M.doe_columns),f(n,M.rows.map(L=>L.case_id),!0),f(a,tc(Ra(),xn()),!0)}catch(M){console.error("Failed to load status:",M)}}Jn(()=>{l(),ko().then(M=>{_o(M),f(s,M.panels,!0),o(M.solver)}).catch(()=>{})});var d=Gd(),m=G(d);qi(m,{});var x=E(m,2);Yi(x,{});var k=E(x,2);xl(k,{get totalCases(){return e(a).totalCases},get shownCases(){return e(a).shownCases},get totalRunning(){return e(a).totalRunning},get shownRunning(){return e(a).shownRunning},get totalConverged(){return e(a).totalConverged},get shownConverged(){return e(a).shownConverged}});var b=E(k,2),z=_(b);{var A=M=>{Ac(M,{onRefresh:l})},h=V(()=>i("status"));Y(z,M=>{e(h)&&M(A)})}var c=E(z,2);{var C=M=>{eu(M,{get allCases(){return e(n)}})},R=V(()=>i("residuals"));Y(c,M=>{e(R)&&M(C)})}var p=E(c,2);{var w=M=>{Cu(M,{get allCases(){return e(n)}})},$=V(()=>i("probes"));Y(p,M=>{e($)&&M(w)})}var g=E(p,2);{var N=M=>{Fu(M,{get allCases(){return e(n)}})},I=V(()=>i("performance"));Y(g,M=>{e(I)&&M(N)})}var T=E(g,2);{var q=M=>{Pd(M,{get allCases(){return e(n)}})},H=V(()=>i("compare"));Y(T,M=>{e(H)&&M(q)})}var j=E(T,2);{var U=M=>{sd(M,{get allCases(){return e(n)}})},X=V(()=>i("tail"));Y(j,M=>{e(X)&&M(U)})}var K=E(j,2);{var re=M=>{Kd(M,{get allCases(){return e(n)}})},y=V(()=>i("errors"));Y(K,M=>{e(y)&&M(re)})}v(b);var S=E(b,2),O=E(_(S),2),F=_(O);v(O),v(S),ee(()=>ke(F,"src",yl)),u(t,d),Ae()}export{rf as component}; + ${n()||s()?"bg-edf-bleu-moyen border-edf-bleu-moyen text-white":"bg-white border-edf-gris-moyen hover:border-edf-bleu-clair"}`),Ze(c,`width: ${o()??""}px; height: ${o()??""}px;`),ke(c,"aria-checked",s()?"mixed":n()),ke(c,"aria-disabled",i()),ke(c,"tabindex",i()?-1:0)}),ue("click",c,m),ue("keydown",c,x),u(h,c)};let n=me(r,"checked",3,!1),s=me(r,"indeterminate",3,!1),i=me(r,"disabled",3,!1),o=me(r,"size",3,16),l=me(r,"label",3,""),d=me(r,"labelFirst",3,!1);function m(h){var C;if(h.stopPropagation(),i())return;const c=s()?!0:!n();(C=r.onchange)==null||C.call(r,c)}function x(h){(h.key===" "||h.key==="Enter")&&(h.preventDefault(),h.stopPropagation(),m(h))}var k=be(),b=G(k);{var z=h=>{var c=rl(),C=_(c);{var R=y=>{var $=el(),g=G($),N=_(g,!0);v(g);var I=E(g);a(I),ee(()=>ae(N,l())),u(y,$)},p=y=>{var $=tl(),g=G($);a(g);var N=E(g),I=_(N,!0);v(N),ee(()=>ae(I,l())),u(y,$)};Y(C,y=>{d()?y(R):y(p,-1)})}v(c),ue("click",c,m),ue("keydown",c,x),u(h,c)},A=h=>{a(h)};Y(b,h=>{l()?h(z):h(A,-1)})}u(t,k),Ae()}Ge(["click","keydown"]);function mn(t,r){try{const a=localStorage.getItem(t);return a===null?r:JSON.parse(a)}catch{return r}}function Hr(t,r){localStorage.setItem(t,JSON.stringify(r))}const tn=new Map;function mr(t,r,a){vt(t),tn.set(t,setInterval(r,a))}function vt(t){const r=tn.get(t);r!==void 0&&(clearInterval(r),tn.delete(t))}const Tt={status:1e3,tail:1e3,plot:3e3,probe:3e3,errors:5e3},nl=["status","plot","probe","tail","errors"],Wr={status:{label:"Status table",min:500},plot:{label:"Residual plots",min:1e3},probe:{label:"Probe plots",min:1e3},tail:{label:"Log tail",min:500},errors:{label:"Recent errors",min:2e3}},ir=mn("csauto_refresh_rates",{});let bt=D(ce({status:ir.status??Tt.status,tail:ir.tail??Tt.tail,plot:ir.plot??Tt.plot,probe:ir.probe??Tt.probe,errors:ir.errors??Tt.errors}));function al(){return e(bt)}function sl(t,r){f(bt,{...e(bt),[t]:r},!0),Hr("csauto_refresh_rates",e(bt))}function ol(){return e(bt).status}function On(){return e(bt).tail}function Hn(){return e(bt).plot}function il(){return e(bt).probe}function jn(){return e(bt).errors}const rn=new Set;function _r(t){return rn.add(t),()=>rn.delete(t)}function ll(){rn.forEach(t=>t())}const nn=mn("csauto_autorefresh_enabled",{});function tr(t){return nn[t]??!0}function rr(t,r){nn[t]=r,Hr("csauto_autorefresh_enabled",nn)}var cl=P(" ",1),ul=P('
        ms
        '),dl=P('
        Telemetry
        '),fl=P('
        API token
        Auto-refresh intervals
        ',1);function vl(t,r){Me(r,!0);let a=D(ce(wa())),n=D(ce({...al()})),s=D(!0),i=D(!1);Ne(()=>{Ho().then(m=>{f(s,m.enabled,!0),f(i,!0)}).catch(()=>{f(i,!1)})});function o(m){const{min:x}=Wr[m];let k=e(n)[m];(typeof k!="number"||isNaN(k))&&(k=Tt[m]),f(n,{...e(n),[m]:Math.max(x,Math.round(k))},!0)}function l(m,x){const k=x.target,b=k.value.replace(/[^0-9]/g,""),z=parseInt(b,10);isNaN(z)?b===""&&f(n,{...e(n),[m]:Wr[m].min},!0):f(n,{...e(n),[m]:z},!0),k.value=String(e(n)[m])}function d(){ka(e(a));for(const m of Object.keys(e(n)))o(m),sl(m,e(n)[m]);e(i)&&jo(e(s)).catch(()=>{}),r.onClose()}hr(t,{title:"Settings",titleId:"settings-dialog-title",get onCancel(){return r.onClose},onConfirm:d,maxWidth:"400px",portal:!0,footer:x=>{var k=cl(),b=G(k);Pe(b,{variant:"secondary",get onclick(){return r.onClose},children:(A,h)=>{we();var c=Ve("Cancel");u(A,c)},$$slots:{default:!0}});var z=E(b,2);Pe(z,{variant:"primary",onclick:d,children:(A,h)=>{we();var c=Ve("Save");u(A,c)},$$slots:{default:!0}}),u(x,k)},children:(x,k)=>{var b=fl(),z=G(b),A=E(_(z),2);Ye(A),v(z);var h=E(z,2),c=_(h),C=E(_(c),2);v(c);var R=E(c,2);He(R,20,()=>nl,$=>$,($,g)=>{const N=V(()=>Wr[g]);var I=ul(),L=_(I),q=_(L),H=_(q,!0);v(q);var j=E(q,2),U=_(j);v(j),v(L);var X=E(L,2),K=_(X);Ye(K),we(2),v(X),v(I),ee(()=>{ae(H,e(N).label),ae(U,`min ${e(N).min??""}ms`),fn(K,e(n)[g])}),ue("input",K,re=>l(g,re)),rt("blur",K,()=>o(g)),u($,I)}),v(R),v(h);var p=E(h,2);{var y=$=>{var g=dl(),N=E(_(g),2);Ot(N,{get checked(){return e(s)},onchange:I=>f(s,I,!0),label:"Send anonymous usage statistics"}),v(g),u($,g)};Y(p,$=>{e(i)&&$(y)})}tt(A,()=>e(a),$=>f(a,$)),ue("click",C,()=>f(n,{...Tt},!0)),u(x,b)},$$slots:{footer:!0,default:!0}}),Ae()}Ge(["click","input"]);const gl=""+new URL("../assets/code-saturne.BHojVttu.svg",import.meta.url).href,hl="data:image/svg+xml,%3c?xml%20version='1.0'%20encoding='UTF-8'?%3e%3csvg%20id='Calque_1'%20xmlns='http://www.w3.org/2000/svg'%20version='1.1'%20height='400px'%20viewBox='0%200%20397.89%2095.85'%3e%3c!--%20Generator:%20Adobe%20Illustrator%2029.8.1,%20SVG%20Export%20Plug-In%20.%20SVG%20Version:%202.1.1%20Build%202)%20--%3e%3cdefs%3e%3cstyle%3e%20.st0%20{%20fill:%20%23ff861d;%20}%20.st1%20{%20fill:%20%23ffb210;%20}%20.st2%20{%20fill:%20%231057c8;%20}%20.st3%20{%20fill:%20%23d6430a;%20}%20%3c/style%3e%3c/defs%3e%3cpath%20class='st2'%20d='M26.25,61.73c-12.02,0-20.11-8.16-20.11-21.37s8.01-21.37,20.33-21.37c8.83,0,15.06,3.78,17.36,11.28l-5.64,2.52c-1.48-5.94-4.97-8.69-11.87-8.69-8.46,0-14.25,6.01-14.25,16.25s5.93,16.25,13.95,16.25,11.73-2.82,12.84-8.61l5.71,1.93c-2,7.79-9.13,11.8-18.33,11.8'/%3e%3cpath%20class='st2'%20d='M89.96,40.36c0,13.21-7.87,21.37-20.48,21.37s-20.48-8.16-20.48-21.37,7.86-21.37,20.48-21.37,20.48,8.16,20.48,21.37M54.94,40.36c0,10.24,5.64,16.25,14.54,16.25s14.54-6.01,14.54-16.25-5.64-16.25-14.54-16.25-14.54,6.01-14.54,16.25'/%3e%3cpath%20class='st2'%20d='M134.3,40.29c0,13.43-8.01,20.85-21.81,20.85h-14.84V19.58h14.84c13.8,0,21.81,7.42,21.81,20.7M128.36,40.29c0-9.94-5.57-15.58-15.44-15.58h-9.35v31.32h9.35c9.87,0,15.44-5.71,15.44-15.73'/%3e%3cpolygon%20class='st2'%20points='174.11%2056.02%20174.11%2061.14%20141.98%2061.14%20141.98%2019.58%20173.14%2019.58%20173.14%2024.7%20147.91%2024.7%20147.91%2037.54%20167.65%2037.54%20167.65%2042.66%20147.91%2042.66%20147.91%2056.02%20174.11%2056.02'/%3e%3cpath%20class='st2'%20d='M213.81,22.13c4.16-1.92,9.75-3.11,14.3-3.11,12.55,0,17.74,5.19,17.74,17.34v5.27c0,4.16.08,7.28.16,10.31.08,3.12.24,5.99.48,9.19h-9.43c-.4-2.16-.4-4.88-.48-6.15h-.16c-2.48,4.56-7.83,7.11-12.71,7.11-7.28,0-14.39-4.4-14.39-12.23,0-6.15,2.96-9.75,7.03-11.75,4.08-2,9.35-2.4,13.83-2.4h5.91c0-6.63-2.96-8.87-9.27-8.87-4.56,0-9.11,1.76-12.71,4.48l-.32-9.19ZM226.36,54.26c3.27,0,5.83-1.44,7.51-3.68,1.76-2.32,2.24-5.27,2.24-8.47h-4.63c-4.8,0-11.91.8-11.91,7.11,0,3.52,2.96,5.04,6.79,5.04'/%3e%3cpath%20class='st2'%20d='M280.98,28.61c-3.28-1.12-5.67-1.76-9.59-1.76-2.88,0-6.31,1.04-6.31,4.56,0,6.56,18.62,2.4,18.62,17.27,0,9.59-8.55,13.43-17.27,13.43-4.08,0-8.23-.72-12.15-1.76l.64-8.79c3.36,1.68,6.87,2.72,10.55,2.72,2.72,0,7.03-1.04,7.03-5.04,0-8.07-18.62-2.56-18.62-17.42,0-8.87,7.75-12.79,16.14-12.79,5.03,0,8.31.8,11.67,1.52l-.72,8.07Z'/%3e%3cpath%20class='st2'%20d='M295.25,27.81h-7.91v-7.83h7.91v-8.23l10.71-3.44v11.67h9.51v7.83h-9.51v19.18c0,3.52.96,6.79,5.04,6.79,1.92,0,3.76-.4,4.88-1.12l.32,8.47c-2.24.64-4.72.96-7.91.96-8.39,0-13.03-5.2-13.03-13.35v-20.94Z'/%3e%3cpath%20class='st2'%20d='M355.22,58.82c-3.92,2.16-8.39,3.28-14.15,3.28-13.59,0-21.5-7.83-21.5-21.34,0-11.91,6.31-21.74,19.1-21.74,15.27,0,19.58,10.47,19.58,24.85h-28.45c.48,6.63,5.11,10.39,11.75,10.39,5.2,0,9.67-1.92,13.67-4.15v8.71ZM348.03,36.52c-.32-5.19-2.72-9.67-8.71-9.67s-9.03,4.16-9.51,9.67h18.22Z'/%3e%3cpath%20class='st2'%20d='M366.57,19.98h9.51v9.35h.16c.48-3.84,4.87-10.31,11.27-10.31,1.04,0,2.16,0,3.27.32v10.79c-.96-.56-2.88-.88-4.79-.88-8.71,0-8.71,10.87-8.71,16.78v15.11h-10.71V19.98Z'/%3e%3cpath%20class='st0'%20d='M229.73,80.9c0,3.67-2.97,6.64-6.64,6.64s-6.64-2.97-6.64-6.64,2.97-6.64,6.64-6.64,6.64,2.97,6.64,6.64'/%3e%3cpath%20class='st0'%20d='M203.17,80.9c0,3.67-2.97,6.64-6.64,6.64s-6.64-2.97-6.64-6.64,2.97-6.64,6.64-6.64,6.64,2.97,6.64,6.64'/%3e%3cpath%20class='st1'%20d='M296.14,80.9c0,3.67-2.97,6.64-6.64,6.64s-6.64-2.97-6.64-6.64,2.97-6.64,6.64-6.64,6.64,2.97,6.64,6.64'/%3e%3cpath%20class='st1'%20d='M378.47,80.9c0,3.67-2.97,6.64-6.64,6.64s-6.64-2.97-6.64-6.64,2.97-6.64,6.64-6.64,6.64,2.97,6.64,6.64'/%3e%3cpath%20class='st1'%20d='M269.57,80.9c0,3.67-2.97,6.64-6.64,6.64s-6.64-2.97-6.64-6.64,2.97-6.64,6.64-6.64,6.64,2.97,6.64,6.64'/%3e%3cpath%20class='st3'%20d='M107.55,80.9c0,3.67-2.97,6.64-6.64,6.64s-6.64-2.97-6.64-6.64,2.97-6.64,6.64-6.64,6.64,2.97,6.64,6.64'/%3e%3crect%20class='st0'%20x='134.12'%20y='75.59'%20width='42.5'%20height='10.62'/%3e%3crect%20class='st1'%20x='309.42'%20y='75.59'%20width='42.5'%20height='10.62'/%3e%3c/svg%3e";var pl=P('CODE_SATURNE'),ml=P('CODE_ASTER'),_l=P(' '),bl=P('
        ',1);function xl(t,r){Me(r,!0);let a=V(()=>r.shownCases!==r.totalCases),n=D(!1);var s=bl(),i=G(s),o=_(i),l=_(o);{var d=F=>{var M=pl();ee(()=>ke(M,"src",gl)),u(F,M)},m=V(()=>{var F;return!Ft()||((F=Ft())==null?void 0:F.solver)==="code_saturne"}),x=F=>{var M=ml();ee(()=>ke(M,"src",hl)),u(F,M)},k=V(()=>{var F;return((F=Ft())==null?void 0:F.solver)==="code_aster"}),b=F=>{var M=_l(),T=_(M,!0);v(M),ee(B=>ae(T,B),[()=>{var B;return(B=Ft())==null?void 0:B.solver}]),u(F,M)};Y(l,F=>{e(m)?F(d):e(k)?F(x,1):F(b,-1)})}v(o);var z=E(o,2),A=_(z),h=_(A),c=_(h);{var C=F=>{var M=Ve();ee(()=>ae(M,`${r.shownCases??""}/`)),u(F,M)};Y(c,F=>{e(a)&&F(C)})}var R=E(c,1,!0);v(h);var p=E(h,2),y=_(p,!0);v(p),v(A);var $=E(A,2),g=_($),N=_(g);{var I=F=>{var M=Ve();ee(()=>ae(M,`${r.shownRunning??""}/`)),u(F,M)};Y(N,F=>{e(a)&&F(I)})}var L=E(N,1,!0);v(g),we(2),v($);var q=E($,2),H=_(q),j=_(H);{var U=F=>{var M=Ve();ee(()=>ae(M,`${r.shownConverged??""}/`)),u(F,M)};Y(j,F=>{e(a)&&F(U)})}var X=E(j,1,!0);v(H),we(2),v(q),v(z);var K=E(z,2),re=_(K),w=_(re);Te(w,{get icon(){return Ri},size:18}),v(re),v(K),v(i);var S=E(i,2);{var O=F=>{vl(F,{onClose:()=>f(n,!1)})};Y(S,F=>{e(n)&&F(O)})}ee(()=>{ae(R,r.totalCases),ae(y,r.totalCases===1?"case":"cases"),ae(L,r.totalRunning),ae(X,r.totalConverged)}),ue("click",re,()=>f(n,!0)),u(t,s),Ae()}Ge(["click"]);const yl=""+new URL("../assets/simvia-logo.kLDq7Uoj.svg",import.meta.url).href;var wl=P('
        '),kl=P('

        '),Cl=P("
        "),Sl=P('
        '),$l=P('
        '),Pl=P('
        ');function jt(t,r){let a=me(r,"eyebrow",3,""),n=me(r,"title",3,""),s=me(r,"wide",3,!1);var i=Pl(),o=E(_(i),2);{var l=m=>{var x=$l(),k=_(x);{var b=c=>{var C=be(),R=G(C);ft(R,()=>r.titleSlot),u(c,C)},z=c=>{var C=Cl(),R=_(C);{var p=g=>{var N=wl(),I=_(N,!0);v(N),ee(()=>ae(I,a())),u(g,N)};Y(R,g=>{a()&&g(p)})}var y=E(R,2);{var $=g=>{var N=kl(),I=_(N,!0);v(N),ee(()=>ae(I,n())),u(g,N)};Y(y,g=>{n()&&g($)})}v(C),u(c,C)};Y(k,c=>{r.titleSlot?c(b):c(z,-1)})}var A=E(k,2);{var h=c=>{var C=Sl(),R=_(C);ft(R,()=>r.actions),v(C),u(c,C)};Y(A,c=>{r.actions&&c(h)})}v(x),u(m,x)};Y(o,m=>{(a()||n()||r.titleSlot||r.actions)&&m(l)})}var d=E(o,2);ft(d,()=>r.children),v(i),ee(()=>{Se(i,1,`${s()?"col-span-12":"col-span-6"} bg-card border border-border rounded-[10px] p-[16px_18px_18px] relative overflow-clip animate-rise`),ke(i,"id",r.id)}),u(t,i)}function nr(t,r){Me(r,!0);let a=me(r,"checked",15,!0);Ne(()=>{a()?mr(r.name,r.onRefresh,r.intervalMs):vt(r.name)}),vr(()=>vt(r.name)),Ot(t,{get checked(){return a()},onchange:n=>a(n),size:14,label:"Auto-refresh",labelFirst:!0}),Ae()}var El=P(" ",1),Rl=P('
      • '),Nl=P(''),Ml=P(' ',1);function Al(t,r){Me(r,!0);let a=me(r,"label",3,"More"),n=D(!1),s=D(void 0),i=D(void 0),o=D("");function l(){if(!e(s)||!e(i))return;const c=e(s).getBoundingClientRect(),C=e(i).offsetHeight,R=4,p=window.innerHeight-c.bottom-R,$=pp?c.top-R-C:c.bottom+R,g=Math.min(c.left,window.innerWidth-180);f(o,`position:fixed; top:${$}px; left:${g}px; min-width:170px; z-index:9999;`)}async function d(){f(n,!e(n)),e(n)&&(await Et(),l())}function m(c){c.disabled||(f(n,!1),c.onClick())}function x(c){e(n)&&e(s)&&!e(s).contains(c.target)&&e(i)&&!e(i).contains(c.target)&&f(n,!1)}var k=Ml();rt("mousedown",it,x),rt("keydown",it,c=>{c.key==="Escape"&&f(n,!1)}),rt("scroll",it,()=>{e(n)&&f(n,!1)});var b=G(k),z=_(b);Pe(z,{variant:"secondary",size:"sm",onclick:d,children:(c,C)=>{var R=El(),p=G(R);Te(p,{get icon(){return xi}});var y=E(p);ee(()=>ae(y,` ${a()??""}`)),u(c,R)},$$slots:{default:!0}}),v(b),nt(b,c=>f(s,c),()=>e(s));var A=E(b,2);{var h=c=>{Jt(c,{children:(C,R)=>{var p=Nl();He(p,21,()=>r.items,y=>y.label,(y,$)=>{var g=Rl(),N=_(g),I=_(N);Te(I,{get icon(){return e($).icon},size:14});var L=E(I);v(N),v(g),ee(()=>{N.disabled=e($).disabled,ke(N,"aria-disabled",e($).disabled),Se(N,1,`w-full flex items-center gap-2 text-left py-[7px] px-[10px] bg-transparent border border-transparent rounded-md text-[12.5px] text-ink transition-[background] duration-[120ms] ease-in-out disabled:opacity-40 disabled:pointer-events-none ${e($).disabled?"":"cursor-pointer hover:bg-edf-gris-clair hover:border-border"}`),ae(L,` ${e($).label??""}`)}),ue("mousedown",N,q=>{q.stopPropagation(),m(e($))}),u(y,g)}),v(p),nt(p,y=>f(i,y),()=>e(i)),ee(()=>Ze(p,e(o))),u(C,p)}})};Y(A,c=>{e(n)&&c(h)})}u(t,k),Ae()}Ge(["mousedown"]);const It=[{key:"note",label:"Note"},{key:"nprocs",label:"MPI Ranks"},{key:"nt",label:"Thread Count"},{key:"last_iter",label:"Last Iter"},{key:"duration",label:"Duration"},{key:"last_mod",label:"Last Modified"},{key:"resu_size_mb",label:"RESU Size (MB)"}];let Xt=D(ce([])),Lr=D(ce([])),pt=D(ce(new Set)),br=D(""),_n=D(""),Rt=D(ce([])),st=D(ce([])),Yt=D(""),Pa=D(!0),Ll=D(!0),Kt=D(ce(mn("csauto_status_views",{}))),bn=D(ce(localStorage.getItem("csauto_status_view_selected")??""));function xn(){return e(Xt)}function yn(){return e(Lr)}function Bt(){return e(pt)}function Vn(){return e(br)}function an(){return e(_n)}function Tl(){return e(Rt)}function Fl(){return e(Rt).filter(t=>e(Lr).includes(t))}function Il(){return e(Rt).filter(t=>It.some(r=>r.key===t))}function zl(){return e(st)}function Dl(){return e(Yt)}function Ol(){return e(Pa)}function Un(){return e(Kt)}function Hl(){return e(bn)}function jl(t){f(Xt,t,!0)}function Vl(t){const r=e(Lr).length===0&&t.length>0;f(Lr,t,!0),r&&e(Rt).length===0&&f(Rt,[...t,...It.map(a=>a.key)],!0)}function Ul(t){f(br,t,!0)}function Bn(t){f(Rt,t,!0)}function Bl(t){f(Yt,t,!0)}function ql(t){f(Pa,t,!0)}function Kl(t){f(Ll,t,!0)}function wn(t){f(bn,t,!0),localStorage.setItem("csauto_status_view_selected",t)}function sn(t){const r=new Set(e(pt));r.has(t)?r.delete(t):r.add(t),f(pt,r,!0),f(br,t,!0),f(_n,t,!0)}function Ea(t){f(pt,new Set([t]),!0),f(br,t,!0),f(_n,t,!0)}function on(t,r,a){const n=a.indexOf(t),s=a.indexOf(r);if(n<0||s<0)return;const[i,o]=n{const a=Gl(r);return t.every(n=>a.includes(n))})}function Wl(t){if(t==null||t==="")return"";const r=Number(t);return Number.isFinite(r)?r:String(t).toLowerCase()}function Gn(t,r){var n;if(r==="case_id")return t.case_id;if(r==="status")return t.status??"";if(r==="note")return t.note??"";if(r==="nprocs")return t.nprocs??0;if(r==="nt")return t.nt??0;if(r==="last_iter")return t.last_iter??0;if(r==="duration")return t.duration_s??0;if(r==="last_mod")return t.last_mod??"";if(r==="resu_size_mb")return t.resu_size_mb??0;const a=(n=t.doe)==null?void 0:n[r];return a!==void 0?Wl(a):""}function Xl(t,r){return t===""&&r===""?0:t===""?1:r===""?-1:typeof t=="number"&&typeof r=="number"?t-r:String(t).localeCompare(String(r))}function Na(){const t=Ra();return e(st).length===0?t:[...t].sort((r,a)=>{for(const n of e(st)){const s=Gn(r,n.key),i=Gn(a,n.key),o=Xl(s,i);if(o!==0)return n.dir==="asc"?o:-o}return 0})}function Rr(){return Na().map(t=>t.case_id)}function Yl(t,r){const a=e(st).findIndex(n=>n.key===t);if(r)if(a>=0){const n=[...e(st)];n[a]={key:t,dir:n[a].dir==="asc"?"desc":"asc"},f(st,n,!0)}else f(st,[...e(st),{key:t,dir:"asc"}],!0);else a>=0&&e(st).length===1?f(st,[{key:t,dir:e(st)[0].dir==="asc"?"desc":"asc"}],!0):f(st,[{key:t,dir:"asc"}],!0)}function Ql(){return{visibleColumns:[...e(Rt)],search:e(Yt),sorts:e(st).map(t=>({...t}))}}function Zl(t){f(Rt,[...t.visibleColumns],!0),f(Yt,t.search,!0),f(st,t.sorts.map(r=>({...r})),!0)}function Jl(t){f(Kt,{...e(Kt),[t]:Ql()},!0),Hr("csauto_status_views",e(Kt)),wn(t)}function ec(t){const r={...e(Kt)};delete r[t],f(Kt,r,!0),Hr("csauto_status_views",r),e(bn)===t&&wn("")}function Sr(){const t=new Set;for(const r of e(Xt))e(pt).has(r.case_id)&&t.add((r.status??"").toUpperCase());return t}function Wn(){for(const t of e(Xt))if(e(pt).has(t.case_id)&&t.resu_size_mb!=null&&t.resu_size_mb>0)return!0;return!1}function tc(t,r){const a=r.length,n=t.length,s=r.filter(d=>d.status==="RUNNING").length,i=t.filter(d=>d.status==="RUNNING").length,o=r.filter(d=>d.convergence==="converged").length,l=t.filter(d=>d.convergence==="converged").length;return{totalCases:a,shownCases:n,totalRunning:s,shownRunning:i,totalConverged:o,shownConverged:l}}var rc=P('
        DOE columns
        '),nc=P('
        ');function ac(t,r){Me(r,!0);let a=me(r,"initialName",3,""),n=V(yn),s=D(ce(a())),i=D(ce(new Set(r.initialColumns??[...e(n),...It.map(U=>U.key)])));function o(U){const X=new Set(e(i));X.has(U)?X.delete(U):X.add(U),f(i,X,!0)}function l(U,X){const K=new Set(e(i));U.forEach(re=>X?K.add(re):K.delete(re)),f(i,K,!0)}function d(){const U=e(s).trim();U&&r.onSave(U,[...e(i)])}function m(U){U.key==="Escape"&&r.onCancel()}function x(U){U.target.dataset.backdrop!==void 0&&r.onCancel()}let k=D(void 0);Ne(()=>{var U,X;(U=e(k))==null||U.focus(),(X=e(k))==null||X.select()});var b=nc(),z=_(b),A=_(z),h=_(A,!0);v(A);var c=E(A,2),C=E(_(c),2);Ye(C),nt(C,U=>f(k,U),()=>e(k)),v(c);var R=E(c,2);{var p=U=>{var X=rc(),K=_(X),re=E(_(K),2),w=_(re),S=E(w,2);v(re),v(K);var O=E(K,2);He(O,20,()=>e(n),F=>F,(F,M)=>{{let T=V(()=>e(i).has(M));Ot(F,{get checked(){return e(T)},onchange:()=>o(M),size:14,get label(){return M}})}}),v(O),v(X),ue("click",w,()=>l(e(n),!0)),ue("click",S,()=>l(e(n),!1)),u(U,X)};Y(R,U=>{e(n).length>0&&U(p)})}var y=E(R,2),$=_(y),g=E(_($),2),N=_(g),I=E(N,2);v(g),v($);var L=E($,2);He(L,21,()=>It,U=>U.key,(U,X)=>{{let K=V(()=>e(i).has(e(X).key));Ot(U,{get checked(){return e(K)},onchange:()=>o(e(X).key),size:14,get label(){return e(X).label}})}}),v(L),v(y);var q=E(y,2),H=_(q);Pe(H,{variant:"secondary",get onclick(){return r.onCancel},children:(U,X)=>{we();var K=Ve("Cancel");u(U,K)},$$slots:{default:!0}});var j=E(H,2);{let U=V(()=>!e(s).trim());Pe(j,{variant:"primary",onclick:d,get disabled(){return e(U)},children:(X,K)=>{we();var re=Ve("Save");u(X,re)},$$slots:{default:!0}})}v(q),v(z),v(b),ee(()=>ae(h,r.mode==="create"?"New view":"Edit view")),ue("keydown",b,m),ue("click",b,x),tt(C,()=>e(s),U=>f(s,U)),ue("click",N,()=>l(It.map(U=>U.key),!0)),ue("click",I,()=>l(It.map(U=>U.key),!1)),u(t,b),Ae()}Ge(["keydown","click"]);var sc=P('
        ',1);function oc(t,r){Me(r,!0);let a=V(()=>Object.keys(Un())),n=D(ce(Hl())),s=D(!1),i=D("create"),o=V(()=>[{value:"",label:"All columns"},...e(a).map($=>({value:$,label:$}))]);function l($){if(f(n,$,!0),wn($),$==="")Bn([...yn(),...It.map(g=>g.key)]);else{const g=Un();g[$]&&Zl(g[$])}}function d(){f(i,"edit"),f(s,!0)}function m(){f(i,"create"),f(s,!0)}function x($,g){Bn(g),e(i)==="edit"&&e(n)&&$!==e(n)&&ec(e(n)),Jl($),f(n,$,!0),f(s,!1)}function k(){f(s,!1)}var b=sc(),z=G(b),A=_(z);lt(A,{class:"w-[140px]",get options(){return e(o)},get value(){return e(n)},onchange:l});var h=E(A,2),c=_(h);Te(c,{get icon(){return Si},size:14}),v(h);var C=E(h,2),R=_(C);Te(R,{get icon(){return $i},size:14}),v(C),v(z);var p=E(z,2);{var y=$=>{Jt($,{children:(g,N)=>{{let I=V(()=>e(i)==="edit"?e(n):""),L=V(Tl);ac(g,{get mode(){return e(i)},get initialName(){return e(I)},get initialColumns(){return e(L)},onSave:x,onCancel:k})}}})};Y(p,$=>{e(s)&&$(y)})}ue("click",h,d),ue("click",C,m),u(t,b),Ae()}Ge(["click"]);var ic=P('
        '),lc=P(""),cc=P(' '),uc=P(' '),dc=P('
        '),fc=P(" "),vc=P(''),gc=P(''),hc=P(" Open GUI",1),pc=P(""),mc=P(" "),_c=P(""),bc=P('
        ');function xc(t,r){Me(r,!0);let a=V(()=>r.rows.length>0&&r.rows.every(w=>e(m).has(w.case_id))),n=V(()=>r.rows.some(w=>e(m).has(w.case_id))),s=V(()=>e(n)&&!e(a)),i=V(Fl),o=V(()=>new Set(Il())),l=V(zl),d=V(Ol),m=V(Bt),x=V(()=>{const w=[{key:"_select",label:"",kind:"meta",sticky:"left"},{key:"case_id",label:"Case",kind:"meta",sticky:"left"},{key:"status",label:"Status",kind:"meta",sticky:"left"},...e(o).has("note")?[{key:"note",label:"Note",kind:"meta"}]:[]],S=e(i).map(T=>({key:T,label:T,kind:"doe"})),F=[{key:"nprocs",label:"MPI Ranks",kind:"calc"},{key:"nt",label:"Thread Count",kind:"calc"},{key:"last_iter",label:"Last Iter",kind:"calc"},{key:"duration",label:"Duration",kind:"calc"},{key:"last_mod",label:"Last Modified",kind:"calc"},{key:"resu_size_mb",label:"RESU Size (MB)",kind:"calc"}].filter(T=>e(o).has(T.key)),M=cr("gui")?[{key:"_actions",label:"",kind:"meta",sticky:"right"}]:[];return[...w,...S,...F,...M]});function k(w){const S=e(l).findIndex(F=>F.key===w);if(S<0)return"";const O=e(l)[S].dir==="asc"?"▲":"▼";return e(l).length>1?`${O}${S+1}`:O}function b(w,S){w!=="_actions"&&Yl(w,S.ctrlKey||S.metaKey)}function z(w,S){S.shiftKey?on(an()||w,w,Rr()):S.ctrlKey||S.metaKey?sn(w):Ea(w)}function A(w,S){var F;const O=((F=w.status)==null?void 0:F.toUpperCase())??"";O!=="DONE"&&O!=="FAILED"||(S.preventDefault(),r.onContextMenu(w.case_id,S.clientX,S.clientY,w.convergence??""))}async function h(w){const S=w.note??"",O=await gn("Case note:",S,`Note — ${w.case_id}`,"",!0);if(O!==null)try{await zo(w.case_id,O)}catch(F){await Xe(`Failed to set note: ${F instanceof Error?F.message:F}`,"Error")}}async function c(w){try{await Oo(w)}catch(S){await Xe(`Failed to open GUI: ${S instanceof Error?S.message:S}`,"Error")}}function C(w,S){const O=(w==null?void 0:w.toUpperCase())??"";return(O==="DONE"||O==="FAILED")&&S==="converged"?"CONVERGED":(O==="DONE"||O==="FAILED")&&S==="not_converged"?"NOT CONVERGED":O}function R(w,S){const O=(w==null?void 0:w.toUpperCase())??"";return(O==="DONE"||O==="FAILED")&&S==="converged"?"status-converged":(O==="DONE"||O==="FAILED")&&S==="not_converged"?"status-not-converged":O==="RUNNING"?"status-running":O==="DONE"?"status-done":O==="FAILED"?"status-failed":O==="PREPARED"?"status-prepared":"status-unknown"}function p(w){return e(m).has(w.case_id)?"row-selected":""}function y(w,S){var O;return S.key==="case_id"?w.case_id:S.key==="nprocs"?w.nprocs!=null?String(w.nprocs):"":S.key==="nt"?w.nt!=null?String(w.nt):"":S.key==="last_iter"?w.last_iter!=null?String(w.last_iter):"":S.key==="duration"?w.duration??"":S.key==="last_mod"?$(w.last_mod):S.key==="resu_size_mb"?w.resu_size_mb!=null?String(w.resu_size_mb):"":S.kind==="doe"?((O=w.doe)==null?void 0:O[S.key])!=null&&String(w.doe[S.key])!==""?String(w.doe[S.key]):"—":""}function $(w){if(!w)return"";try{const S=new Date(w),O=new Date,F=S.toDateString()===O.toDateString(),M=new Date(O);M.setDate(M.getDate()-1);const T=S.toDateString()===M.toDateString(),B=S.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"});return F?`Today ${B}`:T?`Yesterday ${B}`:S.toLocaleDateString([],{month:"short",day:"numeric"})+` ${B}`}catch{return w}}let g=D(ce({})),N=D(void 0);Ne(()=>{e(d),e(x),r.rows,!(!e(N)||!e(d))&&Et().then(()=>{if(!e(N))return;const w=Array.from(e(N).querySelectorAll("thead th.sticky-left")),S={};let O=0;w.forEach(F=>{const M=F.dataset.colKey??"";S[M]=O,O+=F.getBoundingClientRect().width}),f(g,S,!0)})});function I(w,S,O){const F=O&&w.key!=="_select"&&w.key!=="_actions"?["status-sortable"]:[];if(!O&&w.key==="case_id"&&F.push("case-id"),w.kind==="doe"&&F.push("status-col-doe"),!O&&w.key==="note"&&F.push("max-w-[140px] whitespace-nowrap overflow-hidden text-ellipsis cursor-pointer hover:underline hover:decoration-dotted hover:underline-offset-2"),e(d)&&w.sticky==="left"&&F.push("sticky-left"),e(d)&&w.sticky==="right"&&F.push("sticky-right"),e(d)&&w.sticky==="left"){const M=e(x)[S+1];(!M||M.sticky!=="left")&&F.push("sticky-divider")}if(e(d)&&w.sticky==="right"){const M=e(x)[S-1];(!M||M.sticky!=="right")&&F.push("sticky-divider")}return F.join(" ")}function L(w,S){return I(w,S,!0)}function q(w,S){return I(w,S,!1)}function H(w){return!e(d)||!w.sticky?"":w.sticky==="right"?"position:sticky;right:0;":w.sticky==="left"&&w.key in e(g)?`position:sticky;left:${e(g)[w.key]}px;`:""}var j=bc(),U=_(j),X=_(U),K=_(X);He(K,23,()=>e(x),w=>w.key,(w,S,O)=>{var F=be(),M=G(F);{var T=ie=>{var W=ic(),ne=_(W),oe=_(ne);Ot(oe,{get checked(){return e(a)},get indeterminate(){return e(s)},get onchange(){return r.onToggleAll},size:14}),v(ne),v(W),ee((de,ve)=>{Se(W,1,`${de??""} !px-2.5 !py-0 w-9`),ke(W,"data-col-key",e(S).key),Ze(W,ve)},[()=>L(e(S),e(O)),()=>H(e(S))]),u(ie,W)},B=ie=>{var W=lc();ee((ne,oe)=>{Se(W,1,ne),ke(W,"data-col-key",e(S).key),Ze(W,oe)},[()=>dt(L(e(S),e(O))),()=>H(e(S))]),u(ie,W)},te=ie=>{var W=uc(),ne=_(W),oe=E(ne);{var de=se=>{var _e=cc(),he=_(_e,!0);v(_e),ee(Ee=>ae(he,Ee),[()=>k(e(S).key)]),u(se,_e)},ve=V(()=>k(e(S).key));Y(oe,se=>{e(ve)&&se(de)})}v(W),ee((se,_e)=>{Se(W,1,se),ke(W,"data-col-key",e(S).key),Ze(W,_e),ae(ne,`${e(S).label??""} `)},[()=>dt(L(e(S),e(O))),()=>H(e(S))]),ue("click",W,se=>b(e(S).key,se)),ue("keydown",W,se=>{(se.key==="Enter"||se.key===" ")&&(se.preventDefault(),b(e(S).key,se))}),u(ie,W)};Y(M,ie=>{e(S).key==="_select"?ie(T):e(S).key==="_actions"?ie(B,1):ie(te,-1)})}u(w,F)}),v(K),v(X);var re=E(X);He(re,21,()=>r.rows,w=>w.case_id,(w,S)=>{var O=_c();He(O,23,()=>e(x),F=>F.key,(F,M,T)=>{var B=be(),te=G(B);{var ie=ve=>{var se=dc(),_e=_(se),he=_(_e);{let Ee=V(()=>e(m).has(e(S).case_id));Ot(he,{get checked(){return e(Ee)},onchange:()=>r.onToggleRow(e(S).case_id),size:14})}v(_e),v(se),ee((Ee,pe)=>{Se(se,1,`${Ee??""} !px-2.5 !py-0 w-9`),ke(se,"data-col-key",e(M).key),Ze(se,pe)},[()=>q(e(M),e(T)),()=>H(e(M))]),u(ve,se)},W=ve=>{var se=fc(),_e=_(se),he=_(_e,!0);v(_e),v(se),ee((Ee,pe,Ie,ze)=>{Se(se,1,Ee),ke(se,"data-col-key",e(M).key),Ze(se,pe),Se(_e,1,`status-pill ${Ie??""}`),ae(he,ze)},[()=>dt(q(e(M),e(T))),()=>H(e(M)),()=>R(e(S).status,e(S).convergence),()=>C(e(S).status,e(S).convergence)]),u(ve,se)},ne=ve=>{var se=gc(),_e=_(se);{var he=pe=>{var Ie=Ve();ee(()=>ae(Ie,e(S).note)),u(pe,Ie)},Ee=pe=>{var Ie=vc(),ze=_(Ie);Te(ze,{get icon(){return Ci},size:12}),v(Ie),u(pe,Ie)};Y(_e,pe=>{e(S).note?pe(he):pe(Ee,-1)})}v(se),ee((pe,Ie)=>{Se(se,1,pe),ke(se,"data-col-key",e(M).key),Ze(se,Ie),ke(se,"title",e(S).note||"Add note")},[()=>dt(q(e(M),e(T))),()=>H(e(M))]),ue("click",se,pe=>{pe.stopPropagation(),h(e(S))}),ue("keydown",se,pe=>{(pe.key==="Enter"||pe.key===" ")&&(pe.preventDefault(),pe.stopPropagation(),h(e(S)))}),u(ve,se)},oe=ve=>{var se=pc(),_e=_(se);Pe(_e,{variant:"primary",size:"sm",onclick:he=>{he.stopPropagation(),c(e(S).case_id)},children:(he,Ee)=>{var pe=hc(),Ie=G(pe);Te(Ie,{get icon(){return yi},size:12}),we(),u(he,pe)},$$slots:{default:!0}}),v(se),ee((he,Ee)=>{Se(se,1,he),ke(se,"data-col-key",e(M).key),Ze(se,Ee)},[()=>dt(q(e(M),e(T))),()=>H(e(M))]),u(ve,se)},de=ve=>{var se=mc(),_e=_(se,!0);v(se),ee((he,Ee,pe)=>{Se(se,1,he),ke(se,"data-col-key",e(M).key),Ze(se,Ee),ae(_e,pe)},[()=>dt(q(e(M),e(T))),()=>H(e(M)),()=>y(e(S),e(M))]),u(ve,se)};Y(te,ve=>{e(M).key==="_select"?ve(ie):e(M).key==="status"?ve(W,1):e(M).key==="note"?ve(ne,2):e(M).key==="_actions"?ve(oe,3):ve(de,-1)})}u(F,B)}),v(O),ee(F=>Se(O,1,F),[()=>dt(p(e(S)))]),ue("click",O,F=>z(e(S).case_id,F)),ue("contextmenu",O,F=>A(e(S),F)),u(w,O)}),v(re),v(U),nt(U,w=>f(N,w),()=>e(N)),v(j),ee(()=>Se(j,1,`table-wrap ${e(d)?"sticky-enabled":""}`)),u(t,j),Ae()}Ge(["click","keydown","contextmenu"]);var yc=P('
        ');function wc(t,r){Me(r,!0);let a=V(()=>{const b=Math.min(r.x,window.innerWidth-180),z=Math.min(r.y,window.innerHeight-120);return`left: ${b}px; top: ${z}px;`});async function n(b){try{for(const z of r.cases)await Do(z,b)}catch(z){console.error("Failed to set convergence:",z)}r.onClose()}const s=r.cases.length>1?` (${r.cases.length})`:"";var i=yc();rt("keydown",it,b=>{b.key==="Escape"&&r.onClose()});var o=_(i),l=_(o);v(o);var d=E(o,2),m=_(d);v(d);var x=E(d,2),k=_(x);v(x),v(i),ee(()=>{Ze(i,e(a)),Se(o,1,`w-full text-left py-[7px] px-[10px] bg-transparent border border-transparent rounded-md text-[12.5px] cursor-pointer transition-[background] duration-[120ms] ease-in-out hover:bg-edf-gris-clair hover:border-border ${r.currentValue==="converged"?"is-active":""}`),ae(l,`Mark Converged${s}`),Se(d,1,`w-full text-left py-[7px] px-[10px] bg-transparent border border-transparent rounded-md text-[12.5px] cursor-pointer transition-[background] duration-[120ms] ease-in-out hover:bg-edf-gris-clair hover:border-border ${r.currentValue==="not_converged"?"is-active":""}`),ae(m,`Mark Not Converged${s}`),Se(x,1,`w-full text-left py-[7px] px-[10px] bg-transparent border border-transparent rounded-md text-[12.5px] cursor-pointer transition-[background] duration-[120ms] ease-in-out hover:bg-edf-gris-clair hover:border-border ${r.currentValue===""?"is-active":""}`),ae(k,`Clear Mark${s}`)}),ue("click",o,()=>n("converged")),ue("click",d,()=>n("not_converged")),ue("click",x,()=>n("")),u(t,i),Ae()}Ge(["click"]);var kc=P(" Refresh",1),Cc=P(" ",1),Sc=P(" Run",1),$c=P(" Restart",1),Pc=P(" Stop",1),Ec=P(" Kill",1),Rc=P(" Clean",1),Nc=P('
        ',1),Mc=P('
        ',1);function Ac(t,r){Me(r,!0);async function a(){await To(),r.onRefresh()}function n(){return[...Bt()]}async function s(){const T=n();if(!T.length)return;const B=await Ys(T);if(B)try{await zn({cases:T,n:B.n,nt:B.nt,maxParallel:B.maxParallel,backend:B.backend,options:B.options}),r.onRefresh()}catch(te){await Xe(`Run failed: ${te instanceof Error?te.message:te}`,"Error")}}async function i(){const T=n();if(!T.length)return;const B=await Qs(T);if(B)try{await zn({cases:T,n:B.n,nt:B.nt,maxParallel:B.maxParallel,restart:!0,restartMode:B.restartMode,restartValue:B.restartValue}),r.onRefresh()}catch(te){await Xe(`Restart failed: ${te instanceof Error?te.message:te}`,"Error")}}async function o(){const T=n();if(!(!T.length||!await Fn(`Kill ${T.length} case${T.length>1?"s":""}?`,"Confirm Kill","Kill","danger")))try{await Fo(T),r.onRefresh()}catch(te){await Xe(`Kill failed: ${te instanceof Error?te.message:te}`,"Error")}}async function l(){const T=n();if(!(!T.length||!await Fn(`Stop ${T.length} case${T.length>1?"s":""} gracefully (checkpoint + exit)?`,"Confirm Stop","Stop","danger")))try{await kr({cases:T,action:"stop"}),r.onRefresh(),Cr(`Stop requested for ${T.length} case${T.length>1?"s":""} — will checkpoint and exit`)}catch(te){await Xe(`Stop failed: ${te instanceof Error?te.message:te}`,"Error")}}async function d(){const T=n();if(T.length)try{await kr({cases:T,action:"checkpoint"}),r.onRefresh(),Cr(`Checkpoint requested for ${T.length} case${T.length>1?"s":""}`)}catch(B){await Xe(`Checkpoint failed: ${B instanceof Error?B.message:B}`,"Error")}}async function m(){const T=n();if(!T.length)return;const B=await gn("Extend by how many additional time steps?","500","Extend Run");if(B===null)return;const te=parseInt(B,10);if(!Number.isFinite(te)||te<=0){await Xe("Enter a positive integer number of time steps.","Error");return}try{await kr({cases:T,action:"extend",value:te}),r.onRefresh(),Cr(`Extended ${T.length} case${T.length>1?"s":""} by ${te} time steps`)}catch(ie){await Xe(`Extend failed: ${ie instanceof Error?ie.message:ie}`,"Error")}}async function x(){const T=n();if(T.length)try{await kr({cases:T,action:"flush"}),Cr(`Flush requested for ${T.length} case${T.length>1?"s":""}`)}catch(B){await Xe(`Flush failed: ${B instanceof Error?B.message:B}`,"Error")}}async function k(){const T=n();if(!T.length)return;const B=await Zs(T);if(B)try{await Io({cases:T,keepLast:B.keepLast,keepResu:B.keepResu,deleteResu:B.deleteResu,pruneResu:!0}),r.onRefresh(),ll()}catch(te){await Xe(`Cleanup failed: ${te instanceof Error?te.message:te}`,"Error")}}function b(){const T=Rr();T.length>0&&T.every(te=>Bt().has(te))?Kn():qn(T)}function z(T){sn(T)}let A=D(ce(tr("status")));Ne(()=>{rr("status",e(A))}),ql(!0);let h=V(()=>{const T=Sr();return T.size>0&&[...T].some(B=>B==="PREPARED"||B==="DONE"||B==="FAILED")}),c=V(()=>{if(!cr("restart"))return!1;const T=Sr();return T.size>0&&[...T].some(B=>B==="DONE"||B==="FAILED")&&Wn()}),C=V(()=>{const T=Sr();return T.size>0&&T.has("RUNNING")}),R=V(()=>{if(!cr("control"))return!1;const T=Sr();return T.size>0&&T.has("RUNNING")});const p=[{label:"Extend",icon:wi,onClick:m,action:"extend"},{label:"Checkpoint",icon:Ei,onClick:d,action:"checkpoint"},{label:"Flush",icon:bi,onClick:x,action:"flush"}];let y=V(()=>p.filter(T=>bo(T.action)).map(T=>({label:T.label,icon:T.icon,onClick:T.onClick,disabled:!e(R)}))),$=V(Wn),g=D(ce(Dl())),N=null;function I(){N&&clearTimeout(N),N=setTimeout(()=>{Bl(e(g))},200)}Ne(()=>Kl(e(A)));let L=D(!1),q=D(0),H=D(0),j=D(ce([])),U=D("");function X(T,B,te,ie){f(j,Bt().has(T)?[...Bt()]:[T],!0),f(U,ie,!0),f(q,B,!0),f(H,te,!0),f(L,!0)}function K(){f(L,!1)}function re(T){const B=Rr();if(B.length){if(T.key==="Escape"){Kn(),K();return}if((T.ctrlKey||T.metaKey)&&T.key==="a"){T.preventDefault(),qn(B);return}if(T.key==="ArrowDown"||T.key==="ArrowUp"){T.preventDefault();const te=T.key==="ArrowDown"?1:-1,ie=Vn(),W=B.indexOf(ie),ne=Math.max(0,Math.min(B.length-1,W+te)),oe=B[ne];T.shiftKey?on(an()||B[0],oe,B):Ea(oe),Ul(oe);return}if(T.key===" "){T.preventDefault();const te=Vn();te&&(T.shiftKey?on(an()||te,te,Rr()):sn(te))}}}var w=Mc();rt("click",it,K),rt("scroll",it,K);var S=G(w),O=_(S);jt(O,{eyebrow:"Overview",title:"Status",wide:!0,id:"status-card-shell",actions:B=>{var te=Cc(),ie=G(te);{let oe=V(ol);nr(ie,{name:"status",get intervalMs(){return e(oe)},get onRefresh(){return r.onRefresh},get checked(){return e(A)},set checked(de){f(A,de,!0)}})}var W=E(ie,2);{var ne=oe=>{Pe(oe,{variant:"primary",onclick:a,children:(de,ve)=>{var se=kc(),_e=G(se);Te(_e,{get icon(){return er}}),we(),u(de,se)},$$slots:{default:!0}})};Y(W,oe=>{e(A)||oe(ne)})}u(B,te)},children:(B,te)=>{var ie=Nc(),W=G(ie),ne=_(W),oe=_(ne);Ye(oe);var de=E(oe,2);oc(de,{}),v(ne);var ve=E(ne,2),se=_(ve),_e=_(se);v(se);var he=E(se,2);{let J=V(()=>!e(h));Pe(he,{variant:"run",size:"sm",onclick:s,get disabled(){return e(J)},children:(le,xe)=>{var ye=Sc(),fe=G(ye);Te(fe,{get icon(){return Sa}}),we(),u(le,ye)},$$slots:{default:!0}})}var Ee=E(he,2);{var pe=J=>{{let le=V(()=>!e(c));Pe(J,{variant:"warning",size:"sm",onclick:i,get disabled(){return e(le)},children:(xe,ye)=>{var fe=$c(),ge=G(fe);Te(ge,{get icon(){return Pi}}),we(),u(xe,fe)},$$slots:{default:!0}})}},Ie=V(()=>cr("restart"));Y(Ee,J=>{e(Ie)&&J(pe)})}var ze=E(Ee,2);{var Qe=J=>{{let le=V(()=>!e(R));Pe(J,{variant:"warning",size:"sm",onclick:l,get disabled(){return e(le)},children:(xe,ye)=>{var fe=Pc(),ge=G(fe);Te(ge,{get icon(){return mi}}),we(),u(xe,fe)},$$slots:{default:!0}})}},qe=V(()=>cr("control"));Y(ze,J=>{e(qe)&&J(Qe)})}var Je=E(ze,2);{var Le=J=>{Al(J,{get items(){return e(y)}})};Y(Je,J=>{e(y).length>0&&J(Le)})}var $e=E(Je,2);{let J=V(()=>!e(C));Pe($e,{variant:"danger",size:"sm",onclick:o,get disabled(){return e(J)},children:(le,xe)=>{var ye=Ec(),fe=G(ye);Te(fe,{get icon(){return _i}}),we(),u(le,ye)},$$slots:{default:!0}})}var Q=E($e,2);{let J=V(()=>!e($));Pe(Q,{variant:"secondary",size:"sm",onclick:k,get disabled(){return e(J)},children:(le,xe)=>{var ye=Rc(),fe=G(ye);Te(fe,{get icon(){return Ni}}),we(),u(le,ye)},$$slots:{default:!0}})}v(ve),v(W);var Z=E(W,2);{let J=V(Na);xc(Z,{get rows(){return e(J)},onContextMenu:X,onToggleAll:b,onToggleRow:z})}ee(J=>ae(_e,`${J??""} selected`),[()=>Bt().size]),ue("input",oe,I),tt(oe,()=>e(g),J=>f(g,J)),u(B,ie)},$$slots:{actions:!0,default:!0}}),v(S);var F=E(S,2);{var M=T=>{wc(T,{get cases(){return e(j)},get x(){return e(q)},get y(){return e(H)},get currentValue(){return e(U)},onClose:K})};Y(F,T=>{e(L)&&T(M)})}ue("keydown",S,re),u(t,w),Ae()}Ge(["keydown","input"]);var Lc=P('
        ');function Qt(t,r){var a=Lc(),n=_(a);ft(n,()=>r.children),v(a),u(t,a)}function kn(t){return t.map(r=>({value:r,label:r}))}var Tc=P(''),Fc=P(" ",1);function Cn(t,r){Me(r,!0);let a=me(r,"columnLabel",3,"Columns"),n=me(r,"xMinLabel",3,"Iter min"),s=V(()=>kn(r.allCases)),i=V(()=>r.columns.map(d=>({value:d,label:d}))),o=[{value:"zero",label:"Zero"},{value:"restart",label:"Restart start"},{value:"custom",label:"Custom"}];function l(d){r.onXMinChange(Number(d.target.value)||0)}Qt(t,{children:(d,m)=>{var x=Fc(),k=G(x);Re(k,{text:"Cases",children:(y,$)=>{Wt(y,{class:"w-[160px]",get options(){return e(s)},get selected(){return r.selectedCases},get onchange(){return r.onCasesChange},placeholder:"Select cases..."})}});var b=E(k,2);{var z=y=>{var $=be(),g=G($);ft(g,()=>r.middleSlot),u(y,$)};Y(b,y=>{r.middleSlot&&y(z)})}var A=E(b,2);Re(A,{get text(){return a()},children:(y,$)=>{Wt(y,{class:"w-[160px]",get options(){return e(i)},get selected(){return r.selectedColumns},get onchange(){return r.onColumnsChange},placeholder:"Select..."})}});var h=E(A,2);Re(h,{text:"Start from",children:(y,$)=>{lt(y,{class:"w-[140px]",get options(){return o},get value(){return r.startFrom},onchange:g=>r.onStartFromChange(g)})}});var c=E(h,2);{var C=y=>{Re(y,{get text(){return n()},children:($,g)=>{var N=Tc();Ye(N),ee(()=>fn(N,r.xMin)),ue("input",N,l),u($,N)}})};Y(c,y=>{r.startFrom==="custom"&&y(C)})}var R=E(c,2);{var p=y=>{var $=be(),g=G($);ft(g,()=>r.extraSlot),u(y,$)};Y(R,y=>{r.extraSlot&&y(p)})}u(d,x)}}),Ae()}Ge(["input"]);var Ic=P('
        '),zc=P('
        ');function Zt(t,r){let a=me(r,"emptyMessage",3,"");var n=zc(),s=_(n);{var i=l=>{var d=be(),m=G(d);zr(m,()=>r.svgHtml),u(l,d)},o=l=>{var d=Ic(),m=_(d),x=_(m,!0);v(m),v(d),ee(()=>ae(x,a()||"No data to display.")),u(l,d)};Y(s,l=>{r.svgHtml?l(i):l(o,-1)})}v(n),ee(()=>ke(n,"id",r.id)),u(t,n)}async function Sn(t,r="plot.png"){const a=document.getElementById(t),n=a==null?void 0:a.querySelector("svg");if(!n)return;const{width:s,height:i}=Dc(n),o=window.devicePixelRatio||1,l=document.createElement("canvas");l.width=s*o,l.height=i*o;const d=l.getContext("2d");if(!d)return;d.scale(o,o);const m=new XMLSerializer().serializeToString(n),x=new Blob([m],{type:"image/svg+xml;charset=utf-8"}),k=URL.createObjectURL(x),b=new Image;b.width=s,b.height=i,await new Promise((A,h)=>{b.onload=()=>{d.drawImage(b,0,0,s,i),URL.revokeObjectURL(k),A()},b.onerror=h,b.src=k});const z=await new Promise(A=>l.toBlob(A,"image/png"));z&&await Aa(z,Oc(r))}function xr(t,r,a="png"){const n=r.length<=3?r.join("_"):`${r.length}_cases`,s=new Date().toISOString().replace(/[:.]/g,"-").slice(0,19);return Hc(`${t}_${n}_${s}.${a}`)}async function Ma(t,r){const a=new Blob([t],{type:"text/csv;charset=utf-8"});await Aa(a,r)}function Dc(t){const r=t.getAttribute("viewBox");if(r){const a=r.split(/[\s,]+/).map(Number);if(a.length===4)return{width:a[2],height:a[3]}}return{width:t.width.baseVal.value||900,height:t.height.baseVal.value||500}}async function Aa(t,r){if("showSaveFilePicker"in window)try{const i=await(await window.showSaveFilePicker({suggestedName:r,types:[{description:t.type.startsWith("image/")?"Image":"File",accept:{[t.type]:[`.${r.split(".").pop()}`]}}]})).createWritable();await i.write(t),await i.close();return}catch(s){if((s==null?void 0:s.name)==="AbortError")return}const a=URL.createObjectURL(t),n=document.createElement("a");n.href=a,n.download=r,document.body.appendChild(n),n.click(),document.body.removeChild(n),URL.revokeObjectURL(a)}function Oc(t){return t.endsWith(".png")?t:`${t}.png`}function Hc(t){return t.replace(/[^a-zA-Z0-9._-]/g,"_")}let La=D(ce([])),Ta=D(ce([])),Fa=D(ce([])),Ia=D("zero"),za=D(0),Da=D(""),jc=D(!0);function Vc(){return{selectedCases:e(La),columns:e(Ta),selectedColumns:e(Fa),startFrom:e(Ia),iterMin:e(za),svgHtml:e(Da),autoRefresh:e(jc)}}function Xn(t){f(La,t,!0)}function Uc(t){f(Ta,t,!0)}function Xr(t){f(Fa,t,!0)}function Bc(t){f(Ia,t,!0)}function qc(t){f(za,t,!0)}function $r(t){f(Da,t,!0)}var Kc=P(" Refresh",1),Gc=P(" ",1),Wc=P(" Download as PNG",1),Xc=P('
        '),Yc=P('
        ',1),Qc=P('
        '),Zc=P('

        '),Jc=P(" ",1);function eu(t,r){Me(r,!0);let a=V(Vc),n=D("");const s=new Set(["iteration","wall_distance","walldistance"]),i=["velocity","pressure"];async function o(){if(e(a).selectedCases.length)try{const h=await Co(e(a).selectedCases);f(n,"");const c=h.filter(C=>!s.has(C.toLowerCase().replace(/\s+/g,"_")));if(Uc(c),c.length===0){Xr([]),$r("");return}if(e(a).selectedColumns.length===0){const C=c.filter(R=>i.some(p=>R.toLowerCase().includes(p)));Xr(C.length>0?C:[c[0]])}}catch(h){f(n,"Failed to load residual columns"),console.error("Failed to load residual columns:",h)}}async function l(){if(e(a).selectedCases.length&&!((e(a).columns.length===0||e(a).selectedColumns.length===0)&&(await o(),!e(a).selectedColumns.length)))try{let h=0,c=!0;if(e(a).startFrom==="restart"){const R=await Ca(e(a).selectedCases),p=Object.values(R.origins).map(y=>y.iteration).filter(y=>y!==void 0&&Number.isFinite(y));h=p.length>0?Math.min(...p):0}else e(a).startFrom==="custom"&&(h=e(a).iterMin);const C=await So(e(a).selectedCases,e(a).selectedColumns,{xMin:h,includeHistory:c});f(n,""),C&&$r(C)}catch(h){f(n,"Failed to load residual plot"),console.error("Failed to load residual plot:",h)}}function d(h){if(Xn(h),h.length===0){$r("");return}o(),l()}function m(h){if(Xr(h),h.length===0){$r("");return}l()}function x(h){Bc(h),l()}function k(h){qc(h),l()}let b=D(ce(tr("plot")));Ne(()=>{rr("plot",e(b))}),Ne(()=>{e(b)?mr("residualPlot",l,Hn()):vt("residualPlot")});let z=!1;Ne(()=>{r.allCases.length>0&&!z&&(z=!0,Xn([...r.allCases]),o().then(()=>l()))});const A=_r(()=>{z&&o().then(()=>l())});vr(()=>{vt("residualPlot"),A()}),jt(t,{eyebrow:"Diagnostics",title:"Residuals Plot",wide:!0,actions:c=>{var C=Gc(),R=G(C);{let $=V(Hn);nr(R,{name:"residualPlot",get intervalMs(){return e($)},onRefresh:l,get checked(){return e(b)},set checked(g){f(b,g,!0)}})}var p=E(R,2);{var y=$=>{Pe($,{variant:"primary",onclick:l,children:(g,N)=>{var I=Kc(),L=G(I);Te(L,{get icon(){return er}}),we(),u(g,I)},$$slots:{default:!0}})};Y(p,$=>{e(b)||$(y)})}u(c,C)},children:(c,C)=>{var R=Jc(),p=G(R);{var y=I=>{const L=X=>{var K=Xc(),re=_(K);{let w=V(()=>!e(a).svgHtml);Pe(re,{variant:"secondary",size:"sm",onclick:()=>Sn("plot-holder",xr("residuals",e(a).selectedCases)),get disabled(){return e(w)},children:(S,O)=>{var F=Wc(),M=G(F);Te(M,{get icon(){return pr}}),we(),u(S,F)},$$slots:{default:!0}})}v(K),u(X,K)};var q=Yc(),H=G(q);Cn(H,{prefix:"plot",get allCases(){return r.allCases},get selectedCases(){return e(a).selectedCases},onCasesChange:d,columnLabel:"Variables",get columns(){return e(a).columns},get selectedColumns(){return e(a).selectedColumns},onColumnsChange:m,get startFrom(){return e(a).startFrom},onStartFromChange:x,xMinLabel:"Iter min",get xMin(){return e(a).iterMin},onXMinChange:k,get extraSlot(){return L}});var j=E(H,2),U=_(j);{let X=V(()=>e(a).selectedCases.length===0?"Please select at least one case.":e(a).columns.length===0?"No residuals for the selected cases yet.":e(a).selectedColumns.length===0?"Please select at least one variable.":"No data to display.");Zt(U,{id:"plot-holder",get svgHtml(){return e(a).svgHtml},get emptyMessage(){return e(X)}})}v(j),u(I,q)},$=I=>{var L=Qc(),q=_(L);Zt(q,{id:"plot-holder",svgHtml:"",emptyMessage:"No data available. Please run a simulation first."}),v(L),u(I,L)};Y(p,I=>{r.allCases.length>0?I(y):I($,-1)})}var g=E(p,2);{var N=I=>{var L=Zc(),q=_(L,!0);v(L),ee(()=>ae(q,e(n))),u(I,L)};Y(g,I=>{e(n)&&I(N)})}u(c,R)},$$slots:{actions:!0,default:!0}}),Ae()}function Oa(t){const{scope:r,getState:a,setState:n,getAxis:s}=t;async function i(){const k=a();if(k.selectedCases.length)try{let b=await en(k.selectedCases,r);if(t.filterFiles&&(b=t.filterFiles(b)),n({files:b}),b.length===0){n({file:"",columns:[],selectedColumns:[],positionText:"",svgHtml:""});return}(!k.file||!b.includes(k.file))&&(n({file:b[0]}),await o())}catch(b){console.error(`Failed to load ${r} files:`,b)}}async function o(){const k=a();if(!(!k.selectedCases.length||!k.file))try{const b=await No(k.selectedCases,[k.file]),z=t.filterColumns?t.filterColumns(b):b;t.onColumnsLoaded?t.onColumnsLoaded(b):n({columns:z});const A=a();if(A.columns.length===0){n({selectedColumns:[],positionText:"",svgHtml:""});return}A.selectedColumns.length===0&&n({selectedColumns:[A.columns[0]]})}catch(b){console.error(`Failed to load ${r} columns:`,b)}}async function l(){let k=a();if(k.selectedCases.length){if(await i(),k=a(),!k.file){n({positionText:"",svgHtml:""});return}if((k.columns.length===0||k.selectedColumns.length===0)&&(await o(),k=a(),!k.selectedColumns.length)){n({positionText:"",svgHtml:""});return}try{let b;const z=a();if(z.startFrom==="restart"){const h=await Ca(z.selectedCases),c=s().toLowerCase(),C=c==="t"||c==="time"?"time":"iteration",R=Object.values(h.origins).map(p=>p[C]).filter(p=>p!==void 0&&Number.isFinite(p));b=R.length>0?Math.min(...R):void 0}else z.startFrom==="custom"&&(b=z.xMin);const A=await Ao({cases:z.selectedCases,probes:[z.file],columns:z.selectedColumns,axis:s(),timeMin:typeof b=="number"?b:void 0,xMin:b,includeHistory:!0});A&&n({svgHtml:A})}catch(b){console.error(`Failed to load ${r} plot:`,b)}}}async function d(k){if(n({selectedCases:k}),k.length===0){n({svgHtml:""});return}await i(),r==="monitoring"&&await l()}function m(k){if(n({selectedColumns:k}),k.length===0){n({svgHtml:""});return}l()}async function x(k){n({file:k}),await o(),await l()}return{loadFiles:i,loadColumns:o,loadPlot:l,handleCasesChange:d,handleColumnsChange:m,handleFileChange:x}}function Ha(){return{selectedCases:[],file:"",files:[],columns:[],selectedColumns:[],axis:"time",startFrom:"zero",xMin:0,positionText:"",svgHtml:"",autoRefresh:!0}}let qt=D("time"),ln=D(ce(Ha())),cn=D(ce({...Ha(),axis:""})),tu=D(!0),Tr=D(!1),Fr=D(!1);function ru(){return e(qt)}function nu(t){f(qt,t,!0)}function au(){return e(Tr)}function Yn(t){f(Tr,t,!0)}function su(){return e(Fr)}function Qn(t){f(Fr,t,!0)}function ou(){e(qt)==="time"&&!e(Tr)&&e(Fr)?f(qt,"profile"):e(qt)==="profile"&&!e(Fr)&&e(Tr)&&f(qt,"time")}function Zn(){return e(ln)}function Yr(){return e(cn)}function Ct(t){f(ln,{...e(ln),...t},!0)}function At(t){f(cn,{...e(cn),...t},!0)}function iu(){return e(tu)}var lu=P(" Download as PNG",1),cu=P('
        '),uu=P("
        "),du=P('
        '),fu=P('
        ',1),vu=P('
        ');function gu(t,r){Me(r,!0);let a=me(r,"onRefresh",15),n=V(Zn);const s=new Set(["time","t","iteration","iter"]),i=Oa({scope:"monitoring",getState:Zn,setState:Ct,filterFiles:h=>h.filter(c=>{const C=c.replace(/\.csv$/i,"").toLowerCase();return C!=="coords"&&!C.includes("coordinates")}),filterColumns:h=>h.filter(c=>!s.has(c.toLowerCase())),onColumnsLoaded:h=>{const c=h.filter(C=>!s.has(C.toLowerCase()));Ct({columns:c}),o()},getAxis:()=>"time"});async function o(){if(!(!e(n).selectedCases.length||!e(n).file||!e(n).selectedColumns.length))try{const h=[];for(const $ of e(n).selectedColumns){const g=await Mo(e(n).selectedCases[0],e(n).file,[$]);g.found&&h.push({col:$,x:g.x,y:g.y,z:g.z})}if(!h.length){Ct({positionText:""});return}const c=$=>{let g=0;for(const N of $){if(N===void 0)continue;const I=String(N),L=I.indexOf(".");L>=0&&(g=Math.max(g,I.length-L-1))}return g},C=c(h.map($=>$.x)),R=c(h.map($=>$.y)),p=c(h.map($=>$.z)),y=h.map($=>{const g=[];return $.x!==void 0&&g.push(`x=${$.x.toFixed(C)}`),$.y!==void 0&&g.push(`y=${$.y.toFixed(R)}`),$.z!==void 0&&g.push(`z=${$.z.toFixed(p)}`),`Probe position [${$.col}] : ${g.join(" · ")}`});Ct({positionText:y.join(` +`)})}catch{Ct({positionText:""})}}function l(h){i.handleColumnsChange(h),h.length>0&&o()}let d=V(()=>e(n).files.map(h=>({value:h,label:h.replace(/\.csv$/i,"").replace(/^probes?_/i,"")}))),m=!1;Ne(()=>{r.allCases.length>0&&!m&&(m=!0,Ct({selectedCases:[...r.allCases]}),i.loadFiles().then(()=>i.loadPlot()))}),Ne(()=>{a(i.loadPlot)});let x=V(()=>e(n).selectedCases.length===0?"Please select at least one case.":e(n).files.length===0?"No probe data for the selected cases yet.":e(n).selectedColumns.length===0?"Please select at least one probe.":"No data to display.");var k=be(),b=G(k);{var z=h=>{const c=I=>{Re(I,{text:"Quantity",children:(L,q)=>{lt(L,{class:"w-[160px]",get options(){return e(d)},get value(){return e(n).file},get onchange(){return i.handleFileChange}})}})},C=I=>{var L=cu(),q=_(L);{let H=V(()=>!e(n).svgHtml);Pe(q,{variant:"secondary",size:"sm",onclick:()=>Sn("probe-plot-holder",xr("probe",e(n).selectedCases)),get disabled(){return e(H)},children:(j,U)=>{var X=lu(),K=G(X);Te(K,{get icon(){return pr}}),we(),u(j,X)},$$slots:{default:!0}})}v(L),u(I,L)};var R=fu(),p=G(R);Cn(p,{prefix:"probe",get allCases(){return r.allCases},get selectedCases(){return e(n).selectedCases},get onCasesChange(){return i.handleCasesChange},get middleSlot(){return c},columnLabel:"Probes",get columns(){return e(n).columns},get selectedColumns(){return e(n).selectedColumns},onColumnsChange:l,get startFrom(){return e(n).startFrom},onStartFromChange:I=>{Ct({startFrom:I}),i.loadPlot()},xMinLabel:"Time min",get xMin(){return e(n).xMin},onXMinChange:I=>{Ct({xMin:I}),i.loadPlot()},get extraSlot(){return C}});var y=E(p,2);{var $=I=>{var L=du();He(L,21,()=>e(n).positionText.split(` +`).sort(),dn,(q,H)=>{var j=uu(),U=_(j,!0);v(j),ee(()=>ae(U,e(H))),u(q,j)}),v(L),u(I,L)};Y(y,I=>{e(n).positionText&&I($)})}var g=E(y,2),N=_(g);Zt(N,{id:"probe-plot-holder",get svgHtml(){return e(n).svgHtml},get emptyMessage(){return e(x)}}),v(g),u(h,R)},A=h=>{var c=vu(),C=_(c);Zt(C,{id:"probe-plot-holder",svgHtml:"",emptyMessage:"No probe data available. Please run a simulation first."}),v(c),u(h,c)};Y(b,h=>{r.allCases.length>0?h(z):h(A,-1)})}u(t,k),Ae()}var hu=P(" ",1),pu=P(" Download as PNG",1),mu=P('
        '),_u=P('
        ',1),bu=P('
        ');function xu(t,r){Me(r,!0);let a=me(r,"onRefresh",15),n=V(Yr);const s=["s","x","abscissa","distance","arclength","arc_length","curvilinear","length","r","y","z","coord","position"];function i(c){const C=c.map(R=>R.toLowerCase());for(const R of s){const p=C.indexOf(R);if(p>=0)return c[p]}return c[0]??""}const o=Oa({scope:"profiles",getState:Yr,setState:At,onColumnsLoaded:c=>{const C=e(n).axis||i(c),R=c.filter(p=>p!==C).toSorted((p,y)=>p.localeCompare(y));At({columns:R,axis:C,selectedColumns:e(n).selectedColumns.length?e(n).selectedColumns:R.length?[R[0]]:[]})},getAxis:()=>Yr().axis});function l(c){const C=e(n).axis;At({axis:c});const p=[...e(n).columns,...C?[C]:[]].filter(y=>y!==c).toSorted((y,$)=>y.localeCompare($));At({columns:p}),o.loadPlot()}let d=V(()=>e(n).files.map(c=>({value:c,label:c.replace(/\.csv$/i,"").replace(/^profiles\//i,"").replace(/_/g," ")}))),m=V(()=>[...e(n).axis?[e(n).axis]:[],...e(n).columns].toSorted((c,C)=>c.localeCompare(C)).map(c=>({value:c,label:c}))),x=!1;Ne(()=>{r.allCases.length>0&&!x&&(x=!0,At({selectedCases:[...r.allCases]}),o.loadFiles().then(()=>o.loadPlot()))}),Ne(()=>{a(o.loadPlot)});let k=V(()=>e(n).selectedCases.length===0?"Please select at least one case.":e(n).files.length===0?"No profile data for the selected cases yet.":e(n).selectedColumns.length===0?"Please select at least one value.":"No data to display.");var b=be(),z=G(b);{var A=c=>{const C=N=>{var I=hu(),L=G(I);Re(L,{text:"Profile",children:(H,j)=>{lt(H,{class:"w-[160px]",get options(){return e(d)},get value(){return e(n).file},get onchange(){return o.handleFileChange}})}});var q=E(L,2);Re(q,{text:"X axis",children:(H,j)=>{lt(H,{class:"w-[140px]",get options(){return e(m)},get value(){return e(n).axis},onchange:l})}}),u(N,I)},R=N=>{var I=mu(),L=_(I);{let q=V(()=>!e(n).svgHtml);Pe(L,{variant:"secondary",size:"sm",onclick:()=>Sn("profile-plot-holder",xr("profile",e(n).selectedCases)),get disabled(){return e(q)},children:(H,j)=>{var U=pu(),X=G(U);Te(X,{get icon(){return pr}}),we(),u(H,U)},$$slots:{default:!0}})}v(I),u(N,I)};var p=_u(),y=G(p);Cn(y,{prefix:"profile",get allCases(){return r.allCases},get selectedCases(){return e(n).selectedCases},get onCasesChange(){return o.handleCasesChange},get middleSlot(){return C},columnLabel:"Values",get columns(){return e(n).columns},get selectedColumns(){return e(n).selectedColumns},get onColumnsChange(){return o.handleColumnsChange},get startFrom(){return e(n).startFrom},onStartFromChange:N=>{At({startFrom:N}),o.loadPlot()},xMinLabel:"X min",get xMin(){return e(n).xMin},onXMinChange:N=>{At({xMin:N}),o.loadPlot()},get extraSlot(){return R}});var $=E(y,2),g=_($);Zt(g,{id:"profile-plot-holder",get svgHtml(){return e(n).svgHtml},get emptyMessage(){return e(k)}}),v($),u(c,p)},h=c=>{var C=bu(),R=_(C);Zt(R,{id:"profile-plot-holder",svgHtml:"",emptyMessage:"No profile data available. Please run a simulation first."}),v(C),u(c,C)};Y(z,c=>{r.allCases.length>0?c(A):c(h,-1)})}u(t,b),Ae()}var yu=P('
        Diagnostics
        '),wu=P(" Refresh",1),ku=P(" ",1);function Cu(t,r){Me(r,!0);let a=V(ru),n=V(iu),s=V(au),i=V(su),o=D(ce(tr("probe")));Ne(()=>{rr("probe",e(o))});let l=D(void 0);function d(){var A;(A=e(l))==null||A()}async function m(){if(r.allCases.length){try{const h=(await en(r.allCases,"monitoring")).filter(c=>{const C=c.replace(/\.csv$/i,"").toLowerCase();return C!=="coords"&&!C.includes("coordinates")});Yn(h.length>0)}catch{Yn(!1)}try{const A=await en(r.allCases,"profiles");Qn(A.length>0)}catch{Qn(!1)}}}Ne(()=>{r.allCases.length>0&&m()}),_r(()=>{m(),d()}),Ne(()=>{e(s),e(i),ou()});function x(A){A==="time"&&!e(s)||A==="profile"&&!e(i)||nu(A)}var k=be(),b=G(k);{var z=A=>{{const h=C=>{var R=yu(),p=E(_(R),2),y=_(p),$=E(y,2);v(p),v(R),ee(()=>{Se(y,1,`bg-transparent border-none p-0 m-0 text-lg font-bold tracking-tight transition-colors duration-150 + ${e(s)?e(a)==="time"?"text-edf-bleu-fonce cursor-pointer":"text-edf-gris-moyen cursor-pointer hover:text-muted":"text-edf-gris-moyen cursor-not-allowed opacity-50"}`),y.disabled=!e(s),ke(y,"title",e(s)?"":"No probe data available. Run a simulation first."),Se($,1,`bg-transparent border-none p-0 m-0 text-lg font-bold tracking-tight transition-colors duration-150 + ${e(i)?e(a)==="profile"?"text-edf-bleu-fonce cursor-pointer":"text-edf-gris-moyen cursor-pointer hover:text-muted":"text-edf-gris-moyen cursor-not-allowed opacity-50"}`),$.disabled=!e(i),ke($,"title",e(i)?"":"No profile data available. Run a simulation first.")}),ue("click",y,()=>x("time")),ue("click",$,()=>x("profile")),u(C,R)};jt(A,{wide:!0,titleSlot:h,tabs:h,actions:C=>{var R=ku(),p=G(R);{let g=V(()=>e(a)==="time"?"probePlot":"profilePlot"),N=V(il);nr(p,{get name(){return e(g)},get intervalMs(){return e(N)},onRefresh:d,get checked(){return e(o)},set checked(I){f(o,I,!0)}})}var y=E(p,2);{var $=g=>{Pe(g,{variant:"primary",onclick:d,children:(N,I)=>{var L=wu(),q=G(L);Te(q,{get icon(){return er}}),we(),u(N,L)},$$slots:{default:!0}})};Y(y,g=>{e(o)||g($)})}u(C,R)},children:(C,R)=>{var p=be(),y=G(p);{var $=N=>{gu(N,{get allCases(){return r.allCases},get onRefresh(){return e(l)},set onRefresh(I){f(l,I,!0)}})},g=N=>{xu(N,{get allCases(){return r.allCases},get onRefresh(){return e(l)},set onRefresh(I){f(l,I,!0)}})};Y(y,N=>{e(a)==="time"?N($):N(g,-1)})}u(C,p)},$$slots:{tabs:!0,actions:!0,default:!0}})}};Y(b,A=>{e(n)&&A(z)})}u(t,k),Ae()}Ge(["click"]);var Su=P(" Refresh",1),$u=P(" ",1),Pu=P(" Download as CSV",1),Eu=P('
        ',1),Ru=P(" "),Nu=P(' '),Mu=P(' '),Au=P('
        Case
        '),Lu=P('

        '),Tu=P(" ",1);function Fu(t,r){Me(r,!0);let a=D(ce([])),n=D(ce([])),s=D(!1),i=D(!1),o=D(ce(tr("perf")));Ne(()=>{rr("perf",e(o))});const l=5e3;let m=D(ce([{key:"elapsed_time",label:"Elapsed (s)",kind:"time"},{key:"io_time",label:"I/O (s)",kind:"time"},{key:"linear_solver_time",label:"Linear Solver (s)",kind:"time"},{key:"gradients_time",label:"Gradients (s)",kind:"time"},{key:"balances_time",label:"Balances (s)",kind:"time"},{key:"mpi_ranks",label:"MPI Ranks",kind:"int"},{key:"threads",label:"Threads",kind:"int"}])),x=V(()=>r.allCases.map(R=>({value:R,label:R}))),k=V(()=>e(n).length>0);async function b(){var R;if(e(a).length){f(i,!0);try{const p=await In(e(a));(R=p.columns)!=null&&R.length&&f(m,p.columns,!0),f(n,p.records,!0),e(n).length>0&&f(s,!0)}catch(p){console.error("Failed to load perf:",p)}f(i,!1)}}function z(R){if(f(a,R,!0),R.length===0){f(n,[],!0);return}b()}function A(R,p){if(p==null||p==="")return"-";if(R==="text")return p;const y=Number(p);return Number.isFinite(y)?R==="int"?String(Math.round(y)):y.toFixed(3):p}async function h(){if(!e(n).length)return;const R=["case_id",...e(m).map(g=>g.key)],p=e(n).map(g=>[g.case_id,...e(m).map(N=>g[N.key]??"")].join(",")),y=[R.join(","),...p].join(` +`),$=xr("timing_snapshot",e(a),"csv");await Ma(y,$)}let c=!1;Ne(()=>{r.allCases.length>0&&!c&&(c=!0,f(a,[...r.allCases],!0),In(r.allCases).then(R=>{var p;(p=R.columns)!=null&&p.length&&f(m,R.columns,!0),f(s,R.records.length>0),f(n,R.records,!0)}).catch(()=>{}))}),Ne(()=>{e(o)?mr("perf",b,l):vt("perf")});const C=_r(()=>{c&&b()});vr(()=>{vt("perf"),C()}),jt(t,{eyebrow:"Performance",title:"Timing Snapshot",wide:!0,actions:p=>{var y=$u(),$=G(y);nr($,{name:"perf",intervalMs:l,onRefresh:b,get checked(){return e(o)},set checked(I){f(o,I,!0)}});var g=E($,2);{var N=I=>{Pe(I,{variant:"primary",onclick:b,children:(L,q)=>{var H=Su(),j=G(H);Te(j,{get icon(){return er}}),we(),u(L,H)},$$slots:{default:!0}})};Y(g,I=>{e(o)||I(N)})}u(p,y)},children:(p,y)=>{var $=Tu(),g=G($);{var N=H=>{Qt(H,{children:(j,U)=>{var X=Eu(),K=G(X);Re(K,{text:"Cases",children:(S,O)=>{Wt(S,{class:"w-[160px]",get options(){return e(x)},get selected(){return e(a)},onchange:z,placeholder:"Select cases..."})}});var re=E(K,2),w=_(re);{let S=V(()=>!e(k));Pe(w,{variant:"secondary",size:"sm",onclick:h,get disabled(){return e(S)},children:(O,F)=>{var M=Pu(),T=G(M);Te(T,{get icon(){return pr}}),we(),u(O,M)},$$slots:{default:!0}})}v(re),u(j,X)}})};Y(g,H=>{e(s)&&H(N)})}var I=E(g,2);{var L=H=>{var j=Au(),U=_(j),X=_(U),K=_(X),re=E(_(K));He(re,17,()=>e(m),S=>S.key,(S,O)=>{var F=Ru(),M=_(F,!0);v(F),ee(()=>ae(M,e(O).label)),u(S,F)}),v(K),v(X);var w=E(X);He(w,21,()=>e(n),S=>S.case_id,(S,O)=>{var F=Mu(),M=_(F),T=_(M,!0);v(M);var B=E(M);He(B,17,()=>e(m),te=>te.key,(te,ie)=>{var W=Nu(),ne=_(W,!0);v(W),ee(oe=>ae(ne,oe),[()=>A(e(ie).kind,e(O)[e(ie).key])]),u(te,W)}),v(F),ee(()=>ae(T,e(O).case_id)),u(S,F)}),v(w),v(U),v(j),u(H,j)},q=H=>{var j=Lu(),U=_(j);{var X=re=>{var w=Ve("Select cases above to view timing information.");u(re,w)},K=re=>{var w=Ve("No performance data available. Please run a simulation first.");u(re,w)};Y(U,re=>{e(s)?re(X):re(K,-1)})}v(j),u(H,j)};Y(I,H=>{e(k)?H(L):H(q,-1)})}u(p,$)},$$slots:{actions:!0,default:!0}}),Ae()}const Iu={"&":"&","<":"<",">":">",'"':""","'":"'"};function ja(t){return t.replace(/[&<>"']/g,r=>Iu[r]??r)}var zu=P("
        "),Du=P('
        ');function Ou(t,r){Me(r,!0);let a=D(void 0);function n(m){const x=ja(m);if(!r.searchQuery.trim())return x;try{const k=new RegExp(`(${r.searchQuery})`,"gi");return x.replace(k,'$1')}catch{return x}}function s(m){return m==="error"?"tail-sev-error":m==="warn"?"tail-sev-warn":m==="info"?"tail-sev-info":""}function i(){return e(a)?e(a).scrollHeight-e(a).scrollTop-e(a).clientHeight<=24:!0}let o=D(!0);function l(){f(o,i(),!0)}Ne(()=>{r.autoScroll&&r.lines.length>0&&e(a)&&e(o)&&Et().then(()=>{e(a)&&(e(a).scrollTop=e(a).scrollHeight)})});var d=Du();He(d,21,()=>r.lines,m=>m.index,(m,x)=>{var k=zu();zr(k,()=>n(e(x).text),!0),v(k),ee(b=>Se(k,1,`tail-line ${b??""} ${e(x).isNew?"tail-new":""}`),[()=>s(e(x).severity)]),u(m,k)}),v(d),nt(d,m=>f(a,m),()=>e(a)),rt("scroll",d,l),u(t,d),Ae()}const Hu=/(fatal error|error detected|error reading|error writing|segmentation fault|sigterm|sigsegv|sigkill|abort|killed|terminated|core dumped|traceback|exception|errno)/i,ju=/(warning|divergence|non-convergence|clipping|negative|nan detected|overflow|underflow)/i;function Vu(t){return Hu.test(t)?"error":ju.test(t)?"warn":"none"}function Uu(t,r){return r==="all"||r===""?!0:r==="info"?t!=="none":t===r}var Bu=P(" Refresh",1),qu=P(" ",1),Ku=P(''),Gu=P(''),Wu=P("    ",1),Xu=Ht(''),Yu=Ht(''),Qu=Ht(''),Zu=P(" Resume",1),Ju=P(" Pause",1),ed=P('Case not running'),td=P(' 
        ',1),rd=P('

        No log data available. Please run a simulation first.

        '),nd=P('

        '),ad=P(" ",1);function sd(t,r){Me(r,!0);const a=80;let n=D(""),s=D(""),i=D(!1),o=D(ce([])),l=D(a),d=D(ce([])),m=D(ce(new Set)),x=D(!1),k=D(ce(tr("tail")));Ne(()=>{rr("tail",e(k))});let b=D(!0),z=D("all"),A=D(""),h=D(""),c=D(void 0),C=V(()=>kn(r.allCases)),R=V(()=>e(o).map(w=>{const S=w.split("/").pop()??w;return{value:w,label:S}})),p=[{value:"all",label:"All"},{value:"error",label:"Error"},{value:"warn",label:"Warn"}],y=V(()=>e(o).length>0),$=V(()=>xn().some(w=>{var S;return w.case_id===e(n)&&((S=w.status)==null?void 0:S.toUpperCase())==="RUNNING"}));const g={"run_solver.log":1,listing:2,"run_status.running":3,"csauto.stdout":4,"csauto.stderr":5,"performance.log":6};function N(w){const S=w.split("/").pop()??w;return g[S]??99}async function I(){if(e(n))try{const O=(await Eo(e(n))).filter(F=>{const M=F.split("/").pop()??F;return F.endsWith(".log")||F.endsWith("/summary")||M in g}).sort((F,M)=>N(F)-N(M));if(f(o,O,!0),O.length>0){const F=O[0];!e(s)||!O.includes(e(s))?(f(s,F,!0),f(i,!1)):!e(i)&&N(F)0&&O[O.length-1]===""?O.slice(0,-1):O;if(e(d).length>0){const M=q(e(d),F),T=new Set;for(let B=M;B{let w=e(d).map((S,O)=>({text:S,index:O,severity:Vu(S),isNew:e(m).has(O)}));if(e(z)!=="all"&&(w=w.filter(S=>Uu(S.severity,e(z)))),e(A).trim())try{const S=new RegExp(e(A),"i");w=w.filter(O=>S.test(O.text))}catch{}return w});function j(w){f(n,w,!0),f(s,""),f(i,!1),f(d,[],!0),I().then(()=>L(!0))}function U(w){f(s,w,!0),f(i,!0),f(d,[],!0),L(!0)}function X(){f(x,!e(x)),e(x)||L(!0)}function K(w){(w.ctrlKey||w.metaKey)&&w.key==="f"&&e(c)&&(w.preventDefault(),e(c).focus())}Ne(()=>{r.allCases.length>0&&!e(n)&&(f(n,r.allCases[0],!0),I().then(()=>L(!0)))}),Ne(()=>{e(k)&&!e(x)&&e(n)?mr("tail",()=>L(!1),On()):vt("tail")});const re=_r(()=>{e(n)&&I().then(()=>L(!0))});vr(()=>{vt("tail"),re()}),rt("keydown",it,K),jt(t,{eyebrow:"Live",title:"Log Tail",wide:!0,actions:S=>{var O=qu(),F=G(O);{let B=V(On);nr(F,{name:"tail",get intervalMs(){return e(B)},onRefresh:()=>L(!1),get checked(){return e(k)},set checked(te){f(k,te,!0)}})}var M=E(F,2);{var T=B=>{Pe(B,{variant:"primary",onclick:()=>L(!0),children:(te,ie)=>{var W=Bu(),ne=G(W);Te(ne,{get icon(){return er}}),we(),u(te,W)},$$slots:{default:!0}})};Y(M,B=>{e(k)||B(T)})}u(S,O)},children:(S,O)=>{var F=ad(),M=G(F);{var T=oe=>{Qt(oe,{children:(de,ve)=>{var se=Wu(),_e=G(se);Re(_e,{text:"Case",children:(ze,Qe)=>{lt(ze,{class:"w-[130px]",get options(){return e(C)},get value(){return e(n)},onchange:j})}});var he=E(_e,2);Re(he,{text:"File",children:(ze,Qe)=>{lt(ze,{class:"w-[150px]",get options(){return e(R)},get value(){return e(s)},onchange:U})}});var Ee=E(he,2);Re(Ee,{text:"Lines",children:(ze,Qe)=>{var qe=Ku();Ye(qe),ue("change",qe,()=>L(!0)),tt(qe,()=>e(l),Je=>f(l,Je)),u(ze,qe)}});var pe=E(Ee,2);Re(pe,{text:"Filter",children:(ze,Qe)=>{var qe=Gu();Ye(qe),nt(qe,Je=>f(c,Je),()=>e(c)),tt(qe,()=>e(A),Je=>f(A,Je)),u(ze,qe)}});var Ie=E(pe,2);Re(Ie,{text:"Severity",children:(ze,Qe)=>{lt(ze,{class:"w-[90px]",get options(){return p},get value(){return e(z)},onchange:qe=>f(z,qe,!0)})}}),u(de,se)}})};Y(M,oe=>{e(y)&&oe(T)})}var B=E(M,2);{var te=oe=>{var de=td(),ve=G(de);Ou(ve,{get lines(){return e(H)},get searchQuery(){return e(A)},get autoScroll(){return e(b)}});var se=E(ve,2),_e=_(se),he=_(_e);{var Ee=Q=>{var Z=Xu();u(Q,Z)},pe=Q=>{var Z=Yu();u(Q,Z)},Ie=Q=>{var Z=Qu();u(Q,Z)};Y(he,Q=>{e(x)?Q(Ee):e($)?Q(pe,1):Q(Ie,-1)})}var ze=E(he);v(_e);var Qe=E(_e,2);{var qe=Q=>{Pe(Q,{variant:"secondary",size:"sm",onclick:X,children:(Z,J)=>{var le=be(),xe=G(le);{var ye=ge=>{var Ce=Zu(),De=G(Ce);Te(De,{get icon(){return Sa}}),we(),u(ge,Ce)},fe=ge=>{var Ce=Ju(),De=G(Ce);Te(De,{get icon(){return ki}}),we(),u(ge,Ce)};Y(xe,ge=>{e(x)?ge(ye):ge(fe,-1)})}u(Z,le)},$$slots:{default:!0}})},Je=Q=>{var Z=ed();u(Q,Z)};Y(Qe,Q=>{e($)?Q(qe):Q(Je,-1)})}var Le=E(Qe,2),$e=_(Le);Ot($e,{get checked(){return e(b)},onchange:Q=>f(b,Q,!0),size:14,label:"Auto-scroll",labelFirst:!0}),v(Le),v(se),ee(()=>ae(ze,` ${e(H).length??""} / ${e(d).length??""}`)),u(oe,de)},ie=oe=>{var de=rd();u(oe,de)};Y(B,oe=>{e(d).length>0?oe(te):oe(ie,-1)})}var W=E(B,2);{var ne=oe=>{var de=nd(),ve=_(de,!0);v(de),ee(()=>ae(ve,e(h))),u(oe,de)};Y(W,oe=>{e(h)&&oe(ne)})}u(S,F)},$$slots:{actions:!0,default:!0}}),Ae()}Ge(["change"]);function od(t,r=3){var z,A;const a=t.split(` +`),n=[];let s=0,i=0,o=!1;for(const h of a){if(h.startsWith("===")||h.startsWith("---")||h.startsWith("+++"))continue;const c=h.match(/^@@ -(\d+),?\d* \+(\d+),?\d* @@/);if(c){s=parseInt(c[1],10)-1,i=parseInt(c[2],10)-1,o=!0;continue}if(o)if(h.startsWith("-"))s++,n.push({type:"del",leftNum:s,rightNum:null,leftContent:h.slice(1),rightContent:""});else if(h.startsWith("+"))i++,n.push({type:"add",leftNum:null,rightNum:i,leftContent:"",rightContent:h.slice(1)});else{s++,i++;const C=h.startsWith(" ")?h.slice(1):h;n.push({type:"equal",leftNum:s,rightNum:i,leftContent:C,rightContent:C})}}const l=[];let d=0;for(;d=0&&h-k>1&&x.push({kind:"separator",skipped:h-k-1});const c=l[h].type!=="equal",C=c&&!b;x.push({kind:"line",row:l[h],hunkStart:C}),b=c,k=h}if(k=0&&x.push({kind:"separator",skipped:l.length-1-k}),x.length>0&&x[0].kind==="line"){const h=((z=x[0].row)==null?void 0:z.leftNum)??0,c=((A=x[0].row)==null?void 0:A.rightNum)??0,C=Math.max(h,c)-1;C>0&&x.unshift({kind:"separator",skipped:C})}return x}var id=P('
        '),ld=P('
        '),cd=P(' ',1),ud=P('No matches'),dd=P('
        '),fd=P('

        Files are identical.

        '),vd=P('
        Loading...
        '),gd=P('
        ');function hd(t,r){Me(r,!0);let a=me(r,"loading",3,!1),n=D(void 0),s=V(()=>od(r.diffText)),i=V(()=>{const Q=[];return e(s).forEach((Z,J)=>{Z.hunkStart&&Q.push(J)}),Q}),o=V(()=>e(i).length>0),l=V(()=>{var Z;const Q=new Array(e(s).length).fill(-1);for(let J=0;J{r.diffText!==m&&(m=r.diffText,f(d,-1))});let x=V(()=>{if(!r.search.trim())return[];try{const Q=new RegExp(r.search,"i"),Z=[];return e(s).forEach((J,le)=>{J.kind==="line"&&J.row&&(Q.test(J.row.leftContent)||Q.test(J.row.rightContent))&&Z.push(le)}),Z}catch{return[]}}),k=D(-1),b="";Ne(()=>{r.search!==b&&(b=r.search,f(k,e(x).length>0?0:-1,!0))});async function z(Q){var le;f(k,Q,!0),await Et();const Z=e(x)[Q],J=(le=e(p))==null?void 0:le.querySelector(`[data-item-idx="${Z}"]`);if(J&&e(p)&&e(y)){const xe=e(p).getBoundingClientRect(),fe=J.getBoundingClientRect().top-xe.top+e(p).scrollTop-e(p).clientHeight/3;g=!0,e(p).scrollTop=fe,e(y).scrollTop=fe,requestAnimationFrame(()=>{g=!1})}}function A(){e(k)>0&&z(e(k)-1)}function h(){e(k)<0&&e(x).length>0?z(0):e(k)$1`)}catch{return J}}function C(Q,Z){return Q==="equal"?"":Q==="del"&&Z==="left"?"bg-[rgba(214,67,10,0.08)] text-edf-orange-fonce":Q==="del"&&Z==="right"?"bg-[rgba(48,122,16,0.08)] text-edf-vert-fonce":Q==="add"&&Z==="left"?"bg-edf-gris-clair":Q==="add"&&Z==="right"?"bg-[rgba(48,122,16,0.08)] text-edf-vert-fonce":""}function R(Q,Z){return Q==="del"&&Z==="left"?"rgb(251,237,233)":Q==="del"&&Z==="right"?"rgb(238,247,236)":Q==="add"&&Z==="left"?"var(--color-edf-gris-clair)":Q==="add"&&Z==="right"?"rgb(238,247,236)":"white"}let p=D(void 0),y=D(void 0),$=!1,g=!1;function N(Q){$||g||!e(p)||!e(y)||($=!0,Q==="left"?(e(y).scrollTop=e(p).scrollTop,e(y).scrollLeft=e(p).scrollLeft):(e(p).scrollTop=e(y).scrollTop,e(p).scrollLeft=e(y).scrollLeft),requestAnimationFrame(()=>{$=!1}))}async function I(Q){var J;f(d,Q,!0),await Et();const Z=(J=e(p))==null?void 0:J.querySelector(`[data-hunk-start="${Q}"]`);if(Z&&e(p)&&e(y)){g=!0;const le=e(p).getBoundingClientRect(),ye=Z.getBoundingClientRect().top-le.top+e(p).scrollTop-e(p).clientHeight/3;e(p).scrollTop=ye,e(y).scrollTop=ye,requestAnimationFrame(()=>{g=!1})}}function L(){e(d)>0&&I(e(d)-1)}function q(){e(d)<0?I(0):e(d)=0&&f(d,Z,!0)}function j(Q){Q.target instanceof HTMLInputElement||(Q.key==="n"&&!Q.shiftKey?(Q.preventDefault(),q()):Q.key==="N"||Q.key==="n"&&Q.shiftKey?(Q.preventDefault(),L()):Q.key==="j"?(Q.preventDefault(),h()):(Q.key==="J"||Q.key==="j"&&Q.shiftKey)&&(Q.preventDefault(),A()))}var U=gd();{const Q=(Z,J=Cs)=>{var le=be(),xe=G(le);He(xe,17,()=>e(s),dn,(ye,fe,ge)=>{const Ce=V(()=>e(i).indexOf(ge)),De=V(()=>e(l)[ge]>=0&&e(l)[ge]===e(d)),We=V(()=>{var at,Ke;return J()==="left"?(at=e(fe).row)==null?void 0:at.leftNum:(Ke=e(fe).row)==null?void 0:Ke.rightNum}),gt=V(()=>{var at,Ke;return J()==="left"?((at=e(fe).row)==null?void 0:at.leftContent)??"":((Ke=e(fe).row)==null?void 0:Ke.rightContent)??""});var Vt=be(),jr=G(Vt);{var et=at=>{var Ke=id(),Nt=E(_(Ke),2),Vr=_(Nt);v(Nt),v(Ke),ee(()=>ae(Vr,`... ${e(fe).skipped??""} lines hidden ...`)),u(at,Ke)},ut=at=>{var Ke=ld(),Nt=_(Ke),Vr=_(Nt,!0);v(Nt);var $n=E(Nt,2);zr($n,()=>c(e(gt),e(k)>=0&&e(x)[e(k)]===ge),!0),v($n),v(Ke),ee((wt,Mt,yr,Va,Ua)=>{Se(Ke,1,`flex w-full ${wt??""} ${Mt??""}`),Ze(Ke,e(De)?"background-color: rgba(16,87,200,0.12); color: rgb(16,87,200);":""),ke(Ke,"role",yr),ke(Ke,"tabindex",Va),ke(Ke,"data-hunk-start",J()==="left"&&e(Ce)>=0?e(Ce):void 0),ke(Ke,"data-item-idx",J()==="left"?ge:void 0),Ze(Nt,`background: ${Ua??""}; color: ${e(De)?"white":"var(--color-muted)"};`),ae(Vr,e(We)??"")},[()=>C(e(fe).row.type,J()),()=>e(fe).row.type!=="equal"||e(x).includes(ge)?"cursor-pointer":"",()=>e(fe).row.type!=="equal"||e(x).includes(ge)?"button":void 0,()=>e(fe).row.type!=="equal"||e(x).includes(ge)?0:void 0,()=>e(De)?"rgb(16,87,200)":R(e(fe).row.type,J())]),ue("click",Ke,()=>{var Mt;((Mt=e(fe).row)==null?void 0:Mt.type)!=="equal"&&H(ge);const wt=e(x).indexOf(ge);wt>=0&&f(k,wt,!0)}),ue("keydown",Ke,wt=>{var Mt;if(wt.key==="Enter"||wt.key===" "){wt.preventDefault(),((Mt=e(fe).row)==null?void 0:Mt.type)!=="equal"&&H(ge);const yr=e(x).indexOf(ge);yr>=0&&f(k,yr,!0)}}),u(at,Ke)};Y(jr,at=>{e(fe).kind==="separator"?at(et):e(fe).row&&at(ut,1)})}u(ye,Vt)}),u(Z,le)};var X=_(U),K=_(X),re=_(K),w=_(re,!0);v(re);var S=E(re,2),O=_(S,!0);v(S),v(K);var F=E(K,2),M=_(F),T=_(M,!0);v(M);var B=E(M,2),te=_(B,!0);v(B),v(F),v(X);var ie=E(X,2),W=_(ie),ne=_(W);Ye(ne);var oe=E(ne,2);{var de=Z=>{var J=cd(),le=G(J);{let ge=V(()=>e(k)<=0);Pe(le,{variant:"secondary",size:"sm",onclick:A,get disabled(){return e(ge)},children:(Ce,De)=>{we();var We=Ve("Prev");u(Ce,We)},$$slots:{default:!0}})}var xe=E(le,2),ye=_(xe);v(xe);var fe=E(xe,2);{let ge=V(()=>e(k)>=e(x).length-1);Pe(fe,{variant:"secondary",size:"sm",onclick:h,get disabled(){return e(ge)},children:(Ce,De)=>{we();var We=Ve("Next");u(Ce,We)},$$slots:{default:!0}})}ee(()=>ae(ye,`${e(k)>=0?e(k)+1:"–"} / ${e(x).length??""} occurrence${e(x).length!==1?"s":""}`)),u(Z,J)},ve=V(()=>r.search.trim()&&e(x).length>0),se=Z=>{var J=ud();u(Z,J)},_e=V(()=>r.search.trim());Y(oe,Z=>{e(ve)?Z(de):e(_e)&&Z(se,1)})}v(W);var he=E(W,2),Ee=_(he);{let Z=V(()=>e(i).length===0||e(d)<=0);Pe(Ee,{variant:"secondary",size:"sm",onclick:L,get disabled(){return e(Z)},children:(J,le)=>{we();var xe=Ve("Prev");u(J,xe)},$$slots:{default:!0}})}var pe=E(Ee,2),Ie=_(pe);v(pe);var ze=E(pe,2);{let Z=V(()=>e(i).length===0||e(d)>=e(i).length-1);Pe(ze,{variant:"secondary",size:"sm",onclick:q,get disabled(){return e(Z)},children:(J,le)=>{we();var xe=Ve("Next");u(J,xe)},$$slots:{default:!0}})}v(he),v(ie);var Qe=E(ie,2);{var qe=Z=>{var J=dd(),le=_(J),xe=_(le),ye=_(xe);Q(ye,()=>"left"),v(xe),v(le),nt(le,De=>f(p,De),()=>e(p));var fe=E(le,2),ge=_(fe),Ce=_(ge);Q(Ce,()=>"right"),v(ge),v(fe),nt(fe,De=>f(y,De),()=>e(y)),v(J),rt("scroll",le,()=>N("left")),rt("scroll",fe,()=>N("right")),u(Z,J)},Je=Z=>{var J=fd();u(Z,J)};Y(Qe,Z=>{e(o)?Z(qe):Z(Je,-1)})}var Le=E(Qe,2);{var $e=Z=>{var J=vd();u(Z,J)};Y(Le,Z=>{a()&&Z($e)})}v(U),nt(U,Z=>f(n,Z),()=>e(n)),ee(()=>{ae(w,r.leftLabel),ae(O,r.kind),ae(T,r.rightLabel),ae(te,r.kind),fn(ne,r.search),ae(Ie,`${e(d)>=0?e(d)+1:"–"} / ${e(i).length??""} diff${e(i).length!==1?"s":""}`)}),ue("input",ne,Z=>r.onSearchChange(Z.target.value)),ue("keydown",ne,Z=>{Z.key==="Enter"&&(Z.preventDefault(),h())})}ue("keydown",U,j),u(t,U),Ae()}Ge(["keydown","click","input"]);var pd=P(" ",1),md=P('

        Please select two different cases to compare.

        '),_d=P('

        Select two cases to compare.

        '),bd=P(' '),xd=P('
        Parameter
        '),yd=P('

        All parameters are identical.

        '),wd=P('
        ',1),kd=P('

        '),Cd=P('

        Loading...

        '),Sd=P(" ",1),$d=P(" ",1);function Pd(t,r){Me(r,!0);let a=D(""),n=D(""),s=D(""),i=D(""),o=D(""),l=D(!1),d=D(!1),m=D(""),x=V(()=>r.allCases.map(g=>({value:g,label:g})));const k=[{value:"setup.xml",label:"setup.xml"},{value:"doe_row.csv",label:"doe_row.csv"},{value:"run_solver.log",label:"run_solver.log"},{value:"performance.log",label:"performance.log"}];let b=V(()=>{var g;return((g=Ft())==null?void 0:g.compare_kinds)??k});Ne(()=>{var g;e(b).some(N=>N.value===e(s))||f(s,((g=e(b)[0])==null?void 0:g.value)??"",!0)});let z=D(!1),A=V(()=>e(a)&&e(n)&&e(a)!==e(n));function h(){const g=e(a);f(a,e(n),!0),f(n,g,!0)}async function c(){if(e(A)){f(l,!0),f(m,"");try{const g=await Lo({cases:[e(a),e(n)],base:e(a),kind:e(s)});f(o,g,!0),f(d,!0)}catch(g){console.error("Failed to load diff:",g),f(o,""),f(d,!1);const N=g instanceof Error?g.message:String(g);f(m,N.includes("404")?`File "${e(s)}" not found for one of the selected cases. It may not have been run yet.`:"Failed to load comparison.",!0)}f(l,!1)}}Ne(()=>{e(a)&&e(n)&&e(a)!==e(n)&&e(s)?c():e(a)&&e(n)&&e(a)===e(n)&&(f(o,""),f(d,!0))});let C=!1;Ne(()=>{r.allCases.length>=2&&!C?(C=!0,f(a,r.allCases[0],!0),f(n,r.allCases[1],!0)):r.allCases.length===1&&!C&&(C=!0,f(a,r.allCases[0],!0))});let R=V(yn),p=V(()=>{if(!e(a)||!e(n)||e(a)===e(n)||e(R).length===0)return[];const g=xn(),N=g.find(L=>L.case_id===e(a)),I=g.find(L=>L.case_id===e(n));return!N&&!I?[]:e(R).map(L=>{var j,U;const q=((j=N==null?void 0:N.doe)==null?void 0:j[L])!=null&&String(N.doe[L])!==""?String(N.doe[L]):"—",H=((U=I==null?void 0:I.doe)==null?void 0:U[L])!=null&&String(I.doe[L])!==""?String(I.doe[L]):"—";return{param:L,left:q,right:H,differs:q!==H}})}),y=V(()=>e(z)?e(p):e(p).filter(g=>g.differs)),$=V(()=>e(p).filter(g=>g.differs).length);jt(t,{eyebrow:"Compare",title:"Side-by-Side Comparison",wide:!0,children:(g,N)=>{var I=$d(),L=G(I);Qt(L,{children:(X,K)=>{var re=pd(),w=G(re);Re(w,{text:"First case",children:(F,M)=>{lt(F,{class:"w-32.5",get options(){return e(x)},get value(){return e(a)},onchange:T=>f(a,T,!0),placeholder:"Select..."})}});var S=E(w,2);{let F=V(()=>!e(a)&&!e(n));Pe(S,{variant:"secondary",size:"sm",onclick:h,get disabled(){return e(F)},children:(M,T)=>{Te(M,{get icon(){return gi}})},$$slots:{default:!0}})}var O=E(S,2);Re(O,{text:"Second case",children:(F,M)=>{lt(F,{class:"w-32.5",get options(){return e(x)},get value(){return e(n)},onchange:T=>f(n,T,!0),placeholder:"Select..."})}}),u(X,re)}});var q=E(L,2);{var H=X=>{var K=md();u(X,K)},j=X=>{var K=_d();u(X,K)},U=X=>{var K=Sd(),re=G(K);{var w=B=>{var te=wd(),ie=G(te),W=_(ie),ne=_(W);v(W);var oe=E(W,2),de=_(oe,!0);v(oe),v(ie);var ve=E(ie,2);{var se=he=>{var Ee=xd(),pe=_(Ee),Ie=_(pe),ze=_(Ie),Qe=_(ze),qe=_(Qe,!0);v(Qe);var Je=E(Qe,2),Le=_(Je,!0);v(Je),v(ze),v(Ie);var $e=E(Ie);He($e,21,()=>e(y),Q=>Q.param,(Q,Z)=>{var J=bd(),le=_(J),xe=_(le,!0);v(le);var ye=E(le),fe=_(ye,!0);v(ye);var ge=E(ye),Ce=_(ge,!0);v(ge),v(J),ee(()=>{Se(le,1,dt(e(Z).differs?"text-edf-orange-fonce bg-[rgba(214,67,10,0.04)]":"")),ae(xe,e(Z).left),ae(fe,e(Z).param),Se(ge,1,dt(e(Z).differs?"text-edf-vert-fonce bg-[rgba(48,122,16,0.04)]":"")),ae(Ce,e(Z).right)}),u(Q,J)}),v($e),v(pe),v(Ee),ee(()=>{ae(qe,e(a)),ae(Le,e(n))}),u(he,Ee)},_e=he=>{var Ee=yd();u(he,Ee)};Y(ve,he=>{e(y).length>0?he(se):he(_e,-1)})}ee(()=>{ae(ne,`${e($)??""} difference${e($)!==1?"s":""} out of ${e(p).length??""} + parameters`),ae(de,e(z)?"Show differences only":"Show all parameters")}),ue("click",oe,()=>f(z,!e(z))),u(B,te)};Y(re,B=>{e(p).length>0&&B(w)})}var S=E(re,2);Qt(S,{children:(B,te)=>{Re(B,{text:"File",children:(ie,W)=>{lt(ie,{class:"w-37.5",get options(){return e(b)},get value(){return e(s)},onchange:ne=>f(s,ne,!0)})}})}});var O=E(S,2);{var F=B=>{var te=kd(),ie=_(te,!0);v(te),ee(()=>ae(ie,e(m))),u(B,te)},M=B=>{hd(B,{get diffText(){return e(o)},get leftLabel(){return e(a)},get rightLabel(){return e(n)},get kind(){return e(s)},get search(){return e(i)},get loading(){return e(l)},onSearchChange:te=>f(i,te,!0)})},T=B=>{var te=Cd();u(B,te)};Y(O,B=>{e(m)?B(F):e(d)?B(M,1):e(l)&&B(T,2)})}u(X,K)};Y(q,X=>{e(a)&&e(n)&&e(a)===e(n)?X(H):!e(a)||!e(n)?X(j,1):X(U,-1)})}u(g,I)},$$slots:{default:!0}}),Ae()}Ge(["click"]);var Ed=P(" Refresh",1),Rd=P(" ",1),Nd=P(''),Md=P(''),Ad=P(" Download as CSV",1),Ld=P('
        ',1),Td=P(" ",1),Fd=P(""),Id=P('
        Sort by:
        '),zd=P('

        No errors found. Select cases and adjust filters above.

        '),Dd=P(' '),Od=P('NEW'),Hd=P('
        '),jd=P('
        '),Vd=P(' '),Ud=P('
        '),Bd=P('

        '),qd=P("
        ",1);function Kd(t,r){Me(r,!0);const a=6,n=["csauto.stderr","run_solver.log","listing"];let s=D(ce([])),i=D(ce([...n])),o=!1;Ne(()=>{var ne;const W=(ne=Ft())==null?void 0:ne.error_files;W!=null&&W.length&&!o&&f(i,[...W],!0)});let l=D("all"),d=D(""),m=D(a),x=D(!1),k=D(""),b=D(ce(tr("errors")));Ne(()=>{rr("errors",e(b))});let z=D(ce([])),A=D(ce(new Set));function h(W){return W.toLowerCase().replace(/[0-9]+/g,"#").replace(/0x[0-9a-f]+/gi,"#").replace(/\s+/g," ").trim()}function c(W){const ne=W.line_html.replace(/<[^>]*>/g,"").slice(0,120);return`${W.case_id}|${W.file}|${W.severity}|${h(ne)}`}function C(W){const ne=new Map;for(const oe of W){const de=c(oe),ve=ne.get(de);ve?ve.count++:ne.set(de,{...oe,count:1,fingerprint:de,isNew:!e(A).has(de)})}return Array.from(ne.values())}async function R(){if(!(!e(s).length||!e(i).length)){f(x,!0);try{const W=await $o({cases:e(s),files:e(i),context:e(m),sev:e(l)==="all"?"":e(l),q:e(d)});f(k,"");const ne=C(W.items);f(z,W.items,!0);const oe=new Set(ne.map(de=>de.fingerprint));f(A,oe,!0),f(p,ne,!0)}catch(W){f(k,"Failed to load errors"),console.error("Failed to load errors:",W)}finally{f(x,!1)}}}let p=D(ce([])),y=null;function $(){y&&clearTimeout(y),y=setTimeout(R,250)}function g(W){return W==="error"?"error-sev-error":W==="warn"?"error-sev-warn":"error-sev-info"}let N=V(()=>kn(r.allCases));const I=["csauto.stderr","run_solver.log","listing","csauto.stdout"];let L=V(()=>{var W;return(((W=Ft())==null?void 0:W.error_files)??I).map(ne=>({value:ne,label:ne}))}),q=[{value:"all",label:"All"},{value:"error",label:"Error"},{value:"warn",label:"Warn"},{value:"info",label:"Info"}];function H(W){if(f(s,W,!0),W.length===0){f(p,[],!0),f(z,[],!0);return}R()}function j(W){if(o=!0,f(i,W,!0),W.length===0){f(p,[],!0),f(z,[],!0);return}R()}function U(W){f(l,W,!0),R()}function X(){R()}let K=!1;Ne(()=>{r.allCases.length>0&&!K&&(K=!0,f(s,[...r.allCases],!0),R())}),Ne(()=>{e(b)?mr("errors",R,jn()):vt("errors")});const re=_r(()=>{K&&R()});vr(()=>{vt("errors"),re()});let w=D(ce(new Set));function S(W){const ne=new Set(e(w));ne.has(W)?ne.delete(W):ne.add(W),f(w,ne,!0)}let O=D("severity"),F=D("desc");const M={info:0,warn:1,error:2};let T=V(()=>{const W=[...e(p)];return W.sort((ne,oe)=>{let de=0;return e(O)==="severity"?de=(M[ne.severity]??9)-(M[oe.severity]??9):e(O)==="count"?de=ne.count-oe.count:de=ne[e(O)].localeCompare(oe[e(O)]),e(F)==="asc"?de:-de}),W});const B={severity:"desc",count:"desc",case_id:"asc",file:"asc"};function te(W){e(O)===W?f(F,e(F)==="asc"?"desc":"asc",!0):(f(O,W,!0),f(F,B[W],!0))}function ie(){if(!e(p).length)return;const W=["case_id","file","severity","count","is_new","line_text"],ne=e(T).map(ve=>[ve.case_id,ve.file,ve.severity,ve.count,ve.isNew?"yes":"no",`"${ve.line_html.replace(/<[^>]*>/g,"").replace(/"/g,'""')}"`].join(",")),oe=[W.join(","),...ne].join(` +`),de=xr("errors",e(s),"csv");Ma(oe,de)}jt(t,{eyebrow:"Diagnostics",title:"Recent Errors",wide:!0,actions:ne=>{var oe=Rd(),de=G(oe);{let _e=V(jn);nr(de,{name:"errors",get intervalMs(){return e(_e)},onRefresh:R,get checked(){return e(b)},set checked(he){f(b,he,!0)}})}var ve=E(de,2);{var se=_e=>{Pe(_e,{variant:"primary",onclick:R,children:(he,Ee)=>{var pe=Ed(),Ie=G(pe);Te(Ie,{get icon(){return er}}),we(),u(he,pe)},$$slots:{default:!0}})};Y(ve,_e=>{e(b)||_e(se)})}u(ne,oe)},children:(ne,oe)=>{var de=qd(),ve=G(de);Qt(ve,{children:(Le,$e)=>{var Q=Ld(),Z=G(Q);Re(Z,{text:"Cases",children:(Ce,De)=>{Wt(Ce,{class:"w-[160px]",get options(){return e(N)},get selected(){return e(s)},onchange:H,placeholder:"Select cases..."})}});var J=E(Z,2);Re(J,{text:"Files",children:(Ce,De)=>{Wt(Ce,{class:"w-[160px]",get options(){return e(L)},get selected(){return e(i)},onchange:j,placeholder:"Select files..."})}});var le=E(J,2);Re(le,{text:"Severity",children:(Ce,De)=>{lt(Ce,{class:"w-[90px]",get options(){return q},get value(){return e(l)},onchange:U})}});var xe=E(le,2);Re(xe,{text:"Search",children:(Ce,De)=>{var We=Nd();Ye(We),ue("input",We,$),tt(We,()=>e(d),gt=>f(d,gt)),u(Ce,We)}});var ye=E(xe,2);Re(ye,{text:"Context",children:(Ce,De)=>{var We=Md();Ye(We),ue("change",We,X),tt(We,()=>e(m),gt=>f(m,gt)),u(Ce,We)}});var fe=E(ye,2),ge=_(fe);{let Ce=V(()=>e(p).length===0);Pe(ge,{variant:"secondary",size:"sm",onclick:ie,get disabled(){return e(Ce)},children:(De,We)=>{var gt=Ad(),Vt=G(gt);Te(Vt,{get icon(){return pr}}),we(),u(De,gt)},$$slots:{default:!0}})}v(fe),u(Le,Q)}});var se=E(ve,2);{var _e=Le=>{var $e=Id(),Q=E(_($e),2);He(Q,16,()=>[{key:"severity",label:"Severity"},{key:"count",label:"Count"},{key:"case_id",label:"Case"},{key:"file",label:"File"}],Z=>Z.key,(Z,J)=>{var le=Fd(),xe=_(le,!0),ye=E(xe);{var fe=ge=>{var Ce=Td(),De=E(G(Ce));{let We=V(()=>e(F)==="asc"?hi:vi);Te(De,{get icon(){return e(We)},size:12})}u(ge,Ce)};Y(ye,ge=>{e(O)===J.key&&ge(fe)})}v(le),ee(()=>{Se(le,1,`cursor-pointer bg-transparent border-none text-xs hover:underline ${e(O)===J.key?"text-edf-bleu-fonce font-bold":"text-muted"}`),ae(xe,J.label)}),ue("click",le,()=>te(J.key)),u(Z,le)}),v($e),u(Le,$e)};Y(se,Le=>{e(T).length>0&&Le(_e)})}var he=E(se,2),Ee=_(he);{var pe=Le=>{var $e=zd();u(Le,$e)};Y(Ee,Le=>{e(T).length===0&&Le(pe)})}var Ie=E(Ee,2);He(Ie,17,()=>e(T),Le=>Le.fingerprint,(Le,$e)=>{var Q=jd(),Z=_(Q),J=_(Z),le=_(J);{let et=V(()=>e(w).has(e($e).fingerprint)?pn:pi);Te(le,{get icon(){return e(et)},size:14})}v(J);var xe=E(J,2),ye=_(xe);v(xe);var fe=E(xe,2),ge=_(fe);{var Ce=et=>{var ut=Dd(),at=_(ut);v(ut),ee(()=>ae(at,`${e($e).count??""}x`)),u(et,ut)};Y(ge,et=>{e($e).count>1&&et(Ce)})}var De=E(ge,2);{var We=et=>{var ut=Od();u(et,ut)};Y(De,et=>{e($e).isNew&&et(We)})}v(fe),v(Z);var gt=E(Z,2);{var Vt=et=>{var ut=Hd();zr(ut,()=>e($e).line_html,!0),v(ut),u(et,ut)},jr=V(()=>e(w).has(e($e).fingerprint));Y(gt,et=>{e(jr)&&et(Vt)})}v(Q),ee(et=>{Se(Q,1,`error-item ${et??""}`),ae(ye,`${e($e).case_id??""} / ${e($e).file??""} : ${e($e).severity??""}`)},[()=>g(e($e).severity)]),ue("click",Z,()=>S(e($e).fingerprint)),u(Le,Q)}),v(he);var ze=E(he,2);{var Qe=Le=>{var $e=Ud(),Q=_($e),Z=_(Q);v(Q);var J=E(Q,2);{var le=ye=>{var fe=Vd(),ge=_(fe);v(fe),ee(Ce=>ae(ge,`${Ce??""} new`),[()=>e(p).filter(Ce=>Ce.isNew).length]),u(ye,fe)},xe=V(()=>e(p).some(ye=>ye.isNew));Y(J,ye=>{e(xe)&&ye(le)})}v($e),ee(()=>ae(Z,`${e(p).length??""} unique / ${e(z).length??""} total`)),u(Le,$e)};Y(ze,Le=>{e(T).length>0&&Le(Qe)})}var qe=E(ze,2);{var Je=Le=>{var $e=Bd(),Q=_($e,!0);v($e),ee(()=>ae(Q,e(k))),u(Le,$e)};Y(qe,Le=>{e(k)&&Le(Je)})}ee(()=>Se(he,1,`error-list ${e(x)?"opacity-50 pointer-events-none":""}`)),u(ne,de)},$$slots:{actions:!0,default:!0}}),Ae()}Ge(["input","change","click"]);var Gd=P('
        Developed by Simvia
        ',1);function rf(t,r){Me(r,!0);let a=D(ce({totalCases:0,shownCases:0,totalRunning:0,shownRunning:0,totalConverged:0,shownConverged:0})),n=D(ce([])),s=D(null);const i=M=>e(s)===null||e(s).includes(M);async function o(M){var te;const T=`/favicon-${M}.svg`;try{const ie=await fetch(T);if(!ie.ok||!((te=ie.headers.get("content-type"))!=null&&te.includes("svg")))return}catch{return}const B=document.querySelector('link[rel="icon"]');B&&(B.href=T)}async function l(){try{const M=await wo();jl(M.rows),Vl(M.doe_columns),f(n,M.rows.map(T=>T.case_id),!0),f(a,tc(Ra(),xn()),!0)}catch(M){console.error("Failed to load status:",M)}}Jn(()=>{l(),ko().then(M=>{_o(M),f(s,M.panels,!0),o(M.solver)}).catch(()=>{})});var d=Gd(),m=G(d);qi(m,{});var x=E(m,2);Yi(x,{});var k=E(x,2);xl(k,{get totalCases(){return e(a).totalCases},get shownCases(){return e(a).shownCases},get totalRunning(){return e(a).totalRunning},get shownRunning(){return e(a).shownRunning},get totalConverged(){return e(a).totalConverged},get shownConverged(){return e(a).shownConverged}});var b=E(k,2),z=_(b);{var A=M=>{Ac(M,{onRefresh:l})},h=V(()=>i("status"));Y(z,M=>{e(h)&&M(A)})}var c=E(z,2);{var C=M=>{eu(M,{get allCases(){return e(n)}})},R=V(()=>i("residuals"));Y(c,M=>{e(R)&&M(C)})}var p=E(c,2);{var y=M=>{Cu(M,{get allCases(){return e(n)}})},$=V(()=>i("probes"));Y(p,M=>{e($)&&M(y)})}var g=E(p,2);{var N=M=>{Fu(M,{get allCases(){return e(n)}})},I=V(()=>i("performance"));Y(g,M=>{e(I)&&M(N)})}var L=E(g,2);{var q=M=>{Pd(M,{get allCases(){return e(n)}})},H=V(()=>i("compare"));Y(L,M=>{e(H)&&M(q)})}var j=E(L,2);{var U=M=>{sd(M,{get allCases(){return e(n)}})},X=V(()=>i("tail"));Y(j,M=>{e(X)&&M(U)})}var K=E(j,2);{var re=M=>{Kd(M,{get allCases(){return e(n)}})},w=V(()=>i("errors"));Y(K,M=>{e(w)&&M(re)})}v(b);var S=E(b,2),O=E(_(S),2),F=_(O);v(O),v(S),ee(()=>ke(F,"src",yl)),u(t,d),Ae()}export{rf as component}; diff --git a/frontend/dist/_app/version.json b/frontend/dist/_app/version.json index d1564de..3e411a9 100644 --- a/frontend/dist/_app/version.json +++ b/frontend/dist/_app/version.json @@ -1 +1 @@ -{"version":"1789462193110"} \ No newline at end of file +{"version":"1789462962656"} \ No newline at end of file diff --git a/frontend/dist/index.html b/frontend/dist/index.html index b6e164b..30a7bdb 100644 --- a/frontend/dist/index.html +++ b/frontend/dist/index.html @@ -5,11 +5,11 @@ csauto by Simvia - - + + - + @@ -20,15 +20,15 @@