sone
Version:
A declarative Canvas layout engine for JavaScript with advanced rich text support.
814 lines (806 loc) • 32.5 kB
text/typescript
import { GradientNode } from 'gradient-parser';
import { QrCodeGenerateData, QrCodeGenerateSvgOptions } from 'uqr';
import { Yoga, Node } from 'yoga-layout/load';
declare const colors: {
readonly aliceblue: "#f0f8ff";
readonly antiquewhite: "#faebd7";
readonly aqua: "#00ffff";
readonly aquamarine: "#7fffd4";
readonly azure: "#f0ffff";
readonly beige: "#f5f5dc";
readonly bisque: "#ffe4c4";
readonly black: "#000000";
readonly blanchedalmond: "#ffebcd";
readonly blue: "#0000ff";
readonly blueviolet: "#8a2be2";
readonly brown: "#a52a2a";
readonly burlywood: "#deb887";
readonly cadetblue: "#5f9ea0";
readonly chartreuse: "#7fff00";
readonly chocolate: "#d2691e";
readonly coral: "#ff7f50";
readonly cornflowerblue: "#6495ed";
readonly cornsilk: "#fff8dc";
readonly crimson: "#dc143c";
readonly cyan: "#00ffff";
readonly darkblue: "#00008b";
readonly darkcyan: "#008b8b";
readonly darkgoldenrod: "#b8860b";
readonly darkgray: "#a9a9a9";
readonly darkgreen: "#006400";
readonly darkgrey: "#a9a9a9";
readonly darkkhaki: "#bdb76b";
readonly darkmagenta: "#8b008b";
readonly darkolivegreen: "#556b2f";
readonly darkorange: "#ff8c00";
readonly darkorchid: "#9932cc";
readonly darkred: "#8b0000";
readonly darksalmon: "#e9967a";
readonly darkseagreen: "#8fbc8f";
readonly darkslateblue: "#483d8b";
readonly darkslategray: "#2f4f4f";
readonly darkslategrey: "#2f4f4f";
readonly darkturquoise: "#00ced1";
readonly darkviolet: "#9400d3";
readonly deeppink: "#ff1493";
readonly deepskyblue: "#00bfff";
readonly dimgray: "#696969";
readonly dimgrey: "#696969";
readonly dodgerblue: "#1e90ff";
readonly firebrick: "#b22222";
readonly floralwhite: "#fffaf0";
readonly forestgreen: "#228b22";
readonly fuchsia: "#ff00ff";
readonly gainsboro: "#dcdcdc";
readonly ghostwhite: "#f8f8ff";
readonly goldenrod: "#daa520";
readonly gold: "#ffd700";
readonly gray: "#808080";
readonly green: "#008000";
readonly greenyellow: "#adff2f";
readonly grey: "#808080";
readonly honeydew: "#f0fff0";
readonly hotpink: "#ff69b4";
readonly indianred: "#cd5c5c";
readonly indigo: "#4b0082";
readonly ivory: "#fffff0";
readonly khaki: "#f0e68c";
readonly lavenderblush: "#fff0f5";
readonly lavender: "#e6e6fa";
readonly lawngreen: "#7cfc00";
readonly lemonchiffon: "#fffacd";
readonly lightblue: "#add8e6";
readonly lightcoral: "#f08080";
readonly lightcyan: "#e0ffff";
readonly lightgoldenrodyellow: "#fafad2";
readonly lightgray: "#d3d3d3";
readonly lightgreen: "#90ee90";
readonly lightgrey: "#d3d3d3";
readonly lightpink: "#ffb6c1";
readonly lightsalmon: "#ffa07a";
readonly lightseagreen: "#20b2aa";
readonly lightskyblue: "#87cefa";
readonly lightslategray: "#778899";
readonly lightslategrey: "#778899";
readonly lightsteelblue: "#b0c4de";
readonly lightyellow: "#ffffe0";
readonly lime: "#00ff00";
readonly limegreen: "#32cd32";
readonly linen: "#faf0e6";
readonly magenta: "#ff00ff";
readonly maroon: "#800000";
readonly mediumaquamarine: "#66cdaa";
readonly mediumblue: "#0000cd";
readonly mediumorchid: "#ba55d3";
readonly mediumpurple: "#9370db";
readonly mediumseagreen: "#3cb371";
readonly mediumslateblue: "#7b68ee";
readonly mediumspringgreen: "#00fa9a";
readonly mediumturquoise: "#48d1cc";
readonly mediumvioletred: "#c71585";
readonly midnightblue: "#191970";
readonly mintcream: "#f5fffa";
readonly mistyrose: "#ffe4e1";
readonly moccasin: "#ffe4b5";
readonly navajowhite: "#ffdead";
readonly navy: "#000080";
readonly oldlace: "#fdf5e6";
readonly olive: "#808000";
readonly olivedrab: "#6b8e23";
readonly orange: "#ffa500";
readonly orangered: "#ff4500";
readonly orchid: "#da70d6";
readonly palegoldenrod: "#eee8aa";
readonly palegreen: "#98fb98";
readonly paleturquoise: "#afeeee";
readonly palevioletred: "#db7093";
readonly papayawhip: "#ffefd5";
readonly peachpuff: "#ffdab9";
readonly peru: "#cd853f";
readonly pink: "#ffc0cb";
readonly plum: "#dda0dd";
readonly powderblue: "#b0e0e6";
readonly purple: "#800080";
readonly rebeccapurple: "#663399";
readonly red: "#ff0000";
readonly rosybrown: "#bc8f8f";
readonly royalblue: "#4169e1";
readonly saddlebrown: "#8b4513";
readonly salmon: "#fa8072";
readonly sandybrown: "#f4a460";
readonly seagreen: "#2e8b57";
readonly seashell: "#fff5ee";
readonly sienna: "#a0522d";
readonly silver: "#c0c0c0";
readonly skyblue: "#87ceeb";
readonly slateblue: "#6a5acd";
readonly slategray: "#708090";
readonly slategrey: "#708090";
readonly snow: "#fffafa";
readonly springgreen: "#00ff7f";
readonly steelblue: "#4682b4";
readonly tan: "#d2b48c";
readonly teal: "#008080";
readonly thistle: "#d8bfd8";
readonly tomato: "#ff6347";
readonly turquoise: "#40e0d0";
readonly violet: "#ee82ee";
readonly wheat: "#f5deb3";
readonly white: "#ffffff";
readonly whitesmoke: "#f5f5f5";
readonly yellow: "#ffff00";
readonly yellowgreen: "#9acd32";
};
interface CssShadowProperties {
inset: boolean;
offsetX: number;
offsetY: number;
blurRadius: number;
spreadRadius?: number;
color?: string;
}
/**
* Default text styling properties used as fallbacks
*/
declare const DEFAULT_TEXT_PROPS: DefaultTextProps;
/**
* Debug configuration for development
*/
interface SoneDebugConfig {
/** show text measurement debugging */
text: boolean;
/** show layout bounding boxes */
layout: boolean;
}
/**
* Platform-specific renderer interface for Node.js/browser environments
*/
interface SoneRenderer {
/** Generate word/character break points for text wrapping */
breakIterator(text: string): Generator<number, void, boolean>;
/** Create canvas with specified dimensions */
createCanvas(width: number, height: number): HTMLCanvasElement;
/** Measure text dimensions with given styling */
measureText(text: string, props: SpanProps): TextMetrics;
/** Check if font is available */
hasFont(name: string): boolean;
/** Register custom font from file/URL */
registerFont(name: string, source: string[] | string): Promise<void>;
/** Remove registered font */
unregisterFont(name: string): Promise<void>;
/** Clear all registered fonts */
resetFonts(): void;
/** Load image from URL or buffer */
loadImage(src: string | Uint8Array): Promise<HTMLImageElement>;
/** Get platform default text properties */
getDefaultTextProps(): DefaultTextProps;
/** Get device pixel ratio for high-DPI displays */
dpr(): number;
/** Path2D constructor for drawing paths */
Path2D: typeof Path2D;
/** Get debug configuration */
debug(): SoneDebugConfig;
}
type SoneImage = Awaited<ReturnType<SoneRenderer["loadImage"]>>;
/**
* Context passed during node compilation phase
*/
interface SoneCompileContext {
/** Default text properties to inherit */
defaultTextProps: DefaultTextProps;
/** Image loading function */
loadImage: SoneRenderer["loadImage"];
/** Text breaking function */
breakIterator: SoneRenderer["breakIterator"];
}
/**
* Compile and resolve node tree - loads images, parses gradients, cascades text properties
* @param node - root node to compile
* @param context - compilation context with defaults and functions
* @returns compiled node tree ready for layout
*/
declare function compile<T extends SoneNode>(node: T, { defaultTextProps, loadImage, breakIterator }: SoneCompileContext): Promise<T | undefined>;
/**
* Extract all fonts used in a node tree for preloading
* @param node - node to scan for font usage
* @returns set of font names used
*/
declare function findFonts(node: SoneNode): Set<FontValue> | undefined;
/**
* Create Yoga layout tree from compiled node tree
* Requires: resolved assets, font info, and line breaking data
* @param node - compiled node to create layout for
* @param renderer - platform renderer for text measurement
* @returns Yoga layout node tree
*/
declare function createLayoutNode(node: SoneNode, renderer: SoneRenderer, Yoga: Yoga): Node | undefined;
/**
* Configuration for rendering
*/
interface SoneRenderConfig {
/** canvas width (auto-sized if not specified) */
width?: number;
/** canvas height (auto-sized if not specified) */
height?: number;
/** canvas background color */
background?: ColorValue;
/** image cache for performance */
cache?: Map<string | Uint8Array, SoneImage>;
}
/**
* Main rendering function - compiles, layouts, and draws to canvas
* @param node - root node to render
* @param renderer - platform-specific renderer
* @param config - rendering configuration
* @returns rendered canvas
*/
declare function render<T = HTMLCanvasElement>(node: SoneNode, renderer: SoneRenderer, config?: SoneRenderConfig): Promise<T>;
/**
* Draw background, shadows, and borders for layout nodes
* @param renderer - platform renderer
* @param ctx - canvas rendering context
* @param node - node to draw
* @param layout - yoga layout node
* @param x - x position
* @param y - y position
*/
declare function drawLayoutNode(renderer: SoneRenderer, ctx: CanvasRenderingContext2D, node: ColumnNode | RowNode | TextNode | PathNode | TableNode | TableRowNode | TableCellNode, layout: Node, x: number, y: number): Path2D;
interface SoneParagraphLineSegment {
metrics: TextMetrics;
props: SpanProps;
text: string;
width: number;
height: number;
}
interface SoneParagraphLine {
baseline: number;
segments: SoneParagraphLineSegment[];
spacesCount: number;
width: number;
height: number;
}
interface SoneParagraph {
width: number;
height: number;
lines: SoneParagraphLine[];
offsetY: number;
}
interface SoneParagraphBlock {
paragraph: SoneParagraph;
}
/**
* Utility type to ensure non-undefined values
*/
type Required<T> = T extends undefined ? never : T;
/**
* Color values - predefined color names or CSS color strings
*/
type ColorValue = keyof typeof colors | "transparent" | (string & {});
/**
* Layout positioning types (similar to CSS position)
*/
type LayoutPositionType = "static" | "relative" | "absolute";
/**
* Flexbox direction - same as CSS flex-direction
*/
type FlexDirection = "column" | "column-reverse" | "row" | "row-reverse";
/**
* Flexbox align-content values
*/
type AlignContent = "flex-start" | "flex-end" | "center" | "stretch" | "space-between" | "space-around" | "space-evenly";
type AlignItems = "flex-start" | "flex-end" | "center" | "stretch" | "baseline";
type JustifyContent = "flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "space-evenly";
/**
* Core layout properties - mirrors CSS Flexbox/Grid with additional visual styling
*/
interface LayoutProps {
/** Debug identifier for development */
tag?: string;
alignContent?: AlignContent;
alignItems?: AlignItems;
alignSelf?: AlignItems;
aspectRatio?: number;
borderBottomWidth?: number;
borderEndWidth?: number;
borderLeftWidth?: number;
borderRightWidth?: number;
borderStartWidth?: number;
borderTopWidth?: number;
borderWidth?: number;
borderInlineWidth?: number;
borderBlockWidth?: number;
bottom?: number | `${number}%`;
boxSizing?: "border-box" | "content-box";
direction?: "ltr" | "rtl";
display?: "none" | "flex" | "contents";
end?: number | `${number}%`;
flex?: number;
flexBasis?: number | "auto" | `${number}%`;
flexDirection?: "row" | "column" | "row-reverse" | "column-reverse";
rowGap?: number;
gap?: number;
columnGap?: number;
flexGrow?: number;
flexShrink?: number;
flexWrap?: "wrap" | "nowrap" | "wrap-reverse";
height?: number | "auto" | `${number}%`;
justifyContent?: JustifyContent;
left?: number | `${number}%`;
margin?: number | "auto" | `${number}%`;
marginBottom?: number | "auto" | `${number}%`;
marginEnd?: number | "auto" | `${number}%`;
marginLeft?: number | "auto" | `${number}%`;
marginRight?: number | "auto" | `${number}%`;
marginStart?: number | "auto" | `${number}%`;
marginTop?: number | "auto" | `${number}%`;
marginInline?: number | "auto" | `${number}%`;
marginBlock?: number | "auto" | `${number}%`;
maxHeight?: number | `${number}%`;
maxWidth?: number | `${number}%`;
minHeight?: number | `${number}%`;
minWidth?: number | `${number}%`;
overflow?: "visible" | "hidden" | "scroll";
padding?: number | `${number}%`;
paddingBottom?: number | `${number}%`;
paddingEnd?: number | `${number}%`;
paddingLeft?: number | `${number}%`;
paddingRight?: number | `${number}%`;
paddingStart?: number | `${number}%`;
paddingTop?: number | `${number}%`;
paddingInline?: number | `${number}%`;
paddingBlock?: number | `${number}%`;
position?: "absolute" | "relative" | "static";
right?: number | `${number}%`;
start?: number | `${number}%`;
top?: number | `${number}%`;
insetInline?: number | `${number}%`;
insetBlock?: number | `${number}%`;
inset?: number | `${number}%`;
width?: number | "auto" | `${number}%`;
borderColor?: ColorValue;
background?: Array<ColorValue | PhotoNode | GradientNode>;
rotation?: number;
scale?: [number, number];
translateX?: number;
translateY?: number;
cornerRadius?: number[];
cornerSmoothing?: number;
corner?: "cut" | "round";
opacity?: number;
shadows?: Array<CssShadowProperties | string>;
filters?: string[];
}
/**
* Fluent API builder for layout properties
*/
interface LayoutPropsBuilder<T, P = LayoutProps> {
props: P;
/** Apply multiple properties at once */
apply(value: LayoutProps): T;
/** Set debug tag */
tag(value: Required<LayoutProps["tag"]>): T;
alignContent(value: Required<LayoutProps["alignContent"]>): T;
alignItems(value: Required<LayoutProps["alignItems"]>): T;
alignSelf(value: Required<LayoutProps["alignSelf"]>): T;
aspectRatio(value: Required<LayoutProps["aspectRatio"]>): T;
/**
* Border width with CSS-like shorthand values
* @example borderWidth(10) // all sides
* @example borderWidth(10, 20) // top/bottom, left/right
* @example borderWidth(10, 20, 30) // top, left/right, bottom
* @example borderWidth(10, 20, 30, 40) // top, right, bottom, left
*/
borderWidth(all: number): T;
borderWidth(topBottom: number, leftRight: number): T;
borderWidth(top: number, leftRight: number, bottom: number): T;
borderWidth(top: number, right: number, bottom: number, left: number): T;
borderColor(value: Required<LayoutProps["borderColor"]>): T;
/** Margin with CSS-like shorthand (same pattern as borderWidth) */
margin(all: Required<LayoutProps["margin"]>): T;
margin(topBottom: Required<LayoutProps["marginTop"]>, leftRight: Required<LayoutProps["marginLeft"]>): T;
margin(top: Required<LayoutProps["marginTop"]>, leftRight: Required<LayoutProps["marginRight"]>, bottom: Required<LayoutProps["marginBottom"]>): T;
margin(top: Required<LayoutProps["marginTop"]>, right: Required<LayoutProps["marginRight"]>, bottom: Required<LayoutProps["marginBottom"]>, left: Required<LayoutProps["marginLeft"]>): T;
marginTop(value: Required<LayoutProps["marginTop"]>): T;
marginBottom(value: Required<LayoutProps["marginBottom"]>): T;
marginLeft(value: Required<LayoutProps["marginLeft"]>): T;
marginRight(value: Required<LayoutProps["marginRight"]>): T;
/** Padding with CSS-like shorthand (same pattern as borderWidth) */
padding(all: Required<LayoutProps["padding"]>): T;
padding(topBottom: Required<LayoutProps["paddingTop"]>, leftRight: Required<LayoutProps["paddingLeft"]>): T;
padding(top: Required<LayoutProps["paddingTop"]>, leftRight: Required<LayoutProps["paddingRight"]>, bottom: Required<LayoutProps["paddingBottom"]>): T;
padding(top: Required<LayoutProps["paddingTop"]>, right: Required<LayoutProps["paddingRight"]>, bottom: Required<LayoutProps["paddingBottom"]>, left: Required<LayoutProps["paddingLeft"]>): T;
paddingTop(value: Required<LayoutProps["paddingTop"]>): T;
paddingBottom(value: Required<LayoutProps["paddingBottom"]>): T;
paddingLeft(value: Required<LayoutProps["paddingLeft"]>): T;
paddingRight(value: Required<LayoutProps["paddingRight"]>): T;
boxSizing(value: Required<LayoutProps["boxSizing"]>): T;
display(value: Required<LayoutProps["display"]>): T;
flex(value: Required<LayoutProps["flex"]>): T;
basis(value: Required<LayoutProps["flexBasis"]>): T;
direction(value: Required<LayoutProps["flexDirection"]>): T;
rowGap(value: Required<LayoutProps["rowGap"]>): T;
gap(value: Required<LayoutProps["gap"]>): T;
columnGap(value: Required<LayoutProps["columnGap"]>): T;
grow(value: Required<LayoutProps["flexGrow"]>): T;
shrink(value: Required<LayoutProps["flexShrink"]>): T;
wrap(value: Required<LayoutProps["flexWrap"]>): T;
justifyContent(value: Required<LayoutProps["justifyContent"]>): T;
left(value: Required<LayoutProps["left"]>): T;
right(value: Required<LayoutProps["right"]>): T;
bottom(value: Required<LayoutProps["bottom"]>): T;
top(value: Required<LayoutProps["top"]>): T;
start(value: Required<LayoutProps["start"]>): T;
end(value: Required<LayoutProps["end"]>): T;
/**
* Set width and height together - height defaults to width if omitted (square)
*/
size(width: Required<LayoutProps["width"]>, height?: Required<LayoutProps["height"]>): T;
width(value: Required<LayoutProps["width"]>): T;
height(value: Required<LayoutProps["height"]>): T;
maxWidth(value: Required<LayoutProps["maxWidth"]>): T;
maxHeight(value: Required<LayoutProps["maxHeight"]>): T;
minWidth(value: Required<LayoutProps["minWidth"]>): T;
minHeight(value: Required<LayoutProps["minHeight"]>): T;
position(value: Required<LayoutProps["position"]>): T;
inset(value: Required<LayoutProps["inset"]>): T;
overflow(value: Required<LayoutProps["overflow"]>): T;
/** CSS-like transforms */
translateX(value: number): T;
translateY(value: number): T;
/** @param value rotation in degrees */
rotate(value: number): T;
/** uniform scaling */
scale(value: number): T;
/** separate x/y scaling */
scale(x: number, y: number): T;
/**
* Background - supports colors, gradients, images
* @example .bg("red")
* @example .bg("linear-gradient(...)")
* @example .bg(Photo("url"))
*/
bg(...values: Required<LayoutProps["background"]>): T;
background(...values: Required<LayoutProps["background"]>): T;
opacity(value: Required<LayoutProps["opacity"]>): T;
rounded(...values: Required<LayoutProps["cornerRadius"]>): T;
borderRadius(...values: Required<LayoutProps["cornerRadius"]>): T;
borderSmoothing(value: Required<LayoutProps["cornerSmoothing"]>): T;
cornerRadius(...values: Required<LayoutProps["cornerRadius"]>): T;
cornerSmoothing(value: Required<LayoutProps["cornerSmoothing"]>): T;
corner(value: Required<LayoutProps["corner"]>): T;
/**
* CSS box-shadow
* @example .shadow("2px 2px 4px rgba(0,0,0,0.3)")
*/
shadow(...values: string[]): T;
/** @param value blur radius in pixels */
blur(value: number): T;
/** @param value 1.0 = normal, 2.0 = twice as bright */
brightness(value: number): T;
/** @param value 1.0 = normal */
contrast(value: number): T;
/** @param value 0.0 to 1.0 */
grayscale(value: number): T;
/** @param value hue rotation in degrees */
huerotate(value: number): T;
/** @param value 0.0 to 1.0 */
invert(value: number): T;
/** @param value 1.0 = normal */
saturate(value: number): T;
/** @param value 0.0 to 1.0 */
sepia(value: number): T;
}
/**
* Image display properties
*/
interface PhotoProps extends LayoutProps {
/** maintain original width/height ratio */
preserveAspectRatio?: boolean;
/** how image fits container */
scaleType?: "cover" | "fill" | "contain";
scaleAlignment?: number;
flipHorizontal?: boolean;
flipVertical?: boolean;
fill?: ColorValue;
/** image source */
src?: string | Uint8Array | HTMLImageElement;
/** resolved image (set during compilation) */
image?: HTMLImageElement;
}
interface PhotoPropsBuilder<T> extends LayoutPropsBuilder<T, PhotoProps> {
scaleType(value: Required<PhotoProps["scaleType"]>, alignment?: Required<PhotoProps["scaleAlignment"]>): T;
scaleType(value: Required<PhotoProps["scaleType"]>, alignment?: "center" | "end" | "start"): T;
preserveAspectRatio(value?: Required<PhotoProps["preserveAspectRatio"]>): T;
flipHorizontal(value?: Required<PhotoProps["flipHorizontal"]>): T;
flipVertical(value?: Required<PhotoProps["flipVertical"]>): T;
fill(value: Required<PhotoProps["fill"]>): T;
}
/**
* Image display node
*/
interface PhotoNode extends PhotoPropsBuilder<PhotoNode> {
type: "photo";
}
/**
* Font family - system fonts or custom font names
*/
type FontValue = "sans-serif" | "serif" | "monospace" | (string & {});
/**
* Text styling properties for spans and text
*/
interface SpanProps {
/** font size in pixels */
size?: number;
/** text color or gradient */
color?: ColorValue | GradientNode[];
/** font family stack @example ["Arial", "sans-serif"] */
font?: FontValue[];
style?: "normal" | "italic" | "oblique";
/** font weight @example 100-900 or "bold" */
weight?: "normal" | "bold" | "lighter" | "bolder" | (string & {}) | number;
/** spacing between characters */
letterSpacing?: number;
/** spacing between words */
wordSpacing?: number;
/** text shadows */
dropShadows?: Array<CssShadowProperties | string>;
/** text outline color */
strokeColor?: ColorValue;
/** text outline width */
strokeWidth?: number;
/** vertical offset for baseline adjustment */
offsetY?: number;
/** underline thickness */
underline?: number;
underlineColor?: ColorValue | null;
/** strikethrough thickness */
lineThrough?: number;
lineThroughColor?: ColorValue | null;
/** overline thickness */
overline?: number;
overlineColor?: ColorValue | null;
/** background highlight */
highlightColor?: ColorValue | null;
}
/**
* Text block properties extending span properties
*/
interface TextProps extends SpanProps, LayoutProps {
/** prevent text wrapping */
nowrap?: boolean;
/** line height multiplier @example 1.5 = 150% */
lineHeight?: number;
/** first line indent in pixels */
indentSize?: number;
/** subsequent lines indent */
hangingIndentSize?: number;
/** text alignment */
align?: "left" | "right" | "center" | "justify";
/** resolved paragraph (internal) */
blocks?: SoneParagraphBlock[];
}
type RequiredNonNullValues<T> = {
[K in keyof T]-?: Required<T[K]>;
};
type DefaultTextProps = RequiredNonNullValues<Omit<TextProps, keyof LayoutProps>>;
interface SpanPropsBuilder<T> {
props: SpanProps;
color: (value: Required<TextProps["color"]>) => T;
size: (value: Required<TextProps["size"]>) => T;
font: (...values: Required<TextProps["font"]>) => T;
style: (value: Required<TextProps["style"]>) => T;
weight: (value: Required<TextProps["weight"]>) => T;
letterSpacing: (value: Required<TextProps["letterSpacing"]>) => T;
wordSpacing: (value: Required<TextProps["wordSpacing"]>) => T;
underline: (value?: Required<TextProps["underline"]>) => T;
underlineColor: (value?: TextProps["underlineColor"]) => T;
lineThrough: (value?: Required<TextProps["lineThrough"]>) => T;
lineThroughColor: (value?: TextProps["lineThroughColor"]) => T;
overline: (value?: Required<TextProps["overline"]>) => T;
overlineColor: (value?: TextProps["overlineColor"]) => T;
highlight: (value?: TextProps["highlightColor"]) => T;
dropShadow: (...values: Required<TextProps["dropShadows"]>) => T;
strokeColor: (value: Required<TextProps["strokeColor"]>) => T;
strokeWidth: (value: Required<TextProps["strokeWidth"]>) => T;
offsetY: (value: Required<TextProps["offsetY"]>) => T;
}
/**
* Styled text span within a text block
*/
interface SpanNode extends SpanPropsBuilder<SpanNode> {
type: "span";
/** the text content */
children: string;
}
interface TextPropsBuilder<T> extends Omit<LayoutPropsBuilder<T, TextProps>, "size" | "alignContent" | "alignItems" | "justifyContent" | "gap" | "rowGap" | "columnGap" | "wrap" | "direction" | "display" | "overflow">, Omit<SpanPropsBuilder<T>, "props"> {
nowrap(): T;
wrap(value?: boolean): T;
lineHeight(value: Required<TextProps["lineHeight"]>): T;
align(value: Required<TextProps["align"]>): T;
indent(value: Required<TextProps["indentSize"]>): T;
hangingIndent(value: Required<TextProps["hangingIndentSize"]>): T;
}
/**
* Text block containing styled spans and plain strings
*/
interface TextNode extends TextPropsBuilder<TextNode> {
type: "text";
/** mixed content */
children: Array<string | SpanNode>;
}
type TextDefaultProps = Omit<TextProps, keyof LayoutProps>;
interface TextDefaultPropsBuilder<T> extends Omit<SpanPropsBuilder<T>, "props"> {
props: TextDefaultProps;
nowrap(): T;
wrap(value?: boolean): T;
lineHeight(value: Required<TextProps["lineHeight"]>): T;
align(value: Required<TextProps["align"]>): T;
indent(value: Required<TextProps["indentSize"]>): T;
}
/**
* Text defaults container - sets text properties for child nodes
*/
interface TextDefaultNode extends TextDefaultPropsBuilder<TextDefaultNode> {
type: "text-default";
children: SoneNode[];
}
/**
* Horizontal layout container (flexDirection: row)
*/
interface RowNode extends LayoutPropsBuilder<RowNode> {
type: "row";
children: SoneNode[];
}
/**
* Vertical layout container (flexDirection: column)
*/
interface ColumnNode extends LayoutPropsBuilder<ColumnNode> {
type: "column";
children: SoneNode[];
}
/**
* Union of all possible Sone node types
*/
type SoneNode = ColumnNode | RowNode | TextNode | TextDefaultNode | PhotoNode | PathNode | TableNode | TableRowNode | TableCellNode | null | undefined;
/**
* Creates a styled text span
* @param children - the text content
* @example Span("Hello").color("red").size(16)
*/
declare function Span(children: string): SpanNode;
/**
* Creates a text defaults container for cascading text properties
* @param children - child nodes that will inherit text properties
* @example TextDefault(Text("Hello")).color("blue") // all child text inherits blue color
*/
declare function TextDefault(...children: SoneNode[]): TextDefaultNode;
/**
* Creates a text block with mixed content
* @param children - strings and span nodes
* @example Text("Hello ", Span("world").color("red")).size(14)
*/
declare function Text(...children: Array<SpanNode | string | undefined | null>): TextNode;
/**
* Creates a vertical layout container
* @param children - child nodes to layout vertically
* @example Column(Text("Top"), Text("Bottom")).gap(10)
*/
declare function Column(...children: SoneNode[]): ColumnNode;
/**
* Creates a horizontal layout container
* @param children - child nodes to layout horizontally
* @example Row(Text("Left"), Text("Right")).gap(10)
*/
declare function Row(...children: SoneNode[]): RowNode;
/**
* Creates an image display node
* @param src - image URL or buffer data, resolved before rendering
* @example Photo("./image.jpg").size(200, 100).scaleType("cover")
*/
declare function Photo(src: string | Uint8Array): PhotoNode;
/**
* SVG path drawing properties
*/
interface PathProps extends LayoutProps {
/** SVG path data @example "M10,10 L20,20 Z" */
d: string;
/** path bounding box [left, top, right, bottom] (computed) */
bounds?: number[];
/** outline color */
stroke?: ColorValue;
strokeWidth?: number;
strokeLineCap?: "butt" | "round" | "square";
strokeLineJoin?: "bevel" | "miter" | "round";
strokeMiterLimit?: number;
/** dash pattern @example [5, 5] = 5px dash, 5px gap */
strokeDashArray?: number[];
strokeDashOffset?: number;
/** fill color */
fill?: ColorValue;
/** @param value 0.0 to 1.0 */
fillOpacity?: number;
fillRule?: "evenodd" | "nonzero";
/** uniform scaling of the path */
scalePath?: number;
}
interface PathPropsBuilder<T> extends LayoutPropsBuilder<T, PathProps> {
stroke(value: Required<PathProps["stroke"]>): T;
strokeWidth(value: Required<PathProps["strokeWidth"]>): T;
strokeLineCap(value: Required<PathProps["strokeLineCap"]>): T;
strokeLineJoin(value: Required<PathProps["strokeLineJoin"]>): T;
strokeMiterLimit(value: Required<PathProps["strokeMiterLimit"]>): T;
strokeDashArray(...values: Required<PathProps["strokeDashArray"]>): T;
strokeDashOffset(value: Required<PathProps["strokeDashOffset"]>): T;
fill(value: Required<PathProps["fill"]>): T;
fillOpacity(value: Required<PathProps["fillOpacity"]>): T;
fillRule(value: Required<PathProps["fillRule"]>): T;
scalePath(value: Required<PathProps["scalePath"]>): T;
}
/**
* SVG path drawing node
*/
interface PathNode extends PathPropsBuilder<PathNode> {
type: "path";
}
declare function pathPropsBuilder<T>(props: PathProps): PathPropsBuilder<T>;
/**
* Creates an SVG path drawing node
* @param d - SVG path data string
* @example Path("M10,10 L50,50 Z").fill("red").stroke("black").strokeWidth(2)
*/
declare function Path(d: string): PathNode;
interface TableProps extends LayoutProps {
spacing?: number[];
}
interface TablePropsBuilder<T> extends LayoutPropsBuilder<T, TableProps> {
spacing(...values: Required<TableProps["spacing"]>): T;
}
interface TableNode extends TablePropsBuilder<TableNode> {
type: "table";
children: Array<TableRowNode | undefined | null>;
}
interface TableRowProps extends LayoutProps {
}
interface TableRowPropsBuilder<T> extends LayoutPropsBuilder<T, TableRowProps> {
}
interface TableRowNode extends TableRowPropsBuilder<TableRowNode> {
type: "table-row";
children: Array<TextDefaultNode | TableCellNode | null | undefined>;
}
declare function Table(...children: TableRowNode[]): TableNode;
declare function TableRow(...children: Array<TextDefaultNode | TableCellNode | null | undefined>): TableRowNode;
interface TableCellProps extends LayoutProps {
colspan?: number;
rowspan?: number;
}
interface TableCellPropsBuilder<T> extends LayoutPropsBuilder<T, TableCellProps> {
colspan: (value: Required<TableCellProps["colspan"]>) => T;
rowspan: (value: Required<TableCellProps["rowspan"]>) => T;
}
interface TableCellNode extends TableCellPropsBuilder<TableCellNode> {
type: "table-cell";
children: SoneNode[];
}
declare function TableCell(...children: SoneNode[]): TableCellNode;
declare function qrcode(data: QrCodeGenerateData, options?: QrCodeGenerateSvgOptions): Buffer<ArrayBuffer>;
export { type AlignContent, type AlignItems, type ColorValue, Column, type ColumnNode, DEFAULT_TEXT_PROPS, type DefaultTextProps, type FlexDirection, type FontValue, type JustifyContent, type LayoutPositionType, type LayoutProps, Path, type PathNode, type PathProps, type PathPropsBuilder, Photo, type PhotoNode, type PhotoProps, type PhotoPropsBuilder, type Required, type RequiredNonNullValues, Row, type RowNode, type SoneCompileContext, type SoneDebugConfig, type SoneImage, type SoneNode, type SoneRenderConfig, type SoneRenderer, Span, type SpanNode, type SpanProps, type SpanPropsBuilder, Table, TableCell, type TableCellNode, type TableCellProps, type TableCellPropsBuilder, type TableNode, type TableProps, type TablePropsBuilder, TableRow, type TableRowNode, type TableRowProps, type TableRowPropsBuilder, Text, TextDefault, type TextDefaultNode, type TextDefaultProps, type TextDefaultPropsBuilder, type TextNode, type TextProps, type TextPropsBuilder, compile, createLayoutNode, drawLayoutNode, findFonts, pathPropsBuilder, qrcode, render };