# The two builds

> Single-file vs code-split: how the two tweakit entries load, what panel.ready means, and which to pick.

The page: https://ryankiley.github.io/tweakit/imports.html. Every page as Markdown: https://ryankiley.github.io/tweakit/llms.txt

One source tree, two builds, one API. The only observable difference is *when* heavy controls exist.

| Entry | Size | Loading |
| --- | --- | --- |
| `tweakit` | ~25 KB gzip | code-split; color engine and heavy controls dynamic-import on first use |
| `tweakit/single` | ~64 KB gzip | the single-file build; every control inlined, fully synchronous |
| `tweakit/auto` | ~25 KB gzip | the code-split entry, plus `enhance(document)` once the page has parsed, for a page of `[data-twk]` markup |
| `tweakit/css` | ~10 KB gzip | the stylesheet, the same for both builds; add it to either figure for the full weight on the wire |

The default suits an app: users who never open a picker never download the color engine. Pick `/single` for one file you can drop in, or when you want every control built the moment `tweaks()` returns. Only `tweakit/auto` and the stylesheet act on the page when imported, so a bundler drops an import of the kit that your production code never calls.

## panel.ready

`tweaks()` returns synchronously on both builds: the panel element, params and methods are live at once. On the split build, a panel whose schema needs lazy modules builds all of its controls when `panel.ready` resolves (until then the shell is an empty frame); on the single-file build, `ready` resolves immediately. On either build, a panel that persists an image control builds without its stored images and resolves `ready` once they're back. This page runs the split build; the stamp shows how long the lazy path took (the docs preload the chunks, so this is the import, not a network round trip).

```js
const stamp = target.querySelector(".im-stamp");
const t0 = performance.now();
const panel = tweaks("Ready", {
  motion: { type: "spring", stiffness: 220, damping: 18, mass: 1 }, // lazy here
  speed: [1, 0, 3, 0.1],                                            // built-in
});
mount.append(panel.el);   // the shell mounts now; controls build at ready

panel.ready.then(() => {
  stamp.textContent = `panel.ready resolved in ${Math.round(performance.now() - t0)} ms`;
});
```

`mount` is the panel's slot inside the stage; `target` is the demo surface it controls. In your own page you'd just `document.body.append(panel.el)`.

## What loads when

On the split build, these schema types trigger a dynamic import the first time any panel (or `[data-twk]` host) uses them; once loaded, they're synchronous for the rest of the session:

- **color engine**: `color`, and `gradient` and `shadow` (which build on it)
- **numeric field engine**: `number` (the boxed fields of spring, point, cubicbezier, motion, shadow and the color channels reuse it)
- **one module each**: `interval`, `sides`, `spring`, `cubicbezier`, `point`, `plot`, `image`, `tabs`
- **monitors**: `monitor` and `fpsgraph` share a module
- **motion**: `motion`, which also loads `spring` and `cubicbezier` (its two editors)

Everything else (`slider`, `text`, `checkbox`, `list`, `radiogrid`, `button`, `buttongroup`, `folder`, `separator`) ships in core and is always synchronous.

## The exports map

Everything the package ships, by import path. Types ride along with every entry.

```js
import { tweaks, enhance, mountControl, createColorPicker, gradientCss, gradientStops } from "tweakit";          // code-split
import { tweaks, enhance, mountControl, createColorPicker, gradientCss, gradientStops } from "tweakit/single";   // single-file build
import { tweaks, enhance, mountControl, createColorPicker, gradientCss, gradientStops } from "tweakit/auto";     // code-split, binds [data-twk] on load
import "tweakit/css";                                                                                          // panel styles

import type { Schema, Panel, PanelState, Theme, TweaksOptions, GradientValue, MotionValue, ShadowValue } from "tweakit";
```
