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 ( + +
  • {doc.title}
  • + + ); +} + +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 ( +
    +
    + {group.slug ? ( + + {group.title} + + ) : ( + setIsOpen(!isOpen)}> + {group.title} + + )} + +
    + {isOpen && ( + + )} +
    + ); +} + +export function Sidebar({navStructure, docsMap, onLinkClick}) { const pathname = usePathname(); const isActive = (slug) => pathname.startsWith(`/docs/${slug}`); const handleLinkClick = () => { @@ -13,18 +65,23 @@ export function Sidebar({docs, onLinkClick}) { }; return ( ); } diff --git a/app/docs/DocsLayoutClient.jsx b/app/docs/DocsLayoutClient.jsx index b3556b8..f6bb14a 100644 --- a/app/docs/DocsLayoutClient.jsx +++ b/app/docs/DocsLayoutClient.jsx @@ -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); @@ -32,7 +32,7 @@ export function DocsLayoutClient({docs, children}) {
    - +
    @@ -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 */ /**