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
83 changes: 70 additions & 13 deletions app/Sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,60 @@
import Link from "next/link";
import {usePathname} from "next/navigation";
import {cn} from "./cn.js";
import {useState} from "react";
import {ChevronRight} from "lucide-react";

export function Sidebar({docs, onLinkClick}) {
function NavItem({doc, isActive, onClick}) {
return (
<Link
href={`/docs/${doc.slug}`}
className={cn("block px-3 py-2 rounded-md hover:bg-gray-100", isActive(doc.slug) && "bg-gray-100")}
onClick={onClick}
>
<li>{doc.title}</li>
</Link>
);
}

function NavGroup({group, docsMap, isActive, onClick}) {
const pathname = usePathname();
const isGroupActive = group.slug === pathname.split("/docs/")[1] || group.items.some((item) => isActive(item.slug));
const [isOpen, setIsOpen] = useState(true);

return (
<div>
<div className={cn(isActive(group.slug) && "bg-gray-100", "flex items-center rounded-md hover:bg-gray-100")}>
{group.slug ? (
<Link href={`/docs/${group.slug}`} className={cn("flex-1 px-3 py-2")} onClick={onClick}>
<span className="font-medium">{group.title}</span>
</Link>
) : (
<span className="font-medium cursor-pointer px-3 py-2 flex-1" onClick={() => setIsOpen(!isOpen)}>
{group.title}
</span>
)}
<button
onClick={() => setIsOpen(!isOpen)}
className={cn("px-2 py-2 ")}
aria-label={isOpen ? "Collapse section" : "Expand section"}
>
<ChevronRight className={cn("w-4 h-4 transition-transform", isOpen && "rotate-90")} />
</button>
</div>
{isOpen && (
<ul className="ml-4">
{group.items.map((item) => {
const doc = docsMap[item.slug];
if (!doc) return null;
return <NavItem key={doc.slug} doc={doc} isActive={isActive} onClick={onClick} />;
})}
</ul>
)}
</div>
);
}

export function Sidebar({navStructure, docsMap, onLinkClick}) {
const pathname = usePathname();
const isActive = (slug) => pathname.startsWith(`/docs/${slug}`);
const handleLinkClick = () => {
Expand All @@ -13,18 +65,23 @@ export function Sidebar({docs, onLinkClick}) {
};
return (
<ul className={cn("h-full", "overflow-auto px-4 w-full")}>
{docs
.sort((a, b) => a.order - b.order)
.map((doc) => (
<Link
href={`/docs/${doc.slug}`}
key={doc.title}
className={cn("block px-3 py-2 rounded-md hover:bg-gray-100", isActive(doc.slug) && "bg-gray-100")}
onClick={handleLinkClick}
>
<li>{doc.title}</li>
</Link>
))}
{navStructure.map((item) => {
if (item.type === "group") {
return (
<NavGroup
key={item.slug || item.title}
group={item}
docsMap={docsMap}
isActive={isActive}
onClick={handleLinkClick}
/>
);
} else {
const doc = docsMap[item.slug];
if (!doc) return null;
return <NavItem key={doc.slug} doc={doc} isActive={isActive} onClick={handleLinkClick} />;
}
})}
</ul>
);
}
6 changes: 3 additions & 3 deletions app/docs/DocsLayoutClient.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {cn} from "../cn.js";
import {useState, useEffect, useRef} from "react";
import {TableOfContents} from "lucide-react";

export function DocsLayoutClient({docs, children}) {
export function DocsLayoutClient({navStructure, docsMap, children}) {
const [overlayOpen, setOverlayOpen] = useState(false);
const overlayRef = useRef(null);

Expand Down Expand Up @@ -32,7 +32,7 @@ export function DocsLayoutClient({docs, children}) {
<div className={cn("flex", "h-[calc(100vh-65px)] overflow-auto")}>
<div className={cn("hidden md:block")}>
<div style={{width: "320px"}} className={cn("pt-4 h-full")}>
<Sidebar docs={docs} />
<Sidebar navStructure={navStructure} docsMap={docsMap} />
</div>
</div>
<div className={cn("flex-1", "overflow-auto relative")}>
Expand All @@ -54,7 +54,7 @@ export function DocsLayoutClient({docs, children}) {
<div className={cn("absolute inset-0 bg-black bg-opacity-50")} />
<div ref={overlayRef} className={cn("absolute left-0 top-0 h-full w-80 bg-white shadow-xl overflow-auto")}>
<div className={cn("pt-4")}>
<Sidebar docs={docs} onLinkClick={() => setOverlayOpen(false)} />
<Sidebar navStructure={navStructure} docsMap={docsMap} onLinkClick={() => setOverlayOpen(false)} />
</div>
</div>
</div>
Expand Down
1 change: 0 additions & 1 deletion app/docs/animations-authoring.recho.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/**
* @title Animations Authoring
* @order 5
*/

/**
Expand Down
19 changes: 19 additions & 0 deletions app/docs/api-echo-clear.recho.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* @title echo.clear()
*/

/**
* ============================================================================
* = echo.clear() =
* ============================================================================
*
* Clear the output of the current block.
*
* @returns {void}
*/

{
echo("Hello, World!");
setTimeout(() => echo.clear(), 1000);
}

37 changes: 37 additions & 0 deletions app/docs/api-echo.recho.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* @title echo(...values)
*/

/**
* ============================================================================
* = echo(...values) =
* ============================================================================
*
* Echos one or more values inline with your code as comments. If only one
* value is provided, return the value itself. If multiple values are provided,
* return all the values as an array.
*
* @param {...any} values - The values to echo.
* @returns {any} The values if multiple values are provided, or the single value.
*/

//➜ "Hello, World!"
echo("Hello, World!");

//➜ 1 2 3
echo(1, 2, 3);

//➜ Peter: Age = 20
//➜ Height = 180
echo("Peter: ", "Age = 20\nHeight = 180");

const a = echo(1 + 2);

//➜ 3
echo(a);

const numbers = echo(1, 2, 3);

//➜ [ 1, 2, 3 ]
echo(numbers);

41 changes: 41 additions & 0 deletions app/docs/api-inspect.recho.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* @title recho.inspect(value[, options])
*/

/**
* ============================================================================
* = recho.inspect(value[, options]) =
* ============================================================================
*
* Formats a value for inspection with customizable options.
*
* @param {any} value - The value to inspect.
* @param {Object} [options] - The options to format the output.
* @param {string} [options.quote="double"] - The quote style of the output ("single", "double", or false).
* @param {number} [options.indent=null] - The indentation of the output (null, "\t", or a positive integer).
* @param {number} [options.limit=200] - The character limit of the output.
* @returns {string} The formatted string representation of the value.
*/

//➜ "Hello, World!"
const defaultQuotedString = echo("Hello, World!");

//➜ 'Hello, World!'
const singleQuotedString = echo(recho.inspect("Hello, World!", {quote: "single"}));

//➜ "Hello, World!"
const doubleQuotedString = echo(recho.inspect("Hello, World!", {quote: "double"}));

//➜ Hello, World!
const unquotedString = echo(recho.inspect("Hello, World!", {quote: false}));

//➜ {
//➜ a: 1,
//➜ b: 2,
//➜ c: 3
//➜ }
const indentedObject = echo(recho.inspect({a: 1, b: 2, c: 3}, {indent: 2}));

//➜ [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
const array1000 = echo(recho.inspect(new Array(1000).fill(0), {limit: Infinity}));

20 changes: 20 additions & 0 deletions app/docs/api-interval.recho.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @title recho.interval(milliseconds)
*/

/**
* ============================================================================
* = recho.interval(milliseconds) =
* ============================================================================
*
* Returns a generator that yields values at a specified interval.
*
* @param {number} milliseconds - The interval in milliseconds.
* @returns {Generator<number>}
*/

const interval = recho.interval(1000);

//➜ 1
echo(interval);

29 changes: 29 additions & 0 deletions app/docs/api-invalidation.recho.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* @title invalidation()
*/

/**
* ============================================================================
* = invalidation() =
* ============================================================================
*
* Returns a promise that resolves before re-running the current block.
*
* @returns {Promise<void>}
*/

//➜ 9
{
let count = echo(10);

const timer = setInterval(() => {
if (count-- <= 0) clearInterval(timer);
else {
echo.clear();
echo(count);
}
}, 1000);

invalidation.then(() => clearInterval(timer));
}

19 changes: 19 additions & 0 deletions app/docs/api-now.recho.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* @title recho.now()
*/

/**
* ============================================================================
* = recho.now() =
* ============================================================================
*
* Returns a generator that yields the current time continuously.
*
* @returns {Generator<number>}
*/

const now = recho.now();

//➜ 1757422825350
echo(now);

64 changes: 64 additions & 0 deletions app/docs/api-number.recho.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* @title recho.number(value[, options])
*/

/**
* ============================================================================
* = recho.number(value[, options]) =
* ============================================================================
*
* Creates an interactive number input control that returns a constrained number.
* In the editor, this renders as increment and decrement buttons around the
* number value.
*
* @param {number} value - The initial number value.
* @param {Object} [options] - The options to constrain the number.
* @param {number} [options.min=-Infinity] - The minimum allowed value.
* @param {number} [options.max=Infinity] - The maximum allowed value.
* @param {number} [options.step=1] - The step size for increment/decrement operations.
* @returns {number} The number value, constrained to the specified range.
*/

const count = recho.number(5);

const volume = recho.number(5.5, {min: 0, max: 10, step: 0.1});

//➜ "Volume ▓▓▓▓▓▒▒▒▒▒ 55%"
{
const filled = "▓".repeat(Math.floor(volume));
const empty = "▒".repeat(Math.ceil(10 - volume));
echo(`Volume ${filled}${empty} ${volume * 10}%`);
}

const signal = recho.number(0b1010101010101010, {min: 0, max: 0xffff});

//➜ ╶┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐
//➜ └┘└┘└┘└┘└┘└┘└┘└
{
let upper = "";
let lower = "";
let lastBit = null;
for (const bit of signal.toString(2)) {
if (lastBit === null) {
upper += bit == "1" ? "╶" : " ";
lower += bit == "1" ? " " : "╶";
} else if (lastBit === bit) {
upper += bit == "1" ? "─" : " ";
lower += bit == "1" ? " " : "─";
} else {
upper += bit == "1" ? "┌" : "┐";
lower += bit == "1" ? "┘" : "└";
}
lastBit = bit;
}
echo(upper + "\n" + lower);
}

const temperature = recho.number(24, {min: -10, max: 40, step: 0.5});

//➜ "The room temperature is 24 °C (75.2 °F)."
{
const fahrenheit = (temperature * 9) / 5 + 32;
echo(`The room temperature is ${temperature} °C (${fahrenheit} °F).`);
}

24 changes: 24 additions & 0 deletions app/docs/api-radio.recho.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @title recho.radio(index, options)
*/

/**
* ============================================================================
* = recho.radio(index, options) =
* ============================================================================
*
* Creates an interactive radio button group that returns the selected option.
* In the editor, this renders as radio buttons next to each option in the array.
*
* @param {number} index - The index of the selected option (0-based).
* @param {Array} options - The array of options to choose from.
* @returns {any} The selected option from the options array.
*/

const size = recho.radio(1, ["small", "medium", "large"]);

const color = recho.radio(0, ["red", "green", "blue"]);

//➜ "This is a red medium button."
echo(`This is a ${color} ${size} button.`);

Loading