Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions app/Editor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ const styles = {
iconButton: "w-4 h-4 hover:scale-110 transition-transform duration-100",
};

function metaKey() {
return navigator.userAgent.includes("Mac") ? "cmd" : "ctrl";
}

export function Editor({
initialCode,
onUserInput = () => {},
Expand Down Expand Up @@ -75,6 +71,10 @@ export function Editor({
editorRef.current.run();
}

function metaKey() {
return typeof navigator !== "undefined" || navigator.userAgent.includes("Mac") ? "cmd" : "ctrl";
}

return (
<div className={cn("w-full border border-gray-200 rounded-md")}>
<div
Expand Down
191 changes: 191 additions & 0 deletions app/examples/donut.recho.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/**
* @title Donut, Donut, and Donut!!!
* @author Bairui Su
* @created 2025-09-30
* @pull_request 133
* @github pearmini
*/

/**
* ============================================================================
* = Donut, Donut, and Donut!!! =
* ============================================================================
*
* > This first donut code is ported from https://www.a1k0n.net/js/donut.js,
* > the original author is Alex Naka, please refer to the original post for
* > more math details: https://www.a1k0n.net/2011/07/20/donut-math.html
*
* In this example, we draw a donut by a "donut" code, and the code for
* formatting the "donut" code is also a "donut" code. So, there are three
* donuts in this example!!!
*/

// Click to change the rotation speed of the donut.
const S = recho.number(10, {min: 5, max: 30, step: 1});

const T = recho.interval(30);

//➜
//➜
//➜
//➜ $$$@@@@@@
//➜ #####**###$$$$@@@@$
//➜ ##**!!!==!!!**##$$$@@@$$
//➜ ##*!=!===;;:;=!!*##$$$$$$$$#
//➜ *#**!!=;::~~~~::==**##$$$$$$$#*
//➜ ###*!=;:~-,...,-~;=!*###$$$$$$##*
//➜ !####*!=:~,.......~;=!*###$$$$$$##*
//➜ =#$$$##*=:-..... .~;!!*####$$$$###*!
//➜ *$$$$$#*!;~.. ~=!**###########**=
//➜ !#$$@@$$#*=~. :=!**###########**!=
//➜ !#$@@@@$$#*; ;!!***##########***!!;
//➜ !#$$@@@@$$#*!!!!****##########****!!=:
//➜ =*#$$$$$$$#################*****!!!=:
//➜ ;!*##$$$$##############*******!!!=;;
//➜ :;!**############*********!*!!!==;:
//➜ :=!!!***************!*!*!!!!==;:~
//➜ :;!===!***!!!!!!!!!!!!!!===;:~-
//➜ ~:;=====!!!!!!!!!====;;;::~,
//➜ -~::;;;===;=;;;;;;:::~-.
//➜
{

var b=[];var z=[];
var A=1+(T*0.07*S)/10;var
B=1+(T*0.03*S)/10;var sin=/***/
Math.sin;var cos=Math.cos;var cA=
cos(A),sA=sin(A),cB=cos(B),sB=sin(B);
for(var k=0;k<1760;k++){b[k]=k%80==79?
"\n":" ";z[k]=0;}for (var j=0;j<6.28;j+=
0.07){var ct=/**/ cos(j),st=sin(j);
for(var i=0;i< 6.28;i+=0.02){
var sp=sin(i), cp=cos(i),h=ct
+2,D=1/(sp*h* sA+st*cA+5),t
=sp*h*cA-st*sA; var x=0|(40+30
*D*(cp*h*cB-t* sB)),y=0|(12+15
*D*(cp*h*sB+t*/**/ cB)),o=x+80*y,N=
0|(8*((st*sA-sp*ct* cA)*cB-sp*ct*sA-st*cA
-cp*ct*sB));if(y<22&&y>=0&&x>=0&&x<79&&
D>z[o]){z[o]=D;b[o]=".,-~:;=!*#$@"[N>
0?N:0];}}}echo(b.join(""));/*****
*******************************
*************************
******************/

}


/**
* We use a format function to format the code for drawing the
* donut. The readable code is as follows:
*/

echo(format(`
var b = [];
var z = [];
var A = 1 + (T * 0.07 * S) / 10;
var B = 1 + (T * 0.03 * S) / 10;
var sin = Math.sin;
var cos = Math.cos;
var cA = cos(A),
sA = sin(A),
cB = cos(B),
sB = sin(B);
for (var k = 0; k < 1760; k++) {
b[k] = k % 80 == 79 ? "\\n" : " ";
z[k] = 0;
}
for (var j = 0; j < 6.28; j += 0.07) {
var ct = cos(j),
st = sin(j);
for (var i = 0; i < 6.28; i += 0.02) {
var sp = sin(i),
cp = cos(i),
h = ct + 2,
D = 1 / (sp * h * sA + st * cA + 5),
t = sp * h * cA - st * sA;
var x = 0 | (40 + 30 * D * (cp * h * cB - t * sB)),
y = 0 | (12 + 15 * D * (cp * h * sB + t * cB)),
o = x + 80 * y,
N = 0 | (8 * ((st * sA - sp * ct * cA) * cB - sp * ct * sA - st * cA - cp * ct * sB));
if (y < 22 && y >= 0 && x >= 0 && x < 79 && D > z[o]) {
z[o] = D;
b[o] = ".,-~:;=!*#$@"[N > 0 ? N : 0];
}
}
}
echo(b.join(""));
`));

/**
* !!! The format function is also a "donut" code !!!
*/

function /********/
format(code,{w=80,h=24,K=/**/
["var"],R1=11,R2=4}={}){let n=w
*h,mi=-1,M=new Array(n).fill(null),
T=code.replace('" "',"__")/**********/
.replace(/\s+/g," ").split(" ").filter((d)=>
d!=="").map((d)=>d.replace("__",'" "')),tn=
T.length;for(let i=0;i <n;i++){let x=i%w,y=/**/
Math.floor(i/w),d= Math.hypot((x-w/2)
/2,y-h/2),m=d<R1&& d>R2;if(m)mi=i;/**/
M[i]=m?1:x===w-1 ?"\n":" ";}let ni=
-1;for(let i=0, wi=0;i<n;i++){if
(M[i]!==1)/****/ continue;if(wi<
tn){let s=T[wi];if (K.some((k)=>/**/
s.includes(k)))s=s +" ";let si=0;while
(M[i+si]===1)si++;if(si<s.length-1)s=si-1<=0?
" ":"/*"+"*".repeat(Math.max(0,si-4))+"*/";
else wi++;for(let k=0;k<s.length;k++)M[(ni
=i+k)]=s[k];}else if(i-ni<=2)M[i]="/*"[i
-ni-1];else if(mi-i<=1)M[i]="/*"[mi-i];
else M[i]="*";}return M.join("")
;}/**************************
******************/

/**
* We use format function to format itself. Note that we have to manually
* adjust a little bit to make sure the formatted code actually runs,
* including changing "" to " ", and unwrapping `M.join("")`, etc.
*/

echo(format(`
function format(code, {w = 80, h = 24, K = ["var"], R1 = 11, R2 = 4 } = {}) {
let n = w * h,
mi = -1,
M = new Array(n).fill(null),
T = code
.replace('" "', "__")
.replace(/\s+/g, " ")
.split(" ")
.filter((d) => d !== "")
.map((d) => d.replace("__", '" "')),
tn = T.length;
for (let i = 0; i < n; i++) {
let x = i % w,
y = Math.floor(i / w),
d = Math.hypot((x - w / 2) / 2, y - h / 2),
m = d < R1 && d > R2;
if (m) mi = i;
M[i] = m ? 1 : x === w - 1 ? "\n" : " ";
}
let ni = -1;
for (let i = 0, wi = 0; i < n; i++) {
if (M[i] !== 1) continue;
if (wi < tn) {
let s = T[wi];
if (K.some((k) => s.includes(k))) s = s + " ";
let si = 0;
while (M[i + si] === 1) si++;
if (si < s.length - 1) s = si - 1 <= 0 ? " " : "/*" + "*".repeat(Math.max(0, si - 4)) + "*/";
else wi++;
for (let k = 0; k < s.length; k++) M[(ni = i + k)] = s[k];
} else if (i - ni <= 2) M[i] = "/\*"[i - ni - 1];
else if (mi - i <= 1) M[i] = "/\*"[mi - i];
else M[i] = "*";
}
return M.join("");
}
`, {K:["let", "function", "new", "else", "return"], R1:12}));
Binary file added app/examples/donut.snap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 5 additions & 2 deletions app/examples/matrix-rain.recho.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* a way to render **shapes** in Recho.
*
* My implementation is basically a **particle system**. Each column of the
* matrix is a particle. Each particle has a lifespan, a initial y position,
* matrix is a particle. Each particle has a lifespan, a initial y position,
* and a set of characters. The following rules are applied to each particle:
*
* - If a particle is dead (lifespan < 0), it will be reset,
Expand Down Expand Up @@ -93,7 +93,10 @@ const columns = d3.range(width).map(() => createColumn(height));
for (let j = 0; j < width; ++j) output += buffer[i * width + j];
output += i === height - 1 ? "" : "\n";
}
output = output.split("\n").map((d) => " " + d).join("\n");
output = output
.split("\n")
.map((d) => " " + d)
.join("\n");

echo(output);
}
Expand Down