{"id":100070,"date":"2025-03-04T17:50:40","date_gmt":"2025-03-04T14:20:40","guid":{"rendered":"https:\/\/nabfollower.com\/blog\/building-offline-first-web-apps-with-zero-dependencies-a-srvra-sync-tutorial-42ok\/"},"modified":"2025-03-04T17:50:40","modified_gmt":"2025-03-04T14:20:40","slug":"building-offline-first-web-apps-with-zero-dependencies-a-srvra-sync-tutorial-42ok","status":"publish","type":"post","link":"https:\/\/nabfollower.com\/blog\/building-offline-first-web-apps-with-zero-dependencies-a-srvra-sync-tutorial-42ok\/","title":{"rendered":"\u0633\u0627\u062e\u062a \u0628\u0631\u0646\u0627\u0645\u0647 \u0647\u0627\u06cc \u0648\u0628 \u0622\u0641\u0644\u0627\u06cc\u0646 \u0627\u0648\u0644 \u0628\u0627 \u0648\u0627\u0628\u0633\u062a\u06af\u06cc \u0635\u0641\u0631: \u0622\u0645\u0648\u0632\u0634 SRVRA-SYNC"},"content":{"rendered":"<div data-article-id=\"2310562\" id=\"article-body\">\n<p>\u062f\u0631 \u0686\u0634\u0645 \u0627\u0646\u062f\u0627\u0632 \u062a\u0648\u0633\u0639\u0647 \u0648\u0628 \u0627\u0645\u0631\u0648\u0632 \u060c \u0642\u0627\u0628\u0644\u06cc\u062a \u0622\u0641\u0644\u0627\u06cc\u0646 \u0641\u0642\u0637 \u06cc\u06a9 \u0648\u06cc\u0698\u06af\u06cc \u062e\u0648\u0628 \u0646\u06cc\u0633\u062a-\u0636\u0631\u0648\u0631\u06cc \u0627\u0633\u062a. \u06a9\u0627\u0631\u0628\u0631\u0627\u0646 \u0627\u0646\u062a\u0638\u0627\u0631 \u062f\u0627\u0631\u0646\u062f \u06a9\u0647 \u0628\u0631\u0646\u0627\u0645\u0647 \u0647\u0627 \u0628\u062f\u0648\u0646 \u062f\u0631 \u0646\u0638\u0631 \u06af\u0631\u0641\u062a\u0646 \u0634\u0631\u0627\u06cc\u0637 \u0634\u0628\u06a9\u0647 \u060c \u06cc\u06a9\u067e\u0627\u0631\u0686\u0647 \u06a9\u0627\u0631 \u06a9\u0646\u0646\u062f. <br \/>\u0628\u0627 \u0627\u06cc\u0646 \u062d\u0627\u0644 \u060c \u0627\u062c\u0631\u0627\u06cc \u0639\u0645\u0644\u06a9\u0631\u062f\u0647\u0627\u06cc \u0622\u0641\u0644\u0627\u06cc\u0646 \u0642\u0648\u06cc \u0628\u0647 \u0637\u0648\u0631 \u0645\u0639\u0645\u0648\u0644 \u0628\u0647 \u06a9\u062a\u0627\u0628\u062e\u0627\u0646\u0647 \u0647\u0627 \u0648 \u0686\u0627\u0631\u0686\u0648\u0628 \u0647\u0627\u06cc \u067e\u06cc\u0686\u06cc\u062f\u0647 \u0627\u06cc \u0628\u0627 \u0648\u0627\u0628\u0633\u062a\u06af\u06cc \u0647\u0627\u06cc \u0628\u06cc \u0634\u0645\u0627\u0631\u06cc \u0646\u06cc\u0627\u0632 \u062f\u0627\u0631\u062f \u060c \u0641\u0631\u0622\u06cc\u0646\u062f \u0633\u0627\u062e\u062a \u0634\u0645\u0627 \u0631\u0627 \u067e\u06cc\u0686\u06cc\u062f\u0647 \u0645\u06cc \u06a9\u0646\u062f \u0648 \u0627\u0646\u062f\u0627\u0632\u0647 \u0628\u0633\u062a\u0647 \u0646\u0631\u0645 \u0627\u0641\u0632\u0627\u0631\u06cc \u0634\u0645\u0627 \u0631\u0627 \u0627\u0641\u0632\u0627\u06cc\u0634 \u0645\u06cc \u062f\u0647\u062f.<\/p>\n<p>SRVRA-SYNC \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f: \u06cc\u06a9 \u06a9\u062a\u0627\u0628\u062e\u0627\u0646\u0647 \u0645\u062f\u06cc\u0631\u06cc\u062a \u0648 \u0647\u0645\u0627\u0647\u0646\u06af \u0633\u0627\u0632\u06cc \u062d\u0627\u0644\u062a JavaScript \u062e\u0627\u0644\u0635 \u06a9\u0647 \u0628\u0647 \u0648\u0627\u0628\u0633\u062a\u06af\u06cc \u0635\u0641\u0631 \u0646\u06cc\u0627\u0632 \u062f\u0627\u0631\u062f \u0648 \u0628\u062f\u0648\u0646 Node.js. \u06a9\u0627\u0631 \u0645\u06cc \u06a9\u0646\u062f. \u0627\u06cc\u0646 \u0622\u0645\u0648\u0632\u0634 \u0634\u0645\u0627 \u0631\u0627 \u0627\u0632 \u0637\u0631\u06cc\u0642 \u0633\u0627\u062e\u062a\u0646 \u06cc\u06a9 \u0628\u0631\u0646\u0627\u0645\u0647 \u0648\u0628 \u0622\u0641\u0644\u0627\u06cc\u0646 \u0627\u0648\u0644 \u0631\u0627\u0647\u0646\u0645\u0627\u06cc\u06cc \u0645\u06cc \u06a9\u0646\u062f \u06a9\u0647 \u0647\u0646\u06af\u0627\u0645 \u0628\u0627\u0632\u06af\u0634\u062a \u0627\u062a\u0635\u0627\u0644 \u060c \u062f\u0627\u062f\u0647 \u0647\u0627 \u0631\u0627 \u0628\u0647 \u0635\u0648\u0631\u062a \u0647\u0648\u0634\u0645\u0646\u062f\u0627\u0646\u0647 \u0647\u0645\u06af\u0627\u0645 \u0645\u06cc \u06a9\u0646\u062f.<\/p>\n<p><strong>\u0686\u0631\u0627 \u0628\u0647 \u0635\u0648\u0631\u062a \u0622\u0641\u0644\u0627\u06cc\u0646 \u0627\u0648\u0644 \u0645\u0633\u0627\u0626\u0644<\/strong><br \/>\u0642\u0628\u0644 \u0627\u0632 \u063a\u0648\u0627\u0635\u06cc \u060c \u0628\u06cc\u0627\u06cc\u06cc\u062f \u0631\u0648\u0634\u0646 \u06a9\u0646\u06cc\u0645 \u06a9\u0647 \u0686\u0631\u0627 \u0645\u0639\u0645\u0627\u0631\u06cc \u0627\u0648\u0644 \u0622\u0641\u0644\u0627\u06cc\u0646 \u0628\u0633\u06cc\u0627\u0631 \u0645\u0647\u0645 \u0627\u0633\u062a:<\/p>\n<p>\u062a\u062c\u0631\u0628\u0647 \u06a9\u0627\u0631\u0628\u0631 \u0628\u0647\u0628\u0648\u062f \u06cc\u0627\u0641\u062a\u0647 \u062f\u0631 \u0634\u0631\u0627\u06cc\u0637 \u063a\u06cc\u0631\u0642\u0627\u0628\u0644 \u0627\u0639\u062a\u0645\u0627\u062f \u0634\u0628\u06a9\u0647<br \/>\u0639\u0645\u0644\u06a9\u0631\u062f \u0633\u0631\u06cc\u0639\u062a\u0631 \u0628\u0627 \u062f\u0633\u062a\u0631\u0633\u06cc \u0628\u0647 \u062f\u0627\u062f\u0647 \u0647\u0627\u06cc \u0645\u062d\u0644\u06cc \u0641\u0648\u0631\u06cc<br \/>\u0645\u0642\u0627\u0648\u0645\u062a \u062f\u0631 \u0628\u0631\u0627\u0628\u0631 \u0648\u0642\u0641\u0647 \u0647\u0627\u06cc \u0634\u0628\u06a9\u0647<br \/>\u0639\u0645\u0631 \u0628\u0627\u062a\u0631\u06cc \u0628\u0647\u062a\u0631 \u0628\u0627 \u06a9\u0627\u0647\u0634 \u062a\u0644\u0627\u0634 \u0647\u0627\u06cc \u062b\u0627\u0628\u062a \u0627\u062a\u0635\u0627\u0644<\/p>\n<p><strong>\u0634\u0631\u0648\u0639 \u0628\u0627 SRVRA-SYNC<\/strong><\/p>\n<p>\u0628\u06cc\u0627\u06cc\u06cc\u062f \u0628\u0627 \u0627\u0636\u0627\u0641\u0647 \u06a9\u0631\u062f\u0646 SRVRA-SYNC \u0628\u0647 \u067e\u0631\u0648\u0698\u0647 \u0645\u0627 \u0634\u0631\u0648\u0639 \u06a9\u0646\u06cc\u0645. \u0627\u0632 \u0622\u0646\u062c\u0627 \u06a9\u0647 \u0648\u0627\u0628\u0633\u062a\u06af\u06cc \u0635\u0641\u0631 \u062f\u0627\u0631\u062f \u060c \u0645\u06cc \u062a\u0648\u0627\u0646\u06cc\u062f \u0622\u0646 \u0631\u0627 \u0645\u0633\u062a\u0642\u06cc\u0645\u0627\u064b \u0627\u0632 \u0637\u0631\u06cc\u0642 \u0628\u0631\u0686\u0633\u0628 \u0627\u0633\u06a9\u0631\u06cc\u067e\u062a \u062f\u0631\u062c \u06a9\u0646\u06cc\u062f:<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>\n\n\n    <meta charset=\"UTF-8\"\/>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\/>\n    <title>Offline-First Todo App<\/title>\n    <!-- No need for npm install or complex build processes -->\n    <script src=\"https:\/\/cdn.example.com\/srvra-sync.min.js\"\/>\n\n\n    \n\n    <script src=\"app.js\"\/>\n\n\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\u062d\u0627\u0644 \u060c \u0628\u06cc\u0627\u06cc\u06cc\u062f \u0645\u0646\u0637\u0642 \u0628\u0631\u0646\u0627\u0645\u0647 \u062e\u0648\u062f \u0631\u0627 \u062f\u0631 App.js \u0627\u06cc\u062c\u0627\u062f \u06a9\u0646\u06cc\u0645:<\/p>\n<p>\/\/ \u0627\u062c\u0632\u0627\u06cc \u0627\u0635\u0644\u06cc \u0631\u0627 \u0622\u063a\u0627\u0632 \u06a9\u0646\u06cc\u062f<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>const eventBus = new SrvraEventBus();\nconst stateManager = new SrvraStateManager();\nconst dataSync = new SrvraDataSync({\n    \/\/ Adjust sync interval based on your app's needs\n    syncInterval: 5000,\n    \/\/ Optimize for offline usage\n    retryAttempts: 5,\n    enableDeltaUpdates: true\n});\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u0648\u0636\u0639\u06cc\u062a \u0627\u0648\u0644\u06cc\u0647 \u0645\u0627 \u0631\u0627 \u062a\u0646\u0638\u06cc\u0645 \u06a9\u0646\u06cc\u062f<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>stateManager.setState('todos', []);\nstateManager.setState('connectionStatus', 'online');\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u0639\u0646\u0627\u0635\u0631 UI<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>const todoForm = document.getElementById('todo-form');\nconst todoInput = document.getElementById('todo-input');\nconst todoList = document.getElementById('todo-list');\nconst connectionStatus = document.getElementById('connection-status');\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u0648\u0636\u0639\u06cc\u062a \u0634\u0628\u06a9\u0647 \u0631\u0627 \u067e\u06cc\u06af\u06cc\u0631\u06cc \u06a9\u0646\u06cc\u062f<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>window.addEventListener('online', () =&gt; {\n    stateManager.setState('connectionStatus', 'online');\n    connectionStatus.textContent=\"Online - Syncing...\";\n    \/\/ Trigger sync when connection returns\n    dataSync.sync();\n});\n\nwindow.addEventListener('offline', () =&gt; {\n    stateManager.setState('connectionStatus', 'offline');\n    connectionStatus.textContent=\"Offline - Changes saved locally\";\n});\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u0635\u0641\u062d\u0647 \u0646\u0645\u0627\u06cc\u0634 \u0627\u062a\u0635\u0627\u0644 \u0631\u0627 \u0622\u063a\u0627\u0632 \u06a9\u0646\u06cc\u062f<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>connectionStatus.textContent = navigator.onLine ? 'Online' : 'Offline - Changes saved locally';\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u0627\u0636\u0627\u0641\u0647 \u06a9\u0631\u062f\u0646 \u0627\u0636\u0627\u0641\u0647 \u06a9\u0631\u062f\u0646 \u062a\u0648\u062f\u0648\u0647\u0627\u06cc \u062c\u062f\u06cc\u062f<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>todoForm.addEventListener('submit', (e) =&gt; {\n    e.preventDefault();\n\n    const todoText = todoInput.value.trim();\n    if (!todoText) return;\n\n    const todos = stateManager.getState('todos') || [];\n    const newTodo = {\n        id: Date.now().toString(),\n        text: todoText,\n        completed: false,\n        createdAt: Date.now(),\n        synced: false\n    };\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>\/\/ Update local state immediately\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>    stateManager.setState('todos', [...todos, newTodo]);\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>\/\/ Publish event for sync handling\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>eventBus.publish('data-change', {\n        key: 'todos',\n        value: [...todos, newTodo],\n        timestamp: Date.now()\n    });\n\n    todoInput.value=\"\";\n});\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u0645\u0634\u062a\u0631\u06a9 \u062f\u0631 \u062a\u063a\u06cc\u06cc\u0631\u0627\u062a \u062d\u0627\u0644\u062a \u0628\u0631\u0627\u06cc \u0628\u0647 \u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06cc UI<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>stateManager.subscribe('todos', renderTodos);\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u0644\u06cc\u0633\u062a TODO \u0631\u0627 \u0627\u0631\u0627\u0626\u0647 \u062f\u0647\u06cc\u062f<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>function renderTodos(todos) {\n    todoList.innerHTML = '';\n\n    if (!todos || !todos.length) {\n        todoList.innerHTML = '<li class=\"empty\">No todos yet. Add one above!<\/li>';\n        return;\n    }\n\n    todos.forEach(todo =&gt; {\n        const li = document.createElement('li');\n        li.dataset.id = todo.id;\n        li.className = todo.completed ? 'completed' : '';\n\n        \/\/ Add sync status indicator\n        const syncStatus = todo.synced ? '?' : '?';\n\n        li.innerHTML = `\n            <span class=\"todo-text\">${todo.text}<\/span>\n            <span class=\"sync-status\" title=\"${todo.synced ? 'Synced' : 'Pending sync'}\">${syncStatus}<\/span>\n            <button class=\"toggle-btn\">${todo.completed ? 'Undo' : 'Complete'}<\/button>\n            <button class=\"delete-btn\">Delete<\/button>\n        `;\n\n        \/\/ Add event listeners for toggle and delete\n        li.querySelector('.toggle-btn').addEventListener('click', () =&gt; toggleTodo(todo.id));\n        li.querySelector('.delete-btn').addEventListener('click', () =&gt; deleteTodo(todo.id));\n\n        todoList.appendChild(li);\n    });\n}\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u0648\u0636\u0639\u06cc\u062a \u062a\u06a9\u0645\u06cc\u0644 TODO \u0631\u0627 \u062a\u063a\u06cc\u06cc\u0631 \u062f\u0647\u06cc\u062f<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>function toggleTodo(id) {\n    const todos = stateManager.getState('todos');\n    const updatedTodos = todos.map(todo =&gt; {\n        if (todo.id === id) {\n            return { ...todo, completed: !todo.completed, synced: false };\n        }\n        return todo;\n    });\n\n    stateManager.setState('todos', updatedTodos);\n\n    \/\/ Publish event for sync\n    eventBus.publish('data-change', {\n        key: 'todos',\n        value: updatedTodos,\n        timestamp: Date.now()\n    });\n}\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u062d\u0630\u0641 \u0647\u0645\u0647<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>function deleteTodo(id) {\n    const todos = stateManager.getState('todos');\n    const updatedTodos = todos.filter(todo =&gt; todo.id !== id);\n\n    stateManager.setState('todos', updatedTodos);\n\n    \/\/ Publish event for sync\n    eventBus.publish('data-change', {\n        key: 'todos',\n        value: updatedTodos,\n        timestamp: Date.now()\n    });\n}\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u06a9\u0646\u062a\u0631\u0644 \u0647\u0645\u0627\u0647\u0646\u06af \u0633\u0627\u0632\u06cc<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>eventBus.subscribe('sync-complete', (data) =&gt; {\n    const todos = stateManager.getState('todos');\n\n    \/\/ Mark successfully synced todos\n    const updatedTodos = todos.map(todo =&gt; {\n        if (!todo.synced) {\n            return { ...todo, synced: true };\n        }\n        return todo;\n    });\n\n    stateManager.setState('todos', updatedTodos);\n    connectionStatus.textContent=\"Online - Synced\";\n\n    \/\/ After a moment, simplify the display\n    setTimeout(() =&gt; {\n        if (stateManager.getState('connectionStatus') === 'online') {\n            connectionStatus.textContent=\"Online\";\n        }\n    }, 2000);\n});\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u0628\u0631\u0627\u06cc \u062e\u0637\u0627\u0647\u0627\u06cc \u0647\u0645\u06af\u0627\u0645 \u0633\u0627\u0632\u06cc \u06af\u0648\u0634 \u062f\u0647\u06cc\u062f<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>eventBus.subscribe('sync-error', (data) =&gt; {\n    console.error('Sync error:', data.error);\n    connectionStatus.textContent=\"Sync failed - Will retry\";\n});\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u0628\u0627 \u0647\u0631 \u062f\u0627\u062f\u0647 \u0630\u062e\u06cc\u0631\u0647 \u0634\u062f\u0647 \u0627\u0648\u0644\u06cc\u0647 \u0631\u0627 \u0622\u063a\u0627\u0632 \u06a9\u0646\u06cc\u062f<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>function initializeFromStorage() {\n    const storedTodos = localStorage.getItem('todos');\n    if (storedTodos) {\n        try {\n            const todos = JSON.parse(storedTodos);\n            stateManager.setState('todos', todos);\n        } catch (e) {\n            console.error('Error loading stored todos:', e);\n        }\n    }\n}\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u062a\u063a\u06cc\u06cc\u0631 \u062f\u0648\u0644\u062a \u062f\u0631 \u0645\u062d\u0644\u06cc \u062f\u0631 \u0645\u062d\u0644\u06cc<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>stateManager.subscribe('todos', (todos) =&gt; {\n    localStorage.setItem('todos', JSON.stringify(todos));\n});\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u0627\u0648\u0644\u06cc\u0647 \u0633\u0627\u0632\u06cc<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>initializeFromStorage();\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><strong>\u062a\u0646\u0638\u06cc\u0645 \u0647\u0645\u06af\u0627\u0645 \u0633\u0627\u0632\u06cc \u0633\u0631\u0648\u0631<\/strong><br \/>\u0628\u0631\u0627\u06cc \u06cc\u06a9 \u0628\u0631\u0646\u0627\u0645\u0647 \u06a9\u0627\u0645\u0644 \u0622\u0641\u0644\u0627\u06cc\u0646 \u060c \u0628\u0627\u06cc\u062f \u0647\u0645\u0627\u0647\u0646\u06af \u0633\u0627\u0632\u06cc \u0633\u0631\u0648\u0631 \u0631\u0627 \u062a\u0646\u0638\u06cc\u0645 \u06a9\u0646\u06cc\u0645. \u062f\u0631 \u0627\u06cc\u0646\u062c\u0627 \u0646\u062d\u0648\u0647 \u0631\u0633\u06cc\u062f\u06af\u06cc \u0628\u0647 \u0627\u062f\u063a\u0627\u0645 \u0633\u0645\u062a \u0633\u0631\u0648\u0631 \u0622\u0648\u0631\u062f\u0647 \u0634\u062f\u0647 \u0627\u0633\u062a:<\/p>\n<p>\/\/ \u0627\u06cc\u0646 \u0628\u0647 App.js \u0634\u0645\u0627 \u0627\u0636\u0627\u0641\u0647 \u0645\u06cc \u0634\u0648\u062f<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>function setupServerSync() {\n    \/\/ Configure sync endpoint\n    const syncEndpoint=\"https:\/\/api.example.com\/sync\";\n\n    \/\/ Override the default sendToServer method in SrvraDataSync\n    dataSync.sendToServer = async function(batch) {\n        try {\n            \/\/ Only attempt to send if online\n            if (!navigator.onLine) {\n                return { success: [], conflicts: [], errors: [] };\n            }\n\n            const response = await fetch(syncEndpoint, {\n                method: 'POST',\n                headers: {\n                    'Content-Type': 'application\/json'\n                },\n                body: JSON.stringify(batch)\n            });\n\n            if (!response.ok) {\n                throw new Error(`Server responded with ${response.status}`);\n            }\n\n            return await response.json();\n        } catch (error) {\n            console.error('Sync error:', error);\n            \/\/ Return empty results but don't fail - we'll retry later\n            return { success: [], conflicts: [], errors: [error] };\n        }\n    };\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>\/\/ Handle conflict resolution\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>eventBus.subscribe('conflict', (conflict) =&gt; {\n        \/\/ For this example, we'll use a \"client wins\" strategy for conflicts\n        return dataSync.handleConflict({\n            ...conflict,\n            forcedStrategy: 'client-wins'\n        });\n    });\n}\n\nsetupServerSync();\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><strong>\u0627\u0648\u0644 \u0627\u0644\u06af\u0648\u0647\u0627\u06cc \u0622\u0641\u0644\u0627\u06cc\u0646-\u0627\u0648\u0644 \u0628\u0627 SRVRA-SYNC<\/strong><br \/>\u062d\u0627\u0644 \u0628\u06cc\u0627\u06cc\u06cc\u062f \u0628\u0631\u062e\u06cc \u0627\u0632 \u0627\u0644\u06af\u0648\u0647\u0627\u06cc \u067e\u06cc\u0634\u0631\u0641\u062a\u0647 \u0622\u0641\u0644\u0627\u06cc\u0646 \u0631\u0627 \u0627\u062c\u0631\u0627 \u06a9\u0646\u06cc\u0645:<\/p>\n<p><strong>1. \u0628\u0647 \u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06cc \u0647\u0627\u06cc \u062e\u0648\u0634 \u0628\u06cc\u0646\u0627\u0646\u0647 UI<\/strong><br \/>\/\/ \u0627\u06cc\u0646 \u0631\u0627 \u0628\u0647 App.js \u0627\u0636\u0627\u0641\u0647 \u06a9\u0646\u06cc\u062f \u062a\u0627 \u0628\u0647 \u0631\u0648\u0632\u0631\u0633\u0627\u0646\u06cc \u0647\u0627\u06cc \u062e\u0648\u0634 \u0628\u06cc\u0646 UI \u0631\u0627 \u0641\u0639\u0627\u0644 \u06a9\u0646\u06cc\u062f<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>function optimisticallyUpdateTodo(todoId, changes) {\n    const todos = stateManager.getState('todos');\n\n    \/\/ Immediately update the UI\n    const optimisticTodos = todos.map(todo =&gt; {\n        if (todo.id === todoId) {\n            return { ...todo, ...changes, synced: false };\n        }\n        return todo;\n    });\n\n    \/\/ Update state immediately\n    stateManager.setState('todos', optimisticTodos);\n\n    \/\/ Then attempt to sync\n    eventBus.publish('data-change', {\n        key: 'todos',\n        value: optimisticTodos,\n        timestamp: Date.now()\n    });\n}\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0645\u062b\u0627\u0644 \u0628\u0631\u0627\u06cc \u062a\u063a\u06cc\u06cc\u0631 \u0627\u0648\u0644\u0648\u06cc\u062a<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>function updateTodoPriority(todoId, priority) {\n    optimisticallyUpdateTodo(todoId, { priority });\n}\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><strong>5. \u0627\u0633\u062a\u0631\u0627\u062a\u0698\u06cc \u0647\u0627\u06cc \u062d\u0644 \u0648 \u0641\u0635\u0644 \u0645\u0646\u0627\u0642\u0634\u0647<\/strong><br \/>\/\/ \u0648\u0636\u0648\u062d \u062f\u0631\u06af\u06cc\u0631\u06cc \u067e\u06cc\u0634\u0631\u0641\u062a\u0647 \u0628\u0631 \u0627\u0633\u0627\u0633 \u0627\u0646\u0648\u0627\u0639 \u062f\u0627\u062f\u0647 \u0647\u0627<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>dataSync.conflictResolver.registerCustomStrategy('smart-todo-merge', (conflict) =&gt; {\n    const serverTodo = conflict.serverValue;\n    const clientTodo = conflict.clientValue;\n\n    \/\/ If completed status differs, prefer the completed version\n    const completed = serverTodo.completed || clientTodo.completed;\n\n    \/\/ For text content, use the most recently edited version\n    const text = serverTodo.updatedAt &gt; clientTodo.updatedAt \n        ? serverTodo.text \n        : clientTodo.text;\n\n    return {\n        value: {\n            ...clientTodo,\n            text,\n            completed,\n            synced: true\n        },\n        source: 'smart-merge',\n        metadata: {\n            mergedAt: Date.now(),\n            conflictResolution: 'smart-todo-merge'\n        }\n    };\n});\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u062a\u0646\u0638\u06cc\u0645\u0627\u062a TODOS \u0631\u0627 \u0628\u0631\u0627\u06cc \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0627\u0632 \u0627\u0633\u062a\u0631\u0627\u062a\u0698\u06cc \u0633\u0641\u0627\u0631\u0634\u06cc \u0645\u0627 \u067e\u06cc\u06a9\u0631\u0628\u0646\u062f\u06cc \u06a9\u0646\u06cc\u062f<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>dataSync.conflictResolver.registerMergeRule('todos', \n    dataSync.conflictResolver.customStrategies.get('smart-todo-merge'));\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><strong>3. \u0645\u062f\u06cc\u0631\u06cc\u062a \u0633\u0647\u0645\u06cc\u0647 \u0630\u062e\u06cc\u0631\u0647 \u0633\u0627\u0632\u06cc<\/strong><br \/>\/\/ \u0627\u06cc\u0646 \u0631\u0627 \u0628\u0631\u0627\u06cc \u0645\u062f\u06cc\u0631\u06cc\u062a \u0633\u0647\u0645\u06cc\u0647 \u0647\u0627\u06cc \u0645\u062d\u0644\u06cc \u0627\u0636\u0627\u0641\u0647 \u06a9\u0646\u06cc\u062f<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>async function checkStorageQuota() {\n    if ('storage' in navigator &amp;&amp; 'estimate' in navigator.storage) {\n        const estimate = await navigator.storage.estimate();\n        const percentUsed = (estimate.usage \/ estimate.quota) * 100;\n\n        if (percentUsed &gt; 80) {\n            \/\/ Alert user or clean up old data\n            const todos = stateManager.getState('todos');\n\n            \/\/ Clean up completed and synced todos older than 30 days\n            const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);\n            const cleanedTodos = todos.filter(todo =&gt; {\n                return !(todo.completed &amp;&amp; todo.synced &amp;&amp; todo.createdAt &lt; thirtyDaysAgo);\n            });\n\n            if (cleanedTodos.length &lt; todos.length) {\n                stateManager.setState('todos', cleanedTodos);\n                console.log(`Cleaned up ${todos.length - cleanedTodos.length} old todos to save space.`);\n            }\n        }\n    }\n}\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u0633\u0647\u0645\u06cc\u0647 \u0647\u0641\u062a\u06af\u06cc \u0631\u0627 \u0628\u0631\u0631\u0633\u06cc \u06a9\u0646\u06cc\u062f<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>setInterval(checkStorageQuota, 7 * 24 * 60 * 60 * 1000);\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u0647\u0645\u0686\u0646\u06cc\u0646 \u062f\u0631 \u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u0628\u0631\u0631\u0633\u06cc \u06a9\u0646\u06cc\u062f<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>checkStorageQuota();\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><strong>\u0631\u0633\u06cc\u062f\u06af\u06cc \u0628\u0647 \u0627\u062a\u0635\u0627\u0644 \u0645\u062c\u062f\u062f \u0634\u0628\u06a9\u0647 \u0647\u0648\u0634\u0645\u0646\u062f\u0627\u0646\u0647<\/strong><br \/>\u062c\u0646\u0628\u0647 \u0645\u0647\u0645 \u0628\u0631\u0646\u0627\u0645\u0647 \u0647\u0627\u06cc \u0622\u0641\u0644\u0627\u06cc\u0646 \u0627\u0648\u0644 \u0645\u062f\u06cc\u0631\u06cc\u062a \u0627\u062a\u0635\u0627\u0644 \u0645\u062c\u062f\u062f \u0628\u0647 \u0622\u0631\u0627\u0645\u06cc \u0627\u0633\u062a:<\/p>\n<p>\/\/ \u0627\u0636\u0627\u0641\u0647 \u06a9\u0631\u062f\u0646 \u0645\u062c\u062f\u062f \u0627\u062a\u0635\u0627\u0644 \u0645\u062c\u062f\u062f \u067e\u06cc\u0686\u06cc\u062f\u0647<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>let reconnectionAttempts = 0;\nconst MAX_BACKOFF = 60000; \/\/ Maximum 1 minute between retries\n\nfunction handleReconnection() {\n    \/\/ Reset if we're online\n    if (navigator.onLine) {\n        reconnectionAttempts = 0;\n        dataSync.sync(); \/\/ Sync immediately when we reconnect\n        return;\n    }\n\n    \/\/ Exponential backoff for reconnection attempts\n    reconnectionAttempts++;\n    const backoff = Math.min(\n        1000 * Math.pow(2, reconnectionAttempts), \n        MAX_BACKOFF\n    );\n\n    connectionStatus.textContent = `Offline - Retrying in ${backoff\/1000}s`;\n\n    setTimeout(() =&gt; {\n        \/\/ Check if we're back online\n        if (navigator.onLine) {\n            stateManager.setState('connectionStatus', 'online');\n            connectionStatus.textContent=\"Online - Syncing...\";\n            dataSync.sync();\n        } else {\n            handleReconnection(); \/\/ Try again with increased backoff\n        }\n    }, backoff);\n}\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\/\/ \u0634\u0631\u0648\u0639 \u0628\u0647 \u06a9\u0627\u0631 \u062f\u0631 \u0634\u0628\u06a9\u0647<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>if (!navigator.onLine) {\n    handleReconnection();\n}\n\n\/\/ Listen for network changes\nwindow.addEventListener('online', () =&gt; {\n    stateManager.setState('connectionStatus', 'online');\n    connectionStatus.textContent=\"Online - Syncing...\";\n    dataSync.sync();\n});\n\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u06a9\u0646\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>\u0645\u062e\u0632\u0646 GitHub:<br \/>srvra-sync https:\/\/github.com\/sinhasantos\/srvra-sync<\/p>\n<\/p><\/div>\n","protected":false},"excerpt":{"rendered":"<p>\u062f\u0631 \u0686\u0634\u0645 \u0627\u0646\u062f\u0627\u0632 \u062a\u0648\u0633\u0639\u0647 \u0648\u0628 \u0627\u0645\u0631\u0648\u0632 \u060c \u0642\u0627\u0628\u0644\u06cc\u062a \u0622\u0641\u0644\u0627\u06cc\u0646 \u0641\u0642\u0637 \u06cc\u06a9 \u0648\u06cc\u0698\u06af\u06cc \u062e\u0648\u0628 \u0646\u06cc\u0633\u062a-\u0636\u0631\u0648\u0631\u06cc \u0627\u0633\u062a. \u06a9\u0627\u0631\u0628\u0631\u0627\u0646 \u0627\u0646\u062a\u0638\u0627\u0631 \u062f\u0627\u0631\u0646\u062f \u06a9\u0647 \u0628\u0631\u0646\u0627\u0645\u0647 \u0647\u0627 \u0628\u062f\u0648\u0646 \u062f\u0631 \u0646\u0638\u0631 \u06af\u0631\u0641\u062a\u0646 \u0634\u0631\u0627\u06cc\u0637 \u0634\u0628\u06a9\u0647 \u060c \u06cc\u06a9\u067e\u0627\u0631\u0686\u0647 \u06a9\u0627\u0631 \u06a9\u0646\u0646\u062f. \u0628\u0627 \u0627\u06cc\u0646 \u062d\u0627\u0644 \u060c \u0627\u062c\u0631\u0627\u06cc \u0639\u0645\u0644\u06a9\u0631\u062f\u0647\u0627\u06cc \u0622\u0641\u0644\u0627\u06cc\u0646 \u0642\u0648\u06cc \u0628\u0647 \u0637\u0648\u0631 \u0645\u0639\u0645\u0648\u0644 \u0628\u0647 \u06a9\u062a\u0627\u0628\u062e\u0627\u0646\u0647 \u0647\u0627 \u0648 \u0686\u0627\u0631\u0686\u0648\u0628 \u0647\u0627\u06cc \u067e\u06cc\u0686\u06cc\u062f\u0647 \u0627\u06cc \u0628\u0627 \u0648\u0627\u0628\u0633\u062a\u06af\u06cc \u0647\u0627\u06cc \u0628\u06cc \u0634\u0645\u0627\u0631\u06cc &hellip;<\/p>\n","protected":false},"author":2,"featured_media":100071,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"fifu_image_url":"","fifu_image_alt":"","footnotes":""},"categories":[339],"tags":[],"class_list":["post-100070","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dev"],"_links":{"self":[{"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/posts\/100070","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/comments?post=100070"}],"version-history":[{"count":0,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/posts\/100070\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/media\/100071"}],"wp:attachment":[{"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/media?parent=100070"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/categories?post=100070"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/tags?post=100070"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}