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
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,19 @@ npm install --save cereb
import { pinch } from "cereb";
import { zoom } from "cereb/operators";

let scale = 1.0;
const MIN_SCALE = 0.5, MAX_SCALE = 3.0;

// pipe creates a pipeline where signals flow through operators
// Each operator extends the signal (signals are immutable)
pinch(element)
// Operator: Determine scale value.
.pipe(zoom({ minScale: 0.5, maxScale: 3.0 })).on((signal) => {
// The scale property is extended from the value.
// - pinch emits distance → zoom calculates scale
// - zoom also works with other inputs (keyboard, wheel, etc.)
element.style.transform = `scale(${signal.value.scale})`;
// Operator: Convert ratio to scale delta
.pipe(zoom())
.on((signal) => {
// zoom outputs frame-by-frame delta, accumulate and clamp
scale += signal.value.scale;
scale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, scale));
element.style.transform = `scale(${scale})`;
});
```

Expand Down
120 changes: 90 additions & 30 deletions docs/public/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -501,21 +501,78 @@ singlePointer(canvas)

### zoom

Calculate bounded scale from ratio input.
Convert ratio input to frame-by-frame scale delta. Consumer accumulates and clamps.

```typescript
function zoom<T extends SignalWith<{ ratio: number }>>(options?: {
minScale?: number;
maxScale?: number;
baseScale?: number | (() => number);
mode?: "multiply" | "add";
}): Operator<T, T & { scale: number; deltaScale: number }>
function zoom<T extends SignalWith<{ ratio: number; phase?: string }>>(
options?: ZoomOptions
): Operator<T, T & { scale: number; deltaScale: number }>
```

**Output:**
- `scale`: Frame-by-frame scale delta (not absolute scale)
- `deltaScale`: Same as scale (deprecated)

```typescript
let scale = 1.0;
const MIN_SCALE = 0.5, MAX_SCALE = 3.0;

pinch(element)
.pipe(zoom({ minScale: 0.5, maxScale: 3.0 }))
.on((s) => element.style.transform = `scale(${s.value.scale})`);
.pipe(zoom())
.on((s) => {
scale += s.value.scale;
scale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, scale));
element.style.transform = `scale(${scale})`;
});
```

### rotate3d

Convert 2D pan to 3D rotation delta. Horizontal pan → Y-axis rotation, vertical pan → X-axis rotation.

```typescript
function rotate3d<T extends SignalWith<{ delta: Vector; phase?: string }>>(options?: {
sensitivityX?: number; // default 1.0
sensitivityY?: number; // default 1.0
invertX?: boolean; // default false
invertY?: boolean; // default false
}): Operator<T, T & { rotation: Vector; deltaRotation: Vector }>
```

**Output:**
- `rotation`: Frame-by-frame rotation delta in radians `[rx, ry, rz]`

```typescript
let rotation = [0, 0, 0];

pan(element)
.pipe(rotate3d({ sensitivityX: 0.5, sensitivityY: 0.5 }))
.on((s) => {
const [drx, dry, drz] = s.value.rotation;
rotation[0] += drx;
rotation[1] += dry;
element.style.transform = `rotateX(${rotation[0]}rad) rotateY(${rotation[1]}rad)`;
});
```

### translate

Convert pan delta to 2D translation coordinates.

```typescript
function translate<T extends SignalWith<{ delta: Vector }>>(options?: {
baseTranslate?: [number, number] | (() => [number, number]); // default [0, 0]
sensitivity?: number; // default 1.0
}): Operator<T, T & { translate: [number, number] }>
```

```typescript
pan(element)
.pipe(translate())
.on((s) => {
const [x, y] = s.value.translate;
element.style.transform = `translate(${x}px, ${y}px)`;
});
```

### when
Expand Down Expand Up @@ -650,38 +707,41 @@ singlePointer(element)
import { wheel, keyheld, keydown, pinch } from "cereb";
import { when, extend, spy, zoom } from "cereb/operators";

let currentScale = 1;
const zoomOp = () => zoom({ minScale: 0.5, maxScale: 3.0, baseScale: () => currentScale });
let scale = 1;
const MIN_SCALE = 0.5, MAX_SCALE = 3.0;
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));

const zoomMode$ = keyheld(window, { code: "KeyZ" })
.pipe(extend((s) => ({ opened: s.value.held })));

// Pinch zoom
pinch(element).pipe(zoomOp()).on(apply);
// Pinch zoom - uses delta-based zoom operator
pinch(element)
.pipe(zoom())
.on((s) => {
scale = clamp(scale + s.value.scale, MIN_SCALE, MAX_SCALE);
element.style.transform = `scale(${scale})`;
});

// Z + wheel zoom
// Z + wheel zoom - compute scale directly
wheel(element, { passive: false })
.pipe(
when(zoomMode$),
spy((s) => s.value.originalEvent.preventDefault()),
extend((s) => ({ ratio: Math.exp(-s.value.deltaY * 0.005) })),
zoomOp()
spy((s) => s.value.originalEvent.preventDefault())
)
.on(apply);
.on((s) => {
const multiplier = Math.exp(-s.value.deltaY * 0.005);
scale = clamp(scale * multiplier, MIN_SCALE, MAX_SCALE);
element.style.transform = `scale(${scale})`;
});

// Z + +/- keyboard zoom
// Z + +/- keyboard zoom - compute scale directly
keydown(window, { code: ["Equal", "Minus"] })
.pipe(
when(zoomMode$),
extend((s) => ({ ratio: s.value.code === "Equal" ? 1.2 : 1 / 1.2 })),
zoomOp()
)
.on(apply);

function apply(signal) {
currentScale = signal.value.scale;
element.style.transform = `scale(${currentScale})`;
}
.pipe(when(zoomMode$))
.on((s) => {
const multiplier = s.value.code === "Equal" ? 1.2 : 1 / 1.2;
scale = clamp(scale * multiplier, MIN_SCALE, MAX_SCALE);
element.style.transform = `scale(${scale})`;
});
```

### Drawing Application
Expand Down
4 changes: 3 additions & 1 deletion docs/public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@
- [extend](https://cereb.dev/operator-api/extend): Add properties to signal value
- [session](https://cereb.dev/operator-api/session): Group start-to-end as session
- [offset](https://cereb.dev/operator-api/offset): Element-relative coordinates
- [zoom](https://cereb.dev/operator-api/zoom): Bounded scale calculation
- [zoom](https://cereb.dev/operator-api/zoom): Convert ratio to scale delta
- [rotate3d](https://cereb.dev/operator-api/rotate3d): Convert pan to 3D rotation delta
- [translate](https://cereb.dev/operator-api/translate): Convert pan to 2D translation
- [when](https://cereb.dev/operator-api/when): Gate by another stream's state
- [throttle](https://cereb.dev/operator-api/throttle): Rate limiting
- [debounce](https://cereb.dev/operator-api/debounce): Wait for silence
Expand Down
Loading