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
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ Compose: `effect name = sepia >> contrast(1.5) >> vignette;`

Types: `crossfade`, `slideLeft`, `slideRight`, `slideUp`, `slideDown`, `wipe`, `fadeBlack`, `zoom`

Easing: `linear`, `easeIn`, `easeOut`, `easeInOut`, `bounce`
Easing: `linear`, `easeIn`, `easeOut`, `easeInOut`

Syntax: `--- crossfade 300ms easeInOut ---` between gif entries

Expand Down Expand Up @@ -194,7 +194,7 @@ Version lives in the `VERSION` file (single source of truth). CMake injects it a
- `MAC_OUTPUT_DIR` env var overrides default output directory (`~/mac/output/`)
- Output paths are sanitized (directory components stripped) to prevent path traversal
- Template resolution validates canonical paths stay under script/binary directories
- Max image dimensions clamped to 4096x4096, max GIF frames capped at 200
- Max image dimensions clamped to 4096x4096, max GIF frames capped at 500

## Code Style

Expand Down Expand Up @@ -222,7 +222,7 @@ Version lives in the `VERSION` file (single source of truth). CMake injects it a
| `include/MacEnum.h` | Enum sum type (MacEnumDef, MacEnum) |
| `include/MacLambda.h` | Lambda/closure with tail expression support |
| `include/MacMeme.h` | Template map, resolveTemplate, path traversal protection |
| `include/MacGif.h` | GIF frame storage, frame limit (200 max) |
| `include/MacGif.h` | GIF frame storage, frame limit (500 max) |
| `include/GifLimits.h` | `MAX_GIF_FRAMES` constant |
| `include/MacAnalyzer.h` | Semantic analyzer entry point |
| `include/AnalyzerWalk.h` | AST walk for analysis (match binding scope fix) |
Expand Down
12 changes: 9 additions & 3 deletions docs/reference/src/meme/effects.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ These take a numeric argument and return a function that transforms a meme.

| Effect | Parameter | Description |
|--------|-----------|-------------|
| `blur(radius)` | 1-20 | Gaussian blur |
| `blur(radius)` | 1-20 | Box blur |
| `pixelate(blockSize)` | 2-50 | Pixelation |
| `noise(amount)` | 0.0-1.0 | Random noise overlay |
| `saturate(factor)` | 0.0-5.0 | Color saturation (1.0 = normal) |
Expand All @@ -49,12 +49,18 @@ These take a numeric argument and return a function that transforms a meme.
| `posterize(levels)` | 2-32 | Reduce color levels |
| `chromatic(offset)` | 1-20 | RGB channel displacement |
| `threshold(level)` | 0-255 | Black/white binarization |
| `tint(hexColor)` | hex string | Color tint overlay |
| `jpeg(quality)` | 1-100 | JPEG compression artifacts |

> `tint()` is currently not usable from Mac source code. It exists in the
> registry but requires a numeric parameter and hardcodes alpha to 0.5;
> the previously-documented `tint("#RRGGBBAA")` form rejects strings.
> Until this is fixed, prefer `hueShift`, `saturate`, or a `background`
> color on a style for similar effects. See the tracking issue for
> `tint()` before using it.

```
@blank "Blurry" |> blur(5) => "blurred.png";
@blank "Warm" |> tint("#FF880044") => "tinted.png";
@blank "Posterized" |> posterize(4) => "poster.png";
```

## Named Effects
Expand Down
27 changes: 23 additions & 4 deletions docs/reference/src/meme/gif.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ gif {
| `easeIn` | Accelerate from rest |
| `easeOut` | Decelerate to rest |
| `easeInOut` | Accelerate then decelerate |
| `bounce` | Bouncing effect at end |

### Loop-Back Transition

Expand Down Expand Up @@ -138,7 +137,7 @@ Frames with transitions can also have effects:
gif {
@blank "Day" : 2s
--- crossfade 500ms ---
@blank "Night" |> tint("#000044AA") |> vignette : 2s
@blank "Night" |> vignette |> brightness(0.5) : 2s
} => "day_night.gif";
```

Expand All @@ -156,15 +155,25 @@ gif {

## Programmatic GIFs

Use the `animate()` function to create GIFs from arrays:
Use the `animate()` function to create GIFs from an array of frames where
every frame has the same duration. The duration argument must be a
`Duration` instance, not a raw number.

```
var frames = [
@blank "One",
@blank "Two",
@blank "Three"
];
animate(frames, 500) => "uniform.gif";
animate(frames, Duration(500)) => "uniform.gif";
```

For GIFs with per-frame durations, build up a `Gif` with `reduce`:

```
var gif = frames
|> reduce((g, m) -> g.frame(m, Duration(500)), Gif());
gif.save("uniform.gif");
```

## Looping
Expand All @@ -178,6 +187,16 @@ GIFs loop infinitely by default.
- Cannot be placed inside grids or other frames
- Cannot be nested inside other gifs

## Operational Limits

- **Frame cap: 500 total frames per GIF.** This includes transition
frames, which are rendered at 15 fps. A 500ms `crossfade` between two
keyframes contributes ~7 interpolated frames on top of the keyframes
themselves. Exceeding 500 frames raises `GIF frame limit exceeded`.
- **Transition frame rate: 15 fps.** A 200ms transition is only 3 frames
and will look stepped; target 400-800ms for smooth transitions.
- **Image dimensions capped at 4096x4096.** Larger values are clamped.

## See Also

- [Saving Output](./save.md)
Expand Down
33 changes: 33 additions & 0 deletions docs/reference/src/meme/grid.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,39 @@ grid 1x2 {
} => "nested.png";
```

## Spread from an Array

When the entries are already in an array, use `...` to spread them into
the grid body, or omit the body entirely and let `grid` derive the shape
from the array's length.

```
var memes = data |> map(d -> @two_panel { top: d[0] bottom: d[1] });

grid 2x2 { ...memes } => "explicit.png"; // explicit shape
grid memes => "auto.png"; // auto-dimensioned
```

If an explicit `COLSxROWS` is given and the array size doesn't match,
extras are truncated and missing cells are filled with blank.

## Programmatic Composition

For cases where a literal `grid { }` block is awkward, three native
functions compose memes without syntax sugar:

| Function | Description |
|----------|-------------|
| `beside(a, b)` | Place two memes side by side |
| `stack(a, b)` | Stack two memes vertically |
| `toGrid(arr, cols, rows)` | Arrange an array of memes into a grid |

```
beside(@blank "before", @blank "after") => "compare.png";
stack(@blank "setup", @blank "punchline") => "bit.png";
toGrid(memes, 3, 2) => "dashboard.png";
```

## Composition Type

`grid` produces a frame type (Meme), which means it can be used inside:
Expand Down
31 changes: 29 additions & 2 deletions docs/reference/src/meme/meme_literal.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,41 @@ Pipe memes through effect functions.

## With Positioned Text

Use `x` and `y` for absolute text positioning.
Use `text:` followed by `x:` and `y:` to place text at absolute pixel
coordinates. Multiple positioned entries are allowed and can mix with
the named positions (`top:`, `bottom:`, `center:`).

```
@blank 720x720 {
text: "Anywhere" x: 100 y: 200
top: "HEADER"
text: "label A" x: 120 y: 200 fontSize: "sm"
text: "label B" x: 520 y: 200 fontSize: 64
bottom: "footer"
}
```

`fontSize` accepts either a pixel number or one of the size tier strings
`"sm"`, `"md"`, `"lg"`, `"xlg"`. If omitted, the meme's default sizing
applies.

All positioned text on a single meme shares the meme's outer style
(color, outline, weight, etc.). Per-entry colors are not currently
supported — use `beside`, `stack`, or `grid` to combine memes with
different styles.

## Meme Assets

The `@meme.<name>` syntax resolves to a built-in meme asset image.

```
@meme.shrek_smirk "When your code compiles" => "shrek.png";
@meme.girl_side_eye { bottom: "QA finds a bug" } => "qa.png";
```

Available assets: `shrek_smirk`, `shrek_side_eye`, `girl_side_eye`,
`king_bach_stare`, `jordan_crying`, `kid_crying`, `window_despair`,
`guy_crying`, `idk_about_that`.

## See Also

- [Styles](./styles.md) -- text color, outline, font
Expand Down
19 changes: 8 additions & 11 deletions docs/reference/src/meme/styles.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,18 @@ style subtitle {

## Inline Style

Styles can also be applied using keyword modifiers after the template:
Styles can be applied after the template (and after any dimensions):

```
@blank cinematic { top: "Movie Style" }
```
// Quick form: dimensions are optional
@blank cinematic "Movie Style" => "movie.png";

Built-in style keywords:
// Block form: dimensions are required when a style is used
@blank 720x720 cinematic { top: "Movie Style" } => "movie.png";
```

| Keyword | Effect |
|---------|--------|
| `impact` | White text, black outline, bold, uppercase |
| `cinematic` | Letter-boxed look |
| `shout` | Large bold uppercase |
| `whisper` | Small, normal weight |
| `panic` | Red text, heavy outline |
A style must be defined with `style name { ... }` before it can be
referenced this way. There are no pre-defined style keywords.

## See Also

Expand Down