{"_id":"@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web","_rev":"9-c38f86e5e31d535db72e77be6c87817a","name":"@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web","dist-tags":{"latest":"1.0.6"},"versions":{"1.0.2":{"name":"@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web","version":"1.0.2","_id":"@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web@1.0.2","maintainers":[{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"}],"homepage":"https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web#readme","bugs":{"url":"https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web/issues"},"dist":{"shasum":"e95193319aac000b52c2a3b3fb859ae367531bab","tarball":"https://registry.npmjs.org/@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web/-/cty-qr-mobile-camera-sync-web-1.0.2.tgz","fileCount":9,"integrity":"sha512-yRJMy3mjin6pSGbKguxfT2ERiNmVggTOQeYxi2D5dG77dtpa0wMdhIEI/PuqPirKi+A1yUi2pnxlUuGKv7WuHw==","signatures":[{"sig":"MEYCIQD9z5144/slzj5HUMRnzfXdmX0xkkxr0qfLsavkaikJIgIhANyR1WjoJVI9ViqTYWqRZBJoVFx0Bq1qDsTJyL21+4KN","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":37431},"main":"./dist/cty-qr-mobile-camera-sync.umd.js","type":"module","module":"./dist/cty-qr-mobile-camera-sync.es.js","exports":{".":{"import":"./dist/cty-qr-mobile-camera-sync.es.js","require":"./dist/cty-qr-mobile-camera-sync.umd.js"},"./mobile":{"import":"./dist/cty-qr-mobile-camera-sync-mobile.es.js","require":"./dist/cty-qr-mobile-camera-sync-mobile.umd.js"}},"gitHead":"e64e51a6949f59f4e243b000bcd572a06eb3a1f6","scripts":{"build":"vite build --config vite.config.pc.js && vite build --config vite.config.mobile.js"},"_npmUser":{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"},"repository":{"url":"git+https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web.git","type":"git"},"_npmVersion":"10.8.2","description":"Bilingual documentation: [Bahasa Indonesia](#bahasa-indonesia) | [English](#english)","directories":{},"_nodeVersion":"20.20.2","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^5.2.0"},"_npmOperationalInternal":{"tmp":"tmp/cty-qr-mobile-camera-sync-web_1.0.2_1783090313540_0.966910205184357","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web","version":"1.0.3","_id":"@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web@1.0.3","maintainers":[{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"}],"homepage":"https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web#readme","bugs":{"url":"https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web/issues"},"dist":{"shasum":"be9610092fafd7f657c8458f5dd96502200bc7ae","tarball":"https://registry.npmjs.org/@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web/-/cty-qr-mobile-camera-sync-web-1.0.3.tgz","fileCount":9,"integrity":"sha512-P5trcnn4VXwkB1YhhhyInCTh8ZEEkkVaVZynyXOGnVkS8WvMthxXJE/Z2KDIfkdBu/pp1MA9UmS7AK0HabqJAA==","signatures":[{"sig":"MEUCIBRZ29ws5pnP1qMM0whGgpm3lBxfr2asMrrQ4s5Vj3qqAiEA2QnTHxUoJikaDRUdizc6Q5PvKgcxLFbbJN+YE8qTmqY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":37431},"main":"./dist/cty-qr-mobile-camera-sync.umd.js","type":"module","module":"./dist/cty-qr-mobile-camera-sync.es.js","exports":{".":{"import":"./dist/cty-qr-mobile-camera-sync.es.js","require":"./dist/cty-qr-mobile-camera-sync.umd.js"},"./mobile":{"import":"./dist/cty-qr-mobile-camera-sync-mobile.es.js","require":"./dist/cty-qr-mobile-camera-sync-mobile.umd.js"}},"gitHead":"31c7f8e66350afa7ca1e37be5bacb24e4c9e3c9c","scripts":{"build":"vite build --config vite.config.pc.js && vite build --config vite.config.mobile.js"},"_npmUser":{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"},"repository":{"url":"git+https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web.git","type":"git"},"_npmVersion":"10.8.2","description":"Bilingual documentation: [Bahasa Indonesia](#bahasa-indonesia) | [English](#english)","directories":{},"_nodeVersion":"20.20.2","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^5.2.0"},"_npmOperationalInternal":{"tmp":"tmp/cty-qr-mobile-camera-sync-web_1.0.3_1783090387903_0.09926103371557948","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web","version":"1.0.4","license":"SEE LICENSE IN LICENSE","_id":"@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web@1.0.4","maintainers":[{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"}],"homepage":"https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web#readme","bugs":{"url":"https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web/issues"},"dist":{"shasum":"03c161a63399b8bf7102f9a1fa8c9e7bb6f635e6","tarball":"https://registry.npmjs.org/@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web/-/cty-qr-mobile-camera-sync-web-1.0.4.tgz","fileCount":9,"integrity":"sha512-sBIlq6qdcZDdJ/C1ZpNAzPaA1UgeIurYdHttZRZn2TGpZv/IOF3eoAIzh9fZ8pvepGhqMoHBT2PVsPqoZbF3/A==","signatures":[{"sig":"MEQCIFGF2ET5vq+kQe2QuB571XRDZ3ns3wPp5S9UWBO7xWXZAiBMBf+S3Yp2b8owtvDkSnvxqjtKwtvOkBBC6kTRZmQKWQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":37431},"main":"./dist/cty-qr-mobile-camera-sync.umd.js","type":"module","module":"./dist/cty-qr-mobile-camera-sync.es.js","exports":{".":{"import":"./dist/cty-qr-mobile-camera-sync.es.js","require":"./dist/cty-qr-mobile-camera-sync.umd.js"},"./mobile":{"import":"./dist/cty-qr-mobile-camera-sync-mobile.es.js","require":"./dist/cty-qr-mobile-camera-sync-mobile.umd.js"}},"gitHead":"bde0ba660abac4aa37d3c610d091c09422ebc79e","scripts":{"build":"vite build --config vite.config.pc.js && vite build --config vite.config.mobile.js"},"_npmUser":{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"},"repository":{"url":"git+https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web.git","type":"git"},"_npmVersion":"10.8.2","description":"Bilingual documentation: [Bahasa Indonesia](#bahasa-indonesia) | [English](#english)","directories":{},"_nodeVersion":"20.20.2","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^5.2.0"},"_npmOperationalInternal":{"tmp":"tmp/cty-qr-mobile-camera-sync-web_1.0.4_1783146513464_0.12572082107922755","host":"s3://npm-registry-packages-npm-production"}},"1.0.5":{"name":"@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web","version":"1.0.5","license":"SEE LICENSE IN LICENSE","_id":"@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web@1.0.5","maintainers":[{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"}],"homepage":"https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web#readme","bugs":{"url":"https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web/issues"},"dist":{"shasum":"ca44743daf6d60a78d56211f30e863af8ece5c07","tarball":"https://registry.npmjs.org/@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web/-/cty-qr-mobile-camera-sync-web-1.0.5.tgz","fileCount":9,"integrity":"sha512-reLYJfuj/ZpPMXv6tIz1fc18zRCHTmoffpl5vG5RjxerFFrpwGwfDuofQREGpbVQisH5lqVpXBkq8S2knBJM8w==","signatures":[{"sig":"MEYCIQCTeBjaRppQrzUoISNHweJD5tQqKFrsAH3xp1qnS0XMVQIhALx9S/Ia69wEZJuikhwKB7ju8rcMoygxRV7wPu6xCfGG","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":38441},"main":"./dist/cty-qr-mobile-camera-sync.umd.js","type":"module","module":"./dist/cty-qr-mobile-camera-sync.es.js","exports":{".":{"import":"./dist/cty-qr-mobile-camera-sync.es.js","require":"./dist/cty-qr-mobile-camera-sync.umd.js"},"./mobile":{"import":"./dist/cty-qr-mobile-camera-sync-mobile.es.js","require":"./dist/cty-qr-mobile-camera-sync-mobile.umd.js"}},"gitHead":"61f1d5adc61ea2acc2acf0e864c4366e6a2fdc63","scripts":{"build":"vite build --config vite.config.pc.js && vite build --config vite.config.mobile.js"},"_npmUser":{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"},"repository":{"url":"git+https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web.git","type":"git"},"_npmVersion":"10.8.2","description":"Bilingual documentation: [Bahasa Indonesia](#bahasa-indonesia) | [English](#english)","directories":{},"_nodeVersion":"20.20.2","dependencies":{},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^5.2.0"},"_npmOperationalInternal":{"tmp":"tmp/cty-qr-mobile-camera-sync-web_1.0.5_1783394277192_0.4891976315766078","host":"s3://npm-registry-packages-npm-production"}},"1.0.6":{"name":"@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web","version":"1.0.6","license":"SEE LICENSE IN LICENSE","type":"module","repository":{"type":"git","url":"git+https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web.git"},"bugs":{"url":"https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web/issues"},"homepage":"https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web#readme","main":"./dist/cty-qr-mobile-camera-sync.umd.js","module":"./dist/cty-qr-mobile-camera-sync.es.js","exports":{".":{"import":"./dist/cty-qr-mobile-camera-sync.es.js","require":"./dist/cty-qr-mobile-camera-sync.umd.js"},"./mobile":{"import":"./dist/cty-qr-mobile-camera-sync-mobile.es.js","require":"./dist/cty-qr-mobile-camera-sync-mobile.umd.js"}},"scripts":{"build":"vite build --config vite.config.pc.js && vite build --config vite.config.mobile.js"},"publishConfig":{"access":"public"},"dependencies":{},"devDependencies":{"vite":"^5.2.0"},"_id":"@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web@1.0.6","gitHead":"adeac550c8f576f546b5f558ee92b20a455fb1e4","description":"Bilingual documentation: [Bahasa Indonesia](#bahasa-indonesia) | [English](#english)","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-j5/lX0Bkx2f8StzPQvL1HL7LvRp1YbWdf5Ff4RoZ33r3TrAGd69/0zrxbk4uV2b8oxMG909MNvbh3o6XwRZcjg==","shasum":"191a073e5e8c2c80a05085c1c3ff77d0a0305183","tarball":"https://registry.npmjs.org/@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web/-/cty-qr-mobile-camera-sync-web-1.0.6.tgz","fileCount":9,"unpackedSize":38663,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFTX0BJ6bO5Vi7NyhpmDBpE8QR19fszaP6/u26AWAjJDAiEAnyduMIPPi4NoYsItyBqDb1/H+zkq0AGpexz9BRZ/cMI="}]},"_npmUser":{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"},"directories":{},"maintainers":[{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/cty-qr-mobile-camera-sync-web_1.0.6_1783394777185_0.3935324010914769"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-03T14:51:53.346Z","modified":"2026-07-07T03:26:17.528Z","1.0.0":"2026-07-03T11:41:10.513Z","1.0.1":"2026-07-03T11:43:48.744Z","1.0.2":"2026-07-03T14:51:53.689Z","1.0.3":"2026-07-03T14:53:08.054Z","1.0.4":"2026-07-04T06:28:33.602Z","1.0.5":"2026-07-07T03:17:57.312Z","1.0.6":"2026-07-07T03:26:17.376Z"},"bugs":{"url":"https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web/issues"},"license":"SEE LICENSE IN LICENSE","homepage":"https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web#readme","repository":{"type":"git","url":"git+https://github.com/CraftThingy-Digital-Innovation/cty-qr-mobile-camera-sync-web.git"},"description":"Bilingual documentation: [Bahasa Indonesia](#bahasa-indonesia) | [English](#english)","maintainers":[{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"}],"readme":"# cty-qr-mobile-camera-sync-web\n\nBilingual documentation: [Bahasa Indonesia](#bahasa-indonesia) | [English](#english)\n\n---\n\n## Bahasa Indonesia\n\nLibrary client-side Javascript untuk melakukan sinkronisasi penangkapan kamera HP (melalui scan QR Code) secara persisten dan instan ke layar komputer (PC) secara real-time tanpa perlu menginstal aplikasi native (cukup menggunakan web browser biasa).\n\nSangat cocok digunakan untuk sistem input data paspor, KTP, dokumen fisik, bukti penyerahan barang, tanda tangan, ataupun sistem OCR yang memerlukan pemrosesan multi-foto berturut-turut.\n\n### Fitur Utama\n- 📸 **Persistent Camera Session:** Sambungan kamera HP tetap aktif setelah memotret. Kamera secara otomatis di-reset dalam delay tertentu agar pengguna dapat mengambil foto berikutnya tanpa memindai QR Code ulang.\n- 🔄 **Multi-photo & Background Queue Support:** Mendukung penerimaan dan pemrosesan antrean banyak foto secara latar belakang di PC.\n- ⚡ **Real-Time Polling & Connection Status:** Mengirimkan detak jantung (heartbeat/ping) dari HP sehingga PC dapat memantau status koneksi kamera secara real-time.\n- 🛠 **Dual Entry Point:** Menyediakan entry point modular terpisah untuk PC Client (`/`) dan Mobile Client (`/mobile`).\n\n### Lisensi\nProyek ini dilisensikan di bawah **Public-Source Corporate Royalty License (PSCRL)**. Gratis sepenuhnya untuk penggunaan non-komersil, personal, akademis, dan proyek open-source. Penggunaan oleh entitas bisnis/komersial dikenakan royalti 1% jika pendapatan kotor tahunan telah melampaui $10,000 USD. Selengkapnya lihat file [LICENSE](./LICENSE).\n\n---\n\n### Cara Penggunaan (Bahasa Indonesia)\n\n#### 1. Instalasi\n```bash\nnpm install @craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web\n```\n\n#### 2. Implementasi Sisi PC Client (Frontend PC)\nImpor class `QrMobileSync` dari library utama untuk membuat sesi baru, mendapatkan URL QR Code, dan mendengarkan pengiriman gambar secara asinkron.\n\n```javascript\nimport { QrMobileSync } from '@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web';\n\n// Inisialisasi SDK\nconst sync = new QrMobileSync({\n  apiUrl: 'https://aplikasi-anda.com/api', // Endpoint server API Anda\n  pollInterval: 2000 // Interval pooling ke server (default: 2000ms)\n});\n\n// Dapatkan token sesi aktif\nconst sessionToken = sync.sessionToken;\n\n// Dapatkan URL tujuan yang akan disematkan ke dalam QR Code\n// Contoh: halaman kamera HP Anda berada di https://aplikasi-anda.com/mobile-camera\nconst qrUrlForMobile = sync.getQrUrl('https://aplikasi-anda.com/mobile-camera');\n\n// Render `qrUrlForMobile` ke elemen gambar QR Code (menggunakan library qr-code eksternal Anda)\nrenderQrCode(qrUrlForMobile);\n\n// Mulai polling server secara asinkron\nsync.startPolling();\n\n// Tangkap perubahan status koneksi HP\nsync.on('connection-change', ({ connected }) => {\n  if (connected) {\n    console.log(\"Kamera HP terhubung!\");\n  } else {\n    console.log(\"Kamera HP terputus.\");\n  }\n});\n\n// Tangkap setiap gambar baru yang terkirim\nsync.on('image', ({ filename, imageUrl }) => {\n  console.log(\"Gambar diterima di PC:\", imageUrl);\n  \n  // Tambahkan gambar ke viewport atau antrean lokal PC Anda\n  displayImageInQueue(imageUrl);\n  \n  // Rekomendasi: Hapus file temporary di server agar hemat penyimpanan\n  sync.clearFile(filename);\n});\n\n// Tangkap error jika terjadi gangguan jaringan\nsync.on('error', (err) => {\n  console.error(\"Terjadi error sinkronisasi:\", err);\n});\n```\n\n#### 3. Implementasi Sisi Kamera HP (Mobile Web Page)\nPada halaman mobile scanner (`mobile-camera`), gunakan entry point `/mobile` untuk mengakses hardware kamera dan mengupload gambar langsung ke PC.\n\n```javascript\nimport { MobileCameraScanner } from '@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web/mobile';\n\n// Ambil token dari parameter query URL (?token=xxxx)\nconst urlParams = new URLSearchParams(window.location.search);\nconst token = urlParams.get('token');\n\nconst scanner = new MobileCameraScanner({\n  token: token,\n  uploadUrl: 'https://aplikasi-anda.com/api/ocr-phone/upload',\n  pingUrl: 'https://aplikasi-anda.com/api/ocr-phone/ping',\n  autoResetDelay: 2500 // Kamera aktif kembali 2.5 detik setelah berhasil upload\n});\n\n// Jalankan preview webcam\nconst videoElement = document.getElementById('camera-preview');\nscanner.startCamera(videoElement);\n\n// Kirim heartbeat status aktif ke server agar PC tahu HP sedang terhubung\nscanner.startPing();\n\n// Tombol Ambil Foto\ndocument.getElementById('capture-btn').addEventListener('click', async () => {\n  const canvas = document.createElement('canvas');\n  try {\n    const result = await scanner.captureAndUpload(canvas);\n    console.log(\"Gambar sukses diupload ke server!\", result);\n  } catch (err) {\n    console.error(\"Gagal mengupload gambar:\", err);\n  }\n});\n\n// Tangani event pemrosesan upload\nscanner.on('upload-start', () => {\n  showSpinner();\n});\n\nscanner.on('upload-success', () => {\n  hideSpinner();\n  showSuccessOverlay();\n  \n  // Kamera otomatis siap memotret kembali setelah `autoResetDelay`\n  setTimeout(() => {\n    hideSuccessOverlay();\n  }, scanner.autoResetDelay);\n});\n```\n\n#### 4. Struktur API Backend yang Diperlukan\nAgar library ini dapat berkomunikasi, server backend Anda harus mendukung endpoint berikut:\n\n##### A. Polling Status Kamera (`GET /ocr-phone/check/:token`)\nMengembalikan status koneksi HP dan daftar file terunggah.\n**Response JSON:**\n```json\n{\n  \"status\": \"success\",\n  \"connected\": true,\n  \"images\": [\n    {\n      \"filename\": \"paspor_1.jpg\",\n      \"image_url\": \"https://aplikasi-anda.com/uploads/ocr_temp/paspor_1.jpg\"\n    }\n  ]\n}\n```\n\n##### B. Ping Heartbeat HP (`GET /ocr-phone/ping/:token`)\nMemperbarui timestamp aktivitas HP agar status `connected` bernilai `true`.\n\n##### C. Upload Gambar (`POST /ocr-phone/upload/:token`)\nMenerima file gambar berformat `multipart/form-data` dengan nama field `image`.\n\n##### D. Clear File (`GET/DELETE /ocr-phone/clear-file/:filename`)\nMenghapus file temporary tertentu di server demi privasi data dan efisiensi penyimpanan server.\n\n---\n\n## English\n\nA client-side JavaScript library to sync mobile camera captures (via scanning a QR Code) persistently and instantly to a computer (PC) screen in real-time, without installing any native applications (runs on standard mobile browsers).\n\nPerfect for passport/ID document data input systems, physical receipts, delivery verification, signatures, or OCR engines requiring rapid multi-photo background queues.\n\n### Key Features\n- 📸 **Persistent Camera Session:** The mobile camera stream remains open after capture. The shutter resets automatically after a configurable delay so users can take the next photo without scanning the QR Code again.\n- 🔄 **Multi-photo & Background Queue Support:** Supports receiving and handling multiple image queues concurrently on the PC side.\n- ⚡ **Real-Time Polling & Connection Status:** Sends heartbeat/ping signals from the mobile browser to allow the PC client to display real-time connection status.\n- 🛠 **Dual Entry Point:** Offers separate modular bundles for the PC Client (`/`) and Mobile Client (`/mobile`).\n\n### Licensing\nThis project is licensed under the **Public-Source Corporate Royalty License (PSCRL)**. Completely free for non-commercial, personal, academic, and open-source projects. For-profit/commercial corporate usage requires a 1% royalty agreement once gross annual revenue exceeds $10,000 USD. For details, refer to the [LICENSE](./LICENSE) file.\n\n---\n\n### Usage Guide (English)\n\n#### 1. Installation\n```bash\nnpm install @craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web\n```\n\n#### 2. PC Client Implementation (Frontend PC)\nImport the `QrMobileSync` class to initiate sessions, generate QR target URLs, and listen for incoming images.\n\n```javascript\nimport { QrMobileSync } from '@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web';\n\n// Initialize SDK\nconst sync = new QrMobileSync({\n  apiUrl: 'https://your-app.com/api', // Your backend server API base URL\n  pollInterval: 2000 // Interval for server polling (default: 2000ms)\n});\n\n// Retrieve the active session token\nconst sessionToken = sync.sessionToken;\n\n// Build the target mobile URL containing the session token\n// For example, if your mobile camera page is hosted at https://your-app.com/mobile-camera\nconst qrUrlForMobile = sync.getQrUrl('https://your-app.com/mobile-camera');\n\n// Render the `qrUrlForMobile` into a QR Code image (using your own external QR renderer library)\nrenderQrCode(qrUrlForMobile);\n\n// Start async server polling\nsync.startPolling();\n\n// Listen for connection status updates\nsync.on('connection-change', ({ connected }) => {\n  if (connected) {\n    console.log(\"Mobile camera connected!\");\n  } else {\n    console.log(\"Mobile camera disconnected.\");\n  }\n});\n\n// Listen for incoming images\nsync.on('image', ({ filename, imageUrl }) => {\n  console.log(\"Image received on PC:\", imageUrl);\n  \n  // Append the received image URL to your local PC workspace gallery or queue\n  displayImageInQueue(imageUrl);\n  \n  // Recommended: Clear the temporary server file to save disk space\n  sync.clearFile(filename);\n});\n\n// Listen for connection or sync errors\nsync.on('error', (err) => {\n  console.error(\"Sync error occurred:\", err);\n});\n```\n\n#### 3. Mobile Camera Implementation (Mobile Web Page)\nOn your mobile web page (`mobile-camera`), import the helper SDK from `/mobile` to control hardware camera devices and post captures.\n\n```javascript\nimport { MobileCameraScanner } from '@craftthingy-digital-innovation/cty-qr-mobile-camera-sync-web/mobile';\n\n// Parse session token from query parameters (?token=xxxx)\nconst urlParams = new URLSearchParams(window.location.search);\nconst token = urlParams.get('token');\n\nconst scanner = new MobileCameraScanner({\n  token: token,\n  uploadUrl: 'https://your-app.com/api/ocr-phone/upload',\n  pingUrl: 'https://your-app.com/api/ocr-phone/ping',\n  autoResetDelay: 2500 // Camera automatically resets 2.5s after successful upload\n});\n\n// Bind to video element for webcam preview\nconst videoElement = document.getElementById('camera-preview');\nscanner.startCamera(videoElement);\n\n// Start sending heartbeat signals to the backend\nscanner.startPing();\n\n// Capture shutter button\ndocument.getElementById('capture-btn').addEventListener('click', async () => {\n  const canvas = document.createElement('canvas');\n  try {\n    const result = await scanner.captureAndUpload(canvas);\n    console.log(\"Image successfully uploaded!\", result);\n  } catch (err) {\n    console.error(\"Upload failed:\", err);\n  }\n});\n\n// Listen for upload progress/status\nscanner.on('upload-start', () => {\n  showSpinner();\n});\n\nscanner.on('upload-success', () => {\n  hideSpinner();\n  showSuccessOverlay();\n  \n  // The camera resets and is ready for the next capture automatically after `autoResetDelay`\n  setTimeout(() => {\n    hideSuccessOverlay();\n  }, scanner.autoResetDelay);\n});\n```\n\n#### 4. Required Backend API Schema\nTo establish communication, your backend server must provide the following HTTP endpoints:\n\n##### A. Fetch Camera Status (`GET /ocr-phone/check/:token`)\nReturns the active connection status and any pending uploaded images.\n**Response JSON:**\n```json\n{\n  \"status\": \"success\",\n  \"connected\": true,\n  \"images\": [\n    {\n      \"filename\": \"passport_1.jpg\",\n      \"image_url\": \"https://your-app.com/uploads/ocr_temp/passport_1.jpg\"\n    }\n  ]\n}\n```\n\n##### B. Mobile Heartbeat Ping (`GET /ocr-phone/ping/:token`)\nUpdates the last active timestamp for the session, marking `connected` as `true` in the PC check endpoint.\n\n##### C. Post Shutter Capture (`POST /ocr-phone/upload/:token`)\nAccepts file uploads as `multipart/form-data` under the field key `image`.\n\n##### D. Clean Server Storage (`GET/DELETE /ocr-phone/clear-file/:filename`)\nDeletes specific temporary image files on the server to enforce data privacy and optimize server storage space.\n","readmeFilename":"README.md"}