From c5071bf1cadcd3a3a4e6cf65f746a71b8acd117c Mon Sep 17 00:00:00 2001 From: flop Date: Wed, 13 May 2026 13:40:50 +0200 Subject: [PATCH] feat: types from spec --- ts/src/types.ts | 449 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 449 insertions(+) create mode 100644 ts/src/types.ts diff --git a/ts/src/types.ts b/ts/src/types.ts new file mode 100644 index 0000000..168bc48 --- /dev/null +++ b/ts/src/types.ts @@ -0,0 +1,449 @@ +// ============================================================================= +// Mono Display File Format - type definitions +// Spec: Mono Display File Format Specification +// ============================================================================= + +// --------------------------------------------------------------------------- +// Enums +// --------------------------------------------------------------------------- + +export enum FileVersion { + /** Illegal - must not appear in a valid file */ + Illegal = 0, + /** Current specification version */ + V1 = 1, +} + +export enum SectionType { + /** List of drawn elements - always shown */ + ElementsAlways = 1, + /** List of drawn elements - shown only within a POSIX timestamp window */ + ElementsTimespan = 2, + /** U8G2-format custom font used by draw elements in this file */ + CustomFont = 32, +} + +export enum ElementType { + Image2D = 1, + Animation = 2, + HorizontalScroll = 3, + VerticalScroll = 4, + Line = 5, + ClippedText = 16, + HScrollText = 17, + /** + * Current time display. + * NOTE: the spec diagram labels this element "Type: 16", which collides with + * ClippedText. This is assumed to be a spec typo; 18 is used here as the + * probable intended value until the spec is corrected. + */ + CurrentTime = 18, +} + +// --------------------------------------------------------------------------- +// Section flags (section types 1 and 2) +// --------------------------------------------------------------------------- + +/** Bit-field flags for ElementsAlways / ElementsTimespan sections. */ +export interface SectionFlags { + /** Bit 0 - render elements onto the front-side buffer. */ + drawFront?: boolean; + /** Bit 1 - render elements onto the back-side buffer. */ + drawBack?: boolean; + /** Bit 2 - clear targeted buffer(s) before drawing this section's elements. */ + clearBuffer?: boolean; + // Bits 3-15: reserved; writers MUST write 0. +} + +// --------------------------------------------------------------------------- +// Scroll element flags (HorizontalScroll / VerticalScroll / HScrollText) +// --------------------------------------------------------------------------- + +export interface ScrollElementFlags { + /** Bit 0 - wrap content endlessly. */ + endless?: boolean; + /** Bit 1 - reverse scroll direction. */ + invertDirection?: boolean; + /** Bit 2 - pad at start edge (left for H-scroll, top for V-scroll). */ + padStart?: boolean; + /** Bit 3 - pad at end edge (right for H-scroll, bottom for V-scroll). */ + padEnd?: boolean; + // Bits 4-7: reserved. +} + +// --------------------------------------------------------------------------- +// CurrentTime element flags +// --------------------------------------------------------------------------- + +export interface CurrentTimeFlags { + /** Bit 0 - use 12-hour clock (default 24-hour). */ + clock12h?: boolean; + /** Bit 1 - show hours. */ + showHours?: boolean; + /** Bit 2 - show minutes. */ + showMinutes?: boolean; + /** Bit 3 - show seconds. */ + showSeconds?: boolean; + // Bits 4-15: reserved. +} + +// --------------------------------------------------------------------------- +// Line element flags +// --------------------------------------------------------------------------- + +export interface LineFlags { + /** Bit 0 - invert pixel values along the line. */ + invertPixels?: boolean; + // Bits 1-7: reserved. +} + +// --------------------------------------------------------------------------- +// Font index helpers +// --------------------------------------------------------------------------- + +/** + * Font index encoding: + * 0 .. 0x7FFF built-in font (device-specific; may not exist) + * 0x8000+ custom font embedded in this file + * 0x8000 = first CustomFont section + * 0x8001 = second, etc. + */ +export type FontIndex = number; + +export const CUSTOM_FONT_BASE = 0x8000; + +/** Return true when fontIndex refers to a custom (in-file) font. */ +export function isCustomFont(fontIndex: FontIndex): boolean { + return fontIndex >= CUSTOM_FONT_BASE; +} + +/** Extract zero-based position of a custom font within the file. */ +export function customFontOrdinal(fontIndex: FontIndex): number { + return fontIndex - CUSTOM_FONT_BASE; +} + +// --------------------------------------------------------------------------- +// User-facing element descriptors (MonoDisplayFile / builder input) +// Pixels are 1 byte/pixel (0 = off, 1 = on) in the API; packed to 1bpp on +// serialisation. +// --------------------------------------------------------------------------- + +export interface Image2DElement { + type: ElementType.Image2D; + /** Row-major, 1 byte/pixel (0 = off, 1 = on). Packed to 1bpp on write. */ + pixels: Uint8Array; + width: number; + height: number; + xOffset?: number; // default 0 + yOffset?: number; // default 0 +} + +export interface AnimationFrameDescriptor { + /** 1 byte/pixel, same dimensions as the parent AnimationElement. */ + pixels: Uint8Array; +} + +export interface AnimationElement { + type: ElementType.Animation; + width: number; + height: number; + frames: AnimationFrameDescriptor[]; + /** + * Advance frame every (updateInterval + 1) ticks. + * 0 = every tick, 1 = every 2nd tick, ..., 65535 = every 65536th tick. + * Default 0. + */ + updateInterval?: number; + xOffset?: number; + yOffset?: number; +} + +export interface HScrollElement { + type: ElementType.HorizontalScroll; + /** Viewport width in pixels. */ + width: number; + /** Viewport height in pixels. */ + height: number; + /** Scrolling content pixels (contentWidth × height), 1 byte/pixel. */ + pixels: Uint8Array; + contentWidth: number; + /** + * Scroll speed byte SS; moves (SS + 1) / 16 pixels per tick. + * Range 0-255. Default 0 (= 1/16 px/tick). + */ + scrollSpeed?: number; + flags?: ScrollElementFlags; + xOffset?: number; + yOffset?: number; +} + +export interface VScrollElement { + type: ElementType.VerticalScroll; + /** Viewport width in pixels. */ + width: number; + /** Viewport height in pixels. */ + height: number; + /** Scrolling content pixels (width × contentHeight), 1 byte/pixel. */ + pixels: Uint8Array; + contentHeight: number; + /** Scroll speed byte SS; moves (SS + 1) / 16 pixels per tick. Default 0. */ + scrollSpeed?: number; + flags?: ScrollElementFlags; + xOffset?: number; + yOffset?: number; +} + +export interface LineElement { + type: ElementType.Line; + xOrigin: number; + yOrigin: number; + xTarget: number; + yTarget: number; + /** Reserved by spec; writers MUST write 0. */ + lineStyle?: number; + invertPixels?: boolean; // flags bit 0 +} + +export interface ClippedTextElement { + type: ElementType.ClippedText; + text: string; // UTF-8 + width: number; + height: number; + /** 0-32767 = built-in font; 0x8000+ = custom font in file. Default 0. */ + fontIndex?: FontIndex; + xOffset?: number; + yOffset?: number; +} + +export interface HScrollTextElement { + type: ElementType.HScrollText; + text: string; // UTF-8 + width: number; + height: number; + scrollSpeed?: number; // SS byte, default 0 + fontIndex?: FontIndex; + flags?: ScrollElementFlags; + xOffset?: number; + yOffset?: number; +} + +export interface CurrentTimeElement { + type: ElementType.CurrentTime; + width: number; + height: number; + /** 0-32767 = built-in font; 0x8000+ = custom font in file. Default 0. */ + fontIndex?: FontIndex; + /** + * UTC offset in whole minutes (signed 16-bit integer). + * e.g. UTC+5:30 → 330, UTC-8 → -480. + */ + utcOffsetMinutes?: number; + flags?: CurrentTimeFlags; + xOffset?: number; + yOffset?: number; +} + +export type DrawElement = + | Image2DElement + | AnimationElement + | HScrollElement + | VScrollElement + | LineElement + | ClippedTextElement + | HScrollTextElement + | CurrentTimeElement; + +// --------------------------------------------------------------------------- +// Section descriptors (MonoDisplayFile / builder input) +// --------------------------------------------------------------------------- + +export interface ElementsAlwaysDescriptor { + flags?: SectionFlags; + elements: DrawElement[]; +} + +export interface ElementsTimespanDescriptor { + flags?: SectionFlags; + elements: DrawElement[]; + /** POSIX timestamp (seconds) - section visible when now >= startTimestamp. */ + startTimestamp: bigint; + /** POSIX timestamp (seconds) - section visible when now < endTimestamp. */ + endTimestamp: bigint; +} + +export interface CustomFontDescriptor { + /** Raw U8G2 font data. */ + fontData: Uint8Array; +} + +/** + * Top-level descriptor passed to MonoDisplayFile. + * + * Sections are written in the order they appear here. + * Custom fonts MUST appear before any element that references them. + */ +export interface MonoDisplayFileDescriptor { + /** Section type 1 - drawn every render tick. */ + elements_always?: DrawElement[] | ElementsAlwaysDescriptor; + /** Section type 2 - drawn only within the given timestamp window. */ + elements_timespan?: ElementsTimespanDescriptor[]; + /** Section type 32 - embedded U8G2 custom fonts. */ + custom_fonts?: CustomFontDescriptor[]; +} + +// --------------------------------------------------------------------------- +// MonoFormat types (MonoDisplayParser output → renderer input) +// Pixels are always unpacked (1 byte/pixel) after parsing. +// --------------------------------------------------------------------------- + +export interface MonoFormatPixelImage { + /** Unpacked: 1 byte per pixel, 0 = off, 1 = on, row-major. */ + pixels: Uint8Array; + width: number; + height: number; +} + +export interface MonoFormatImage2D { + type: ElementType.Image2D; + xOffset: number; + yOffset: number; + image: MonoFormatPixelImage; +} + +export interface MonoFormatAnimation { + type: ElementType.Animation; + xOffset: number; + yOffset: number; + width: number; + height: number; + updateInterval: number; + frames: MonoFormatPixelImage[]; +} + +export interface MonoFormatScrollFlags { + endless: boolean; + invertDirection: boolean; + padStart: boolean; + padEnd: boolean; +} + +export interface MonoFormatHScroll { + type: ElementType.HorizontalScroll; + xOffset: number; + yOffset: number; + width: number; + height: number; + contentWidth: number; + scrollSpeed: number; + flags: MonoFormatScrollFlags; + content: MonoFormatPixelImage; +} + +export interface MonoFormatVScroll { + type: ElementType.VerticalScroll; + xOffset: number; + yOffset: number; + width: number; + height: number; + contentHeight: number; + scrollSpeed: number; + flags: MonoFormatScrollFlags; + content: MonoFormatPixelImage; +} + +export interface MonoFormatLine { + type: ElementType.Line; + xOrigin: number; + yOrigin: number; + xTarget: number; + yTarget: number; + lineStyle: number; + invertPixels: boolean; +} + +export interface MonoFormatClippedText { + type: ElementType.ClippedText; + xOffset: number; + yOffset: number; + width: number; + height: number; + fontIndex: FontIndex; + text: string; +} + +export interface MonoFormatHScrollText { + type: ElementType.HScrollText; + xOffset: number; + yOffset: number; + width: number; + height: number; + scrollSpeed: number; + fontIndex: FontIndex; + flags: MonoFormatScrollFlags; + text: string; +} + +export interface MonoFormatCurrentTimeFlags { + clock12h: boolean; + showHours: boolean; + showMinutes: boolean; + showSeconds: boolean; +} + +export interface MonoFormatCurrentTime { + type: ElementType.CurrentTime; + xOffset: number; + yOffset: number; + width: number; + height: number; + fontIndex: FontIndex; + /** UTC offset in minutes (signed). */ + utcOffsetMinutes: number; + flags: MonoFormatCurrentTimeFlags; +} + +export type MonoFormatElement = + | MonoFormatImage2D + | MonoFormatAnimation + | MonoFormatHScroll + | MonoFormatVScroll + | MonoFormatLine + | MonoFormatClippedText + | MonoFormatHScrollText + | MonoFormatCurrentTime; + +export interface MonoFormatSectionFlags { + drawFront: boolean; + drawBack: boolean; + clearBuffer: boolean; +} + +export interface MonoFormatElementsAlways { + sectionType: SectionType.ElementsAlways; + flags: MonoFormatSectionFlags; + elements: MonoFormatElement[]; +} + +export interface MonoFormatElementsTimespan { + sectionType: SectionType.ElementsTimespan; + flags: MonoFormatSectionFlags; + startTimestamp: bigint; // POSIX seconds + endTimestamp: bigint; + elements: MonoFormatElement[]; +} + +export interface MonoFormatCustomFont { + sectionType: SectionType.CustomFont; + /** Raw U8G2 font data (actual bytes, not padded). */ + fontData: Uint8Array; +} + +export type MonoFormatSection = + | MonoFormatElementsAlways + | MonoFormatElementsTimespan + | MonoFormatCustomFont; + +/** Top-level MonoDisplayParser output. */ +export interface MonoFormatFile { + sections: MonoFormatSection[]; +}