diff --git a/app/Sidebar.js b/app/Sidebar.js
index 2cd2860..d9bcb36 100644
--- a/app/Sidebar.js
+++ b/app/Sidebar.js
@@ -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 (
+
+
@@ -54,7 +54,7 @@ export function DocsLayoutClient({docs, children}) {
- setOverlayOpen(false)} />
+ setOverlayOpen(false)} />
diff --git a/app/docs/animations-authoring.recho.js b/app/docs/animations-authoring.recho.js
index 1e60c81..22e97a8 100644
--- a/app/docs/animations-authoring.recho.js
+++ b/app/docs/animations-authoring.recho.js
@@ -1,6 +1,5 @@
/**
* @title Animations Authoring
- * @order 5
*/
/**
diff --git a/app/docs/api-echo-clear.recho.js b/app/docs/api-echo-clear.recho.js
new file mode 100644
index 0000000..6bd9b64
--- /dev/null
+++ b/app/docs/api-echo-clear.recho.js
@@ -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);
+}
+
diff --git a/app/docs/api-echo.recho.js b/app/docs/api-echo.recho.js
new file mode 100644
index 0000000..c8514ae
--- /dev/null
+++ b/app/docs/api-echo.recho.js
@@ -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);
+
diff --git a/app/docs/api-inspect.recho.js b/app/docs/api-inspect.recho.js
new file mode 100644
index 0000000..6452689
--- /dev/null
+++ b/app/docs/api-inspect.recho.js
@@ -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}));
+
diff --git a/app/docs/api-interval.recho.js b/app/docs/api-interval.recho.js
new file mode 100644
index 0000000..d9b13e9
--- /dev/null
+++ b/app/docs/api-interval.recho.js
@@ -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
}
+ */
+
+const interval = recho.interval(1000);
+
+//➜ 1
+echo(interval);
+
diff --git a/app/docs/api-invalidation.recho.js b/app/docs/api-invalidation.recho.js
new file mode 100644
index 0000000..6fcc3a1
--- /dev/null
+++ b/app/docs/api-invalidation.recho.js
@@ -0,0 +1,29 @@
+/**
+ * @title invalidation()
+ */
+
+/**
+ * ============================================================================
+ * = invalidation() =
+ * ============================================================================
+ *
+ * Returns a promise that resolves before re-running the current block.
+ *
+ * @returns {Promise}
+ */
+
+//➜ 9
+{
+ let count = echo(10);
+
+ const timer = setInterval(() => {
+ if (count-- <= 0) clearInterval(timer);
+ else {
+ echo.clear();
+ echo(count);
+ }
+ }, 1000);
+
+ invalidation.then(() => clearInterval(timer));
+}
+
diff --git a/app/docs/api-now.recho.js b/app/docs/api-now.recho.js
new file mode 100644
index 0000000..5053e99
--- /dev/null
+++ b/app/docs/api-now.recho.js
@@ -0,0 +1,19 @@
+/**
+ * @title recho.now()
+ */
+
+/**
+ * ============================================================================
+ * = recho.now() =
+ * ============================================================================
+ *
+ * Returns a generator that yields the current time continuously.
+ *
+ * @returns {Generator}
+ */
+
+const now = recho.now();
+
+//➜ 1757422825350
+echo(now);
+
diff --git a/app/docs/api-number.recho.js b/app/docs/api-number.recho.js
new file mode 100644
index 0000000..56d83dd
--- /dev/null
+++ b/app/docs/api-number.recho.js
@@ -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).`);
+}
+
diff --git a/app/docs/api-radio.recho.js b/app/docs/api-radio.recho.js
new file mode 100644
index 0000000..8ce69ab
--- /dev/null
+++ b/app/docs/api-radio.recho.js
@@ -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.`);
+
diff --git a/app/docs/api-reference.recho.js b/app/docs/api-reference.recho.js
index 9f9e4a5..b1d75f2 100644
--- a/app/docs/api-reference.recho.js
+++ b/app/docs/api-reference.recho.js
@@ -1,288 +1,42 @@
/**
* @title API Reference
- * @order 9
*/
/**
* ============================================================================
- * = API Reference =
+ * = API Reference =
* ============================================================================
*
- * Recho Notebook provides a set of APIs to help you create notebooks.
- */
-
-/**
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * 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.
+ * Recho Notebook provides a set of APIs to help you create reactive notebooks
+ * and interactive visualizations. This page provides an overview of all
+ * available APIs.
*
- * @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);
-
-/**
+ * Click on any API below to see detailed documentation and examples.
+ *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * recho.inspect(value[, options])
+ * Core APIs
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
- * Formats a value for inspection with customizable options.
+ * - echo(...values) - Echo values inline with your code as comments (https://recho.dev/notebook/docs/api-echo)
+ * - echo.clear() - Clear the output of the current block (https://recho.dev/notebook/docs/api-echo-clear)
+ * - invalidation() - Promise that resolves before re-running the current block (https://recho.dev/notebook/docs/api-invalidation)
+ * - recho.inspect(value[, options]) - Format values for inspection (https://recho.dev/notebook/docs/api-inspect)
*
- * @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}));
-
-/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * echo.clear()
+ * Inputs
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * Clear the output of the current block.
- *
- * @returns {void}
- */
-
-{
- echo("Hello, World!");
- setTimeout(() => echo.clear(), 1000);
-}
-
-/**
+ * - recho.toggle(value) - Interactive toggle (checkbox) control (https://recho.dev/notebook/docs/api-toggle)
+ * - recho.radio(index, options) - Interactive radio button group (https://recho.dev/notebook/docs/api-radio)
+ * - recho.number(value[, options]) - Interactive number input control (https://recho.dev/notebook/docs/api-number)
+ *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * invalidation()
+ * Generators
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * Returns a promise that resolves before re-running the current block.
- *
- * @returns {Promise}
- */
-
-//➜ 9
-{
- let count = echo(10);
-
- const timer = setInterval(() => {
- if (count-- <= 0) clearInterval(timer);
- else {
- echo.clear();
- echo(count);
- }
- }, 1000);
-
- invalidation.then(() => clearInterval(timer));
-}
-
-/**
+ * - recho.now() - Generator that yields the current time continuously (https://recho.dev/notebook/docs/api-now)
+ * - recho.interval(milliseconds) - Generator that yields values at intervals (https://recho.dev/notebook/docs/api-interval)
+ *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * recho.now()
+ * Helpers
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * Returns a generator that yields the current time continuously.
- *
- * @returns {Generator}
+ * - recho.require(...names) - Import JavaScript packages from npm (https://recho.dev/notebook/docs/api-require)
*/
-
-const now = recho.now();
-
-//➜ 1757422825350
-echo(now);
-
-/**
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * recho.interval(milliseconds)
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * Returns a generator that yields values at a specified interval.
- *
- * @param {number} milliseconds - The interval in milliseconds.
- * @returns {Generator}
- */
-
-const interval = recho.interval(1000);
-
-//➜ 1
-echo(interval);
-
-/**
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * recho.require(...names)
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * Imports one or more JavaScript packages. The import specifiers must be valid
- * npm package names with optional version specifiers. It use `d3-require`
- * under the hood.
- *
- * @param {string} ...names - The names of the packages to import.
- * @returns {any} The imported package.
- * @see https://github.com/d3/d3-require
- */
-
-const Noise = recho.require("perlin-noise-3d");
-
-//➜ [ 0.5428002520733116, 0.5424832952636395, 0.5414633391270067, 0.5397183031066122…
-{
- const noise = new Noise();
- const values = [];
- for (let i = 0; i < 100; i++) {
- values.push(noise.get(i / 100, i / 100, i / 100));
- }
- echo(recho.inspect(values, {limit: 80}));
-}
-
-const d3 = recho.require("d3-array", "d3-random");
-
-//➜ [ 6, 4, 1, 2, 5, 3, 3, 0, 6, 2 ]
-echo(d3.range(10).map(d3.randomInt(0, 10)));
-
-/**
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * recho.toggle(value)
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * Creates an interactive toggle (checkbox) control that returns the boolean
- * value. In the editor, this renders as a clickable checkbox that updates the
- * code when toggled.
- *
- * @param {boolean} value - The initial boolean value.
- * @returns {boolean} The boolean value.
- */
-
-const isEnabled = recho.toggle(true);
-
-const isVisible = recho.toggle(false);
-
-//➜ "The button is enabled."
-//➜ "The button is hidden."
-{
- echo(`The button is ${isEnabled ? "enabled" : "disabled"}.`);
- echo(`The button is ${isVisible ? "visible" : "hidden"}.`);
-}
-
-/**
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * 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.`);
-
-/**
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * 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).`);
-}
diff --git a/app/docs/api-require.recho.js b/app/docs/api-require.recho.js
new file mode 100644
index 0000000..484e95f
--- /dev/null
+++ b/app/docs/api-require.recho.js
@@ -0,0 +1,35 @@
+/**
+ * @title recho.require(...names)
+ */
+
+/**
+ * ============================================================================
+ * = recho.require(...names) =
+ * ============================================================================
+ *
+ * Imports one or more JavaScript packages. The import specifiers must be valid
+ * npm package names with optional version specifiers. It use `d3-require`
+ * under the hood.
+ *
+ * @param {string} ...names - The names of the packages to import.
+ * @returns {any} The imported package.
+ * @see https://github.com/d3/d3-require
+ */
+
+const Noise = recho.require("perlin-noise-3d");
+
+//➜ [ 0.5428002520733116, 0.5424832952636395, 0.5414633391270067, 0.5397183031066122…
+{
+ const noise = new Noise();
+ const values = [];
+ for (let i = 0; i < 100; i++) {
+ values.push(noise.get(i / 100, i / 100, i / 100));
+ }
+ echo(recho.inspect(values, {limit: 80}));
+}
+
+const d3 = recho.require("d3-array", "d3-random");
+
+//➜ [ 6, 4, 1, 2, 5, 3, 3, 0, 6, 2 ]
+echo(d3.range(10).map(d3.randomInt(0, 10)));
+
diff --git a/app/docs/api-toggle.recho.js b/app/docs/api-toggle.recho.js
new file mode 100644
index 0000000..39464a5
--- /dev/null
+++ b/app/docs/api-toggle.recho.js
@@ -0,0 +1,28 @@
+/**
+ * @title recho.toggle(value)
+ */
+
+/**
+ * ============================================================================
+ * = recho.toggle(value) =
+ * ============================================================================
+ *
+ * Creates an interactive toggle (checkbox) control that returns the boolean
+ * value. In the editor, this renders as a clickable checkbox that updates the
+ * code when toggled.
+ *
+ * @param {boolean} value - The initial boolean value.
+ * @returns {boolean} The boolean value.
+ */
+
+const isEnabled = recho.toggle(true);
+
+const isVisible = recho.toggle(false);
+
+//➜ "The button is enabled."
+//➜ "The button is hidden."
+{
+ echo(`The button is ${isEnabled ? "enabled" : "disabled"}.`);
+ echo(`The button is ${isVisible ? "visible" : "hidden"}.`);
+}
+
diff --git a/app/docs/aynchronous-operations.recho.js b/app/docs/aynchronous-operations.recho.js
index d5ff0cf..4af3518 100644
--- a/app/docs/aynchronous-operations.recho.js
+++ b/app/docs/aynchronous-operations.recho.js
@@ -1,6 +1,5 @@
/**
* @title Asynchronous Operations
- * @order 6
*/
/**
diff --git a/app/docs/errors-handling.recho.js b/app/docs/errors-handling.recho.js
index bd36571..fa4ced9 100644
--- a/app/docs/errors-handling.recho.js
+++ b/app/docs/errors-handling.recho.js
@@ -1,6 +1,5 @@
/**
* @title Errors Handling
- * @order 8
*/
//➜ { [SyntaxError: Identifier directly after number (17:9)] pos: 611, loc: Position { line: 17, column: 9 }, raisedAt: 611, [Symbol(next.console.error.digest)]: "NEXT_CONSOLE_ERROR" }
diff --git a/app/docs/getting-started.recho.js b/app/docs/getting-started.recho.js
index 622be99..d98b049 100644
--- a/app/docs/getting-started.recho.js
+++ b/app/docs/getting-started.recho.js
@@ -1,6 +1,5 @@
/**
* @title Getting Started
- * @order 2
*/
/**
diff --git a/app/docs/inline-echoing.recho.js b/app/docs/inline-echoing.recho.js
index 646f531..eb4d2db 100644
--- a/app/docs/inline-echoing.recho.js
+++ b/app/docs/inline-echoing.recho.js
@@ -1,6 +1,5 @@
/**
* @title Inline Echoing
- * @order 3
*/
/**
diff --git a/app/docs/introduction.recho.js b/app/docs/introduction.recho.js
index 4ff0289..3a03d21 100644
--- a/app/docs/introduction.recho.js
+++ b/app/docs/introduction.recho.js
@@ -1,6 +1,5 @@
/**
* @title Introduction
- * @order 1
*/
/**
diff --git a/app/docs/layout.jsx b/app/docs/layout.jsx
index 6ff841c..77561ac 100644
--- a/app/docs/layout.jsx
+++ b/app/docs/layout.jsx
@@ -1,5 +1,6 @@
import {getAllJSDocs} from "../utils.js";
import {DocsLayoutClient} from "./DocsLayoutClient.jsx";
+import {docsNavConfig} from "./nav.config.js";
export const metadata = {
title: "Docs | Recho Notebook",
@@ -8,5 +9,16 @@ export const metadata = {
export default function Layout({children}) {
const docs = getAllJSDocs();
- return {children};
+
+ // Create a map of slug -> doc for easy lookup
+ const docsMap = docs.reduce((acc, doc) => {
+ acc[doc.slug] = doc;
+ return acc;
+ }, {});
+
+ return (
+
+ {children}
+
+ );
}
diff --git a/app/docs/libraries-imports.recho.js b/app/docs/libraries-imports.recho.js
index 95ecfde..8b12094 100644
--- a/app/docs/libraries-imports.recho.js
+++ b/app/docs/libraries-imports.recho.js
@@ -1,6 +1,5 @@
/**
* @title Libraries Imports
- * @order 7
*/
/**
diff --git a/app/docs/nav.config.js b/app/docs/nav.config.js
new file mode 100644
index 0000000..0bd0af9
--- /dev/null
+++ b/app/docs/nav.config.js
@@ -0,0 +1,92 @@
+/**
+ * Navigation configuration for the docs section.
+ * Defines the structure and grouping of documentation pages.
+ */
+
+export const docsNavConfig = [
+ {
+ type: "page",
+ slug: "introduction",
+ },
+ {
+ type: "page",
+ slug: "getting-started",
+ },
+ {
+ type: "group",
+ title: "Features",
+ items: [
+ {
+ type: "page",
+ slug: "inline-echoing",
+ },
+ {
+ type: "page",
+ slug: "reactive-blocks",
+ },
+ {
+ type: "page",
+ slug: "animations-authoring",
+ },
+ {
+ type: "page",
+ slug: "aynchronous-operations",
+ },
+ {
+ type: "page",
+ slug: "libraries-imports",
+ },
+ {
+ type: "page",
+ slug: "errors-handling",
+ },
+ ],
+ },
+ {
+ type: "group",
+ title: "API Reference",
+ slug: "api-reference",
+ items: [
+ {
+ type: "page",
+ slug: "api-echo",
+ },
+ {
+ type: "page",
+ slug: "api-echo-clear",
+ },
+ {
+ type: "page",
+ slug: "api-inspect",
+ },
+ {
+ type: "page",
+ slug: "api-invalidation",
+ },
+ {
+ type: "page",
+ slug: "api-now",
+ },
+ {
+ type: "page",
+ slug: "api-interval",
+ },
+ {
+ type: "page",
+ slug: "api-require",
+ },
+ {
+ type: "page",
+ slug: "api-toggle",
+ },
+ {
+ type: "page",
+ slug: "api-radio",
+ },
+ {
+ type: "page",
+ slug: "api-number",
+ },
+ ],
+ },
+];
diff --git a/app/docs/reactive-blocks.recho.js b/app/docs/reactive-blocks.recho.js
index c002b7d..dc0e909 100644
--- a/app/docs/reactive-blocks.recho.js
+++ b/app/docs/reactive-blocks.recho.js
@@ -1,6 +1,5 @@
/**
* @title Reactive Blocks
- * @order 4
*/
/**