Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: fade direction fast math + remove first-line color wipe from evaluate

Fade strings like `fade:red-yellow-blue:frame*80` now animate correctly
in S-expressions — the direction part is expanded via expandFastMathMacros
before evaluation, so infix expressions (frame*80, frame/2, etc.) resolve
to actual values instead of being passed as literal strings.

First-line color detection in evaluate() no longer calls wipe/backgroundFill
— the paint setup ($.wipe(this.firstLineColor)) already handles that.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+50 -100
+50 -100
system/public/aesthetic.computer/lib/kidlisp.mjs
··· 5708 5708 // Try to evaluate as KidLisp expression/variable 5709 5709 let evalResult; 5710 5710 5711 - // Special handling for function names like "frame" 5712 - const globalEnv = this.getGlobalEnv(); 5713 - if (globalEnv[direction] && typeof globalEnv[direction] === "function") { 5714 - // It's a function, call it with the API 5715 - evalResult = globalEnv[direction](api, []); 5716 - // console.log(`🎬 COAT DEBUG: Evaluated ${direction} as function = ${evalResult}`); 5711 + // Try expanding as fast math (e.g., "frame*80") before fallback 5712 + const expanded = this.expandFastMathMacros(direction); 5713 + if (Array.isArray(expanded)) { 5714 + evalResult = this.fastEval(expanded, api, env); 5717 5715 } else { 5718 - // Try to evaluate as expression or variable 5719 - evalResult = this.evaluate(direction, api, env); 5720 - // console.log(`🎬 COAT DEBUG: Evaluated ${direction} as expression = ${evalResult}`); 5716 + // Special handling for function names like "frame" 5717 + const globalEnv = this.getGlobalEnv(); 5718 + if (globalEnv[direction] && typeof globalEnv[direction] === "function") { 5719 + // It's a function, call it with the API 5720 + evalResult = globalEnv[direction](api, []); 5721 + } else { 5722 + // Try to evaluate as expression or variable 5723 + evalResult = this.evaluate(direction, api, env); 5724 + } 5721 5725 } 5722 5726 5723 5727 // Convert result to string for the fade string ··· 9678 9682 if (VERBOSE) console.log("🏃 Body:", body); 9679 9683 // console.log("🎯 BODY DEBUG - length:", body.length, "items:", JSON.stringify(body)); 9680 9684 9681 - // 🎨 First-line color shorthand: If the first item is just a color name, 9682 - // treat it as (once (wipe <color>)) for easy backdrop setting 9685 + // 🎨 First-line color shorthand: If the first item is just a color name, 9686 + // consume it so it doesn't get evaluated as a function call. 9687 + // The actual wipe is handled by the paint setup via this.firstLineColor. 9683 9688 if (body.length > 0 && !parsed.body) { 9684 9689 const firstItem = body[0]; 9685 9690 let colorName = null; ··· 9699 9704 9700 9705 // Validate RGB(A) range (0-255) 9701 9706 if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255 && a >= 0 && a <= 255) { 9702 - const colorValues = hasAlpha ? [r, g, b, a] : [r, g, b]; 9703 - 9704 - // Apply backdrop only once per call location 9705 - const position = api.kidlispCallPosition || ""; 9706 - const backdropKey = `first_line_backdrop_rgb_${r}_${g}_${b}_${a}_${position}`; 9707 - if (!this.onceExecuted.has(backdropKey)) { 9708 - this.onceExecuted.add(backdropKey); 9709 - 9710 - // Set the background fill color for reframe operations 9711 - if (api.backgroundFill) { 9712 - api.backgroundFill(colorValues); 9713 - } 9714 - 9715 - // Apply wipe once for first-line RGB/RGBA shorthand 9716 - if (api.wipe) { 9717 - api.wipe(...colorValues); // Spread RGB or RGBA values as separate arguments 9718 - } 9719 - } else { 9720 - // Don't remove the items - let them be processed normally in subsequent frames 9721 - } 9722 - // Remove the processed items so they don't get evaluated again 9707 + // First-line color is handled by the paint setup ($.wipe(this.firstLineColor)) 9708 + // Just consume the tokens so they don't get evaluated as function calls 9723 9709 body = body.slice(hasAlpha ? 4 : 3); 9724 - 9710 + 9725 9711 // Skip the rest of the color detection since we handled RGB/RGBA 9726 9712 if (body.length === 0) { 9727 9713 return undefined; // Nothing left to evaluate ··· 9747 9733 const rgbValues = parseRGBString(colorName); 9748 9734 if (rgbValues) { 9749 9735 isValidFirstLineColor = true; 9750 - 9751 - // Apply backdrop only once per call location 9752 - const position = api.kidlispCallPosition || ""; 9753 - const backdropKey = `first_line_backdrop_${colorName}_${position}`; 9754 - if (!this.onceExecuted.has(backdropKey)) { 9755 - this.onceExecuted.add(backdropKey); 9756 - 9757 - // Set the background fill color for reframe operations 9758 - if (api.backgroundFill) { 9759 - api.backgroundFill(rgbValues); 9760 - } 9761 - 9762 - // Apply wipe once for first-line RGB shorthand 9763 - if (api.wipe) { 9764 - api.wipe(...rgbValues); // Spread RGB values as separate arguments 9765 - } 9766 - } else { 9767 - // Don't remove the first item - let it be processed normally in subsequent frames 9768 - } 9769 - // Remove the first item so it doesn't get evaluated again 9736 + // First-line color is handled by the paint setup ($.wipe(this.firstLineColor)) 9737 + // Just consume the token 9770 9738 body = body.slice(1); 9771 9739 } 9772 9740 } ··· 9799 9767 } 9800 9768 9801 9769 if (isValidFirstLineColor) { 9802 - try { 9803 - // Test if this is a color function by calling it (only for non-fade strings) 9804 - if (!colorName.startsWith("fade:") && globalEnv[colorName] && typeof globalEnv[colorName] === "function") { 9805 - globalEnv[colorName](); 9806 - } 9807 - // If we get here without error, it's a color function 9808 - // For color shortcuts, apply backdrop only once per call location 9809 - const position = api.kidlispCallPosition || ""; 9810 - const backdropKey = `first_line_backdrop_${colorName}_${position}`; 9811 - if (!this.onceExecuted.has(backdropKey)) { 9812 - this.onceExecuted.add(backdropKey); 9813 - 9814 - // if (kidlispInkLoggingEnabled()) { 9815 - // console.log('🎨 FIRST-LINE COLOR: Applying backdrop', colorName); 9816 - // } 9817 - 9818 - // Set the background fill color for reframe operations 9819 - if (api.backgroundFill) { 9820 - api.backgroundFill(colorName); 9821 - } 9822 - 9823 - // Apply wipe once for first-line color shorthand 9824 - if (api.wipe) { 9825 - api.wipe(colorName); 9826 - } 9827 - } else { 9828 - // Don't remove the first item - let it be processed normally in subsequent frames 9829 - } 9830 - // Remove the first item so it doesn't get evaluated again 9831 - body = body.slice(1); 9832 - } catch (e) { 9833 - // Not a color function, proceed normally 9834 - } 9770 + // First-line color is handled by the paint setup ($.wipe(this.firstLineColor)) 9771 + // Just consume the token so it doesn't get evaluated as a function call 9772 + body = body.slice(1); 9835 9773 } 9836 9774 } 9837 9775 } ··· 9944 9882 if (direction === "frame") { 9945 9883 evalResult = this.frameCount; 9946 9884 } else { 9947 - // Special handling for function names 9948 - const globalEnv = this.getGlobalEnv(); 9949 - if (globalEnv[direction] && typeof globalEnv[direction] === "function") { 9950 - // It's a function, call it with the API 9951 - evalResult = globalEnv[direction](api, []); 9885 + // Try expanding as fast math (e.g., "frame*80") before fallback 9886 + const expanded = this.expandFastMathMacros(direction); 9887 + if (Array.isArray(expanded)) { 9888 + evalResult = this.fastEval(expanded, api, env); 9952 9889 } else { 9953 - // Try to evaluate as expression or variable 9954 - evalResult = this.evaluate(direction, api, env); 9890 + // Special handling for function names 9891 + const globalEnv = this.getGlobalEnv(); 9892 + if (globalEnv[direction] && typeof globalEnv[direction] === "function") { 9893 + // It's a function, call it with the API 9894 + evalResult = globalEnv[direction](api, []); 9895 + } else { 9896 + // Try to evaluate as expression or variable 9897 + evalResult = this.evaluate(direction, api, env); 9898 + } 9955 9899 } 9956 9900 } 9957 9901 ··· 10259 10203 // Try to evaluate as KidLisp expression/variable 10260 10204 let evalResult; 10261 10205 10262 - // Special handling for function names like "frame" 10263 - const globalEnv = this.getGlobalEnv(); 10264 - if (globalEnv[direction] && typeof globalEnv[direction] === "function") { 10265 - // It's a function, call it with the API 10266 - evalResult = globalEnv[direction](api, []); 10206 + // Try expanding as fast math (e.g., "frame*80") before fallback 10207 + const expanded = this.expandFastMathMacros(direction); 10208 + if (Array.isArray(expanded)) { 10209 + evalResult = this.fastEval(expanded, api, env); 10267 10210 } else { 10268 - // Try to evaluate as expression or variable 10269 - evalResult = this.evaluate(direction, api, env); 10211 + // Special handling for function names like "frame" 10212 + const globalEnv = this.getGlobalEnv(); 10213 + if (globalEnv[direction] && typeof globalEnv[direction] === "function") { 10214 + // It's a function, call it with the API 10215 + evalResult = globalEnv[direction](api, []); 10216 + } else { 10217 + // Try to evaluate as expression or variable 10218 + evalResult = this.evaluate(direction, api, env); 10219 + } 10270 10220 } 10271 10221 10272 10222 // Convert result to string for the fade string