diff --git a/docs/api.md b/docs/api.md index ae0674e..b2cf56e 100644 --- a/docs/api.md +++ b/docs/api.md @@ -54,7 +54,7 @@ Returns server version for compatibility checking. **Example response:** ```json -{"version": "1.9.2"} +{"version": "1.9.3"} ``` ### `GET /api/status` diff --git a/frontend/index.html b/frontend/index.html index 9c9a315..61b58ed 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -18,7 +18,7 @@
-

OTAMan SIM OTA with a Human Face v1.9.2

+

OTAMan SIM OTA with a Human Face v1.9.3

github @@ -921,8 +921,12 @@
-
+ +
+ + +
@@ -997,9 +1001,13 @@
-
- - + +
+ + + + +
@@ -4291,9 +4299,22 @@ function cardsExport() { el.select(); } -async function cardsImport() { +function downloadJson(name, obj) { + const blob = new Blob([JSON.stringify(obj, null, 2)], {type: 'application/json'}); + const a = document.createElement('a'); + a.href = URL.createObjectURL(blob); + a.download = name; + a.click(); + URL.revokeObjectURL(a.href); +} + +function cardsExportFile() { + downloadJson('otaman-cards.json', cards); +} + +async function cardsImport(text) { try { - const text = await navigator.clipboard.readText(); + if (text == null) text = await navigator.clipboard.readText(); const imported = JSON.parse(text); if (!Array.isArray(imported)) throw new Error('Expected an array'); let added = 0; @@ -4324,6 +4345,25 @@ async function cardsImport() { } } +function cardsImportFile(input) { + const file = input.files && input.files[0]; + input.value = ''; + if (!file) return; + const reader = new FileReader(); + reader.onload = () => cardsImport(reader.result); + reader.readAsText(file); +} + +function cardsImportField() { + const el = document.getElementById('cards-io'); + const val = el.value.trim(); + if (!el.classList.contains('hidden') && val.startsWith('[')) { cardsImport(val); return; } + el.value = ''; + el.placeholder = 'Paste JSON array here, then press Paste & import again'; + el.classList.remove('hidden'); + el.focus(); +} + // ===== pysim sub-tabs ===== let pysimCmdHistory = []; let pysimCmdHistIdx = -1; @@ -5706,9 +5746,13 @@ function pysimCustomExport() { el.select(); } -async function pysimCustomImport() { +function pysimCustomExportFile() { + downloadJson('otaman-custom-files.json', pysimCustomFiles); +} + +async function pysimCustomImport(text) { try { - const text = await navigator.clipboard.readText(); + if (text == null) text = await navigator.clipboard.readText(); const imported = JSON.parse(text); if (!Array.isArray(imported)) throw new Error('Expected an array'); let added = 0; @@ -5736,6 +5780,25 @@ async function pysimCustomImport() { } } +function pysimCustomImportFile(input) { + const file = input.files && input.files[0]; + input.value = ''; + if (!file) return; + const reader = new FileReader(); + reader.onload = () => pysimCustomImport(reader.result); + reader.readAsText(file); +} + +function pysimCustomImportField() { + const el = document.getElementById('pysim-cf-io'); + const val = el.value.trim(); + if (!el.classList.contains('hidden') && val.startsWith('[')) { pysimCustomImport(val); return; } + el.value = ''; + el.placeholder = 'Paste JSON array here, then press Paste & import again'; + el.classList.remove('hidden'); + el.focus(); +} + // Init custom files pysimCustomLoad(); @@ -5833,6 +5896,9 @@ const LANG_RU = { 'Card presets are stored locally in your browser. JSON export/import buttons are below the card list.': 'Данные карт хранятся локально в браузере. Кнопки экспорта/импорта JSON — под списком карт.', 'Add': 'Добавить', 'Export as JSON': 'Экспорт в JSON', + 'Export to file': 'Экспорт в файл', + 'Import from file': 'Импорт из файла', + 'Paste & import': 'Вставить и импортировать', 'Import JSON from clipboard': 'Импорт JSON из буфера', 'Name': 'Имя', 'ICCID': 'ICCID', diff --git a/frontend/sw.js b/frontend/sw.js index 3c9fcbd..4af3d94 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'otaman-v13'; +const CACHE = 'otaman-v14'; const URLS = [ 'index.html', 'help.html', diff --git a/pyproject.toml b/pyproject.toml index 553e1b7..d14e4bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pysim-otaman-server" -version = "1.9.2" +version = "1.9.3" description = "HTTP REST server wrapping pysim for the OTAMan PWA" requires-python = ">=3.8" # pysim is a git-only dependency installed explicitly by setup.bat/setup.sh. diff --git a/pysim_otaman_server/server.py b/pysim_otaman_server/server.py index 9a3e24d..363b149 100644 --- a/pysim_otaman_server/server.py +++ b/pysim_otaman_server/server.py @@ -18,7 +18,7 @@ from osmocom.construct import GsmOrUcs2Adapter from osmocom.tlv import BER_TLV_IE -VERSION = '1.9.2' +VERSION = '1.9.3' # Static file serving (the PWA lives in /frontend, served by this server