diff --git a/.agents/docs/website.md b/.agents/docs/website.md index a4fcfbb..39b60f5 100644 --- a/.agents/docs/website.md +++ b/.agents/docs/website.md @@ -12,7 +12,7 @@ The landing page speaks for TaffyJS as a family and presents the intended comple The home page is only a landing page and uses VitePress's default theme. It contains a concise performance-led introduction, short summaries of the Rust/Taffy foundation, native Node.js performance and portable WebAssembly support, and direct routes to real package documentation and source. Tutorials, complete examples, runtime details and API reference belong to Guide or package documentation rather than the home page. The home page does not use invented testimonials, unsupported benchmark numbers or generic feature text to fill space. -The landing page includes one code-to-layout example below the default Home content. It shows ordinary direct TaffyJS JavaScript rather than JSX or a second wrapper API, and the adjacent rectangles come from that exact source through `@taffyjs/wasm` and `getLayout`. Variable names label the corresponding boxes. One `availableWidth` control demonstrates recomputation; the home page does not grow into an arbitrary code editor or a multi-control playground. +The landing page includes one code-to-layout example below the default Home content. It shows ordinary direct TaffyJS JavaScript rather than JSX or a second wrapper API, and the adjacent rectangles come from that exact source through `@taffyjs/wasm` and `getLayout`. Variable names label the corresponding boxes. Controls expose only inputs whose effects are immediately visible in those rectangles: available width, sidebar width, header height, and the horizontal and vertical gaps. The home page does not grow into an arbitrary code editor or a general-purpose playground. ## Package family @@ -24,7 +24,7 @@ No planned package receives empty reference pages or documentation navigation th ## Documentation structure -The documentation is arranged in three layers: Guide, Essentials, and package sections. Guide contains the Introduction and Getting Started. Essentials teaches the shared layout-engine model in learning order. Package sections hold complete runtime and platform support, initialization, binding-specific behavior, compatibility, migration, and exact public API details. The top navigation links to Guide and a package menu containing the implemented Node and Wasm packages. Empty package sections are not reserved in advance. +The documentation is arranged in three layers: Guide, Essentials, and package sections. Guide contains the Introduction and Getting Started. Essentials teaches the shared layout-engine model in learning order. Package sections hold complete runtime and platform support, initialization, binding-specific behavior, compatibility, migration, and exact public API details. One documentation sidebar lists Guide, Essentials, and every implemented package with its articles, so readers can see the complete documentation structure from any documentation page. The top navigation links to that documentation once as Guide rather than repeating packages in a separate menu. Empty package sections are not reserved in advance. Getting Started and Essentials use `@taffyjs/node` code by default. At points where runtime choice matters, a short callout sends browser or WebAssembly users to `@taffyjs/wasm` and users seeking Yoga compatibility to `@taffyjs/yoga`. Shared teaching is not repeated once per package. @@ -32,7 +32,7 @@ The Introduction first explains what a layout engine does, then why TaffyJS exis Getting Started owns the shortest installation command, the minimum Node.js version needed to run it, and one smallest complete `@taffyjs/node` example. It begins with the equivalent familiar HTML and CSS, keeps one Flexbox relationship stable while translating it into TaffyJS, and explicitly names where the browser analogy stops. Its job is to establish the basic layout-engine model: a tree of nodes, styles, available space, an explicit computation, and stored layout results. Changing only the available width must produce an observable second result so the reader can distinguish a node's relative style from the outside constraint. It does not branch into layout-mode tutorials, caching, error cases, or API reference details. -Getting Started keeps the runnable Node example as the canonical code path and uses `@taffyjs/wasm` for one in-page control that runs the same tree in the browser. The control varies available space while keeping the tree and styles fixed, revealing the same relationship without replacing or forking the Node example. +Getting Started keeps the runnable Node example as the canonical code path and uses `@taffyjs/wasm` for one in-page example that runs the same tree in the browser. The example shows its fixed, read-only source on the left and its available-space control and computed result on the right; the displayed source is the code being executed. The control varies available space while keeping the tree and styles fixed, revealing the same relationship without replacing or forking the Node example. Essentials continues directly from that model. It explains what each shared concept is, the role it plays, how it affects a computation, and how to use it through focused `@taffyjs/node` examples. The sequence moves from the tree-compute-read lifecycle and styles and values into Block, Flexbox, Grid, and measuring text and images. The measurement page first establishes that Taffy only lays out boxes and relies on application-owned text and image systems for intrinsic sizes, then teaches the callback boundary. These pages teach the model rather than enumerate every method or field. @@ -46,7 +46,7 @@ The target page groups are: - `@taffyjs/wasm`: package overview and setup, supported environments, initialization and deployment, observable differences from Node, and a link to the shared direct API reference. - `@taffyjs/yoga`: package overview and installation, compatibility scope, migration from Yoga, Yoga-facing API reference, and documented differences from the direct Taffy API. -The global site navigation and the documentation sidebar serve different purposes. The landing page and later real Blog or Showcase sections are site-level content; Guide and package sections are documentation. Do not create empty Blog, Showcase, version switcher or package pages merely to make the site appear complete. +The global site navigation and the documentation sidebar serve different purposes. The landing page and later real Blog or Showcase sections are site-level content; Guide, Essentials, and package sections all belong to the same documentation sidebar. Do not create empty Blog, Showcase, version switcher or package pages merely to make the site appear complete. ## Content ownership diff --git a/apps/website/.vitepress/components/HomeLayoutShowcase.vue b/apps/website/.vitepress/components/HomeLayoutShowcase.vue index 036ec45..3448d02 100644 --- a/apps/website/.vitepress/components/HomeLayoutShowcase.vue +++ b/apps/website/.vitepress/components/HomeLayoutShowcase.vue @@ -2,6 +2,10 @@ import { computed, onMounted, ref, shallowRef } from "vue"; const availableWidth = ref(380); +const sidebarWidth = ref(88); +const headerHeight = ref(40); +const horizontalGap = ref(12); +const verticalGap = ref(12); const layout = shallowRef(null); const loadError = ref(""); @@ -46,7 +50,13 @@ const visual = computed(() => { function updateLayout() { if (!computeHomeLayout) return; - layout.value = computeHomeLayout(availableWidth.value); + layout.value = computeHomeLayout({ + availableWidth: availableWidth.value, + sidebarWidth: sidebarWidth.value, + headerHeight: headerHeight.value, + horizontalGap: horizontalGap.value, + verticalGap: verticalGap.value, + }); } onMounted(async () => { @@ -76,20 +86,92 @@ onMounted(async () => {
- - +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
@@ -235,21 +317,31 @@ onMounted(async () => { background: var(--vp-c-bg-soft); } -.home-layout-result label { +.home-layout-controls { + display: grid; + gap: 14px; +} + +.home-layout-control { + display: grid; + gap: 6px; +} + +.home-layout-control label { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; } -.home-layout-result output { +.home-layout-control output { color: var(--vp-c-text-2); font-variant-numeric: tabular-nums; } -.home-layout-result input { +.home-layout-control input { width: 100%; - margin: 12px 0 24px; + margin: 0; } .home-layout-canvas { diff --git a/apps/website/.vitepress/components/LayoutWidthDemo.vue b/apps/website/.vitepress/components/LayoutWidthDemo.vue index 8c7fc3b..f4701d9 100644 --- a/apps/website/.vitepress/components/LayoutWidthDemo.vue +++ b/apps/website/.vitepress/components/LayoutWidthDemo.vue @@ -8,40 +8,20 @@ const secondX = ref(null); const loadError = ref(""); const ready = ref(false); -let tree; -let first; -let second; -let root; +let computeWidthLayout; function compute() { - if (!tree) return; + if (!computeWidthLayout) return; - tree.computeLayout({ - root, - availableSpace: { width: availableWidth.value, height: 20 }, - }); - - firstWidth.value = tree.getLayout(first).size.width; - const secondLayout = tree.getLayout(second); - secondWidth.value = secondLayout.size.width; - secondX.value = secondLayout.location.x; + const { first, second } = computeWidthLayout(availableWidth.value); + firstWidth.value = first.size.width; + secondWidth.value = second.size.width; + secondX.value = second.location.x; } onMounted(async () => { try { - const { Dimension, Display, TaffyTree } = await import("@taffyjs/wasm"); - - tree = new TaffyTree(); - first = tree.newLeaf({ flexGrow: 1 }); - second = tree.newLeaf({ flexGrow: 1 }); - root = tree.newWithChildren( - { - display: Display.Flex, - size: { width: Dimension.Percent(100), height: 20 }, - }, - [first, second], - ); - + ({ computeWidthLayout } = await import("../../examples/getting-started-width.js")); compute(); ready.value = true; } catch (error) { @@ -52,48 +32,81 @@ onMounted(async () => { diff --git a/apps/website/.vitepress/config.ts b/apps/website/.vitepress/config.ts index c8c902e..5908a84 100644 --- a/apps/website/.vitepress/config.ts +++ b/apps/website/.vitepress/config.ts @@ -1,65 +1,59 @@ import { defineConfig } from "vitepress"; +const documentationSidebar = [ + { + text: "Guide", + items: [ + { text: "Introduction", link: "/guide/" }, + { text: "Getting Started", link: "/guide/getting-started" }, + ], + }, + { + text: "Essentials", + items: [ + { text: "Tree, Compute, and Read", link: "/guide/tree-compute-read" }, + { text: "Styles and Values", link: "/guide/styles-and-values" }, + { text: "Block", link: "/guide/block" }, + { text: "Flexbox", link: "/guide/flexbox" }, + { text: "Grid", link: "/guide/grid" }, + { text: "Measuring Text and Images", link: "/guide/measuring-content" }, + ], + }, + { + text: "@taffyjs/node", + items: [ + { text: "Overview", link: "/node/" }, + { text: "Nodes and Topology", link: "/node/nodes-and-topology" }, + { text: "Styles and Context", link: "/node/styles-and-context" }, + { text: "Computing Layout", link: "/node/computing-layout" }, + { text: "Layout Results", link: "/node/layout-results" }, + { text: "Style", link: "/node/style" }, + { text: "Value Helpers", link: "/node/value-helpers" }, + { text: "Errors", link: "/node/errors" }, + ], + }, + { + text: "@taffyjs/wasm", + items: [{ text: "Overview", link: "/wasm/" }], + }, +]; + export default defineConfig({ lang: "en-US", title: "TaffyJS", description: "High-performance layout for JavaScript, powered by Taffy and Rust.", themeConfig: { nav: [ - { text: "Guide", link: "/guide/getting-started", activeMatch: "^/guide/" }, { - text: "Packages", - items: [ - { text: "@taffyjs/node", link: "/node/" }, - { text: "@taffyjs/wasm", link: "/wasm/" }, - ], + text: "Guide", + link: "/guide/getting-started", + activeMatch: "^/(guide|node|wasm)/", }, ], sidebar: { - "/guide/": [ - { - text: "Guide", - items: [ - { text: "Introduction", link: "/guide/" }, - { text: "Getting Started", link: "/guide/getting-started" }, - ], - }, - { - text: "Essentials", - items: [ - { text: "Tree, Compute, and Read", link: "/guide/tree-compute-read" }, - { text: "Styles and Values", link: "/guide/styles-and-values" }, - { text: "Block", link: "/guide/block" }, - { text: "Flexbox", link: "/guide/flexbox" }, - { text: "Grid", link: "/guide/grid" }, - { text: "Measuring Text and Images", link: "/guide/measuring-content" }, - ], - }, - ], - "/node/": [ - { - text: "@taffyjs/node", - items: [{ text: "Overview", link: "/node/" }], - }, - { - text: "TaffyTree", - items: [ - { text: "Nodes and Topology", link: "/node/nodes-and-topology" }, - { text: "Styles and Context", link: "/node/styles-and-context" }, - { text: "Computing Layout", link: "/node/computing-layout" }, - { text: "Layout Results", link: "/node/layout-results" }, - ], - }, - { text: "Style", link: "/node/style" }, - { text: "Value Helpers", link: "/node/value-helpers" }, - { text: "Errors", link: "/node/errors" }, - ], - "/wasm/": [ - { - text: "@taffyjs/wasm", - items: [{ text: "Overview", link: "/wasm/" }], - }, - ], + "/guide/": documentationSidebar, + "/node/": documentationSidebar, + "/wasm/": documentationSidebar, }, socialLinks: [{ icon: "github", link: "https://github.com/hyfdev/taffyjs" }], externalLinkIcon: true, diff --git a/apps/website/examples/getting-started-width.js b/apps/website/examples/getting-started-width.js new file mode 100644 index 0000000..3b3c7ab --- /dev/null +++ b/apps/website/examples/getting-started-width.js @@ -0,0 +1,26 @@ +import { Dimension, Display, TaffyTree } from "@taffyjs/wasm"; + +const tree = new TaffyTree(); + +const first = tree.newLeaf({ flexGrow: 1 }); +const second = tree.newLeaf({ flexGrow: 1 }); + +const root = tree.newWithChildren( + { + display: Display.Flex, + size: { width: Dimension.Percent(100), height: 20 }, + }, + [first, second], +); + +export function computeWidthLayout(availableWidth) { + tree.computeLayout({ + root, + availableSpace: { width: availableWidth, height: 20 }, + }); + + return { + first: tree.getLayout(first), + second: tree.getLayout(second), + }; +} diff --git a/apps/website/examples/home-layout.js b/apps/website/examples/home-layout.js index f7e2eaa..830fc2b 100644 --- a/apps/website/examples/home-layout.js +++ b/apps/website/examples/home-layout.js @@ -1,31 +1,37 @@ import { Dimension, Display, FlexDirection, TaffyTree } from "@taffyjs/wasm"; -const tree = new TaffyTree(); +export function computeHomeLayout({ + availableWidth, + sidebarWidth, + headerHeight, + horizontalGap, + verticalGap, +}) { + const tree = new TaffyTree(); -const sidebar = tree.newLeaf({ size: { width: 88 } }); -const header = tree.newLeaf({ size: { height: 40 } }); -const content = tree.newLeaf({ flexGrow: 1 }); + const sidebar = tree.newLeaf({ size: { width: sidebarWidth } }); + const header = tree.newLeaf({ size: { height: headerHeight } }); + const content = tree.newLeaf({ flexGrow: 1 }); -const main = tree.newWithChildren( - { - display: Display.Flex, - flexDirection: FlexDirection.Column, - flexGrow: 1, - gap: { height: 12 }, - }, - [header, content], -); + const main = tree.newWithChildren( + { + display: Display.Flex, + flexDirection: FlexDirection.Column, + flexGrow: 1, + gap: { height: verticalGap }, + }, + [header, content], + ); -const root = tree.newWithChildren( - { - display: Display.Flex, - gap: { width: 12 }, - size: { width: Dimension.Percent(100), height: 220 }, - }, - [sidebar, main], -); + const root = tree.newWithChildren( + { + display: Display.Flex, + gap: { width: horizontalGap }, + size: { width: Dimension.Percent(100), height: 220 }, + }, + [sidebar, main], + ); -export function computeHomeLayout(availableWidth) { tree.computeLayout({ root, availableSpace: { width: availableWidth, height: 220 }, diff --git a/apps/website/guide/getting-started.md b/apps/website/guide/getting-started.md index f0413be..1065a1a 100644 --- a/apps/website/guide/getting-started.md +++ b/apps/website/guide/getting-started.md @@ -89,10 +89,14 @@ The two programs express the same relationships. `newWithChildren` records the p ## Try the same relationship -This control runs the same tree through `@taffyjs/wasm` in your browser. Change only the available width and watch Taffy recompute the two child rectangles. +This example runs the same tree through `@taffyjs/wasm` in your browser. Change only the available width and watch Taffy recompute the two child rectangles. - + + +<<< ../examples/getting-started-width.js + + ## Change one input