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
28 changes: 27 additions & 1 deletion .agents/skills/superdeck-presentations/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,41 @@ Load only the reference needed for the task:
- Images have two authoring paths. Prefer standalone Markdown `![alt](src)` when the image belongs in the Markdown content flow; use `@image { src: ... }` when the image needs block-level layout control such as `fit`, fixed size, `flex`, `align`, `scrollable`, or `data:` source support.
- Markdown class markers such as `{.heading}` or `{.title}` drive Hero transitions for supported Markdown elements; the class does not need a `hero-` prefix. Use the same tag on matching elements across adjacent slides, and do not duplicate the same tag on one slide.
- `@column` is intentionally unsupported; use `@block`.
- Block and widget `align` controls visible content alignment. Current renderer stores `section.align` but does not apply it to child layout; set `align` on child blocks/widgets when visual placement matters.
- Effective content alignment is `block align → section align → centerLeft`.
Use section alignment as a shared default and child alignment for exceptions.
- `flex` is a positive integer. Section flex controls vertical height; child
block flex controls horizontal width.
- Section `spacing` creates finite, non-negative gaps only between sibling
blocks and affects horizontal space allocation. Block `margin` is consumed
inside that block's allocated frame, outside its decoration/border — it
reduces only that block's own usable area, never creates a shared gutter,
and never changes flex ratios (unlike CSS margins; use section `spacing` for
gutters). Block `padding` is consumed inside the decorated container,
between the border and the content. Both accept scalar, symmetric, or
physical-edge forms. Omitted object edges normalize to zero; explicit `null`
edges are invalid. An absent override inherits the resolved style value for
that inset; an explicit `0` removes it. A present override
replaces only the matching inset after variants resolve while preserving
other style data (decoration, clipping, animation).
- `SlideStyler.blockContainer` accepts `BlockStyler`, a constrained Mix styler
supporting only `padding`, `margin`, `decoration`, `foregroundDecoration`,
`clipBehavior`, context/`BlockVariant` variants, and animation. It cannot
express widget modifiers, width/height/constraints, transforms, or box
alignment; use `BoxStyler` for other style slots (`slideContainer`, code
block containers, alert containers).
- `scrollable` is valid on `@block` and widget blocks, not on `@section`.
- `layout: fullscreen` removes resolved header/footer chrome while retaining the slide's resolved background and style. `normal` is the default.
- Built-ins `image`, `dartpad`, `webview`, and `qrcode` are always registered and can be overridden by user widgets with the same name.
- `@image scale` is a finite number greater than zero. It changes painting, not
layout, and clips using the effective alignment and image/content frame.
- `@dartpad` and `@webview` use the same deck-scoped WebView controller cache. A `cacheKey` enables sequential reuse across remounts, never concurrent sharing by two live blocks.
- Custom widgets must be registered in `DeckOptions.widgets`; use shorthand `@widgetName { ... }` in `slides.md` for registered widget names.
- `BlockVariant('name')` is a Dart/Mix stylesheet selector for all `WidgetBlock`s with that exact, case-sensitive name. It affects the matching container and its widget subtree, not `@block` content.
- Styles, templates, widgets, slide parts, and plugins are configured in Dart through `DeckOptions`/`SuperDeckApp`, not through a separate `styles.yaml`.
- The CLI reads `slides.md`, writes `.superdeck/superdeck.json`, and ensures `.superdeck/` is listed in Flutter assets unless `--skip-pubspec` is used.
- `DeckOptions(debug: true)` diagnoses non-scrollable Markdown and custom
widget overflow without changing Markdown wrapping or rebuilding content;
static capture omits diagnostics.

## Source Map

Expand Down
76 changes: 69 additions & 7 deletions .agents/skills/superdeck-presentations/references/authoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,17 +120,71 @@ Left content
Right content
```

Use `align` on child blocks or widget blocks for visible alignment:
Set a shared alignment on the section and override individual children when
needed:

```markdown
@block { align: center }
@section {
spacing: 32
align: center
}

@block { padding: 16 }

# Inherits center

# Centered title
@block {
padding: {
horizontal: 32,
vertical: 16,
}
align: bottomRight
}

# Overrides the section
```

Valid alignments are `topLeft`, `topCenter`, `topRight`, `centerLeft`, `center`, `centerRight`, `bottomLeft`, `bottomCenter`, `bottomRight`.

Current renderer note: `@section { align: ... }` is accepted and stored, but visible content placement is driven by each child block/widget's `align`.
Effective alignment is explicit child alignment, then inherited section
alignment, then `centerLeft`.

Section `spacing` is a finite, non-negative logical-pixel gap between sibling
blocks only. It never adds leading/trailing space and is clamped when an
impossible request would place children outside the section.

Block/widget `margin` and `padding` accept exactly:

```markdown
padding: 16

padding: {
horizontal: 24,
vertical: 16,
}

padding: {
top: 12,
right: 24,
bottom: 12,
left: 24,
}
```

`margin` accepts the same three forms. Omitted object keys become zero, while
an explicitly authored `null` edge is invalid and reports its exact path.
Never mix symmetric and physical-edge keys. All flex values must be positive
integers.

`margin` and `padding` own different layout roles: `margin` is consumed inside
a block's already-allocated frame, outside its decoration/border, and reduces
only that block's own usable area — it never creates a shared gutter with
sibling blocks and never changes flex ratios (unlike CSS margins; use section
`spacing` for gutters). `padding` is consumed inside the decorated container,
between the border and the content. An absent (`null`) override inherits the
resolved style value for that inset; an explicit `0` removes it. A present
override replaces only the matching inset after style variants resolve —
decoration, clipping, and animation are preserved.

Use `scrollable: true` on overflowing blocks/widgets, not on sections:

Expand Down Expand Up @@ -166,7 +220,9 @@ Use `@image` when the image should be its own widget block/column, or when you n
@image {
src: assets/hero.png
fit: cover
width: 640
height: 420
scale: 1.2
align: center
}
```
Expand All @@ -175,7 +231,10 @@ Use `@image` when the image should be its own widget block/column, or when you n

- `src` required: relative asset path, URL, absolute path, `file://`, `data:`, or Windows absolute path.
- `fit`: `fill`, `contain`, `cover`, `fitWidth`, `fitHeight`, `none`, `scaleDown`; default `contain`.
- `width`, `height`: positive logical pixels.
- `width`, `height`: positive logical pixels; integer or decimal values are both accepted.
- `scale`: finite number greater than zero, default `1`; integer or decimal
values are both accepted; changes painted pixels without changing the frame
or flex layout and clips using effective alignment.

Key differences:

Expand Down Expand Up @@ -334,9 +393,12 @@ Or explicit form:
}
```

All properties become the widget factory's `Map<String, Object?>` arguments. Block-level controls such as `flex`, `align`, and `scrollable` are consumed by SuperDeck and are not passed as custom widget args.
All properties become the widget factory's `Map<String, Object?>` arguments.
Block-level controls such as `flex`, `align`, `margin`, `padding`, and
`scrollable` are consumed by SuperDeck and are not passed as custom widget
args.

To style every custom block with the same name, declare `BlockVariant('metricCard')` in a Dart `SlideStyle`. The match is exact and case-sensitive, and it applies to the widget block container plus descendants rather than to individual Markdown block instances.
To style every custom block with the same name, declare `BlockVariant('metricCard')` in a Dart `SlideStyler`. The match is exact and case-sensitive, and it applies to the widget block container plus descendants rather than to individual Markdown block instances.

Custom widget authoring rules:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,9 @@ Ack generation constraints that matter for SuperDeck widgets:
- Generated extension types implement `Map<String, Object?>` and expose `parse`, `safeParse`, and typed getters.
- Nested object fields should reference named top-level schemas when you need typed nested getters.
- Do not expect `Ack.any()`/`Ack.anyOf()` or inline anonymous object branches to generate useful typed wrappers.
- Keep `align`, `flex`, `scrollable`, and `name` out of widget-specific schemas because SuperDeck consumes those reserved block keys before calling the factory.
- Keep `align`, `flex`, `margin`, `padding`, `scrollable`, and `name` out of
widget-specific schemas because SuperDeck consumes those reserved block keys
before calling the factory.

Register it:

Expand All @@ -252,7 +254,8 @@ Use it:

Notes:

- `align`, `flex`, `scrollable`, and `name` are reserved block keys; do not expect them inside `args`.
- `align`, `flex`, `margin`, `padding`, `scrollable`, and `name` are reserved
block keys; do not expect them inside `args`.
- Widget names can include letters, digits, underscores, and hyphens because directive tags match `@[\w-]+`.
- A custom widget can override a built-in by registering `image`, `dartpad`, `webview`, or `qrcode`, but do that only intentionally.
- Use `SlideConfiguration.of(context)` for slide metadata and `LayoutBuilder` for block size.
Expand Down Expand Up @@ -309,6 +312,18 @@ Unknown templates or styles throw `ArgumentError` during configuration build, so

Template styles are isolated. A slide using `template: brand` and `style: cover` looks up `cover` in `SlideTemplate.styles`, not `DeckOptions.styles`.

### `blockContainer` and `BlockStyler`

`SlideStyler.blockContainer` is a `BlockStyler`, not a `BoxStyler`. `BlockStyler`
is a constrained Mix styler exposing only `padding`, `margin` (plus Mix
spacing convenience methods), `decoration`, `foregroundDecoration`,
`clipBehavior`, context/`BlockVariant` variants, and animation. It cannot
express widget modifiers, width/height/constraints, transforms, or box
alignment — block/section `align` owns content placement, and section
`spacing` plus flex own geometry. Use `BoxStyler` for other style slots
(`slideContainer`, code block containers, alert containers); those are
unaffected by this constraint.

### Named Widget Block Styles

Use `BlockVariant` to target every widget block with an exact, case-sensitive name. The selector resolves around the matching block container and its descendants, so container padding/margin and widget subtree styles see the same active variant.
Expand All @@ -320,13 +335,13 @@ import 'package:superdeck/superdeck.dart';
const webviewBlock = BlockVariant('webview');

final options = DeckOptions(
baseStyle: SlideStyle(
blockContainer: BoxStyler(
baseStyle: SlideStyler(
blockContainer: BlockStyler(
padding: EdgeInsetsGeometryMix.all(40),
).variants([
VariantStyle(
webviewBlock,
BoxStyler(padding: EdgeInsetsGeometryMix.all(0)),
BlockStyler(padding: EdgeInsetsGeometryMix.all(0)),
),
]),
),
Expand All @@ -335,6 +350,12 @@ final options = DeckOptions(

`BlockVariant('webview')` matches `@webview` and `@widget { name: "webview" }`; `BlockVariant('chart')` matches `@chart`. It does not select Markdown `@block` content, arbitrary `NamedVariant` values, or individual instances. SuperDeck already applies a zero-padding, zero-margin `BlockVariant('webview')` rule by default, so WebView blocks are edge-to-edge unless the style overrides it.

A Markdown block-level `margin` or `padding` value applies after these Mix
variants resolve. It replaces only the matching inset; the other inset,
decoration, border, animation, and the active selector remain intact. An
absent (`null`) override inherits the resolved style value; an explicit `0`
removes it.

## Images and Assets

The CLI manages `.superdeck/` assets and ensures this entry exists unless `--skip-pubspec` is used:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,20 @@ Invalid directive options:
Unexpected layout:

- Remember: sections stack vertically; blocks inside a section are horizontal.
- Set `align` on blocks/widgets for visible alignment.
- Alignment resolves from child block to section to `centerLeft`.
- Use section `spacing` for gaps between blocks, block/widget `margin` for
inset inside a block's own frame (outside its decoration), and block/widget
`padding` for inset inside the decorated container (between border and
content).
- Use block/widget `scrollable: true`; do not set `scrollable` on sections.
- Use `flex` integers; invalid values throw during parse/model validation.
- Use positive `flex` integers; invalid values throw during parse/model validation.
- For `layout: fullscreen`, verify that header/footer are absent while the intended background and style remain.

Widget does not render:

- Verify the widget name is registered in `DeckOptions.widgets`.
- Built-ins are `image`, `dartpad`, `webview`, and `qrcode`.
- Check that widget arguments do not collide with reserved block keys: `name`, `align`, `flex`, `scrollable`.
- Check that widget arguments do not collide with reserved block keys: `name`, `align`, `flex`, `margin`, `padding`, `scrollable`.
- Remember that reserved block keys are consumed by SuperDeck and are not passed to custom widget args.
- If using shorthand, verify the directive name exactly matches the registered widget name.
- If using Ack-generated args wrappers, confirm the schema is a top-level `@AckType()` declaration, the `part` file is present, generator dependencies are installed, and `build_runner` has regenerated the `.g.dart` file.
Expand Down Expand Up @@ -143,6 +147,8 @@ Images fail:

- Confirm which image path is being used: Markdown `![Alt](src)` inside content or widget-block `@image { src: ... }`.
- If the image should behave like normal slide content, use a standalone Markdown image. If it needs `fit`, fixed dimensions, column sizing, alignment, scrolling, or `data:` URI support, use `@image`.
- Use `scale` only for painting within an image frame; it must be finite and
greater than zero and does not change block allocation.
- For Markdown images, reject `asset:` URLs and `..` traversal.
- For Markdown images, do not use `data:` URIs; use `@image` for `data:` sources.
- For web/release, declare project asset directories in `pubspec.yaml`.
Expand Down
20 changes: 10 additions & 10 deletions .planning/feature-templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Templates provide reusable **chrome configurations** (background, header, footer
- **Rationale:** Keep templates simple, focused, and non-invasive to existing markdown authoring

### 2. Isolated Style Systems
- Templates bundle their own style hierarchy: `baseStyle` + `Map<String, SlideStyle> styles`
- Templates bundle their own style hierarchy: `baseStyle` + `Map<String, SlideStyler> styles`
- When a template is used, deck-level styles are ignored for that slide
- Style resolution: `defaultSlideStyle → template.baseStyle → template.styles[slide.style]`
- **Rationale:** Templates provide complete visual control without style conflicts
Expand Down Expand Up @@ -61,11 +61,11 @@ final class SlideTemplate {

/// Base style applied to all slides using this template.
/// Merged after defaultSlideStyle but before named styles.
final SlideStyle? baseStyle;
final SlideStyler? baseStyle;

/// Named style variants available within this template.
/// Keys match the `style:` field in slide frontmatter.
final Map<String, SlideStyle> styles;
final Map<String, SlideStyler> styles;

const SlideTemplate({
this.parts = const SlideParts(),
Expand All @@ -81,8 +81,8 @@ final class SlideTemplate {
```dart
class DeckOptions {
// Existing fields
final SlideStyle? baseStyle;
final Map<String, SlideStyle> styles;
final SlideStyler? baseStyle;
final Map<String, SlideStyler> styles;
final Map<String, WidgetBlockBuilder> widgets;
final SlideParts parts;
final bool debug;
Expand Down Expand Up @@ -149,7 +149,7 @@ class TemplateResolver {
}

class TemplateResolutionResult {
final SlideStyle style; // Fully merged style
final SlideStyler style; // Fully merged style
final SlideParts parts; // Resolved parts (template or deck)
final bool usingTemplate; // True if template was applied
}
Expand Down Expand Up @@ -519,7 +519,7 @@ final corporateTemplate = SlideTemplate(
),
),
),
baseStyle: SlideStyle(
baseStyle: SlideStyler(
h1: TextStyler().style(
TextStyleMix(fontSize: 64, color: Colors.blue[900]!),
),
Expand All @@ -528,12 +528,12 @@ final corporateTemplate = SlideTemplate(
),
),
styles: {
'title': SlideStyle(
'title': SlideStyler(
h1: TextStyler().style(
TextStyleMix(fontSize: 96, fontWeight: FontWeight.bold),
),
),
'emphasis': SlideStyle(
'emphasis': SlideStyler(
slideContainer: BoxStyler(
decoration: BoxDecorationMix(
border: BorderMix.all(BorderSideMix(color: Colors.blue, width: 8)),
Expand Down Expand Up @@ -753,7 +753,7 @@ Available styles: announcement, quote, emphasis
```dart
final extendedTemplate = SlideTemplate(
inheritsFrom: 'corporate', // Extend another template
baseStyle: SlideStyle(...), // Override specific styles
baseStyle: SlideStyler(...), // Override specific styles
);
```

Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ Slides use `@tag` directives in Markdown to define layout and content:
- Any unrecognized `@name` becomes a `WidgetBlock` (e.g., `@image`, `@chart`, `@callout`)

### Style System
Styles are defined in Dart through `SlideStyle`, `DeckOptions.baseStyle`, and `DeckOptions.styles`.
Styles are defined in Dart through `SlideStyler`, `DeckOptions.baseStyle`, and `DeckOptions.styles`.

## Documentation Locations

Expand Down
4 changes: 2 additions & 2 deletions demo/.superdeck/build_status.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"status": "success",
"timestamp": "2026-07-10T15:12:04.507060Z",
"slideCount": 31,
"timestamp": "2026-07-12T23:35:50.974090Z",
"slideCount": 34,
"error": null
}
Loading
Loading