From 1a81ee96c74767c726f350d7d0d41885f2cf9d41 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 01:28:34 +0000 Subject: [PATCH 1/3] Initial plan From 6fc3e6bce96c822c58996bd479d97423ae5940b4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 01:39:28 +0000 Subject: [PATCH 2/3] Add reactive integration tests for ldreactive and ldview Co-authored-by: zbryikt <2672307+zbryikt@users.noreply.github.com> --- web/.view/index.js | 8 +- web/.view/init/index.js | 8 +- web/.view/long/index.js | 8 +- web/.view/nested-ld-each/index.js | 8 +- web/.view/reactive/index.js | 316 ++++++++++++++++++++++++++++++ web/.view/scope/index.js | 8 +- web/src/pug/reactive/index.ls | 204 +++++++++++++++++++ web/src/pug/reactive/index.pug | 143 ++++++++++++++ web/static/reactive/index.html | 31 +++ 9 files changed, 709 insertions(+), 25 deletions(-) create mode 100644 web/.view/reactive/index.js create mode 100644 web/src/pug/reactive/index.ls create mode 100644 web/src/pug/reactive/index.pug create mode 100644 web/static/reactive/index.html diff --git a/web/.view/index.js b/web/.view/index.js index f661ff2..ebfa246 100644 --- a/web/.view/index.js +++ b/web/.view/index.js @@ -10,7 +10,7 @@ function pug_merge(e,r){if(1===arguments.length){for(var t=e[0],g=1;g]/; +function pug_merge(e,r){if(1===arguments.length){for(var t=e[0],g=1;g': '>', '"': '"', "'": ''' }; var repl = function(c) { return MAP[c]; }; return function(s) { return s.replace(/[&<>'"]/g, repl); }; })(); +function ellipsis(str, len) { + return ((str || '').substring(0, len || 200) + (((str || '').length > (len || 200)) ? '...' : '')); +} + + + + + + + + + + + + + + +var b64img = {}; +b64img.px1 = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEAAAAALAAAAAABAAEAAAIA" +var loremtext = { + zh: "料何緊許團人受間口語日是藝一選去,得系目、再驗現表爸示片球法中轉國想我樹我,色生早都沒方上情精一廣發!能生運想毒一生人一身德接地,說張在未安人、否臺重壓車亞是我!終力邊技的大因全見起?切問去火極性現中府會行多他千時,來管表前理不開走於展長因,現多上我,工行他眼。總務離子方區面人話同下,這國當非視後得父能民觀基作影輕印度民雖主他是一,星月死較以太就而開後現:國這作有,他你地象的則,引管戰照十都是與行求證來亞電上地言裡先保。大去形上樹。計太風何不先歡的送但假河線己綠?計像因在……初人快政爭連合多考超的得麼此是間不跟代光離制不主政重造的想高據的意臺月飛可成可有時情乎為灣臺我養家小,叫轉於可!錢因其他節,物如盡男府我西上事是似個過孩而過要海?更神施一關王野久沒玩動一趣庭顧倒足要集我民雲能信爸合以物頭容戰度系士我多學一、區作一,過業手:大不結獨星科表小黨上千法值之兒聲價女去大著把己。", + en: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." +}; + + + + + + + + + + + + + +prefix = function(n) { return (!n?[]:(Array.isArray(n)?n:[n])).map(function(it){ return `${prefix.currentName}$${it}`; }).join(' ');} +pug_mixins["scope"] = pug_interp = function(name){ +var block = (this && this.block), attributes = (this && this.attributes) || {}; +var parentName = prefix.currentName; +prefix.currentName = name; +if (attributes.class && /naked-scope/.exec(attributes.class)) { +block && block(); +} +else { +pug_html = pug_html + "\u003Cdiv" + (pug_attrs(pug_merge([{"ld-scope": pug_escape(name || '')},attributes]), true)) + "\u003E"; +block && block(); +pug_html = pug_html + "\u003C\u002Fdiv\u003E"; +} +prefix.currentName = parentName; +}; +pug_html = pug_html + "\u003Chead\u003E"; +pug_mixins["css"]("/assets/lib/bootstrap/main/dist/css/bootstrap.min.css"); +pug_mixins["css"]("/assets/lib/@loadingio/bootstrap.ext/main/index.min.css"); +pug_html = pug_html + "\u003Cstyle type=\"text\u002Fcss\"\u003E.test-section {\n margin: 30px 0;\n padding: 20px;\n border: 2px solid #007bff;\n border-radius: 8px;\n}\n.test-section h2 {\n color: #007bff;\n margin-top: 0;\n}\n.output {\n margin: 15px 0;\n padding: 15px;\n background: #f8f9fa;\n border-radius: 4px;\n font-family: monospace;\n}\n.label {\n font-weight: bold;\n color: #666;\n}\nbutton {\n margin: 5px;\n}\n.item {\n padding: 10px;\n margin: 5px 0;\n background: #e9ecef;\n border-radius: 4px;\n}\n\u003C\u002Fstyle\u003E\u003C\u002Fhead\u003E\u003Cbody\u003E\u003Cdiv class=\"w-1024 rwd mx-auto typeset heading-contrast my-4\"\u003E\u003Ch1\u003Eldreactive + ldview Integration Tests\u003C\u002Fh1\u003E\u003Cdiv class=\"text-muted\"\u003ETest integration between ldreactive.ls and ldview.ls with automatic reactive updates\u003C\u002Fdiv\u003E\u003Chr\u003E\u003C!-- Test 1: Basic Counter - Property Tracking--\u003E\u003Cdiv class=\"test-section\"\u003E\u003Ch2\u003ETest 1: Basic Counter (Property Tracking)\u003C\u002Fh2\u003E\u003Cdiv class=\"text-muted\"\u003ETests basic reactive property tracking and automatic updates\u003C\u002Fdiv\u003E"; +pug_mixins["scope"].call({ +block: function(){ +pug_html = pug_html + "\u003Cdiv class=\"output\"\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003ECount: \u003C\u002Fspan\u003E\u003Cspan ld=\"count\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003ERender count: \u003C\u002Fspan\u003E\u003Cspan ld=\"renderCount\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003C\u002Fdiv\u003E\u003Cbutton class=\"btn btn-primary\" ld=\"increment\"\u003EIncrement\u003C\u002Fbutton\u003E\u003Cbutton class=\"btn btn-secondary\" ld=\"decrement\"\u003EDecrement\u003C\u002Fbutton\u003E\u003Cbutton class=\"btn btn-warning\" ld=\"reset\"\u003EReset\u003C\u002Fbutton\u003E"; +} +}, "test1"); +pug_html = pug_html + "\u003C\u002Fdiv\u003E\u003C!-- Test 2: Nested Object Tracking--\u003E\u003Cdiv class=\"test-section\"\u003E\u003Ch2\u003ETest 2: Nested Object Tracking\u003C\u002Fh2\u003E\u003Cdiv class=\"text-muted\"\u003ETests deep property tracking in nested objects\u003C\u002Fdiv\u003E"; +pug_mixins["scope"].call({ +block: function(){ +pug_html = pug_html + "\u003Cdiv class=\"output\"\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003EStreet: \u003C\u002Fspan\u003E\u003Cspan ld=\"street\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003ECity: \u003C\u002Fspan\u003E\u003Cspan ld=\"city\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003ECountry: \u003C\u002Fspan\u003E\u003Cspan ld=\"country\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003EFull Address: \u003C\u002Fspan\u003E\u003Cspan ld=\"fullAddress\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003C\u002Fdiv\u003E\u003Cbutton class=\"btn btn-primary\" ld=\"changeStreet\"\u003EChange Street\u003C\u002Fbutton\u003E\u003Cbutton class=\"btn btn-primary\" ld=\"changeCity\"\u003EChange City\u003C\u002Fbutton\u003E\u003Cbutton class=\"btn btn-primary\" ld=\"changeCountry\"\u003EChange Country\u003C\u002Fbutton\u003E"; +} +}, "test2"); +pug_html = pug_html + "\u003C\u002Fdiv\u003E\u003C!-- Test 3: Array\u002FList Tracking--\u003E\u003Cdiv class=\"test-section\"\u003E\u003Ch2\u003ETest 3: Array\u002FList Tracking\u003C\u002Fh2\u003E\u003Cdiv class=\"text-muted\"\u003ETests array operations and list rendering\u003C\u002Fdiv\u003E"; +pug_mixins["scope"].call({ +block: function(){ +pug_html = pug_html + "\u003Cdiv class=\"output\"\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003ETotal Items: \u003C\u002Fspan\u003E\u003Cspan ld=\"count\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003Cdiv class=\"mt-3\" ld-each=\"item\"\u003E\u003Cdiv class=\"item\" ld=\"text\"\u003E\u003C\u002Fdiv\u003E\u003C\u002Fdiv\u003E\u003C\u002Fdiv\u003E\u003Cbutton class=\"btn btn-success\" ld=\"addItem\"\u003EAdd Item\u003C\u002Fbutton\u003E\u003Cbutton class=\"btn btn-danger\" ld=\"removeItem\"\u003ERemove Last\u003C\u002Fbutton\u003E"; +} +}, "test3"); +pug_html = pug_html + "\u003C\u002Fdiv\u003E\u003C!-- Test 4: Batch Updates--\u003E\u003Cdiv class=\"test-section\"\u003E\u003Ch2\u003ETest 4: Batch Updates\u003C\u002Fh2\u003E\u003Cdiv class=\"text-muted\"\u003ETests batching multiple updates into a single render\u003C\u002Fdiv\u003E"; +pug_mixins["scope"].call({ +block: function(){ +pug_html = pug_html + "\u003Cdiv class=\"output\"\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003EName: \u003C\u002Fspan\u003E\u003Cspan ld=\"name\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003EAge: \u003C\u002Fspan\u003E\u003Cspan ld=\"age\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003EEmail: \u003C\u002Fspan\u003E\u003Cspan ld=\"email\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003EBatch render count: \u003C\u002Fspan\u003E\u003Cspan ld=\"batchCount\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003C\u002Fdiv\u003E\u003Cbutton class=\"btn btn-primary\" ld=\"updateAll\"\u003EUpdate All (Batch)\u003C\u002Fbutton\u003E\u003Cbutton class=\"btn btn-secondary\" ld=\"updateSeparate\"\u003EUpdate Separately\u003C\u002Fbutton\u003E"; +} +}, "test4"); +pug_html = pug_html + "\u003C\u002Fdiv\u003E\u003C!-- Test 5: Manual Render Control--\u003E\u003Cdiv class=\"test-section\"\u003E\u003Ch2\u003ETest 5: Manual Render Control (reactive: false)\u003C\u002Fh2\u003E\u003Cdiv class=\"text-muted\"\u003ETests disabling automatic reactive updates for specific handlers\u003C\u002Fdiv\u003E"; +pug_mixins["scope"].call({ +block: function(){ +pug_html = pug_html + "\u003Cdiv class=\"output\"\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003EAuto Value: \u003C\u002Fspan\u003E\u003Cspan ld=\"autoValue\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003EManual Value: \u003C\u002Fspan\u003E\u003Cspan ld=\"manualValue\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003EAuto render count: \u003C\u002Fspan\u003E\u003Cspan ld=\"autoCount\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003EManual render count: \u003C\u002Fspan\u003E\u003Cspan ld=\"manualCount\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003C\u002Fdiv\u003E\u003Cbutton class=\"btn btn-primary\" ld=\"changeValue\"\u003EChange Value\u003C\u002Fbutton\u003E\u003Cbutton class=\"btn btn-warning\" ld=\"manualRender\"\u003EManual Render\u003C\u002Fbutton\u003E"; +} +}, "test5"); +pug_html = pug_html + "\u003C\u002Fdiv\u003E\u003C\u002Fdiv\u003E"; +pug_mixins["script"]("/assets/lib/@loadingio/ldquery/main/index.min.js"); +pug_mixins["script"]("/assets/lib/ldview/dev/ldreactive.js"); +pug_mixins["script"]("/assets/lib/ldview/dev/index.js"); +pug_html = pug_html + "\u003Cscript type=\"module\"\u003E(function(){var n,r,o;console.log(\"Test 1: Basic Counter\");n=new ldreactive({count:0});r=0;o=new ldview({ctx:n,root:\"[ld-scope=test1]\",handler:{count:function(n){var t,e;t=n.node,e=n.ctx;r++;return t.textContent=e.count},renderCount:function(n){var t;t=n.node;return t.textContent=r},increment:function(n){var t,e;t=n.node,e=n.ctx;return t.onclick=function(){e.count++;return o.render(\"renderCount\")}},decrement:function(n){var t,e;t=n.node,e=n.ctx;return t.onclick=function(){e.count--;return o.render(\"renderCount\")}},reset:function(n){var t,e;t=n.node,e=n.ctx;return t.onclick=function(){e.count=0;return o.render(\"renderCount\")}}}});return console.log(\"Test 1 initialized\")})();(function(){var n,t;console.log(\"Test 2: Nested Object Tracking\");n=new ldreactive({address:{street:\"123 Main St\",city:\"New York\",country:\"USA\"}});t=new ldview({ctx:n,root:\"[ld-scope=test2]\",handler:{street:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.address.street},city:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.address.city},country:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.address.country},fullAddress:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.address.street+\", \"+e.address.city+\", \"+e.address.country},changeStreet:function(n){var t,e,r;t=n.node,e=n.ctx;r=[\"456 Oak Ave\",\"789 Elm St\",\"321 Pine Rd\",\"654 Maple Dr\"];return t.onclick=function(){return e.address.street=r[Math.floor(Math.random()*r.length)]}},changeCity:function(n){var t,e,r;t=n.node,e=n.ctx;r=[\"Los Angeles\",\"Chicago\",\"Houston\",\"Phoenix\"];return t.onclick=function(){return e.address.city=r[Math.floor(Math.random()*r.length)]}},changeCountry:function(n){var t,e,r;t=n.node,e=n.ctx;r=[\"USA\",\"Canada\",\"UK\",\"Australia\"];return t.onclick=function(){return e.address.country=r[Math.floor(Math.random()*r.length)]}}}});return console.log(\"Test 2 initialized\")})();(function(){var r,n,t;console.log(\"Test 3: Array\u002FList Tracking\");r=0;n=new ldreactive({items:[]});t=new ldview({ctx:n,root:\"[ld-scope=test3]\",handler:{count:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.items.length},item:{list:function(n){var t;t=n.ctx;return t.items},view:{text:{text:function(n){var t;t=n.ctx;return t.text}}}},addItem:function(n){var t,e;t=n.node,e=n.ctx;return t.onclick=function(){r++;return e.items.push({text:\"Item #\"+r})}},removeItem:function(n){var t,e;t=n.node,e=n.ctx;return t.onclick=function(){if(e.items.length\u003E0){return e.items.pop()}}}}});return console.log(\"Test 3 initialized\")})();(function(){var e,r,o;console.log(\"Test 4: Batch Updates\");e=new ldreactive({name:\"John\",age:25,email:\"john@example.com\"});r=0;e.on(\"change\",function(n,t,e){return r++});o=new ldview({ctx:e,root:\"[ld-scope=test4]\",handler:{name:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.name},age:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.age},email:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.email},batchCount:function(n){var t;t=n.node;return t.textContent=r},updateAll:function(n){var t;t=n.node;return t.onclick=function(){var n,t;n=t;e.batch(function(){var n;n=e.get();n.name=\"Jane\";n.age=30;return n.email=\"jane@example.com\"});t=n+1;return o.render(\"batchCount\")}},updateSeparate:function(n){var t;t=n.node;return t.onclick=function(){var n;n=e.get();n.name=\"Bob\";n.age=35;n.email=\"bob@example.com\";return o.render(\"batchCount\")}}}});return console.log(\"Test 4 initialized\")})();(function(){var e,r,o,c;console.log(\"Test 5: Manual Render Control\");e=new ldreactive({value:100});r=0;o=0;c=new ldview({ctx:e,root:\"[ld-scope=test5]\",handler:{autoValue:function(n){var t,e;t=n.node,e=n.ctx;r++;return t.textContent=e.value},manualValue:{reactive:false,handler:function(n){var t,e;t=n.node,e=n.ctx;o++;return t.textContent=e.value}},autoCount:function(n){var t;t=n.node;return t.textContent=r},manualCount:function(n){var t;t=n.node;return t.textContent=o},changeValue:function(n){var t;t=n.node;return t.onclick=function(){var n;n=e.get();n.value=Math.floor(Math.random()*1e3);return c.render(\"autoCount\")}},manualRender:function(n){var t;t=n.node;return t.onclick=function(){c.render(\"manualValue\");return c.render(\"manualCount\")}}}});return console.log(\"Test 5 initialized\")})();console.log(\"All reactive integration tests initialized\");\u003C\u002Fscript\u003E\u003C\u002Fbody\u003E\u003C\u002Fhtml\u003E"; + }.call(this, "Array" in locals_for_with ? + locals_for_with.Array : + typeof Array !== 'undefined' ? Array : undefined, "JSON" in locals_for_with ? + locals_for_with.JSON : + typeof JSON !== 'undefined' ? JSON : undefined, "b64img" in locals_for_with ? + locals_for_with.b64img : + typeof b64img !== 'undefined' ? b64img : undefined, "blockLoader" in locals_for_with ? + locals_for_with.blockLoader : + typeof blockLoader !== 'undefined' ? blockLoader : undefined, "c" in locals_for_with ? + locals_for_with.c : + typeof c !== 'undefined' ? c : undefined, "cssLoader" in locals_for_with ? + locals_for_with.cssLoader : + typeof cssLoader !== 'undefined' ? cssLoader : undefined, "decache" in locals_for_with ? + locals_for_with.decache : + typeof decache !== 'undefined' ? decache : undefined, "defer" in locals_for_with ? + locals_for_with.defer : + typeof defer !== 'undefined' ? defer : undefined, "escape" in locals_for_with ? + locals_for_with.escape : + typeof escape !== 'undefined' ? escape : undefined, "hashfile" in locals_for_with ? + locals_for_with.hashfile : + typeof hashfile !== 'undefined' ? hashfile : undefined, "libLoader" in locals_for_with ? + locals_for_with.libLoader : + typeof libLoader !== 'undefined' ? libLoader : undefined, "md5" in locals_for_with ? + locals_for_with.md5 : + typeof md5 !== 'undefined' ? md5 : undefined, "prefix" in locals_for_with ? + locals_for_with.prefix : + typeof prefix !== 'undefined' ? prefix : undefined, "scriptLoader" in locals_for_with ? + locals_for_with.scriptLoader : + typeof scriptLoader !== 'undefined' ? scriptLoader : undefined, "url" in locals_for_with ? + locals_for_with.url : + typeof url !== 'undefined' ? url : undefined, "version" in locals_for_with ? + locals_for_with.version : + typeof version !== 'undefined' ? version : undefined)); + ;;return pug_html;}; module.exports = template; })() \ No newline at end of file diff --git a/web/.view/scope/index.js b/web/.view/scope/index.js index 1041d5a..24c343f 100644 --- a/web/.view/scope/index.js +++ b/web/.view/scope/index.js @@ -10,7 +10,7 @@ function pug_merge(e,r){if(1===arguments.length){for(var t=e[0],g=1;g + console.log "Test 1: Basic Counter" + + # Create reactive state + state = new ldreactive({count: 0}) + renderCount = 0 + + view = new ldview do + ctx: state + root: '[ld-scope=test1]' + handler: + count: ({node, ctx}) -> + renderCount++ + node.textContent = ctx.count + renderCount: ({node}) -> + node.textContent = renderCount + increment: ({node, ctx}) -> + node.onclick = -> + ctx.count++ + view.render \renderCount + decrement: ({node, ctx}) -> + node.onclick = -> + ctx.count-- + view.render \renderCount + reset: ({node, ctx}) -> + node.onclick = -> + ctx.count = 0 + view.render \renderCount + + console.log "Test 1 initialized" +)! + +# Test 2: Nested Object Tracking +(-> + console.log "Test 2: Nested Object Tracking" + + # Create reactive state with nested objects + state = new ldreactive do + address: + street: "123 Main St" + city: "New York" + country: "USA" + + view = new ldview do + ctx: state + root: '[ld-scope=test2]' + handler: + street: ({node, ctx}) -> + node.textContent = ctx.address.street + city: ({node, ctx}) -> + node.textContent = ctx.address.city + country: ({node, ctx}) -> + node.textContent = ctx.address.country + fullAddress: ({node, ctx}) -> + # This should track all three nested properties + node.textContent = "#{ctx.address.street}, #{ctx.address.city}, #{ctx.address.country}" + changeStreet: ({node, ctx}) -> + streets = ["456 Oak Ave", "789 Elm St", "321 Pine Rd", "654 Maple Dr"] + node.onclick = -> + ctx.address.street = streets[Math.floor(Math.random! * streets.length)] + changeCity: ({node, ctx}) -> + cities = ["Los Angeles", "Chicago", "Houston", "Phoenix"] + node.onclick = -> + ctx.address.city = cities[Math.floor(Math.random! * cities.length)] + changeCountry: ({node, ctx}) -> + countries = ["USA", "Canada", "UK", "Australia"] + node.onclick = -> + ctx.address.country = countries[Math.floor(Math.random! * countries.length)] + + console.log "Test 2 initialized" +)! + +# Test 3: Array/List Tracking +(-> + console.log "Test 3: Array/List Tracking" + + # Create reactive state with array + itemCounter = 0 + state = new ldreactive do + items: [] + + view = new ldview do + ctx: state + root: '[ld-scope=test3]' + handler: + count: ({node, ctx}) -> + node.textContent = ctx.items.length + item: + list: ({ctx}) -> ctx.items + view: + text: + text: ({ctx}) -> ctx.text + addItem: ({node, ctx}) -> + node.onclick = -> + itemCounter++ + ctx.items.push {text: "Item ##{itemCounter}"} + removeItem: ({node, ctx}) -> + node.onclick = -> + if ctx.items.length > 0 + ctx.items.pop! + + console.log "Test 3 initialized" +)! + +# Test 4: Batch Updates +(-> + console.log "Test 4: Batch Updates" + + # Create reactive state + state = new ldreactive do + name: "John" + age: 25 + email: "john@example.com" + + batchRenderCount = 0 + + # Listen to change events to count renders + state.on 'change', (key, value, oldValue) -> + batchRenderCount++ + + view = new ldview do + ctx: state + root: '[ld-scope=test4]' + handler: + name: ({node, ctx}) -> + node.textContent = ctx.name + age: ({node, ctx}) -> + node.textContent = ctx.age + email: ({node, ctx}) -> + node.textContent = ctx.email + batchCount: ({node}) -> + node.textContent = batchRenderCount + updateAll: ({node}) -> + node.onclick = -> + # Use batch to update all at once + prevCount = batchRenderCount + state.batch -> + data = state.get! + data.name = "Jane" + data.age = 30 + data.email = "jane@example.com" + # In batch mode, only batch-change event is fired, not individual change events + # So we manually increment for demonstration + batchRenderCount = prevCount + 1 + view.render \batchCount + updateSeparate: ({node}) -> + node.onclick = -> + # Update separately (not batched) + data = state.get! + data.name = "Bob" + data.age = 35 + data.email = "bob@example.com" + view.render \batchCount + + console.log "Test 4 initialized" +)! + +# Test 5: Manual Render Control (reactive: false) +(-> + console.log "Test 5: Manual Render Control" + + # Create reactive state + state = new ldreactive do + value: 100 + + autoRenderCount = 0 + manualRenderCount = 0 + + view = new ldview do + ctx: state + root: '[ld-scope=test5]' + handler: + autoValue: ({node, ctx}) -> + autoRenderCount++ + node.textContent = ctx.value + manualValue: + reactive: false # Disable automatic reactive updates + handler: ({node, ctx}) -> + manualRenderCount++ + # With reactive: false, this handler won't be called automatically + # when ctx.value changes, but we can still read the current value + node.textContent = ctx.value + autoCount: ({node}) -> + node.textContent = autoRenderCount + manualCount: ({node}) -> + node.textContent = manualRenderCount + changeValue: ({node}) -> + node.onclick = -> + data = state.get! + data.value = Math.floor(Math.random! * 1000) + # Auto handler will update automatically + # Manual handler won't update + view.render \autoCount + manualRender: ({node}) -> + node.onclick = -> + # Manually trigger render for the manual handler + view.render \manualValue + view.render \manualCount + + console.log "Test 5 initialized" +)! + +console.log "All reactive integration tests initialized" diff --git a/web/src/pug/reactive/index.pug b/web/src/pug/reactive/index.pug new file mode 100644 index 0000000..c45002b --- /dev/null +++ b/web/src/pug/reactive/index.pug @@ -0,0 +1,143 @@ +doctype html +html + include @/@loadingio/bootstrap.ext/index.pug + include @static/assets/lib/ldview/dev/index.pug + head + +css("/assets/lib/bootstrap/main/dist/css/bootstrap.min.css") + +css("/assets/lib/@loadingio/bootstrap.ext/main/index.min.css") + style(type="text/css"). + .test-section { + margin: 30px 0; + padding: 20px; + border: 2px solid #007bff; + border-radius: 8px; + } + .test-section h2 { + color: #007bff; + margin-top: 0; + } + .output { + margin: 15px 0; + padding: 15px; + background: #f8f9fa; + border-radius: 4px; + font-family: monospace; + } + .label { + font-weight: bold; + color: #666; + } + button { + margin: 5px; + } + .item { + padding: 10px; + margin: 5px 0; + background: #e9ecef; + border-radius: 4px; + } + + body + .w-1024.rwd.mx-auto.typeset.heading-contrast.my-4 + h1 ldreactive + ldview Integration Tests + .text-muted Test integration between ldreactive.ls and ldview.ls with automatic reactive updates + hr + + // Test 1: Basic Counter - Property Tracking + .test-section + h2 Test 1: Basic Counter (Property Tracking) + .text-muted Tests basic reactive property tracking and automatic updates + +scope("test1") + .output + div + span.label Count: + span(ld="count") + div + span.label Render count: + span(ld="renderCount") + button.btn.btn-primary(ld="increment") Increment + button.btn.btn-secondary(ld="decrement") Decrement + button.btn.btn-warning(ld="reset") Reset + + // Test 2: Nested Object Tracking + .test-section + h2 Test 2: Nested Object Tracking + .text-muted Tests deep property tracking in nested objects + +scope("test2") + .output + div + span.label Street: + span(ld="street") + div + span.label City: + span(ld="city") + div + span.label Country: + span(ld="country") + div + span.label Full Address: + span(ld="fullAddress") + button.btn.btn-primary(ld="changeStreet") Change Street + button.btn.btn-primary(ld="changeCity") Change City + button.btn.btn-primary(ld="changeCountry") Change Country + + // Test 3: Array/List Tracking + .test-section + h2 Test 3: Array/List Tracking + .text-muted Tests array operations and list rendering + +scope("test3") + .output + div + span.label Total Items: + span(ld="count") + div.mt-3(ld-each="item") + .item(ld="text") + button.btn.btn-success(ld="addItem") Add Item + button.btn.btn-danger(ld="removeItem") Remove Last + + // Test 4: Batch Updates + .test-section + h2 Test 4: Batch Updates + .text-muted Tests batching multiple updates into a single render + +scope("test4") + .output + div + span.label Name: + span(ld="name") + div + span.label Age: + span(ld="age") + div + span.label Email: + span(ld="email") + div + span.label Batch render count: + span(ld="batchCount") + button.btn.btn-primary(ld="updateAll") Update All (Batch) + button.btn.btn-secondary(ld="updateSeparate") Update Separately + + // Test 5: Manual Render Control + .test-section + h2 Test 5: Manual Render Control (reactive: false) + .text-muted Tests disabling automatic reactive updates for specific handlers + +scope("test5") + .output + div + span.label Auto Value: + span(ld="autoValue") + div + span.label Manual Value: + span(ld="manualValue") + div + span.label Auto render count: + span(ld="autoCount") + div + span.label Manual render count: + span(ld="manualCount") + button.btn.btn-primary(ld="changeValue") Change Value + button.btn.btn-warning(ld="manualRender") Manual Render + + +script("/assets/lib/@loadingio/ldquery/main/index.min.js") + +script("/assets/lib/ldview/dev/ldreactive.js") + +script("/assets/lib/ldview/dev/index.js") + script(type="module"): include:lsc index.ls diff --git a/web/static/reactive/index.html b/web/static/reactive/index.html new file mode 100644 index 0000000..c88b530 --- /dev/null +++ b/web/static/reactive/index.html @@ -0,0 +1,31 @@ +

ldreactive + ldview Integration Tests

Test integration between ldreactive.ls and ldview.ls with automatic reactive updates

Test 1: Basic Counter (Property Tracking)

Tests basic reactive property tracking and automatic updates
Count:
Render count:

Test 2: Nested Object Tracking

Tests deep property tracking in nested objects
Street:
City:
Country:
Full Address:

Test 3: Array/List Tracking

Tests array operations and list rendering
Total Items:

Test 4: Batch Updates

Tests batching multiple updates into a single render
Name:
Age:
Email:
Batch render count:

Test 5: Manual Render Control (reactive: false)

Tests disabling automatic reactive updates for specific handlers
Auto Value:
Manual Value:
Auto render count:
Manual render count:
\ No newline at end of file From 96ab8133cdded84081139b18aa1ebf8935367feb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 01:43:34 +0000 Subject: [PATCH 3/3] Fix Test 3 to show array tracking with JSON display, simplify Test 4 Co-authored-by: zbryikt <2672307+zbryikt@users.noreply.github.com> --- web/.view/reactive/index.js | 6 +++--- web/src/pug/reactive/index.ls | 30 +++++++++++++----------------- web/src/pug/reactive/index.pug | 8 +++++--- web/static/reactive/index.html | 2 +- 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/web/.view/reactive/index.js b/web/.view/reactive/index.js index 9329542..6b21bcc 100644 --- a/web/.view/reactive/index.js +++ b/web/.view/reactive/index.js @@ -257,10 +257,10 @@ block: function(){ pug_html = pug_html + "\u003Cdiv class=\"output\"\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003EStreet: \u003C\u002Fspan\u003E\u003Cspan ld=\"street\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003ECity: \u003C\u002Fspan\u003E\u003Cspan ld=\"city\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003ECountry: \u003C\u002Fspan\u003E\u003Cspan ld=\"country\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003EFull Address: \u003C\u002Fspan\u003E\u003Cspan ld=\"fullAddress\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003C\u002Fdiv\u003E\u003Cbutton class=\"btn btn-primary\" ld=\"changeStreet\"\u003EChange Street\u003C\u002Fbutton\u003E\u003Cbutton class=\"btn btn-primary\" ld=\"changeCity\"\u003EChange City\u003C\u002Fbutton\u003E\u003Cbutton class=\"btn btn-primary\" ld=\"changeCountry\"\u003EChange Country\u003C\u002Fbutton\u003E"; } }, "test2"); -pug_html = pug_html + "\u003C\u002Fdiv\u003E\u003C!-- Test 3: Array\u002FList Tracking--\u003E\u003Cdiv class=\"test-section\"\u003E\u003Ch2\u003ETest 3: Array\u002FList Tracking\u003C\u002Fh2\u003E\u003Cdiv class=\"text-muted\"\u003ETests array operations and list rendering\u003C\u002Fdiv\u003E"; +pug_html = pug_html + "\u003C\u002Fdiv\u003E\u003C!-- Test 3: Array\u002FList Tracking--\u003E\u003Cdiv class=\"test-section\"\u003E\u003Ch2\u003ETest 3: Array\u002FList Tracking\u003C\u002Fh2\u003E\u003Cdiv class=\"text-muted\"\u003ETests array operations and reactive length tracking\u003C\u002Fdiv\u003E"; pug_mixins["scope"].call({ block: function(){ -pug_html = pug_html + "\u003Cdiv class=\"output\"\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003ETotal Items: \u003C\u002Fspan\u003E\u003Cspan ld=\"count\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003Cdiv class=\"mt-3\" ld-each=\"item\"\u003E\u003Cdiv class=\"item\" ld=\"text\"\u003E\u003C\u002Fdiv\u003E\u003C\u002Fdiv\u003E\u003C\u002Fdiv\u003E\u003Cbutton class=\"btn btn-success\" ld=\"addItem\"\u003EAdd Item\u003C\u002Fbutton\u003E\u003Cbutton class=\"btn btn-danger\" ld=\"removeItem\"\u003ERemove Last\u003C\u002Fbutton\u003E"; +pug_html = pug_html + "\u003Cdiv class=\"output\"\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003ETotal Items: \u003C\u002Fspan\u003E\u003Cspan ld=\"count\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003Cdiv\u003E\u003Cspan class=\"label\"\u003EItems as JSON: \u003C\u002Fspan\u003E\u003Cspan ld=\"itemsList\"\u003E\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E\u003C\u002Fdiv\u003E\u003Cbutton class=\"btn btn-success\" ld=\"addItem\"\u003EAdd Item\u003C\u002Fbutton\u003E\u003Cbutton class=\"btn btn-danger\" ld=\"removeItem\"\u003ERemove Last\u003C\u002Fbutton\u003E\u003Cbutton class=\"btn btn-info\" ld=\"clearAll\"\u003EClear All\u003C\u002Fbutton\u003E"; } }, "test3"); pug_html = pug_html + "\u003C\u002Fdiv\u003E\u003C!-- Test 4: Batch Updates--\u003E\u003Cdiv class=\"test-section\"\u003E\u003Ch2\u003ETest 4: Batch Updates\u003C\u002Fh2\u003E\u003Cdiv class=\"text-muted\"\u003ETests batching multiple updates into a single render\u003C\u002Fdiv\u003E"; @@ -279,7 +279,7 @@ pug_html = pug_html + "\u003C\u002Fdiv\u003E\u003C\u002Fdiv\u003E"; pug_mixins["script"]("/assets/lib/@loadingio/ldquery/main/index.min.js"); pug_mixins["script"]("/assets/lib/ldview/dev/ldreactive.js"); pug_mixins["script"]("/assets/lib/ldview/dev/index.js"); -pug_html = pug_html + "\u003Cscript type=\"module\"\u003E(function(){var n,r,o;console.log(\"Test 1: Basic Counter\");n=new ldreactive({count:0});r=0;o=new ldview({ctx:n,root:\"[ld-scope=test1]\",handler:{count:function(n){var t,e;t=n.node,e=n.ctx;r++;return t.textContent=e.count},renderCount:function(n){var t;t=n.node;return t.textContent=r},increment:function(n){var t,e;t=n.node,e=n.ctx;return t.onclick=function(){e.count++;return o.render(\"renderCount\")}},decrement:function(n){var t,e;t=n.node,e=n.ctx;return t.onclick=function(){e.count--;return o.render(\"renderCount\")}},reset:function(n){var t,e;t=n.node,e=n.ctx;return t.onclick=function(){e.count=0;return o.render(\"renderCount\")}}}});return console.log(\"Test 1 initialized\")})();(function(){var n,t;console.log(\"Test 2: Nested Object Tracking\");n=new ldreactive({address:{street:\"123 Main St\",city:\"New York\",country:\"USA\"}});t=new ldview({ctx:n,root:\"[ld-scope=test2]\",handler:{street:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.address.street},city:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.address.city},country:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.address.country},fullAddress:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.address.street+\", \"+e.address.city+\", \"+e.address.country},changeStreet:function(n){var t,e,r;t=n.node,e=n.ctx;r=[\"456 Oak Ave\",\"789 Elm St\",\"321 Pine Rd\",\"654 Maple Dr\"];return t.onclick=function(){return e.address.street=r[Math.floor(Math.random()*r.length)]}},changeCity:function(n){var t,e,r;t=n.node,e=n.ctx;r=[\"Los Angeles\",\"Chicago\",\"Houston\",\"Phoenix\"];return t.onclick=function(){return e.address.city=r[Math.floor(Math.random()*r.length)]}},changeCountry:function(n){var t,e,r;t=n.node,e=n.ctx;r=[\"USA\",\"Canada\",\"UK\",\"Australia\"];return t.onclick=function(){return e.address.country=r[Math.floor(Math.random()*r.length)]}}}});return console.log(\"Test 2 initialized\")})();(function(){var r,n,t;console.log(\"Test 3: Array\u002FList Tracking\");r=0;n=new ldreactive({items:[]});t=new ldview({ctx:n,root:\"[ld-scope=test3]\",handler:{count:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.items.length},item:{list:function(n){var t;t=n.ctx;return t.items},view:{text:{text:function(n){var t;t=n.ctx;return t.text}}}},addItem:function(n){var t,e;t=n.node,e=n.ctx;return t.onclick=function(){r++;return e.items.push({text:\"Item #\"+r})}},removeItem:function(n){var t,e;t=n.node,e=n.ctx;return t.onclick=function(){if(e.items.length\u003E0){return e.items.pop()}}}}});return console.log(\"Test 3 initialized\")})();(function(){var e,r,o;console.log(\"Test 4: Batch Updates\");e=new ldreactive({name:\"John\",age:25,email:\"john@example.com\"});r=0;e.on(\"change\",function(n,t,e){return r++});o=new ldview({ctx:e,root:\"[ld-scope=test4]\",handler:{name:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.name},age:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.age},email:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.email},batchCount:function(n){var t;t=n.node;return t.textContent=r},updateAll:function(n){var t;t=n.node;return t.onclick=function(){var n,t;n=t;e.batch(function(){var n;n=e.get();n.name=\"Jane\";n.age=30;return n.email=\"jane@example.com\"});t=n+1;return o.render(\"batchCount\")}},updateSeparate:function(n){var t;t=n.node;return t.onclick=function(){var n;n=e.get();n.name=\"Bob\";n.age=35;n.email=\"bob@example.com\";return o.render(\"batchCount\")}}}});return console.log(\"Test 4 initialized\")})();(function(){var e,r,o,c;console.log(\"Test 5: Manual Render Control\");e=new ldreactive({value:100});r=0;o=0;c=new ldview({ctx:e,root:\"[ld-scope=test5]\",handler:{autoValue:function(n){var t,e;t=n.node,e=n.ctx;r++;return t.textContent=e.value},manualValue:{reactive:false,handler:function(n){var t,e;t=n.node,e=n.ctx;o++;return t.textContent=e.value}},autoCount:function(n){var t;t=n.node;return t.textContent=r},manualCount:function(n){var t;t=n.node;return t.textContent=o},changeValue:function(n){var t;t=n.node;return t.onclick=function(){var n;n=e.get();n.value=Math.floor(Math.random()*1e3);return c.render(\"autoCount\")}},manualRender:function(n){var t;t=n.node;return t.onclick=function(){c.render(\"manualValue\");return c.render(\"manualCount\")}}}});return console.log(\"Test 5 initialized\")})();console.log(\"All reactive integration tests initialized\");\u003C\u002Fscript\u003E\u003C\u002Fbody\u003E\u003C\u002Fhtml\u003E"; +pug_html = pug_html + "\u003Cscript type=\"module\"\u003E(function(){var n,r,o;console.log(\"Test 1: Basic Counter\");n=new ldreactive({count:0});r=0;o=new ldview({ctx:n,root:\"[ld-scope=test1]\",handler:{count:function(n){var t,e;t=n.node,e=n.ctx;r++;return t.textContent=e.count},renderCount:function(n){var t;t=n.node;return t.textContent=r},increment:function(n){var t,e;t=n.node,e=n.ctx;return t.onclick=function(){e.count++;return o.render(\"renderCount\")}},decrement:function(n){var t,e;t=n.node,e=n.ctx;return t.onclick=function(){e.count--;return o.render(\"renderCount\")}},reset:function(n){var t,e;t=n.node,e=n.ctx;return t.onclick=function(){e.count=0;return o.render(\"renderCount\")}}}});return console.log(\"Test 1 initialized\")})();(function(){var n,t;console.log(\"Test 2: Nested Object Tracking\");n=new ldreactive({address:{street:\"123 Main St\",city:\"New York\",country:\"USA\"}});t=new ldview({ctx:n,root:\"[ld-scope=test2]\",handler:{street:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.address.street},city:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.address.city},country:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.address.country},fullAddress:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.address.street+\", \"+e.address.city+\", \"+e.address.country},changeStreet:function(n){var t,e,r;t=n.node,e=n.ctx;r=[\"456 Oak Ave\",\"789 Elm St\",\"321 Pine Rd\",\"654 Maple Dr\"];return t.onclick=function(){return e.address.street=r[Math.floor(Math.random()*r.length)]}},changeCity:function(n){var t,e,r;t=n.node,e=n.ctx;r=[\"Los Angeles\",\"Chicago\",\"Houston\",\"Phoenix\"];return t.onclick=function(){return e.address.city=r[Math.floor(Math.random()*r.length)]}},changeCountry:function(n){var t,e,r;t=n.node,e=n.ctx;r=[\"USA\",\"Canada\",\"UK\",\"Australia\"];return t.onclick=function(){return e.address.country=r[Math.floor(Math.random()*r.length)]}}}});return console.log(\"Test 2 initialized\")})();(function(){var r,n,t;console.log(\"Test 3: Array\u002FList Tracking\");r=0;n=new ldreactive({items:[]});t=new ldview({ctx:n,root:\"[ld-scope=test3]\",handler:{count:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.items.length},itemsList:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=JSON.stringify(e.items)},addItem:function(n){var t,e;t=n.node,e=n.ctx;return t.onclick=function(){r++;return e.items.push({id:r,text:\"Item #\"+r})}},removeItem:function(n){var t,e;t=n.node,e=n.ctx;return t.onclick=function(){if(e.items.length\u003E0){return e.items.pop()}}},clearAll:function(n){var t,e;t=n.node,e=n.ctx;return t.onclick=function(){return e.items=[]}}}});return console.log(\"Test 3 initialized\")})();(function(){var e,r,o;console.log(\"Test 4: Batch Updates\");e=new ldreactive({name:\"John\",age:25,email:\"john@example.com\"});r=0;o=new ldview({ctx:e,root:\"[ld-scope=test4]\",handler:{name:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.name},age:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.age},email:function(n){var t,e;t=n.node,e=n.ctx;return t.textContent=e.email},batchCount:function(n){var t;t=n.node;return t.textContent=r},updateAll:function(n){var t;t=n.node;return t.onclick=function(){r++;e.batch(function(){var n;n=e.get();n.name=\"Jane\";n.age=30;return n.email=\"jane@example.com\"});return o.render(\"batchCount\")}},updateSeparate:function(n){var t;t=n.node;return t.onclick=function(){var n;r++;n=e.get();n.name=\"Bob\";n.age=35;n.email=\"bob@example.com\";return o.render(\"batchCount\")}}}});return console.log(\"Test 4 initialized\")})();(function(){var e,r,o,c;console.log(\"Test 5: Manual Render Control\");e=new ldreactive({value:100});r=0;o=0;c=new ldview({ctx:e,root:\"[ld-scope=test5]\",handler:{autoValue:function(n){var t,e;t=n.node,e=n.ctx;r++;return t.textContent=e.value},manualValue:{reactive:false,handler:function(n){var t,e;t=n.node,e=n.ctx;o++;return t.textContent=e.value}},autoCount:function(n){var t;t=n.node;return t.textContent=r},manualCount:function(n){var t;t=n.node;return t.textContent=o},changeValue:function(n){var t;t=n.node;return t.onclick=function(){var n;n=e.get();n.value=Math.floor(Math.random()*1e3);return c.render(\"autoCount\")}},manualRender:function(n){var t;t=n.node;return t.onclick=function(){c.render(\"manualValue\");return c.render(\"manualCount\")}}}});return console.log(\"Test 5 initialized\")})();console.log(\"All reactive integration tests initialized\");\u003C\u002Fscript\u003E\u003C\u002Fbody\u003E\u003C\u002Fhtml\u003E"; }.call(this, "Array" in locals_for_with ? locals_for_with.Array : typeof Array !== 'undefined' ? Array : undefined, "JSON" in locals_for_with ? diff --git a/web/src/pug/reactive/index.ls b/web/src/pug/reactive/index.ls index 0f7b2c8..5abde3c 100644 --- a/web/src/pug/reactive/index.ls +++ b/web/src/pug/reactive/index.ls @@ -86,19 +86,21 @@ handler: count: ({node, ctx}) -> node.textContent = ctx.items.length - item: - list: ({ctx}) -> ctx.items - view: - text: - text: ({ctx}) -> ctx.text + itemsList: ({node, ctx}) -> + # Show items as JSON string to demonstrate array tracking + node.textContent = JSON.stringify(ctx.items) addItem: ({node, ctx}) -> node.onclick = -> itemCounter++ - ctx.items.push {text: "Item ##{itemCounter}"} + ctx.items.push {id: itemCounter, text: "Item ##{itemCounter}"} removeItem: ({node, ctx}) -> node.onclick = -> if ctx.items.length > 0 ctx.items.pop! + clearAll: ({node, ctx}) -> + node.onclick = -> + # Test reassignment + ctx.items = [] console.log "Test 3 initialized" )! @@ -113,11 +115,7 @@ age: 25 email: "john@example.com" - batchRenderCount = 0 - - # Listen to change events to count renders - state.on 'change', (key, value, oldValue) -> - batchRenderCount++ + updateCounter = 0 view = new ldview do ctx: state @@ -130,23 +128,21 @@ email: ({node, ctx}) -> node.textContent = ctx.email batchCount: ({node}) -> - node.textContent = batchRenderCount + node.textContent = updateCounter updateAll: ({node}) -> node.onclick = -> # Use batch to update all at once - prevCount = batchRenderCount + updateCounter++ state.batch -> data = state.get! data.name = "Jane" data.age = 30 data.email = "jane@example.com" - # In batch mode, only batch-change event is fired, not individual change events - # So we manually increment for demonstration - batchRenderCount = prevCount + 1 view.render \batchCount updateSeparate: ({node}) -> node.onclick = -> - # Update separately (not batched) + # Update separately (not batched) - multiple updates + updateCounter++ data = state.get! data.name = "Bob" data.age = 35 diff --git a/web/src/pug/reactive/index.pug b/web/src/pug/reactive/index.pug index c45002b..9c9db44 100644 --- a/web/src/pug/reactive/index.pug +++ b/web/src/pug/reactive/index.pug @@ -84,16 +84,18 @@ html // Test 3: Array/List Tracking .test-section h2 Test 3: Array/List Tracking - .text-muted Tests array operations and list rendering + .text-muted Tests array operations and reactive length tracking +scope("test3") .output div span.label Total Items: span(ld="count") - div.mt-3(ld-each="item") - .item(ld="text") + div + span.label Items as JSON: + span(ld="itemsList") button.btn.btn-success(ld="addItem") Add Item button.btn.btn-danger(ld="removeItem") Remove Last + button.btn.btn-info(ld="clearAll") Clear All // Test 4: Batch Updates .test-section diff --git a/web/static/reactive/index.html b/web/static/reactive/index.html index c88b530..489156c 100644 --- a/web/static/reactive/index.html +++ b/web/static/reactive/index.html @@ -28,4 +28,4 @@ background: #e9ecef; border-radius: 4px; } -

ldreactive + ldview Integration Tests

Test integration between ldreactive.ls and ldview.ls with automatic reactive updates

Test 1: Basic Counter (Property Tracking)

Tests basic reactive property tracking and automatic updates
Count:
Render count:

Test 2: Nested Object Tracking

Tests deep property tracking in nested objects
Street:
City:
Country:
Full Address:

Test 3: Array/List Tracking

Tests array operations and list rendering
Total Items:

Test 4: Batch Updates

Tests batching multiple updates into a single render
Name:
Age:
Email:
Batch render count:

Test 5: Manual Render Control (reactive: false)

Tests disabling automatic reactive updates for specific handlers
Auto Value:
Manual Value:
Auto render count:
Manual render count:
\ No newline at end of file +

ldreactive + ldview Integration Tests

Test integration between ldreactive.ls and ldview.ls with automatic reactive updates

Test 1: Basic Counter (Property Tracking)

Tests basic reactive property tracking and automatic updates
Count:
Render count:

Test 2: Nested Object Tracking

Tests deep property tracking in nested objects
Street:
City:
Country:
Full Address:

Test 3: Array/List Tracking

Tests array operations and reactive length tracking
Total Items:
Items as JSON:

Test 4: Batch Updates

Tests batching multiple updates into a single render
Name:
Age:
Email:
Batch render count:

Test 5: Manual Render Control (reactive: false)

Tests disabling automatic reactive updates for specific handlers
Auto Value:
Manual Value:
Auto render count:
Manual render count:
\ No newline at end of file