Распознаю штрихкод…
+ + +diff --git a/t1.js b/t1.js index ec1ae1a..401465f 100644 --- a/t1.js +++ b/t1.js @@ -1 +1,202 @@ -let test = "string"; \ No newline at end of file +// t1.js — удалённый скрипт для Scriptable Remote Loader +// Сканирование штрихкодов/QR по фото + история + +async function __main(ctx) { // точка входа для бутлоадера + const menu = new Alert(); + menu.title = "Сканер (фото) + история"; + menu.addAction("Сканировать"); + menu.addAction("История"); + menu.addDestructiveAction("Очистить историю"); + menu.addCancelAction("Отмена"); + const c = await menu.presentSheet(); + if (c === 0) return scanFromCamera(ctx); + if (c === 1) return showHistory(); + if (c === 2) return clearHistory(); +} + +// --- Настройки/хранилище --- +const FILE_NAME = "scriptable_scans.json"; +const USE_ICLOUD = false; +const fm = USE_ICLOUD ? FileManager.iCloud() : FileManager.local(); +const storePath = fm.joinPath(fm.documentsDirectory(), FILE_NAME); + +function loadHistory() { + try { + if (!fm.fileExists(storePath)) return []; + return JSON.parse(fm.readString(storePath)) || []; + } catch (e) { console.error(e); return []; } +} +function saveHistory(arr) { + try { fm.writeString(storePath, JSON.stringify(arr, null, 2)); } + catch (e) { console.error(e); } +} + +// --- Основные функции --- +async function scanFromCamera(ctx) { + try { + const img = await Photos.fromCamera(); // системный запрос на доступ к камере тут + const base64 = Data.fromJPEG(img).toBase64String(); // JPEG — лучше для 1D + const wv = new WebView(); + + wv.shouldAllowRequest = req => { + const u = req.url || ""; + if (u.startsWith("scriptable://decoded")) { + const q = parseQS(u); + const text = q.text || ""; + const format = q.format || "UNKNOWN"; + if (text) { + const h = loadHistory(); + h.unshift({ text, format, ts: new Date().toISOString() }); + saveHistory(h); + notify(ctx, "Сохранено", `${format}: ${text}`); + } else { + notify(ctx, "Не найдено", "Код распознать не удалось"); + } + return false; // не покидать WebView + } + return true; + }; + + await wv.loadHTML(decoderHTML(base64)); + await wv.present(true); + } catch (e) { + console.error(e); + await notify(ctx, "Ошибка", String(e)); + } +} + +async function showHistory() { + const history = loadHistory(); + const table = new UITable(); + table.showSeparators = true; + + const hdr = new UITableRow(); + const c = hdr.addText("История", `${history.length} записей`); + c.titleFont = Font.boldSystemFont(16); + c.subtitleFont = Font.systemFont(12); + table.addRow(hdr); + + if (!history.length) { + const r = new UITableRow(); + r.addText("Пусто", "Сканируйте код через «Сканировать»."); + table.addRow(r); + await table.present(); return; + } + + for (const it of history) { + const r = new UITableRow(); + r.onSelect = async () => { Pasteboard.copy(it.text); await notify(null, "Скопировано", it.text); }; + r.addText(it.text, `${it.format || "CODE"} · ${new Date(it.ts).toLocaleString()}`); + table.addRow(r); + } + + await table.present(); +} + +async function clearHistory() { + const x = new Alert(); + x.title = "Очистить историю?"; + x.addDestructiveAction("Очистить"); + x.addCancelAction("Отмена"); + if (await x.presentAlert() === 0) { + saveHistory([]); + await notify(null, "Готово", "История очищена"); + } +} + +// --- Вспомогательные --- +function parseQS(url) { + const out = {}; const i = url.indexOf("?"); if (i < 0) return out; + for (const p of url.slice(i + 1).split("&")) { + const [k, v] = p.split("="); + out[decodeURIComponent(k || "")] = decodeURIComponent(v || ""); + } + return out; +} + +async function notify(ctx, title, body) { + try { + if (ctx && typeof ctx.notify === "function") return ctx.notify(title, body); + const n = new Notification(); n.title = title; n.body = body; await n.schedule(); + } catch (e) {} +} + +// --- HTML с ZXing: распознаём 1D + QR, несколько попыток и повороты --- +function decoderHTML(b64) { + return ` +
Распознаю штрихкод…
+ + +