rip-lang 3.13.23 → 3.13.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,9 +9,9 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.13.23-blue.svg" alt="Version"></a>
12
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.13.25-blue.svg" alt="Version"></a>
13
13
  <a href="#zero-dependencies"><img src="https://img.shields.io/badge/dependencies-ZERO-brightgreen.svg" alt="Dependencies"></a>
14
- <a href="#"><img src="https://img.shields.io/badge/tests-1%2C265%2F1%2C265-brightgreen.svg" alt="Tests"></a>
14
+ <a href="#"><img src="https://img.shields.io/badge/tests-1%2C300%2F1%2C300-brightgreen.svg" alt="Tests"></a>
15
15
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
16
16
  </p>
17
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rip-lang",
3
- "version": "3.13.23",
3
+ "version": "3.13.25",
4
4
  "description": "A modern language that compiles to JavaScript",
5
5
  "type": "module",
6
6
  "main": "src/compiler.js",
package/src/compiler.js CHANGED
@@ -688,9 +688,9 @@ export class CodeGenerator {
688
688
 
689
689
  if (this.usesTemplates && !skip) {
690
690
  if (skipRT) {
691
- code += 'var { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __Component } = globalThis.__ripComponent;\n';
691
+ code += 'var { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __Component } = globalThis.__ripComponent;\n';
692
692
  } else if (typeof globalThis !== 'undefined' && globalThis.__ripComponent) {
693
- code += 'const { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __Component } = globalThis.__ripComponent;\n';
693
+ code += 'const { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __Component } = globalThis.__ripComponent;\n';
694
694
  } else {
695
695
  code += this.getComponentRuntime();
696
696
  }
package/src/components.js CHANGED
@@ -106,23 +106,6 @@ function isPublicProp(target) {
106
106
  return Array.isArray(target) && target[0] === '.' && target[1] === 'this';
107
107
  }
108
108
 
109
- /**
110
- * Detect fragment root and collect direct child variables for proper removal.
111
- * After insertBefore, a DocumentFragment is empty — .remove() is a no-op.
112
- * Callers must remove each child element individually.
113
- */
114
- function getFragChildren(rootVar, createLines, localizeVar) {
115
- const root = localizeVar(rootVar);
116
- if (!/_frag\d+$/.test(root)) return null;
117
- const children = [];
118
- const re = new RegExp(`^${root.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\.appendChild\\(([^)]+)\\);`);
119
- for (const line of createLines) {
120
- const m = localizeVar(line).match(re);
121
- if (m) children.push(m[1]);
122
- }
123
- return children.length > 0 ? children : null;
124
- }
125
-
126
109
  // ============================================================================
127
110
  // Prototype Installation
128
111
  // ============================================================================
@@ -398,16 +381,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
398
381
  // Implicit nesting (inject -> before INDENT)
399
382
  // ─────────────────────────────────────────────────────────────────────
400
383
  if (nextToken && nextToken[0] === 'INDENT') {
401
- // Skip fromThen INDENTs inside string interpolation (e.g. "#{if x then y else z}")
402
- if (nextToken.fromThen) {
403
- let depth = 0;
404
- for (let j = i; j >= 0; j--) {
405
- let jt = tokens[j][0];
406
- if (jt === 'INTERPOLATION_END' || jt === 'STRING_END') depth++;
407
- if (jt === 'INTERPOLATION_START' || jt === 'STRING_START') depth--;
408
- if (depth < 0) { return 1; }
409
- }
410
- }
384
+ // fromThen INDENTs are inline conditional values (if x then y else z),
385
+ // never template nesting — normalizeLines only creates them for single-line then clauses
386
+ if (nextToken.fromThen) return 1;
411
387
  if (tag === '->' || tag === '=>' || tag === 'CALL_START' || tag === '(') {
412
388
  return 1;
413
389
  }
@@ -437,31 +413,17 @@ export function installComponentSupport(CodeGenerator, Lexer) {
437
413
  isTemplateElement = true;
438
414
  } else if (tag === 'IDENTIFIER' && isTemplateTag(token[1]) && !isAfterControlFlow) {
439
415
  isTemplateElement = true;
440
- } else if (tag === 'PROPERTY' || tag === 'STRING' || tag === 'STRING_END' || tag === 'IDENTIFIER' || tag === 'NUMBER' || tag === 'BOOL' || tag === 'CALL_END' || tag === ')') {
416
+ } else if (tag === 'IDENTIFIER' && !isAfterControlFlow) {
417
+ isTemplateElement = startsWithTag(tokens, i);
418
+ } else if (tag === 'PROPERTY' || tag === 'STRING' || tag === 'STRING_END' || tag === 'NUMBER' || tag === 'BOOL' || tag === 'CALL_END' || tag === ')') {
441
419
  isTemplateElement = startsWithTag(tokens, i);
442
- }
443
- else if (tag === 'IDENTIFIER' && i > 1 && tokens[i - 1][0] === '...') {
444
- if (startsWithTag(tokens, i)) {
445
- let commaToken = gen(',', ',', token);
446
- let arrowToken = gen('->', '->', token);
447
- arrowToken.newLine = true;
448
- tokens.splice(i + 1, 0, commaToken, arrowToken);
449
- return 3;
450
- }
451
420
  }
452
421
 
453
422
  if (isTemplateElement) {
454
423
  let isClassOrIdTail = tag === 'PROPERTY' && i > 0 && (tokens[i - 1][0] === '.' || tokens[i - 1][0] === '#');
424
+ let isBareTag = isClsxCallEnd || (tag === 'IDENTIFIER' && isTemplateTag(token[1])) || isClassOrIdTail;
455
425
 
456
- if (isClsxCallEnd) {
457
- let callStartToken = gen('CALL_START', '(', token);
458
- let arrowToken = gen('->', '->', token);
459
- arrowToken.newLine = true;
460
- tokens.splice(i + 1, 0, callStartToken, arrowToken);
461
- pendingCallEnds.push(currentIndent + 1);
462
- return 3;
463
- } else if ((tag === 'IDENTIFIER' && isTemplateTag(token[1])) || isClassOrIdTail) {
464
- // Bare tag or tag.class/tag#id (no other args): inject CALL_START -> and manage CALL_END
426
+ if (isBareTag) {
465
427
  let callStartToken = gen('CALL_START', '(', token);
466
428
  let arrowToken = gen('->', '->', token);
467
429
  arrowToken.newLine = true;
@@ -469,7 +431,6 @@ export function installComponentSupport(CodeGenerator, Lexer) {
469
431
  pendingCallEnds.push(currentIndent + 1);
470
432
  return 3;
471
433
  } else {
472
- // Tag with args: inject , -> (call wrapping handled by addImplicitBracesAndParens)
473
434
  let commaToken = gen(',', ',', token);
474
435
  let arrowToken = gen('->', '->', token);
475
436
  arrowToken.newLine = true;
@@ -503,16 +464,6 @@ export function installComponentSupport(CodeGenerator, Lexer) {
503
464
  // Utilities
504
465
  // ==========================================================================
505
466
 
506
- /**
507
- * Localize variable references for block factories.
508
- * Converts this._elN to _elN and this.x to ctx.x.
509
- */
510
- proto.localizeVar = function(line) {
511
- let result = line.replace(/this\.(_el\d+|_t\d+|_anchor\d+|_frag\d+|_slot\d+|_c\d+|_inst\d+|_empty\d+)/g, '$1');
512
- result = result.replace(/\bthis\b/g, 'ctx');
513
- return result;
514
- };
515
-
516
467
  /**
517
468
  * Check if name is an HTML/SVG tag
518
469
  */
@@ -560,12 +511,13 @@ export function installComponentSupport(CodeGenerator, Lexer) {
560
511
  * For component context where state variables are signals.
561
512
  */
562
513
  proto.transformComponentMembers = function(sexpr) {
514
+ const self = this._self;
563
515
  if (!Array.isArray(sexpr)) {
564
516
  if (typeof sexpr === 'string' && this.reactiveMembers && this.reactiveMembers.has(sexpr)) {
565
- return ['.', ['.', 'this', sexpr], 'value'];
517
+ return ['.', ['.', self, sexpr], 'value'];
566
518
  }
567
519
  if (typeof sexpr === 'string' && this.componentMembers && this.componentMembers.has(sexpr)) {
568
- return ['.', 'this', sexpr];
520
+ return ['.', self, sexpr];
569
521
  }
570
522
  return sexpr;
571
523
  }
@@ -574,9 +526,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
574
526
  if (sexpr[0] === '.' && sexpr[1] === 'this' && typeof sexpr[2] === 'string') {
575
527
  const memberName = sexpr[2];
576
528
  if (this.reactiveMembers && this.reactiveMembers.has(memberName)) {
577
- return ['.', sexpr, 'value']; // this.X → this.X.value
529
+ return ['.', ['.', self, memberName], 'value'];
578
530
  }
579
- return sexpr;
531
+ return this._factoryMode ? ['.', self, sexpr[2]] : sexpr;
580
532
  }
581
533
 
582
534
  // Dot access: transform the object but not the property name
@@ -819,10 +771,10 @@ export function installComponentSupport(CodeGenerator, Lexer) {
819
771
  */
820
772
  proto.generateInComponent = function(sexpr, context) {
821
773
  if (typeof sexpr === 'string' && this.reactiveMembers && this.reactiveMembers.has(sexpr)) {
822
- return `this.${sexpr}.value`;
774
+ return `${this._self}.${sexpr}.value`;
823
775
  }
824
776
  if (typeof sexpr === 'string' && this.componentMembers && this.componentMembers.has(sexpr)) {
825
- return `this.${sexpr}`;
777
+ return `${this._self}.${sexpr}`;
826
778
  }
827
779
  if (Array.isArray(sexpr) && this.reactiveMembers) {
828
780
  const transformed = this.transformComponentMembers(sexpr);
@@ -854,6 +806,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
854
806
  this._setupLines = [];
855
807
  this._blockFactories = [];
856
808
  this._loopVarStack = [];
809
+ this._factoryMode = false;
810
+ this._factoryVars = null;
811
+ this._fragChildren = new Map();
857
812
 
858
813
  const statements = this.is(body, 'block') ? body.slice(1) : [body];
859
814
 
@@ -865,10 +820,13 @@ export function installComponentSupport(CodeGenerator, Lexer) {
865
820
  } else {
866
821
  rootVar = this.newElementVar('frag');
867
822
  this._createLines.push(`${rootVar} = document.createDocumentFragment();`);
823
+ const children = [];
868
824
  for (const stmt of statements) {
869
825
  const childVar = this.generateNode(stmt);
870
826
  this._createLines.push(`${rootVar}.appendChild(${childVar});`);
827
+ children.push(childVar);
871
828
  }
829
+ this._fragChildren.set(rootVar, children);
872
830
  }
873
831
 
874
832
  return {
@@ -886,12 +844,30 @@ export function installComponentSupport(CodeGenerator, Lexer) {
886
844
 
887
845
  /** Generate a unique element variable name */
888
846
  proto.newElementVar = function(hint = 'el') {
889
- return `this._${hint}${this._elementCount++}`;
847
+ const name = `_${hint}${this._elementCount++}`;
848
+ if (this._factoryVars) this._factoryVars.add(name);
849
+ return this._factoryMode ? name : `this.${name}`;
890
850
  };
891
851
 
892
852
  /** Generate a unique text node variable name */
893
853
  proto.newTextVar = function() {
894
- return `this._t${this._textCount++}`;
854
+ const name = `_t${this._textCount++}`;
855
+ if (this._factoryVars) this._factoryVars.add(name);
856
+ return this._factoryMode ? name : `this.${name}`;
857
+ };
858
+
859
+ /** Context reference — 'this' in component body, 'ctx' in block factories */
860
+ Object.defineProperty(proto, '_self', {
861
+ get() { return this._factoryMode ? 'ctx' : 'this'; }
862
+ });
863
+
864
+ /** Push an effect line, wrapping with disposer tracking in factory mode */
865
+ proto._pushEffect = function(body) {
866
+ if (this._factoryMode) {
867
+ this._setupLines.push(`disposers.push(__effect(() => { ${body} }));`);
868
+ } else {
869
+ this._setupLines.push(`__effect(() => { ${body} });`);
870
+ }
895
871
  };
896
872
 
897
873
  // --------------------------------------------------------------------------
@@ -911,7 +887,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
911
887
  if (this.reactiveMembers && this.reactiveMembers.has(str)) {
912
888
  const textVar = this.newTextVar();
913
889
  this._createLines.push(`${textVar} = document.createTextNode('');`);
914
- this._setupLines.push(`__effect(() => { ${textVar}.data = this.${str}.value; });`);
890
+ this._pushEffect(`${textVar}.data = ${this._self}.${str}.value;`);
915
891
  return textVar;
916
892
  }
917
893
  // Static tag without content (possibly with #id)
@@ -953,15 +929,15 @@ export function installComponentSupport(CodeGenerator, Lexer) {
953
929
 
954
930
  // Property access on this (e.g., @prop, @children)
955
931
  if (obj === 'this' && typeof prop === 'string') {
932
+ const s = this._self;
956
933
  if (this.reactiveMembers && this.reactiveMembers.has(prop)) {
957
934
  const textVar = this.newTextVar();
958
935
  this._createLines.push(`${textVar} = document.createTextNode('');`);
959
- this._setupLines.push(`__effect(() => { ${textVar}.data = this.${prop}.value; });`);
936
+ this._pushEffect(`${textVar}.data = ${s}.${prop}.value;`);
960
937
  return textVar;
961
938
  }
962
- // Slot/prop — handle DOM nodes (children) and plain values
963
939
  const slotVar = this.newElementVar('slot');
964
- this._createLines.push(`${slotVar} = this.${prop} instanceof Node ? this.${prop} : (this.${prop} != null ? document.createTextNode(String(this.${prop})) : document.createComment(''));`);
940
+ this._createLines.push(`${slotVar} = ${s}.${prop} instanceof Node ? ${s}.${prop} : (${s}.${prop} != null ? document.createTextNode(String(${s}.${prop})) : document.createComment(''));`);
965
941
  return slotVar;
966
942
  }
967
943
 
@@ -1018,7 +994,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1018
994
  const exprCode = this.generateInComponent(sexpr, 'value');
1019
995
  if (this.hasReactiveDeps(sexpr)) {
1020
996
  this._createLines.push(`${textVar} = document.createTextNode('');`);
1021
- this._setupLines.push(`__effect(() => { ${textVar}.data = ${exprCode}; });`);
997
+ this._pushEffect(`${textVar}.data = ${exprCode};`);
1022
998
  } else {
1023
999
  this._createLines.push(`${textVar} = document.createTextNode(String(${exprCode}));`);
1024
1000
  }
@@ -1057,9 +1033,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1057
1033
  this._createLines.push(`${textVar} = document.createTextNode(${val});`);
1058
1034
  } else if (this.reactiveMembers && this.reactiveMembers.has(val)) {
1059
1035
  this._createLines.push(`${textVar} = document.createTextNode('');`);
1060
- this._setupLines.push(`__effect(() => { ${textVar}.data = this.${val}.value; });`);
1036
+ this._pushEffect(`${textVar}.data = ${this._self}.${val}.value;`);
1061
1037
  } else if (this.componentMembers && this.componentMembers.has(val)) {
1062
- this._createLines.push(`${textVar} = document.createTextNode(String(this.${val}));`);
1038
+ this._createLines.push(`${textVar} = document.createTextNode(String(${this._self}.${val}));`);
1063
1039
  } else {
1064
1040
  this._createLines.push(`${textVar} = document.createTextNode(${this.generateInComponent(arg, 'value')});`);
1065
1041
  }
@@ -1112,9 +1088,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1112
1088
  } else {
1113
1089
  const combined = this._pendingClassArgs.join(', ');
1114
1090
  if (isSvg) {
1115
- this._setupLines.push(`__effect(() => { ${elVar}.setAttribute('class', __clsx(${combined})); });`);
1091
+ this._pushEffect(`${elVar}.setAttribute('class', __clsx(${combined}));`);
1116
1092
  } else {
1117
- this._setupLines.push(`__effect(() => { ${elVar}.className = __clsx(${combined}); });`);
1093
+ this._pushEffect(`${elVar}.className = __clsx(${combined});`);
1118
1094
  }
1119
1095
  }
1120
1096
  this._pendingClassArgs = prevClassArgs;
@@ -1151,9 +1127,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1151
1127
  const combined = this._pendingClassArgs.join(', ');
1152
1128
  const isSvg = SVG_TAGS.has(tag) || this._svgDepth > 0;
1153
1129
  if (isSvg) {
1154
- this._setupLines.push(`__effect(() => { ${elVar}.setAttribute('class', __clsx(${combined})); });`);
1130
+ this._pushEffect(`${elVar}.setAttribute('class', __clsx(${combined}));`);
1155
1131
  } else {
1156
- this._setupLines.push(`__effect(() => { ${elVar}.className = __clsx(${combined}); });`);
1132
+ this._pushEffect(`${elVar}.className = __clsx(${combined});`);
1157
1133
  }
1158
1134
  }
1159
1135
  this._pendingClassArgs = prevClassArgs;
@@ -1175,9 +1151,8 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1175
1151
  // Event handler: @click or (. this eventName)
1176
1152
  if (this.is(key, '.') && key[1] === 'this') {
1177
1153
  const eventName = key[2];
1178
- // Bind method references to this
1179
1154
  if (typeof value === 'string' && this.componentMembers?.has(value)) {
1180
- this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => __batch(() => this.${value}(e)));`);
1155
+ this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => __batch(() => ${this._self}.${value}(e)));`);
1181
1156
  } else {
1182
1157
  const handlerCode = this.generateInComponent(value, 'value');
1183
1158
  this._createLines.push(`${elVar}.addEventListener('${eventName}', (e) => __batch(() => (${handlerCode})(e)));`);
@@ -1199,9 +1174,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1199
1174
  this._pendingClassArgs.push(valueCode);
1200
1175
  } else if (this.hasReactiveDeps(value)) {
1201
1176
  if (this._svgDepth > 0) {
1202
- this._setupLines.push(`__effect(() => { ${elVar}.setAttribute('class', __clsx(${valueCode})); });`);
1177
+ this._pushEffect(`${elVar}.setAttribute('class', __clsx(${valueCode}));`);
1203
1178
  } else {
1204
- this._setupLines.push(`__effect(() => { ${elVar}.className = __clsx(${valueCode}); });`);
1179
+ this._pushEffect(`${elVar}.className = __clsx(${valueCode});`);
1205
1180
  }
1206
1181
  } else {
1207
1182
  if (this._svgDepth > 0) {
@@ -1216,7 +1191,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1216
1191
  // Element ref: ref: "name" → this.name = element
1217
1192
  if (key === 'ref') {
1218
1193
  const refName = String(value).replace(/^["']|["']$/g, '');
1219
- this._createLines.push(`this.${refName} = ${elVar};`);
1194
+ this._createLines.push(`${this._self}.${refName} = ${elVar};`);
1220
1195
  continue;
1221
1196
  }
1222
1197
 
@@ -1235,11 +1210,11 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1235
1210
  ? 'e.target.valueAsNumber' : 'e.target.value';
1236
1211
  }
1237
1212
 
1238
- this._setupLines.push(`__effect(() => { ${elVar}.${prop} = ${valueCode}; });`);
1213
+ this._pushEffect(`${elVar}.${prop} = ${valueCode};`);
1239
1214
  let assignCode = `${valueCode} = ${valueAccessor}`;
1240
1215
  const rootMember = !this.isSimpleAssignable(value) && this.findRootReactiveMember(value);
1241
1216
  if (rootMember) {
1242
- assignCode += `; this.${rootMember}.touch?.()`;
1217
+ assignCode += `; ${this._self}.${rootMember}.touch?.()`;
1243
1218
  }
1244
1219
  this._createLines.push(`${elVar}.addEventListener('${event}', (e) => { ${assignCode}; });`);
1245
1220
  continue;
@@ -1249,9 +1224,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1249
1224
 
1250
1225
  // Smart two-way binding for value/checked when bound to reactive state
1251
1226
  if ((key === 'value' || key === 'checked') && this.hasReactiveDeps(value)) {
1252
- this._setupLines.push(`__effect(() => { ${elVar}.${key} = ${valueCode}; });`);
1253
- // Generate reverse binding for simple assignable targets or nested
1254
- // reactive paths (with touch() for Svelte-style invalidation)
1227
+ this._pushEffect(`${elVar}.${key} = ${valueCode};`);
1255
1228
  const rootMemberImplicit = !this.isSimpleAssignable(value) && this.findRootReactiveMember(value);
1256
1229
  if (this.isSimpleAssignable(value) || rootMemberImplicit) {
1257
1230
  const event = key === 'checked' ? 'change' : 'input';
@@ -1260,7 +1233,7 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1260
1233
  : 'e.target.value';
1261
1234
  let assignCode = `${valueCode} = ${accessor}`;
1262
1235
  if (rootMemberImplicit) {
1263
- assignCode += `; this.${rootMemberImplicit}.touch?.()`;
1236
+ assignCode += `; ${this._self}.${rootMemberImplicit}.touch?.()`;
1264
1237
  }
1265
1238
  this._createLines.push(`${elVar}.addEventListener('${event}', (e) => { ${assignCode}; });`);
1266
1239
  }
@@ -1269,18 +1242,18 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1269
1242
 
1270
1243
  if (key === 'innerHTML' || key === 'textContent' || key === 'innerText') {
1271
1244
  if (this.hasReactiveDeps(value)) {
1272
- this._setupLines.push(`__effect(() => { ${elVar}.${key} = ${valueCode}; });`);
1245
+ this._pushEffect(`${elVar}.${key} = ${valueCode};`);
1273
1246
  } else {
1274
1247
  this._createLines.push(`${elVar}.${key} = ${valueCode};`);
1275
1248
  }
1276
1249
  } else if (BOOLEAN_ATTRS.has(key)) {
1277
1250
  if (this.hasReactiveDeps(value)) {
1278
- this._setupLines.push(`__effect(() => { ${elVar}.toggleAttribute('${key}', !!${valueCode}); });`);
1251
+ this._pushEffect(`${elVar}.toggleAttribute('${key}', !!${valueCode});`);
1279
1252
  } else {
1280
1253
  this._createLines.push(`if (${valueCode}) ${elVar}.setAttribute('${key}', '');`);
1281
1254
  }
1282
1255
  } else if (this.hasReactiveDeps(value)) {
1283
- this._setupLines.push(`__effect(() => { ${elVar}.setAttribute('${key}', ${valueCode}); });`);
1256
+ this._pushEffect(`${elVar}.setAttribute('${key}', ${valueCode});`);
1284
1257
  } else {
1285
1258
  this._createLines.push(`${elVar}.setAttribute('${key}', ${valueCode});`);
1286
1259
  }
@@ -1309,10 +1282,13 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1309
1282
 
1310
1283
  const fragVar = this.newElementVar('frag');
1311
1284
  this._createLines.push(`${fragVar} = document.createDocumentFragment();`);
1285
+ const children = [];
1312
1286
  for (const stmt of statements) {
1313
1287
  const childVar = this.generateNode(stmt);
1314
1288
  this._createLines.push(`${fragVar}.appendChild(${childVar});`);
1289
+ children.push(childVar);
1315
1290
  }
1291
+ this._fragChildren.set(fragVar, children);
1316
1292
  return fragVar;
1317
1293
  };
1318
1294
 
@@ -1328,9 +1304,8 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1328
1304
 
1329
1305
  const condCode = this.generateInComponent(condition, 'value');
1330
1306
 
1331
- // Collect loop variables from enclosing for-loops
1332
- const loopParams = this._loopVarStack.map(v => `${v.itemVar}, ${v.indexVar}`).join(', ');
1333
- const extraArgs = loopParams ? `, ${loopParams}` : '';
1307
+ const outerParams = this._loopVarStack.map(v => `${v.itemVar}, ${v.indexVar}`).join(', ');
1308
+ const outerExtra = outerParams ? `, ${outerParams}` : '';
1334
1309
 
1335
1310
  const thenBlockName = this.newBlockVar();
1336
1311
  this.generateConditionBranch(thenBlockName, thenBlock);
@@ -1347,7 +1322,9 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1347
1322
  setupLines.push(` const anchor = ${anchorVar};`);
1348
1323
  setupLines.push(` let currentBlock = null;`);
1349
1324
  setupLines.push(` let showing = null;`);
1350
- setupLines.push(` __effect(() => {`);
1325
+ const effOpen = this._factoryMode ? 'disposers.push(__effect(() => {' : '__effect(() => {';
1326
+ const effClose = this._factoryMode ? '}));' : '});';
1327
+ setupLines.push(` ${effOpen}`);
1351
1328
  setupLines.push(` const show = !!(${condCode});`);
1352
1329
  setupLines.push(` const want = show ? 'then' : ${elseBlock ? "'else'" : 'null'};`);
1353
1330
  setupLines.push(` if (want === showing) return;`);
@@ -1359,20 +1336,20 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1359
1336
  setupLines.push(` showing = want;`);
1360
1337
  setupLines.push(``);
1361
1338
  setupLines.push(` if (want === 'then') {`);
1362
- setupLines.push(` currentBlock = ${thenBlockName}(this${extraArgs});`);
1339
+ setupLines.push(` currentBlock = ${thenBlockName}(${this._self}${outerExtra});`);
1363
1340
  setupLines.push(` currentBlock.c();`);
1364
1341
  setupLines.push(` if (anchor.parentNode) currentBlock.m(anchor.parentNode, anchor.nextSibling);`);
1365
- setupLines.push(` currentBlock.p(this${extraArgs});`);
1342
+ setupLines.push(` currentBlock.p(${this._self}${outerExtra});`);
1366
1343
  setupLines.push(` }`);
1367
1344
  if (elseBlock) {
1368
1345
  setupLines.push(` if (want === 'else') {`);
1369
- setupLines.push(` currentBlock = ${elseBlockName}(this${extraArgs});`);
1346
+ setupLines.push(` currentBlock = ${elseBlockName}(${this._self}${outerExtra});`);
1370
1347
  setupLines.push(` currentBlock.c();`);
1371
1348
  setupLines.push(` if (anchor.parentNode) currentBlock.m(anchor.parentNode, anchor.nextSibling);`);
1372
- setupLines.push(` currentBlock.p(this${extraArgs});`);
1349
+ setupLines.push(` currentBlock.p(${this._self}${outerExtra});`);
1373
1350
  setupLines.push(` }`);
1374
1351
  }
1375
- setupLines.push(` });`);
1352
+ setupLines.push(` ${effClose}`);
1376
1353
  setupLines.push(`}`);
1377
1354
 
1378
1355
  this._setupLines.push(setupLines.join('\n '));
@@ -1385,36 +1362,36 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1385
1362
  // --------------------------------------------------------------------------
1386
1363
 
1387
1364
  proto.generateConditionBranch = function(blockName, block) {
1388
- const savedCreateLines = this._createLines;
1389
- const savedSetupLines = this._setupLines;
1365
+ const saved = [this._createLines, this._setupLines, this._factoryMode, this._factoryVars];
1390
1366
 
1391
1367
  this._createLines = [];
1392
1368
  this._setupLines = [];
1369
+ this._factoryMode = true;
1370
+ this._factoryVars = new Set();
1393
1371
 
1394
1372
  const rootVar = this.generateTemplateBlock(block);
1395
1373
  const createLines = this._createLines;
1396
1374
  const setupLines = this._setupLines;
1375
+ const factoryVars = this._factoryVars;
1376
+
1377
+ [this._createLines, this._setupLines, this._factoryMode, this._factoryVars] = saved;
1397
1378
 
1398
- this._createLines = savedCreateLines;
1399
- this._setupLines = savedSetupLines;
1379
+ const outerParams = this._loopVarStack.map(v => `${v.itemVar}, ${v.indexVar}`).join(', ');
1380
+ const extraParams = outerParams ? `, ${outerParams}` : '';
1400
1381
 
1401
- const localizeVar = (line) => this.localizeVar(line);
1382
+ this.emitBlockFactory(blockName, `ctx${extraParams}`, rootVar, createLines, setupLines, factoryVars);
1383
+ };
1402
1384
 
1403
- // Include enclosing loop variables in the factory signature
1404
- const loopParams = this._loopVarStack.map(v => `${v.itemVar}, ${v.indexVar}`).join(', ');
1405
- const extraParams = loopParams ? `, ${loopParams}` : '';
1385
+ // --------------------------------------------------------------------------
1386
+ // emitBlockFactory shared factory generation for conditionals and loops
1387
+ // --------------------------------------------------------------------------
1406
1388
 
1389
+ proto.emitBlockFactory = function(blockName, params, rootVar, createLines, setupLines, factoryVars, isStatic) {
1407
1390
  const factoryLines = [];
1408
- factoryLines.push(`function ${blockName}(ctx${extraParams}) {`);
1391
+ factoryLines.push(`function ${blockName}(${params}) {`);
1409
1392
 
1410
- // Declare local variables
1411
- const localVars = new Set();
1412
- for (const line of createLines) {
1413
- const match = line.match(/^this\.(_(?:el|t|anchor|frag|slot|c|inst|empty)\d+)\s*=/);
1414
- if (match) localVars.add(match[1]);
1415
- }
1416
- if (localVars.size > 0) {
1417
- factoryLines.push(` let ${[...localVars].join(', ')};`);
1393
+ if (factoryVars.size > 0) {
1394
+ factoryLines.push(` let ${[...factoryVars].join(', ')};`);
1418
1395
  }
1419
1396
 
1420
1397
  const hasEffects = setupLines.length > 0;
@@ -1424,49 +1401,50 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1424
1401
 
1425
1402
  factoryLines.push(` return {`);
1426
1403
 
1427
- // c() - create
1404
+ if (isStatic) {
1405
+ factoryLines.push(` _s: true,`);
1406
+ }
1407
+
1408
+ const fragChildren = this._fragChildren.get(rootVar);
1409
+ const firstNode = fragChildren ? fragChildren[0] : rootVar;
1410
+
1428
1411
  factoryLines.push(` c() {`);
1429
1412
  for (const line of createLines) {
1430
- factoryLines.push(` ${localizeVar(line)}`);
1413
+ factoryLines.push(` ${line}`);
1431
1414
  }
1415
+ factoryLines.push(` this._first = ${firstNode};`);
1432
1416
  factoryLines.push(` },`);
1433
1417
 
1434
- // m() - mount
1435
1418
  factoryLines.push(` m(target, anchor) {`);
1436
- factoryLines.push(` target.insertBefore(${localizeVar(rootVar)}, anchor);`);
1419
+ if (fragChildren) {
1420
+ for (const child of fragChildren) {
1421
+ factoryLines.push(` if (target) target.insertBefore(${child}, anchor);`);
1422
+ }
1423
+ } else {
1424
+ factoryLines.push(` if (target) target.insertBefore(${rootVar}, anchor);`);
1425
+ }
1437
1426
  factoryLines.push(` },`);
1438
1427
 
1439
- // p() - update/patch
1440
- factoryLines.push(` p(ctx${extraParams}) {`);
1428
+ factoryLines.push(` p(${params}) {`);
1441
1429
  if (hasEffects) {
1442
1430
  factoryLines.push(` disposers.forEach(d => d());`);
1443
1431
  factoryLines.push(` disposers = [];`);
1444
1432
  for (const line of setupLines) {
1445
- const localizedLine = localizeVar(line);
1446
- const wrappedLine = localizedLine.replace(
1447
- /__effect\(\(\) => \{/g,
1448
- 'disposers.push(__effect(() => {'
1449
- ).replace(
1450
- /\}\);$/gm,
1451
- '}));'
1452
- );
1453
- factoryLines.push(` ${wrappedLine}`);
1433
+ factoryLines.push(` ${line}`);
1454
1434
  }
1455
1435
  }
1456
1436
  factoryLines.push(` },`);
1457
1437
 
1458
- // d() - destroy
1459
1438
  factoryLines.push(` d(detaching) {`);
1460
1439
  if (hasEffects) {
1461
1440
  factoryLines.push(` disposers.forEach(d => d());`);
1462
1441
  }
1463
- const condFragChildren = getFragChildren(rootVar, createLines, localizeVar);
1464
- if (condFragChildren) {
1465
- for (const child of condFragChildren) {
1442
+ if (fragChildren) {
1443
+ for (const child of fragChildren) {
1466
1444
  factoryLines.push(` if (detaching && ${child}) ${child}.remove();`);
1467
1445
  }
1468
1446
  } else {
1469
- factoryLines.push(` if (detaching && ${localizeVar(rootVar)}) ${localizeVar(rootVar)}.remove();`);
1447
+ factoryLines.push(` if (detaching && ${rootVar}) ${rootVar}.remove();`);
1470
1448
  }
1471
1449
  factoryLines.push(` }`);
1472
1450
 
@@ -1514,136 +1492,46 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1514
1492
  }
1515
1493
  }
1516
1494
 
1517
- // Save state and generate item template in isolation
1518
- const savedCreateLines = this._createLines;
1519
- const savedSetupLines = this._setupLines;
1495
+ const saved = [this._createLines, this._setupLines, this._factoryMode, this._factoryVars];
1520
1496
 
1521
1497
  this._createLines = [];
1522
1498
  this._setupLines = [];
1499
+ this._factoryMode = true;
1500
+ this._factoryVars = new Set();
1523
1501
 
1524
- // Capture enclosing loop variables before pushing current loop
1525
- const outerLoopParams = this._loopVarStack.map(v => `${v.itemVar}, ${v.indexVar}`).join(', ');
1526
- const outerExtra = outerLoopParams ? `, ${outerLoopParams}` : '';
1502
+ const outerParams = this._loopVarStack.map(v => `${v.itemVar}, ${v.indexVar}`).join(', ');
1503
+ const outerExtra = outerParams ? `, ${outerParams}` : '';
1527
1504
 
1528
1505
  this._loopVarStack.push({ itemVar, indexVar });
1529
1506
  const itemNode = this.generateTemplateBlock(body);
1530
1507
  this._loopVarStack.pop();
1531
1508
  const itemCreateLines = this._createLines;
1532
1509
  const itemSetupLines = this._setupLines;
1510
+ const itemFactoryVars = this._factoryVars;
1533
1511
 
1534
- this._createLines = savedCreateLines;
1535
- this._setupLines = savedSetupLines;
1536
-
1537
- const localizeVar = (line) => this.localizeVar(line);
1538
-
1539
- // Generate block factory
1540
- const factoryLines = [];
1541
- factoryLines.push(`function ${blockName}(ctx, ${itemVar}, ${indexVar}${outerExtra}) {`);
1542
-
1543
- const localVars = new Set();
1544
- for (const line of itemCreateLines) {
1545
- const match = line.match(/^this\.(_(?:el|t|anchor|frag|slot|c|inst|empty)\d+)\s*=/);
1546
- if (match) localVars.add(match[1]);
1547
- }
1548
- if (localVars.size > 0) {
1549
- factoryLines.push(` let ${[...localVars].join(', ')};`);
1550
- }
1551
-
1552
- const hasEffects = itemSetupLines.length > 0;
1553
- if (hasEffects) {
1554
- factoryLines.push(` let disposers = [];`);
1555
- }
1512
+ [this._createLines, this._setupLines, this._factoryMode, this._factoryVars] = saved;
1556
1513
 
1557
- factoryLines.push(` return {`);
1514
+ const isStatic = itemSetupLines.length === 0;
1515
+ const loopParams = `ctx, ${itemVar}, ${indexVar}${outerExtra}`;
1516
+ this.emitBlockFactory(blockName, loopParams, itemNode, itemCreateLines, itemSetupLines, itemFactoryVars, isStatic);
1558
1517
 
1559
- // c() - create
1560
- factoryLines.push(` c() {`);
1561
- for (const line of itemCreateLines) {
1562
- factoryLines.push(` ${localizeVar(line)}`);
1563
- }
1564
- factoryLines.push(` },`);
1518
+ // Build key function argument (null = use item as key)
1519
+ const hasCustomKey = keyExpr !== itemVar;
1520
+ const keyFnCode = hasCustomKey ? `(${itemVar}, ${indexVar}) => ${keyExpr}` : 'null';
1565
1521
 
1566
- // m() - mount (also repositions already-mounted blocks)
1567
- const loopFragChildren = getFragChildren(itemNode, itemCreateLines, localizeVar);
1568
- factoryLines.push(` m(target, anchor) {`);
1569
- if (loopFragChildren) {
1570
- for (const child of loopFragChildren) {
1571
- factoryLines.push(` if (target) target.insertBefore(${child}, anchor);`);
1572
- }
1573
- } else {
1574
- factoryLines.push(` if (target) target.insertBefore(${localizeVar(itemNode)}, anchor);`);
1575
- }
1576
- factoryLines.push(` },`);
1577
-
1578
- // p() - update
1579
- factoryLines.push(` p(ctx, ${itemVar}, ${indexVar}${outerExtra}) {`);
1580
- if (hasEffects) {
1581
- factoryLines.push(` disposers.forEach(d => d());`);
1582
- factoryLines.push(` disposers = [];`);
1583
- for (const line of itemSetupLines) {
1584
- const localizedLine = localizeVar(line);
1585
- const wrappedLine = localizedLine.replace(
1586
- /__effect\(\(\) => \{/g,
1587
- 'disposers.push(__effect(() => {'
1588
- ).replace(
1589
- /\}\);$/gm,
1590
- '}));'
1591
- );
1592
- factoryLines.push(` ${wrappedLine}`);
1593
- }
1594
- }
1595
- factoryLines.push(` },`);
1596
-
1597
- // d() - destroy
1598
- factoryLines.push(` d(detaching) {`);
1599
- if (hasEffects) {
1600
- factoryLines.push(` disposers.forEach(d => d());`);
1601
- }
1602
- if (loopFragChildren) {
1603
- for (const child of loopFragChildren) {
1604
- factoryLines.push(` if (detaching && ${child}) ${child}.remove();`);
1605
- }
1606
- } else {
1607
- factoryLines.push(` if (detaching && ${localizeVar(itemNode)}) ${localizeVar(itemNode)}.remove();`);
1608
- }
1609
- factoryLines.push(` }`);
1610
-
1611
- factoryLines.push(` };`);
1612
- factoryLines.push(`}`);
1613
-
1614
- this._blockFactories.push(factoryLines.join('\n'));
1522
+ // Build outer vars argument list for nested loops
1523
+ const outerArgs = outerParams ? `, ${outerParams}` : '';
1615
1524
 
1616
1525
  // Generate reconciliation code in _setup()
1617
1526
  const setupLines = [];
1618
1527
  setupLines.push(`// Loop: ${blockName}`);
1619
1528
  setupLines.push(`{`);
1620
- setupLines.push(` const __anchor = ${anchorVar};`);
1621
- setupLines.push(` const __map = new Map();`);
1622
- setupLines.push(` __effect(() => {`);
1623
- setupLines.push(` const __items = ${collectionCode};`);
1624
- setupLines.push(` const __parent = __anchor.parentNode;`);
1625
- setupLines.push(` const __newMap = new Map();`);
1626
- setupLines.push(``);
1627
- setupLines.push(` for (let ${indexVar} = 0; ${indexVar} < __items.length; ${indexVar}++) {`);
1628
- setupLines.push(` const ${itemVar} = __items[${indexVar}];`);
1629
- setupLines.push(` const __key = ${keyExpr};`);
1630
- setupLines.push(` let __block = __map.get(__key);`);
1631
- setupLines.push(` if (!__block) {`);
1632
- setupLines.push(` __block = ${blockName}(this, ${itemVar}, ${indexVar}${outerExtra});`);
1633
- setupLines.push(` __block.c();`);
1634
- setupLines.push(` }`);
1635
- setupLines.push(` __block.m(__parent, __anchor);`);
1636
- setupLines.push(` __block.p(this, ${itemVar}, ${indexVar}${outerExtra});`);
1637
- setupLines.push(` __newMap.set(__key, __block);`);
1638
- setupLines.push(` }`);
1639
- setupLines.push(``);
1640
- setupLines.push(` for (const [__k, __b] of __map) {`);
1641
- setupLines.push(` if (!__newMap.has(__k)) __b.d(true);`);
1642
- setupLines.push(` }`);
1643
- setupLines.push(``);
1644
- setupLines.push(` __map.clear();`);
1645
- setupLines.push(` for (const [__k, __v] of __newMap) __map.set(__k, __v);`);
1646
- setupLines.push(` });`);
1529
+ setupLines.push(` const __s = { blocks: [], keys: [] };`);
1530
+ const effOpen = this._factoryMode ? 'disposers.push(__effect(() => {' : '__effect(() => {';
1531
+ const effClose = this._factoryMode ? '}));' : '});';
1532
+ setupLines.push(` ${effOpen}`);
1533
+ setupLines.push(` __reconcile(${anchorVar}, __s, ${collectionCode}, ${this._self}, ${blockName}, ${keyFnCode}${outerArgs});`);
1534
+ setupLines.push(` ${effClose}`);
1647
1535
  setupLines.push(`}`);
1648
1536
 
1649
1537
  this._setupLines.push(setupLines.join('\n '));
@@ -1660,15 +1548,16 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1660
1548
  const elVar = this.newElementVar('el');
1661
1549
  const { propsCode, reactiveProps, childrenSetupLines } = this.buildComponentProps(args);
1662
1550
 
1551
+ const s = this._self;
1663
1552
  this._createLines.push(`${instVar} = new ${componentName}(${propsCode});`);
1664
1553
  this._createLines.push(`${elVar} = ${instVar}._create();`);
1665
- this._createLines.push(`(this._children || (this._children = [])).push(${instVar});`);
1554
+ this._createLines.push(`(${s}._children || (${s}._children = [])).push(${instVar});`);
1666
1555
 
1667
1556
  this._setupLines.push(`if (${instVar}._setup) ${instVar}._setup();`);
1668
1557
  this._setupLines.push(`if (${instVar}.mounted) ${instVar}.mounted();`);
1669
1558
 
1670
1559
  for (const { key, valueCode } of reactiveProps) {
1671
- this._setupLines.push(`__effect(() => { if (${instVar}.${key}) ${instVar}.${key}.value = ${valueCode}; });`);
1560
+ this._pushEffect(`if (${instVar}.${key}) ${instVar}.${key}.value = ${valueCode};`);
1672
1561
  }
1673
1562
 
1674
1563
  for (const line of childrenSetupLines) {
@@ -1688,56 +1577,43 @@ export function installComponentSupport(CodeGenerator, Lexer) {
1688
1577
  let childrenVar = null;
1689
1578
  const childrenSetupLines = [];
1690
1579
 
1580
+ // Simple reactive values pass the signal directly for shared reactivity;
1581
+ // complex expressions use normal .value unwrapping to compute the value.
1582
+ const addProp = (key, value) => {
1583
+ const isDirectSignal = this.reactiveMembers && (
1584
+ (typeof value === 'string' && this.reactiveMembers.has(value)) ||
1585
+ (Array.isArray(value) && value[0] === '.' && value[1] === 'this' && typeof value[2] === 'string' && this.reactiveMembers.has(value[2]))
1586
+ );
1587
+ if (isDirectSignal) {
1588
+ const member = typeof value === 'string' ? value : value[2];
1589
+ props.push(`${key}: ${this._self}.${member}`);
1590
+ } else {
1591
+ const valueCode = this.generateInComponent(value, 'value');
1592
+ props.push(`${key}: ${valueCode}`);
1593
+ if (this.hasReactiveDeps(value)) {
1594
+ reactiveProps.push({ key, valueCode });
1595
+ }
1596
+ }
1597
+ };
1598
+
1599
+ const addObjectProps = (objExpr) => {
1600
+ for (let i = 1; i < objExpr.length; i++) {
1601
+ const [key, value] = objExpr[i];
1602
+ if (typeof key === 'string') addProp(key, value);
1603
+ }
1604
+ };
1605
+
1691
1606
  for (const arg of args) {
1692
1607
  if (this.is(arg, 'object')) {
1693
- for (let i = 1; i < arg.length; i++) {
1694
- const [key, value] = arg[i];
1695
- if (typeof key === 'string') {
1696
- // Simple reactive identifier — pass signal directly for shared reactivity.
1697
- // Complex expressions — use normal .value unwrapping to compute the value.
1698
- const isSimpleReactive = this.reactiveMembers && (
1699
- (typeof value === 'string' && this.reactiveMembers.has(value)) ||
1700
- (Array.isArray(value) && value[0] === '.' && value[1] === 'this' && typeof value[2] === 'string' && this.reactiveMembers.has(value[2]))
1701
- );
1702
- if (isSimpleReactive) {
1703
- const member = typeof value === 'string' ? value : value[2];
1704
- props.push(`${key}: this.${member}`);
1705
- } else {
1706
- const valueCode = this.generateInComponent(value, 'value');
1707
- props.push(`${key}: ${valueCode}`);
1708
- if (this.hasReactiveDeps(value)) {
1709
- reactiveProps.push({ key, valueCode });
1710
- }
1711
- }
1712
- }
1713
- }
1608
+ addObjectProps(arg);
1714
1609
  } else if (Array.isArray(arg) && (arg[0] === '->' || arg[0] === '=>')) {
1715
1610
  let block = arg[2];
1716
1611
  if (block) {
1717
- // Indented attributes: extract object nodes from block as props
1718
1612
  if (this.is(block, 'block')) {
1719
1613
  const domChildren = [];
1720
1614
  for (const child of block.slice(1)) {
1721
1615
  if (this.is(child, 'object')) {
1722
- for (let i = 1; i < child.length; i++) {
1723
- const [key, value] = child[i];
1724
- if (typeof key === 'string') {
1725
- const isSimpleReactive = this.reactiveMembers && (
1726
- (typeof value === 'string' && this.reactiveMembers.has(value)) ||
1727
- (Array.isArray(value) && value[0] === '.' && value[1] === 'this' && typeof value[2] === 'string' && this.reactiveMembers.has(value[2]))
1728
- );
1729
- if (isSimpleReactive) {
1730
- const member = typeof value === 'string' ? value : value[2];
1731
- props.push(`${key}: this.${member}`);
1732
- } else {
1733
- const valueCode = this.generateInComponent(value, 'value');
1734
- props.push(`${key}: ${valueCode}`);
1735
- if (this.hasReactiveDeps(value)) {
1736
- reactiveProps.push({ key, valueCode });
1737
- }
1738
- }
1739
- }
1740
- }
1616
+ addObjectProps(child);
1741
1617
  } else {
1742
1618
  domChildren.push(child);
1743
1619
  }
@@ -1900,6 +1776,134 @@ function __clsx(...args) {
1900
1776
  return args.filter(Boolean).join(' ');
1901
1777
  }
1902
1778
 
1779
+ function __lis(arr) {
1780
+ const n = arr.length;
1781
+ if (n === 0) return [];
1782
+ const tails = [], indices = [], prev = new Array(n).fill(-1);
1783
+ for (let i = 0; i < n; i++) {
1784
+ if (arr[i] === -1) continue;
1785
+ let lo = 0, hi = tails.length;
1786
+ while (lo < hi) {
1787
+ const mid = (lo + hi) >> 1;
1788
+ if (tails[mid] < arr[i]) lo = mid + 1; else hi = mid;
1789
+ }
1790
+ tails[lo] = arr[i];
1791
+ indices[lo] = i;
1792
+ if (lo > 0) prev[i] = indices[lo - 1];
1793
+ }
1794
+ const result = [];
1795
+ let k = indices[tails.length - 1];
1796
+ for (let i = tails.length - 1; i >= 0; i--) { result.push(k); k = prev[k]; }
1797
+ result.reverse();
1798
+ return result;
1799
+ }
1800
+
1801
+ function __reconcile(anchor, state, items, ctx, factory, keyFn, ...outer) {
1802
+ const parent = anchor.parentNode;
1803
+ if (!parent) return;
1804
+
1805
+ const oldKeys = state.keys;
1806
+ const oldBlocks = state.blocks;
1807
+ const oldLen = oldKeys.length;
1808
+ const newLen = items.length;
1809
+ const newBlocks = new Array(newLen);
1810
+ const hasKeyFn = keyFn != null;
1811
+ const newKeys = hasKeyFn ? items.map((item, i) => keyFn(item, i)) : items;
1812
+
1813
+ // Phase 0: first render — batch create via DocumentFragment
1814
+ if (oldLen === 0) {
1815
+ if (newLen > 0) {
1816
+ const frag = document.createDocumentFragment();
1817
+ for (let i = 0; i < newLen; i++) {
1818
+ const block = factory(ctx, items[i], i, ...outer);
1819
+ block.c();
1820
+ block.m(frag, null);
1821
+ if (!block._s) block.p(ctx, items[i], i, ...outer);
1822
+ newBlocks[i] = block;
1823
+ }
1824
+ parent.insertBefore(frag, anchor);
1825
+ }
1826
+ state.keys = hasKeyFn ? newKeys : items.slice();
1827
+ state.blocks = newBlocks;
1828
+ return;
1829
+ }
1830
+
1831
+ // Phase 1: prefix scan — skip p() (item+index identical, effects already live)
1832
+ let start = 0;
1833
+ const minLen = oldLen < newLen ? oldLen : newLen;
1834
+ while (start < minLen && oldKeys[start] === newKeys[start]) {
1835
+ newBlocks[start] = oldBlocks[start];
1836
+ start++;
1837
+ }
1838
+
1839
+ // Phase 2: suffix scan — call p() (index may differ)
1840
+ let oldEnd = oldLen - 1;
1841
+ let newEnd = newLen - 1;
1842
+ while (oldEnd >= start && newEnd >= start && oldKeys[oldEnd] === newKeys[newEnd]) {
1843
+ const block = oldBlocks[oldEnd];
1844
+ if (!block._s) block.p(ctx, items[newEnd], newEnd, ...outer);
1845
+ newBlocks[newEnd] = block;
1846
+ oldEnd--;
1847
+ newEnd--;
1848
+ }
1849
+
1850
+ // Remove old blocks in the middle that aren't in the new set
1851
+ if (start > newEnd) {
1852
+ for (let i = start; i <= oldEnd; i++) oldBlocks[i].d(true);
1853
+ } else if (start > oldEnd) {
1854
+ // Phase 3a: pure insertion — batch via DocumentFragment
1855
+ const next = newEnd + 1 < newLen ? newBlocks[newEnd + 1]._first : anchor;
1856
+ const frag = document.createDocumentFragment();
1857
+ for (let i = start; i <= newEnd; i++) {
1858
+ const block = factory(ctx, items[i], i, ...outer);
1859
+ block.c();
1860
+ block.m(frag, null);
1861
+ if (!block._s) block.p(ctx, items[i], i, ...outer);
1862
+ newBlocks[i] = block;
1863
+ }
1864
+ parent.insertBefore(frag, next);
1865
+ } else {
1866
+ // Phase 4: general case — temp Map + LIS
1867
+ const oldKeyIdx = new Map();
1868
+ for (let i = start; i <= oldEnd; i++) oldKeyIdx.set(oldKeys[i], i);
1869
+
1870
+ const seq = new Array(newEnd - start + 1);
1871
+ for (let i = start; i <= newEnd; i++) {
1872
+ const key = newKeys[i];
1873
+ const oldIdx = oldKeyIdx.get(key);
1874
+ if (oldIdx !== undefined) {
1875
+ seq[i - start] = oldIdx - start;
1876
+ const block = oldBlocks[oldIdx];
1877
+ if (!block._s) block.p(ctx, items[i], i, ...outer);
1878
+ newBlocks[i] = block;
1879
+ oldKeyIdx.delete(key);
1880
+ } else {
1881
+ seq[i - start] = -1;
1882
+ const block = factory(ctx, items[i], i, ...outer);
1883
+ block.c();
1884
+ if (!block._s) block.p(ctx, items[i], i, ...outer);
1885
+ newBlocks[i] = block;
1886
+ }
1887
+ }
1888
+
1889
+ for (const idx of oldKeyIdx.values()) oldBlocks[idx].d(true);
1890
+
1891
+ const lis = __lis(seq);
1892
+ const lisSet = new Set(lis);
1893
+ let next = newEnd + 1 < newLen ? newBlocks[newEnd + 1]._first : anchor;
1894
+ for (let i = newEnd; i >= start; i--) {
1895
+ const block = newBlocks[i];
1896
+ if (!lisSet.has(i - start)) {
1897
+ block.m(parent, next);
1898
+ }
1899
+ next = block._first;
1900
+ }
1901
+ }
1902
+
1903
+ state.keys = hasKeyFn ? newKeys : items.slice();
1904
+ state.blocks = newBlocks;
1905
+ }
1906
+
1903
1907
  class __Component {
1904
1908
  constructor(props = {}) {
1905
1909
  Object.assign(this, props);
@@ -1935,7 +1939,7 @@ class __Component {
1935
1939
 
1936
1940
  // Register on globalThis for runtime deduplication
1937
1941
  if (typeof globalThis !== 'undefined') {
1938
- globalThis.__ripComponent = { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __Component };
1942
+ globalThis.__ripComponent = { __pushComponent, __popComponent, setContext, getContext, hasContext, __clsx, __lis, __reconcile, __Component };
1939
1943
  }
1940
1944
 
1941
1945
  `;