// Generated by `cargo run -p compose-rust --bin codegen`. DO NOT EDIT.
package dev.darkpyonix.composerust.protocol
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.charset.StandardCharsets
enum class WidgetKind { Column, Row, Box, Text, TextField, Button, Spacer, LazyColumn, ScrollColumn, Image, Icon, Checkbox, RadioButton, Switch, Slider, ProgressIndicator, Divider, Card, Surface, Dialog, Menu, Tabs, TopAppBar, LazyRow, Tooltip, Canvas, DatePicker, TimePicker, Dropdown, Navigation, NavigationItem, Sheet, Scaffold, ScaffoldSlot, LazyGrid, FileDropTarget, ScrollRow, Chip, FloatingAction, Badge, SelectionContainer, SplitPane, LinearProgressIndicator }
enum class PropertyKind { Text, Placeholder, Enabled, Multiline, OnClick, OnValueChange, OnSubmit, OnFocusLost, OnKeyDown, ItemCount, ItemKey, OnRangeRequested, TypeRole, FontSize, FontWeight, LineHeight, LetterSpacing, Color, TextAlign, MaxLines, Overflow, Arrangement, Spacing, SpaceRole, Alignment, Variant, Asset, Checked, Steps, Determinate, Circular, Vertical, Open, OnDismiss, SelectedIndex, Commands, Value, Min, Max, Icon, Slot, Columns, MinColumnWidth, Spans, OnFilesEntered, OnFilesDropped, Section, Count, Collapsible, Progress }
enum class Key { Enter }
enum class WindowSizeClass { Compact, Medium, Expanded }
enum class WindowHeightClass { Compact, Medium, Expanded }
enum class ColorRole { Primary, OnPrimary, Secondary, OnSecondary, Surface, OnSurface, SurfaceVariant, OnSurfaceVariant, Background, OnBackground, Outline, OutlineVariant, Error, OnError, SurfaceContainer, Tertiary, OnTertiary, PrimaryContainer, OnPrimaryContainer, SecondaryContainer, OnSecondaryContainer, TertiaryContainer, OnTertiaryContainer, SyntaxKeyword, SyntaxString, SyntaxComment, SyntaxNumber, SyntaxConstant, SyntaxType, SyntaxFunction, SyntaxVariable, SyntaxProperty, SyntaxOperator, SyntaxPunctuation, SyntaxTag, SyntaxAttribute, SyntaxEscape, SyntaxMacro, DiffAdded, DiffRemoved, DiffModified, DiffAddedContainer, DiffRemovedContainer, DiffAddedEmphasis, DiffRemovedEmphasis }
enum class TypeRole { Display, Headline, Title, Subtitle, Body, BodyStrong, Label, Caption, Mono }
enum class ShapeRole { None, ExtraSmall, Small, Medium, Large, Full }
enum class MotionRole { Instant, Quick, Standard, Slow, Emphasized }
enum class TileMode { Clamp, Repeat, Mirror }
enum class MaterialRole { Thin, Regular, Thick, Chrome }
enum class SpaceRole { None, Xs, Sm, Md, Lg, Xl, Xxl }
enum class TextAlign { Start, Center, End, Justify }
enum class TextOverflow { Clip, Ellipsis, Visible }
enum class Arrangement { Start, Center, End, SpaceBetween, SpaceAround, SpaceEvenly }
enum class Alignment { TopStart, TopCenter, TopEnd, CenterStart, Center, CenterEnd, BottomStart, BottomCenter, BottomEnd }
enum class ButtonVariant { Filled, Tonal, Outlined, Text, Operator }
enum class DesignSystem { Material3, Cupertino, Fluent, Gnome, Breeze, Deepin, LiquidGlass }
enum class ColorScheme { Light, Dark, FollowSystem }
enum class AssetKind { Png, Jpeg, Svg, VectorIcon, Font, Brush }
enum class IconRole { Back, Forward, Close, Search, Add, Check, Settings, More, Home, List, Inbox, Menu, History, Send, Mic, Image, Video, Library, Sidebar, Compose, Collapse }
enum class MessageDuration { Short, Long }
enum class Chrome { Modern, System }
enum class TitleBar { Normal, Simple }
enum class SlotRole { TopBar, BottomBar, FloatingAction, Content }
enum class NotificationImportance { Normal, Urgent }
enum class NotificationPresentation { Always, WhenInactive }
enum class NotificationPermission { NotDetermined, Granted, Denied, Unsupported }
enum class LoopMode(val wire: Byte) {
/** The Renderer runs the loop and the Host blocks inside it. Desktop. */
Renderer(0),
/** The platform owns the process and the loop. Android, iOS and the web. */
Platform(1),
}
sealed interface Paint {
data class Role(val role: ColorRole) : Paint
/** A literal 0xAARRGGBB colour. */
data class Literal(val argb: Int) : Paint
/**
* A registered brush: a gradient, or a picture laid out as a fill.
*
* An id, because a list of stops does not fit the two words a paint has and because a
* brush has to outlive the frame that draws it.
*/
data class Asset(val assetId: Int) : Paint
}
data class Theme(
val designSystem: DesignSystem,
val fallback: DesignSystem,
val colorScheme: ColorScheme,
val adaptive: Boolean,
/**
* The font asset each type role resolves to, indexed by the role's ordinal, with zero
* where the role keeps the system font.
*
* Per theme rather than per node: an application changes what a role is made of and
* every piece of text in that role changes with it. A node that could name a font
* would be a node deciding typography.
*/
val fonts: List<Int> = List(TypeRole.entries.size) { 0 },
/**
* The application's own colours, laid over whichever design system this resolves to.
*
* Both schemes arrive together, so a switch between light and dark is answered from
* here and never asks the Host. Empty where the application gave none.
*/
val palette: List<PaletteEntry> = emptyList(),
/**
* What was wrong with entries the palette arrived with, one sentence each.
*
* Those entries are already left out of [palette] and the rest of it stands. Whoever
* applies the theme reports each of these as a protocol error: a theme is never thrown
* away whole for one bad entry, because that would open the application with no colours
* at all.
*/
val paletteProblems: List<String> = emptyList(),
) {
/** The font asset for [role], or null where the role keeps the system font. */
fun font(role: TypeRole): Int? = fonts.getOrNull(role.ordinal)?.takeIf { it != 0 }
/** The colour the application gave [role] in this scheme, as 0xAARRGGBB, or null. */
fun paletteColor(role: ColorRole, dark: Boolean): Int? =
palette.firstOrNull { it.role == role && it.dark == dark }?.argb
}
/** One role's colour in one scheme, as the application gave it. */
data class PaletteEntry(val role: ColorRole, val dark: Boolean, val argb: Int)
/**
* What the application asked of its own window.
*
* A zero measurement means the application did not ask, so the choice is the Renderer's.
* This arrives in the first batch, which the Renderer reads before it stands the window
* up; a platform where the window is not ours ignores it.
*/
data class Window(
val chrome: Chrome,
/**
* Which of the two title bars this window has.
*
* `Chrome` says who draws the caption; this says, inside that, what kind of window the
* caption belongs to. What it is worth in pixels is this side's answer: on macOS how
* far in the buttons sit and how round the window is, on Windows and Linux how tall
* the caption is.
*/
val titleBar: TitleBar,
/**
* What the window calls itself.
*
* Empty means the application said nothing and the renderer uses its own name. A
* desktop lists windows by this, so a window with no title of its own is listed
* under whatever the renderer happened to be called.
*/
val title: String,
/**
* The picture the window wears, as an asset id, or zero for none.
*
* An id rather than a path or a name: a path is a fact about the machine the
* application was built on, and a name asks the toolkit to find something it may not
* have. Zero leaves the toolkit's own icon, which is what every window here wore
* until this existed.
*/
val icon: Int,
val width: Int,
val height: Int,
val minWidth: Int,
val minHeight: Int,
val resizable: Boolean,
)
sealed interface PropertyValue {
data object None : PropertyValue
data class Text(val value: String) : PropertyValue
data class Bool(val value: Boolean) : PropertyValue
data class Integer(val value: Long) : PropertyValue
data class Float(val value: kotlin.Float) : PropertyValue
class Bytes(val value: ByteArray) : PropertyValue
}
/**
* One drawing command. Coordinates are dp from the Canvas's top-left corner.
*
* Colour is a [Paint], so a command may name a ColorRole and the design system
* decides what it looks like.
*/
sealed interface DrawCommand {
val paint: Paint
data class Line(override val paint: Paint, val x1: kotlin.Float, val y1: kotlin.Float, val x2: kotlin.Float, val y2: kotlin.Float, val strokeWidth: kotlin.Float) : DrawCommand
data class Rect(override val paint: Paint, val x: kotlin.Float, val y: kotlin.Float, val width: kotlin.Float, val height: kotlin.Float, val strokeWidth: kotlin.Float) : DrawCommand
data class RoundRect(override val paint: Paint, val x: kotlin.Float, val y: kotlin.Float, val width: kotlin.Float, val height: kotlin.Float, val radius: kotlin.Float, val strokeWidth: kotlin.Float) : DrawCommand
data class Circle(override val paint: Paint, val centerX: kotlin.Float, val centerY: kotlin.Float, val radius: kotlin.Float, val strokeWidth: kotlin.Float) : DrawCommand
data class Arc(override val paint: Paint, val centerX: kotlin.Float, val centerY: kotlin.Float, val radius: kotlin.Float, val startDegrees: kotlin.Float, val sweepDegrees: kotlin.Float, val strokeWidth: kotlin.Float) : DrawCommand
data class PolylineRef(override val paint: Paint, val assetId: Int, val strokeWidth: kotlin.Float) : DrawCommand
data class TextAt(override val paint: Paint, val textOffset: Int, val textLength: Int, val x: kotlin.Float, val y: kotlin.Float, val typeRole: TypeRole) : DrawCommand
}
/** Decodes a Canvas command list: fixed-size records, then the TextAt strings. */
object DrawCommands {
const val COMMAND_LENGTH = 36
fun decode(bytes: ByteArray): List<DrawCommand> {
val buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN)
val commands = ArrayList<DrawCommand>(bytes.size / COMMAND_LENGTH)
var offset = 0
while (offset + COMMAND_LENGTH <= bytes.size) {
val tag = buffer.getShort(offset).toInt() and 0xffff
val length = buffer.getShort(offset + 2).toInt() and 0xffff
// The text region starts where the records stop decoding, so an unreadable
// record ends the list rather than failing the whole batch.
if (length != COMMAND_LENGTH) break
val paintBits = buffer.getLong(offset + 4)
val kind = (paintBits ushr 32).toInt()
if (kind != 1 && kind != 2) break
val paint = if (kind == 1) {
Paint.Role(colorRoleOrNull(paintBits.toInt()) ?: break)
} else {
Paint.Literal(paintBits.toInt())
}
fun word(index: Int): Int = buffer.getInt(offset + 12 + index * 4)
fun real(index: Int): kotlin.Float = kotlin.Float.fromBits(word(index))
val command = when (tag) {
1 -> DrawCommand.Line(paint, real(0), real(1), real(2), real(3), real(4))
2 -> DrawCommand.Rect(paint, real(0), real(1), real(2), real(3), real(4))
3 -> DrawCommand.RoundRect(paint, real(0), real(1), real(2), real(3), real(4), real(5))
4 -> DrawCommand.Circle(paint, real(0), real(1), real(2), real(3))
5 -> DrawCommand.Arc(paint, real(0), real(1), real(2), real(3), real(4), real(5))
6 -> DrawCommand.PolylineRef(paint, word(0), real(1))
7 -> DrawCommand.TextAt(paint, word(0), word(1), real(2), real(3), typeRoleOrNull(word(4)) ?: break)
else -> break
}
commands.add(command)
offset += COMMAND_LENGTH
}
return commands
}
/** The string a TextAt points at, measured from the start of the list. */
fun textOf(bytes: ByteArray, command: DrawCommand.TextAt): String {
val end = command.textOffset.toLong() + command.textLength.toLong()
if (command.textOffset < 0 || command.textLength < 0 || end > bytes.size.toLong()) {
return ""
}
return String(bytes, command.textOffset, command.textLength, StandardCharsets.UTF_8)
}
private fun typeRoleOrNull(tag: Int): TypeRole? = when (tag) {
1 -> TypeRole.Display
2 -> TypeRole.Headline
3 -> TypeRole.Title
4 -> TypeRole.Subtitle
5 -> TypeRole.Body
6 -> TypeRole.BodyStrong
7 -> TypeRole.Label
8 -> TypeRole.Caption
9 -> TypeRole.Mono
else -> null
}
private fun colorRoleOrNull(tag: Int): ColorRole? = when (tag) {
1 -> ColorRole.Primary
2 -> ColorRole.OnPrimary
3 -> ColorRole.Secondary
4 -> ColorRole.OnSecondary
5 -> ColorRole.Surface
6 -> ColorRole.OnSurface
7 -> ColorRole.SurfaceVariant
8 -> ColorRole.OnSurfaceVariant
9 -> ColorRole.Background
10 -> ColorRole.OnBackground
11 -> ColorRole.Outline
12 -> ColorRole.OutlineVariant
13 -> ColorRole.Error
14 -> ColorRole.OnError
15 -> ColorRole.SurfaceContainer
16 -> ColorRole.Tertiary
17 -> ColorRole.OnTertiary
18 -> ColorRole.PrimaryContainer
19 -> ColorRole.OnPrimaryContainer
20 -> ColorRole.SecondaryContainer
21 -> ColorRole.OnSecondaryContainer
22 -> ColorRole.TertiaryContainer
23 -> ColorRole.OnTertiaryContainer
24 -> ColorRole.SyntaxKeyword
25 -> ColorRole.SyntaxString
26 -> ColorRole.SyntaxComment
27 -> ColorRole.SyntaxNumber
28 -> ColorRole.SyntaxConstant
29 -> ColorRole.SyntaxType
30 -> ColorRole.SyntaxFunction
31 -> ColorRole.SyntaxVariable
32 -> ColorRole.SyntaxProperty
33 -> ColorRole.SyntaxOperator
34 -> ColorRole.SyntaxPunctuation
35 -> ColorRole.SyntaxTag
36 -> ColorRole.SyntaxAttribute
37 -> ColorRole.SyntaxEscape
38 -> ColorRole.SyntaxMacro
39 -> ColorRole.DiffAdded
40 -> ColorRole.DiffRemoved
41 -> ColorRole.DiffModified
42 -> ColorRole.DiffAddedContainer
43 -> ColorRole.DiffRemovedContainer
44 -> ColorRole.DiffAddedEmphasis
45 -> ColorRole.DiffRemovedEmphasis
else -> null
}
}
/**
* One run of different treatment inside a Text's string, as the Host wrote it.
*
* Offsets are bytes of the string's UTF-8, because that is what the string is measured in
* on the side that wrote them. A null paint is a run that says nothing about that colour.
*/
data class TextSpanRecord(
val start: Int,
val length: Int,
val typeRole: TypeRole?,
val color: Paint?,
/** What is painted behind this run's letters only. */
val background: Paint?,
val bold: Boolean,
val italic: Boolean,
val underline: Boolean,
val strikethrough: Boolean,
/** The handler a press on this run reports to, or zero where it is not a link. */
val handlerId: Long,
)
/** Decodes the run list a Text carries in its `Spans` property. */
object SpanRecords {
const val SPAN_LENGTH = 36
private const val START_AT = 0
private const val LENGTH_AT = 4
private const val ROLE_AT = 8
private const val FLAGS_AT = 10
private const val COLOR_AT = 12
private const val BACKGROUND_AT = 20
private const val HANDLER_AT = 28
private const val FLAG_BOLD = 1
private const val FLAG_ITALIC = 2
private const val FLAG_UNDERLINE = 4
private const val FLAG_STRIKETHROUGH = 8
/** The runs in [bytes], or null where it is not a whole number of records. */
fun decode(bytes: ByteArray): List<TextSpanRecord>? {
if (bytes.size % SPAN_LENGTH != 0) return null
val buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN)
return List(bytes.size / SPAN_LENGTH) { index ->
val at = index * SPAN_LENGTH
val flags = buffer.getShort(at + FLAGS_AT).toInt() and 0xffff
TextSpanRecord(
start = buffer.getInt(at + START_AT),
length = buffer.getInt(at + LENGTH_AT),
typeRole = typeRoleOrNull(buffer.getShort(at + ROLE_AT).toInt() and 0xffff),
color = paintOrNull(buffer.getLong(at + COLOR_AT)),
background = paintOrNull(buffer.getLong(at + BACKGROUND_AT)),
bold = flags and FLAG_BOLD != 0,
italic = flags and FLAG_ITALIC != 0,
underline = flags and FLAG_UNDERLINE != 0,
strikethrough = flags and FLAG_STRIKETHROUGH != 0,
handlerId = buffer.getLong(at + HANDLER_AT),
)
}
}
/** A paint, or null for zero and for anything that does not decode. */
private fun paintOrNull(bits: Long): Paint? {
val value = bits.toInt()
return when ((bits ushr 32).toInt()) {
1 -> colorRoleOrNull(value)?.let { Paint.Role(it) }
2 -> Paint.Literal(value)
3 -> Paint.Asset(value)
else -> null
}
}
private fun colorRoleOrNull(tag: Int): ColorRole? = when (tag) {
1 -> ColorRole.Primary
2 -> ColorRole.OnPrimary
3 -> ColorRole.Secondary
4 -> ColorRole.OnSecondary
5 -> ColorRole.Surface
6 -> ColorRole.OnSurface
7 -> ColorRole.SurfaceVariant
8 -> ColorRole.OnSurfaceVariant
9 -> ColorRole.Background
10 -> ColorRole.OnBackground
11 -> ColorRole.Outline
12 -> ColorRole.OutlineVariant
13 -> ColorRole.Error
14 -> ColorRole.OnError
15 -> ColorRole.SurfaceContainer
16 -> ColorRole.Tertiary
17 -> ColorRole.OnTertiary
18 -> ColorRole.PrimaryContainer
19 -> ColorRole.OnPrimaryContainer
20 -> ColorRole.SecondaryContainer
21 -> ColorRole.OnSecondaryContainer
22 -> ColorRole.TertiaryContainer
23 -> ColorRole.OnTertiaryContainer
24 -> ColorRole.SyntaxKeyword
25 -> ColorRole.SyntaxString
26 -> ColorRole.SyntaxComment
27 -> ColorRole.SyntaxNumber
28 -> ColorRole.SyntaxConstant
29 -> ColorRole.SyntaxType
30 -> ColorRole.SyntaxFunction
31 -> ColorRole.SyntaxVariable
32 -> ColorRole.SyntaxProperty
33 -> ColorRole.SyntaxOperator
34 -> ColorRole.SyntaxPunctuation
35 -> ColorRole.SyntaxTag
36 -> ColorRole.SyntaxAttribute
37 -> ColorRole.SyntaxEscape
38 -> ColorRole.SyntaxMacro
39 -> ColorRole.DiffAdded
40 -> ColorRole.DiffRemoved
41 -> ColorRole.DiffModified
42 -> ColorRole.DiffAddedContainer
43 -> ColorRole.DiffRemovedContainer
44 -> ColorRole.DiffAddedEmphasis
45 -> ColorRole.DiffRemovedEmphasis
else -> null
}
private fun typeRoleOrNull(tag: Int): TypeRole? = when (tag) {
1 -> TypeRole.Display
2 -> TypeRole.Headline
3 -> TypeRole.Title
4 -> TypeRole.Subtitle
5 -> TypeRole.Body
6 -> TypeRole.BodyStrong
7 -> TypeRole.Label
8 -> TypeRole.Caption
9 -> TypeRole.Mono
else -> null
}
}
sealed interface Modifier {
data object Empty : Modifier
data class Padding(val value: kotlin.Float) : Modifier
data object FillMaxWidth : Modifier
data object FillMaxHeight : Modifier
data class Width(val value: kotlin.Float) : Modifier
data class Height(val value: kotlin.Float) : Modifier
data class Size(val width: kotlin.Float, val height: kotlin.Float) : Modifier
data class Background(val paint: Paint) : Modifier
data class Clickable(val handlerId: Long) : Modifier
data class PaddingRole(val role: dev.darkpyonix.composerust.protocol.SpaceRole) : Modifier
data class PaddingEach(val start: kotlin.Float, val top: kotlin.Float, val end: kotlin.Float, val bottom: kotlin.Float) : Modifier
data class Weight(val value: kotlin.Float) : Modifier
data class Shape(val topStart: kotlin.Float, val topEnd: kotlin.Float, val bottomEnd: kotlin.Float, val bottomStart: kotlin.Float) : Modifier
data class ShapeRole(val role: dev.darkpyonix.composerust.protocol.ShapeRole) : Modifier
data class Border(val width: kotlin.Float, val paint: Paint) : Modifier
data class Elevation(val value: kotlin.Float) : Modifier
data class ObserveSize(val token: Int) : Modifier
data class Motion(val role: dev.darkpyonix.composerust.protocol.MotionRole) : Modifier
data class Material(val role: dev.darkpyonix.composerust.protocol.MaterialRole) : Modifier
}
sealed interface Mutation {
data class Create(val nodeId: Int, val widget: WidgetKind) : Mutation
data class SetProp(val nodeId: Int, val property: PropertyKind, val value: PropertyValue) : Mutation
data class SetModifier(val nodeId: Int, val index: Int, val modifier: Modifier) : Mutation
data class Insert(val parentId: Int, val nodeId: Int, val index: Int) : Mutation
data class Move(val parentId: Int, val nodeId: Int, val index: Int) : Mutation
data class Remove(val nodeId: Int) : Mutation
data class SetText(val nodeId: Int, val text: String, val selectionStart: Int, val selectionEnd: Int) : Mutation
data class AppendText(val nodeId: Int, val text: String) : Mutation
data class SetTheme(val theme: Theme) : Mutation
data class SetWindow(val window: Window) : Mutation
/**
* The bytes of one asset. `kind` is the raw wire tag rather than an [AssetKind],
* because a kind this Renderer cannot read has to be reported as a protocol error by
* the asset cache instead of stopping the rest of the batch from being applied.
*
* `bytes` is already a copy: the batch buffer is only valid for the call that carried
* it, and an asset has to outlive the frame that draws it.
*/
data class RegisterAsset(val assetId: Int, val kind: Int, val bytes: ByteArray) : Mutation {
override fun equals(other: Any?): Boolean =
this === other ||
(other is RegisterAsset && assetId == other.assetId && kind == other.kind &&
bytes.contentEquals(other.bytes))
override fun hashCode(): Int =
(assetId * 31 + kind) * 31 + bytes.contentHashCode()
}
data class ReleaseAsset(val assetId: Int) : Mutation
/**
* One sentence to say to the user, with an optional thing to do about it.
*
* It names no node because it is not in the tree. The Host says it once; how long it
* stays, where it sits and what happens when a second one arrives while the first is
* still up are the Renderer's to decide.
*
* `handlerId` is 0 when the message has no action, and `action` is then empty.
*/
data class ShowMessage(
val handlerId: Long,
val text: String,
val action: String,
val duration: MessageDuration,
) : Mutation
/**
* One notification to show outside the window.
*
* Not a node: it outlives the component that posted it, and when it goes away is up to
* the user in the notification centre. Posting again under the same `key` replaces it;
* an empty `key` is named by the Renderer and can be neither replaced nor withdrawn. An
* empty action label is no button.
*/
data class PostNotification(
val key: String,
val title: String,
val body: String,
val channel: String,
val action1: String,
val action2: String,
val importance: NotificationImportance,
val presentation: NotificationPresentation,
) : Mutation
/** Takes back the notification posted under `key`, if it is still showing. */
data class WithdrawNotification(val key: String) : Mutation
/** Asks the platform for permission. The answer comes back as an event. */
data object RequestNotificationPermission : Mutation
}
sealed interface HostEvent {
val nodeId: Int
val handlerId: Long
data class Clicked(override val nodeId: Int, override val handlerId: Long) : HostEvent
data class TextChanged(override val nodeId: Int, override val handlerId: Long, val text: String) : HostEvent
data class TextSubmitted(override val nodeId: Int, override val handlerId: Long, val text: String) : HostEvent
data class FocusLost(override val nodeId: Int, override val handlerId: Long) : HostEvent
data class ProtocolError(override val nodeId: Int, override val handlerId: Long, val code: Int, val message: String) : HostEvent
data class KeyDown(override val nodeId: Int, override val handlerId: Long, val key: Key, val shiftKey: Boolean, val ctrlKey: Boolean, val altKey: Boolean, val metaKey: Boolean) : HostEvent
data class RangeRequested(override val nodeId: Int, override val handlerId: Long, val start: Int, val count: Int) : HostEvent
data class ValueChanged(override val nodeId: Int, override val handlerId: Long, val value: Double) : HostEvent
data class WindowSizeChanged(override val nodeId: Int, override val handlerId: Long, val widthDp: kotlin.Float, val heightDp: kotlin.Float, val sizeClass: WindowSizeClass, val heightClass: WindowHeightClass) : HostEvent
data class Resync(override val nodeId: Int, override val handlerId: Long) : HostEvent
data class LifecycleStart(override val nodeId: Int, override val handlerId: Long) : HostEvent
data class LifecycleStop(override val nodeId: Int, override val handlerId: Long) : HostEvent
data class DesignSystemResolved(override val nodeId: Int, override val handlerId: Long, val system: DesignSystem) : HostEvent
data class FilesEntered(override val nodeId: Int, override val handlerId: Long) : HostEvent
data class FilesDropped(override val nodeId: Int, override val handlerId: Long, val text: String) : HostEvent
data class NotificationActivated(override val nodeId: Int, override val handlerId: Long, val action: Int, val key: String) : HostEvent
data class NotificationPermissionChanged(override val nodeId: Int, override val handlerId: Long, val state: NotificationPermission) : HostEvent
}
class ProtocolException(message: String, val offset: Int) :
IllegalArgumentException("$message at byte offset $offset")
object Protocol {
const val SCHEMA_HASH: Long = -6082458096398358368L
const val PROTOCOL_VERSION: Int = 1
private const val TAG_ENVELOPE = 0
private const val TAG_CREATE = 1
private const val TAG_SET_PROP = 2
private const val TAG_SET_MODIFIER = 3
private const val TAG_INSERT = 4
private const val TAG_MOVE = 5
private const val TAG_REMOVE = 6
private const val TAG_SET_TEXT = 7
private const val TAG_APPEND_TEXT = 8
private const val TAG_SET_THEME = 9
private const val TAG_REGISTER_ASSET = 10
private const val TAG_RELEASE_ASSET = 11
private const val TAG_SHOW_MESSAGE = 12
private const val TAG_SET_WINDOW = 13
private const val TAG_POST_NOTIFICATION = 14
private const val TAG_WITHDRAW_NOTIFICATION = 15
private const val TAG_REQUEST_NOTIFICATION_PERMISSION = 16
private const val ENVELOPE_LENGTH = 12
/** Four role tags, one font asset id per type role, then the palette's reference. */
private val THEME_RECORD_LENGTH = 20 + 4 * TypeRole.entries.size
/** Bytes of one palette entry: a role tag, a scheme tag and a colour. */
const val PALETTE_ENTRY_LENGTH = 8
/**
* The high bit of each of eight bytes, which is where UTF-8 stops being ASCII. Written
* as a negative literal because 0x8080808080808080 does not fit a signed `Long`; the
* bits are what matter and they are the same either way.
*/
private const val ASCII_HIGH_BITS = -0x7f7f7f7f7f7f7f80L
private const val VALUE_NONE = 0
private const val VALUE_TEXT = 1
private const val VALUE_BOOL = 2
private const val VALUE_INTEGER = 3
private const val VALUE_FLOAT = 4
private const val VALUE_BYTES = 5
/** Decodes a batch in place. Must not copy the buffer; only strings become Kotlin Strings. */
fun decode(batch: ByteBuffer, onMutation: (Mutation) -> Unit) {
val base = batch.position()
val available = batch.remaining()
val previousOrder = batch.order()
batch.order(ByteOrder.LITTLE_ENDIAN)
try {
requireRange(available, 0, ENVELOPE_LENGTH, 0)
if (readU16(batch, base, available, 0) != TAG_ENVELOPE ||
readU16(batch, base, available, 2) != ENVELOPE_LENGTH
) {
throw ProtocolException("invalid batch envelope", 0)
}
val recordsLengthLong = readU32(batch, base, available, 4)
val countLong = readU32(batch, base, available, 8)
if (recordsLengthLong < ENVELOPE_LENGTH ||
recordsLengthLong > available.toLong() ||
recordsLengthLong % 4L != 0L
) {
throw ProtocolException("invalid records length", 4)
}
if (countLong > Int.MAX_VALUE.toLong()) {
throw ProtocolException("record count is too large", 8)
}
val recordsLength = recordsLengthLong.toInt()
val count = countLong.toInt()
var offset = ENVELOPE_LENGTH
var decoded = 0
while (offset < recordsLength) {
requireRange(recordsLength, offset, 4, offset)
val tag = readU16(batch, base, available, offset)
val length = readU16(batch, base, available, offset + 2)
if (length < 4 || length % 4 != 0 || offset.toLong() + length > recordsLength) {
throw ProtocolException("invalid record length", offset + 2)
}
val mutation = when (tag) {
TAG_CREATE -> {
requireRecordLength(length, 12, offset)
Mutation.Create(
readU32(batch, base, available, offset + 4).toInt(),
widgetKind(readU16(batch, base, available, offset + 8), offset + 8),
)
}
TAG_SET_PROP -> {
requireRecordLength(length, 24, offset)
val value = when (val valueKind = readU16(batch, base, available, offset + 10)) {
VALUE_NONE -> PropertyValue.None
VALUE_TEXT -> PropertyValue.Text(readString(batch, base, available, offset + 12))
VALUE_BOOL -> PropertyValue.Bool(readU64(batch, base, available, offset + 12) != 0L)
VALUE_INTEGER -> PropertyValue.Integer(readU64(batch, base, available, offset + 12))
VALUE_FLOAT -> PropertyValue.Float(
kotlin.Float.fromBits(readU64(batch, base, available, offset + 12).toInt()),
)
VALUE_BYTES -> PropertyValue.Bytes(readBytes(batch, base, available, offset + 12))
else -> throw ProtocolException("unknown property value tag $valueKind", offset + 10)
}
Mutation.SetProp(
readU32(batch, base, available, offset + 4).toInt(),
propertyKind(readU16(batch, base, available, offset + 8), offset + 8),
value,
)
}
TAG_SET_MODIFIER -> {
requireRecordLength(length, 28, offset)
Mutation.SetModifier(
readU32(batch, base, available, offset + 4).toInt(),
readU16(batch, base, available, offset + 8),
modifier(
readU16(batch, base, available, offset + 10),
readU64(batch, base, available, offset + 12),
readU64(batch, base, available, offset + 20),
offset + 10,
),
)
}
TAG_INSERT -> {
requireRecordLength(length, 16, offset)
Mutation.Insert(
readU32(batch, base, available, offset + 4).toInt(),
readU32(batch, base, available, offset + 8).toInt(),
readU32(batch, base, available, offset + 12).toInt(),
)
}
TAG_MOVE -> {
requireRecordLength(length, 16, offset)
Mutation.Move(
readU32(batch, base, available, offset + 4).toInt(),
readU32(batch, base, available, offset + 8).toInt(),
readU32(batch, base, available, offset + 12).toInt(),
)
}
TAG_REMOVE -> {
requireRecordLength(length, 8, offset)
Mutation.Remove(readU32(batch, base, available, offset + 4).toInt())
}
TAG_SET_TEXT -> {
requireRecordLength(length, 24, offset)
Mutation.SetText(
readU32(batch, base, available, offset + 4).toInt(),
readString(batch, base, available, offset + 8),
readU32(batch, base, available, offset + 16).toInt(),
readU32(batch, base, available, offset + 20).toInt(),
)
}
TAG_APPEND_TEXT -> {
requireRecordLength(length, 16, offset)
Mutation.AppendText(
readU32(batch, base, available, offset + 4).toInt(),
readString(batch, base, available, offset + 8),
)
}
TAG_SET_THEME -> {
requireRecordLength(length, THEME_RECORD_LENGTH, offset)
val adaptive = readU16(batch, base, available, offset + 10)
if (adaptive > 1) {
throw ProtocolException("invalid adaptive flag $adaptive", offset + 10)
}
// One slot per type role, in the order the wire fixes them in.
val fonts = List(TypeRole.entries.size) { role ->
readU32(batch, base, available, offset + 12 + 4 * role).toInt()
}
val (entries, problems) = palette(
readBytes(batch, base, available, offset + 12 + 4 * TypeRole.entries.size),
)
Mutation.SetTheme(
Theme(
designSystem(readU16(batch, base, available, offset + 4), offset + 4),
designSystem(readU16(batch, base, available, offset + 6), offset + 6),
colorScheme(readU16(batch, base, available, offset + 8), offset + 8),
adaptive == 1,
fonts,
entries,
problems,
),
)
}
TAG_SET_WINDOW -> {
requireRecordLength(length, 32, offset)
val resizable = readU16(batch, base, available, offset + 16)
if (resizable > 1) {
throw ProtocolException("invalid resizable flag $resizable", offset + 16)
}
Mutation.SetWindow(
Window(
chrome(readU16(batch, base, available, offset + 4), offset + 4),
titleBar(readU16(batch, base, available, offset + 6), offset + 6),
readString(batch, base, available, offset + 20),
readU32(batch, base, available, offset + 28).toInt(),
readU16(batch, base, available, offset + 8),
readU16(batch, base, available, offset + 10),
readU16(batch, base, available, offset + 12),
readU16(batch, base, available, offset + 14),
resizable == 1,
),
)
}
TAG_REGISTER_ASSET -> {
requireRecordLength(length, 20, offset)
Mutation.RegisterAsset(
readU32(batch, base, available, offset + 4).toInt(),
readU16(batch, base, available, offset + 8),
readBytes(batch, base, available, offset + 12),
)
}
TAG_RELEASE_ASSET -> {
requireRecordLength(length, 8, offset)
Mutation.ReleaseAsset(readU32(batch, base, available, offset + 4).toInt())
}
TAG_SHOW_MESSAGE -> {
requireRecordLength(length, 32, offset)
Mutation.ShowMessage(
readU64(batch, base, available, offset + 4),
readString(batch, base, available, offset + 12),
readString(batch, base, available, offset + 20),
messageDuration(readU16(batch, base, available, offset + 28), offset + 28),
)
}
TAG_POST_NOTIFICATION -> {
requireRecordLength(length, 56, offset)
Mutation.PostNotification(
readString(batch, base, available, offset + 4),
readString(batch, base, available, offset + 12),
readString(batch, base, available, offset + 20),
readString(batch, base, available, offset + 28),
readString(batch, base, available, offset + 36),
readString(batch, base, available, offset + 44),
notificationImportance(readU16(batch, base, available, offset + 52), offset + 52),
notificationPresentation(readU16(batch, base, available, offset + 54), offset + 54),
)
}
TAG_WITHDRAW_NOTIFICATION -> {
requireRecordLength(length, 12, offset)
Mutation.WithdrawNotification(readString(batch, base, available, offset + 4))
}
TAG_REQUEST_NOTIFICATION_PERMISSION -> {
requireRecordLength(length, 4, offset)
Mutation.RequestNotificationPermission
}
else -> throw ProtocolException("unknown mutation tag $tag", offset)
}
onMutation(mutation)
decoded += 1
offset += length
}
if (offset != recordsLength || decoded != count) {
throw ProtocolException("record count does not match envelope", offset)
}
} finally {
batch.order(previousOrder)
}
}
/**
* Reads a palette, keeping every sound entry and saying what was wrong with the rest.
*
* Lenient on purpose, unlike the rest of the batch: an unknown role, a scheme that is
* neither light nor dark, a second value for the same role and scheme, or a length
* that is not a whole number of entries is one entry's fault, and the others are
* still what the application asked for.
*/
fun palette(bytes: ByteArray): Pair<List<PaletteEntry>, List<String>> {
if (bytes.isEmpty()) return emptyList<PaletteEntry>() to emptyList()
val buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN)
val entries = ArrayList<PaletteEntry>(bytes.size / PALETTE_ENTRY_LENGTH)
val problems = ArrayList<String>()
if (bytes.size % PALETTE_ENTRY_LENGTH != 0) {
problems += "a palette of ${bytes.size} bytes is not a whole number of " +
"$PALETTE_ENTRY_LENGTH byte entries; the bytes past the last whole one are ignored"
}
for (index in 0 until bytes.size / PALETTE_ENTRY_LENGTH) {
val at = index * PALETTE_ENTRY_LENGTH
val roleTag = buffer.getShort(at).toInt() and 0xffff
val schemeTag = buffer.getShort(at + 2).toInt() and 0xffff
val argb = buffer.getInt(at + 4)
val role = paletteRole(roleTag)
if (role == null) {
problems += "palette entry $index names colour role $roleTag, which does not exist"
continue
}
val dark = when (schemeTag) {
1 -> false
2 -> true
else -> {
problems += "palette entry $index gives $role for scheme $schemeTag; " +
"an entry is for light (1) or dark (2), and following the system is not a scheme"
continue
}
}
if (entries.any { it.role == role && it.dark == dark }) {
problems += "palette entry $index gives $role in ${if (dark) "dark" else "light"} " +
"a second time; the first value stands"
continue
}
entries += PaletteEntry(role, dark, argb)
}
return entries to problems
}
/** Encodes one event into `out` (little-endian), returns bytes written. */
fun encodeEvent(event: HostEvent, out: ByteBuffer): Int {
val start = out.position()
val previousOrder = out.order()
out.order(ByteOrder.LITTLE_ENDIAN)
try {
val text = when (event) {
is HostEvent.Clicked -> null
is HostEvent.TextChanged -> event.text.toByteArray(StandardCharsets.UTF_8)
is HostEvent.TextSubmitted -> event.text.toByteArray(StandardCharsets.UTF_8)
is HostEvent.FocusLost -> null
is HostEvent.ProtocolError -> event.message.toByteArray(StandardCharsets.UTF_8)
is HostEvent.KeyDown -> null
is HostEvent.RangeRequested -> null
is HostEvent.ValueChanged -> null
is HostEvent.WindowSizeChanged -> null
is HostEvent.Resync -> null
is HostEvent.LifecycleStart -> null
is HostEvent.LifecycleStop -> null
is HostEvent.DesignSystemResolved -> null
is HostEvent.FilesEntered -> null
is HostEvent.FilesDropped -> event.text.toByteArray(StandardCharsets.UTF_8)
is HostEvent.NotificationActivated -> event.key.toByteArray(StandardCharsets.UTF_8)
is HostEvent.NotificationPermissionChanged -> null
}
val recordLength = when (event) {
is HostEvent.Clicked -> 16
is HostEvent.TextChanged -> 24
is HostEvent.TextSubmitted -> 24
is HostEvent.FocusLost -> 16
is HostEvent.ProtocolError -> 28
is HostEvent.KeyDown -> 20
is HostEvent.RangeRequested -> 24
is HostEvent.ValueChanged -> 24
is HostEvent.WindowSizeChanged -> 32
is HostEvent.Resync -> 16
is HostEvent.LifecycleStart -> 16
is HostEvent.LifecycleStop -> 16
is HostEvent.DesignSystemResolved -> 20
is HostEvent.FilesEntered -> 16
is HostEvent.FilesDropped -> 24
is HostEvent.NotificationActivated -> 28
is HostEvent.NotificationPermissionChanged -> 20
}
val totalLength = recordLength.toLong() + (text?.size ?: 0)
if (totalLength > Int.MAX_VALUE || totalLength > out.remaining().toLong()) {
throw ProtocolException("event output buffer is too small", 0)
}
val tag = when (event) {
is HostEvent.Clicked -> 1
is HostEvent.TextChanged -> 2
is HostEvent.TextSubmitted -> 3
is HostEvent.FocusLost -> 4
is HostEvent.ProtocolError -> 5
is HostEvent.KeyDown -> 6
is HostEvent.RangeRequested -> 7
is HostEvent.ValueChanged -> 16
is HostEvent.WindowSizeChanged -> 17
is HostEvent.Resync -> 18
is HostEvent.LifecycleStart -> 19
is HostEvent.LifecycleStop -> 20
is HostEvent.DesignSystemResolved -> 21
is HostEvent.FilesEntered -> 22
is HostEvent.FilesDropped -> 23
is HostEvent.NotificationActivated -> 24
is HostEvent.NotificationPermissionChanged -> 25
}
out.putShort(tag.toShort())
out.putShort(recordLength.toShort())
out.putInt(event.nodeId)
out.putLong(event.handlerId)
when (event) {
is HostEvent.Clicked -> Unit
is HostEvent.TextChanged -> writeStringReference(out, recordLength, text!!)
is HostEvent.TextSubmitted -> writeStringReference(out, recordLength, text!!)
is HostEvent.FocusLost -> Unit
is HostEvent.ProtocolError -> {
out.putInt(event.code)
writeStringReference(out, recordLength, text!!)
}
is HostEvent.KeyDown -> {
out.putShort(keyTag(event.key).toShort())
var modifiers = 0
if (event.shiftKey) modifiers = modifiers or 0x01
if (event.ctrlKey) modifiers = modifiers or 0x02
if (event.altKey) modifiers = modifiers or 0x04
if (event.metaKey) modifiers = modifiers or 0x08
out.put(modifiers.toByte())
out.put(0.toByte())
}
is HostEvent.RangeRequested -> {
out.putInt(event.start)
out.putInt(event.count)
}
is HostEvent.ValueChanged -> out.putDouble(event.value)
is HostEvent.WindowSizeChanged -> {
out.putFloat(event.widthDp)
out.putFloat(event.heightDp)
out.putInt(windowSizeClassTag(event.sizeClass))
out.putInt(windowHeightClassTag(event.heightClass))
}
is HostEvent.Resync -> Unit
is HostEvent.LifecycleStart -> Unit
is HostEvent.LifecycleStop -> Unit
is HostEvent.DesignSystemResolved -> {
out.putInt(designSystemTag(event.system))
}
is HostEvent.FilesEntered -> Unit
is HostEvent.FilesDropped -> writeStringReference(out, recordLength, text!!)
is HostEvent.NotificationActivated -> {
out.putInt(event.action)
writeStringReference(out, recordLength, text!!)
}
is HostEvent.NotificationPermissionChanged -> {
out.putInt(notificationPermissionTag(event.state))
}
}
if (text != null) out.put(text)
return out.position() - start
} finally {
out.order(previousOrder)
}
}
/**
* Handshake payload the Renderer sends to compose_rust_host_init.
*
* `loopMode` says who owns the frame loop: the Renderer on desktop, the platform on
* Android, iOS and the web.
*/
fun handshake(out: ByteBuffer, loopMode: LoopMode = LoopMode.Renderer): Int {
val start = out.position()
if (out.remaining() < 12) {
throw ProtocolException("handshake output buffer is too small", 0)
}
val previousOrder = out.order()
out.order(ByteOrder.LITTLE_ENDIAN)
try {
out.putLong(SCHEMA_HASH)
out.putShort(PROTOCOL_VERSION.toShort())
out.put(loopMode.wire)
out.put(0.toByte()) // Reserved for alignment.
return out.position() - start
} finally {
out.order(previousOrder)
}
}
private fun writeStringReference(out: ByteBuffer, offset: Int, bytes: ByteArray) {
out.putInt(offset)
out.putInt(bytes.size)
}
private fun widgetKind(tag: Int, offset: Int): WidgetKind = when (tag) {
1 -> WidgetKind.Column
2 -> WidgetKind.Row
3 -> WidgetKind.Box
4 -> WidgetKind.Text
5 -> WidgetKind.TextField
6 -> WidgetKind.Button
7 -> WidgetKind.Spacer
8 -> WidgetKind.LazyColumn
9 -> WidgetKind.ScrollColumn
10 -> WidgetKind.Image
11 -> WidgetKind.Icon
12 -> WidgetKind.Checkbox
13 -> WidgetKind.RadioButton
14 -> WidgetKind.Switch
15 -> WidgetKind.Slider
16 -> WidgetKind.ProgressIndicator
17 -> WidgetKind.Divider
18 -> WidgetKind.Card
19 -> WidgetKind.Surface
20 -> WidgetKind.Dialog
21 -> WidgetKind.Menu
22 -> WidgetKind.Tabs
23 -> WidgetKind.TopAppBar
24 -> WidgetKind.LazyRow
25 -> WidgetKind.Tooltip
26 -> WidgetKind.Canvas
27 -> WidgetKind.DatePicker
28 -> WidgetKind.TimePicker
29 -> WidgetKind.Dropdown
30 -> WidgetKind.Navigation
31 -> WidgetKind.NavigationItem
32 -> WidgetKind.Sheet
33 -> WidgetKind.Scaffold
34 -> WidgetKind.ScaffoldSlot
35 -> WidgetKind.LazyGrid
36 -> WidgetKind.FileDropTarget
37 -> WidgetKind.ScrollRow
38 -> WidgetKind.Chip
39 -> WidgetKind.FloatingAction
40 -> WidgetKind.Badge
41 -> WidgetKind.SelectionContainer
42 -> WidgetKind.SplitPane
100 -> WidgetKind.LinearProgressIndicator
else -> throw ProtocolException("unknown widget tag $tag", offset)
}
private fun propertyKind(tag: Int, offset: Int): PropertyKind = when (tag) {
1 -> PropertyKind.Text
2 -> PropertyKind.Placeholder
3 -> PropertyKind.Enabled
4 -> PropertyKind.Multiline
5 -> PropertyKind.OnClick
6 -> PropertyKind.OnValueChange
7 -> PropertyKind.OnSubmit
8 -> PropertyKind.OnFocusLost
9 -> PropertyKind.OnKeyDown
10 -> PropertyKind.ItemCount
11 -> PropertyKind.ItemKey
12 -> PropertyKind.OnRangeRequested
13 -> PropertyKind.TypeRole
14 -> PropertyKind.FontSize
15 -> PropertyKind.FontWeight
16 -> PropertyKind.LineHeight
17 -> PropertyKind.LetterSpacing
18 -> PropertyKind.Color
19 -> PropertyKind.TextAlign
20 -> PropertyKind.MaxLines
21 -> PropertyKind.Overflow
22 -> PropertyKind.Arrangement
23 -> PropertyKind.Spacing
24 -> PropertyKind.SpaceRole
25 -> PropertyKind.Alignment
26 -> PropertyKind.Variant
28 -> PropertyKind.Asset
32 -> PropertyKind.Checked
33 -> PropertyKind.Steps
34 -> PropertyKind.Determinate
35 -> PropertyKind.Circular
36 -> PropertyKind.Vertical
40 -> PropertyKind.Open
41 -> PropertyKind.OnDismiss
42 -> PropertyKind.SelectedIndex
50 -> PropertyKind.Commands
51 -> PropertyKind.Value
52 -> PropertyKind.Min
53 -> PropertyKind.Max
60 -> PropertyKind.Icon
61 -> PropertyKind.Slot
62 -> PropertyKind.Columns
63 -> PropertyKind.MinColumnWidth
64 -> PropertyKind.Spans
65 -> PropertyKind.OnFilesEntered
66 -> PropertyKind.OnFilesDropped
76 -> PropertyKind.Section
80 -> PropertyKind.Count
90 -> PropertyKind.Collapsible
27 -> PropertyKind.Progress
else -> throw ProtocolException("unknown property tag $tag", offset)
}
private fun keyTag(key: Key): Int = when (key) {
Key.Enter -> 1
}
private fun windowSizeClassTag(sizeClass: WindowSizeClass): Int = when (sizeClass) {
WindowSizeClass.Compact -> 0
WindowSizeClass.Medium -> 1
WindowSizeClass.Expanded -> 2
}
private fun windowHeightClassTag(heightClass: WindowHeightClass): Int = when (heightClass) {
WindowHeightClass.Compact -> 0
WindowHeightClass.Medium -> 1
WindowHeightClass.Expanded -> 2
}
private fun designSystemTag(system: DesignSystem): Int = when (system) {
DesignSystem.Material3 -> 1
DesignSystem.Cupertino -> 2
DesignSystem.Fluent -> 3
DesignSystem.Gnome -> 4
DesignSystem.Breeze -> 5
DesignSystem.Deepin -> 6
DesignSystem.LiquidGlass -> 7
}
private fun notificationPermissionTag(state: NotificationPermission): Int = when (state) {
NotificationPermission.NotDetermined -> 1
NotificationPermission.Granted -> 2
NotificationPermission.Denied -> 3
NotificationPermission.Unsupported -> 4
}
private fun paletteRole(tag: Int): ColorRole? = when (tag) {
1 -> ColorRole.Primary
2 -> ColorRole.OnPrimary
3 -> ColorRole.Secondary
4 -> ColorRole.OnSecondary
5 -> ColorRole.Surface
6 -> ColorRole.OnSurface
7 -> ColorRole.SurfaceVariant
8 -> ColorRole.OnSurfaceVariant
9 -> ColorRole.Background
10 -> ColorRole.OnBackground
11 -> ColorRole.Outline
12 -> ColorRole.OutlineVariant
13 -> ColorRole.Error
14 -> ColorRole.OnError
15 -> ColorRole.SurfaceContainer
16 -> ColorRole.Tertiary
17 -> ColorRole.OnTertiary
18 -> ColorRole.PrimaryContainer
19 -> ColorRole.OnPrimaryContainer
20 -> ColorRole.SecondaryContainer
21 -> ColorRole.OnSecondaryContainer
22 -> ColorRole.TertiaryContainer
23 -> ColorRole.OnTertiaryContainer
24 -> ColorRole.SyntaxKeyword
25 -> ColorRole.SyntaxString
26 -> ColorRole.SyntaxComment
27 -> ColorRole.SyntaxNumber
28 -> ColorRole.SyntaxConstant
29 -> ColorRole.SyntaxType
30 -> ColorRole.SyntaxFunction
31 -> ColorRole.SyntaxVariable
32 -> ColorRole.SyntaxProperty
33 -> ColorRole.SyntaxOperator
34 -> ColorRole.SyntaxPunctuation
35 -> ColorRole.SyntaxTag
36 -> ColorRole.SyntaxAttribute
37 -> ColorRole.SyntaxEscape
38 -> ColorRole.SyntaxMacro
39 -> ColorRole.DiffAdded
40 -> ColorRole.DiffRemoved
41 -> ColorRole.DiffModified
42 -> ColorRole.DiffAddedContainer
43 -> ColorRole.DiffRemovedContainer
44 -> ColorRole.DiffAddedEmphasis
45 -> ColorRole.DiffRemovedEmphasis
else -> null
}
private fun colorRole(tag: Int, offset: Int): ColorRole = when (tag) {
1 -> ColorRole.Primary
2 -> ColorRole.OnPrimary
3 -> ColorRole.Secondary
4 -> ColorRole.OnSecondary
5 -> ColorRole.Surface
6 -> ColorRole.OnSurface
7 -> ColorRole.SurfaceVariant
8 -> ColorRole.OnSurfaceVariant
9 -> ColorRole.Background
10 -> ColorRole.OnBackground
11 -> ColorRole.Outline
12 -> ColorRole.OutlineVariant
13 -> ColorRole.Error
14 -> ColorRole.OnError
15 -> ColorRole.SurfaceContainer
16 -> ColorRole.Tertiary
17 -> ColorRole.OnTertiary
18 -> ColorRole.PrimaryContainer
19 -> ColorRole.OnPrimaryContainer
20 -> ColorRole.SecondaryContainer
21 -> ColorRole.OnSecondaryContainer
22 -> ColorRole.TertiaryContainer
23 -> ColorRole.OnTertiaryContainer
24 -> ColorRole.SyntaxKeyword
25 -> ColorRole.SyntaxString
26 -> ColorRole.SyntaxComment
27 -> ColorRole.SyntaxNumber
28 -> ColorRole.SyntaxConstant
29 -> ColorRole.SyntaxType
30 -> ColorRole.SyntaxFunction
31 -> ColorRole.SyntaxVariable
32 -> ColorRole.SyntaxProperty
33 -> ColorRole.SyntaxOperator
34 -> ColorRole.SyntaxPunctuation
35 -> ColorRole.SyntaxTag
36 -> ColorRole.SyntaxAttribute
37 -> ColorRole.SyntaxEscape
38 -> ColorRole.SyntaxMacro
39 -> ColorRole.DiffAdded
40 -> ColorRole.DiffRemoved
41 -> ColorRole.DiffModified
42 -> ColorRole.DiffAddedContainer
43 -> ColorRole.DiffRemovedContainer
44 -> ColorRole.DiffAddedEmphasis
45 -> ColorRole.DiffRemovedEmphasis
else -> throw ProtocolException("unknown ColorRole tag $tag", offset)
}
private fun typeRole(tag: Int, offset: Int): TypeRole = when (tag) {
1 -> TypeRole.Display
2 -> TypeRole.Headline
3 -> TypeRole.Title
4 -> TypeRole.Subtitle
5 -> TypeRole.Body
6 -> TypeRole.BodyStrong
7 -> TypeRole.Label
8 -> TypeRole.Caption
9 -> TypeRole.Mono
else -> throw ProtocolException("unknown TypeRole tag $tag", offset)
}
private fun shapeRole(tag: Int, offset: Int): ShapeRole = when (tag) {
1 -> ShapeRole.None
2 -> ShapeRole.ExtraSmall
3 -> ShapeRole.Small
4 -> ShapeRole.Medium
5 -> ShapeRole.Large
6 -> ShapeRole.Full
else -> throw ProtocolException("unknown ShapeRole tag $tag", offset)
}
private fun motionRole(tag: Int, offset: Int): MotionRole = when (tag) {
1 -> MotionRole.Instant
2 -> MotionRole.Quick
3 -> MotionRole.Standard
4 -> MotionRole.Slow
5 -> MotionRole.Emphasized
else -> throw ProtocolException("unknown MotionRole tag $tag", offset)
}
private fun tileMode(tag: Int, offset: Int): TileMode = when (tag) {
1 -> TileMode.Clamp
2 -> TileMode.Repeat
3 -> TileMode.Mirror
else -> throw ProtocolException("unknown TileMode tag $tag", offset)
}
private fun materialRole(tag: Int, offset: Int): MaterialRole = when (tag) {
1 -> MaterialRole.Thin
2 -> MaterialRole.Regular
3 -> MaterialRole.Thick
4 -> MaterialRole.Chrome
else -> throw ProtocolException("unknown MaterialRole tag $tag", offset)
}
private fun spaceRole(tag: Int, offset: Int): SpaceRole = when (tag) {
1 -> SpaceRole.None
2 -> SpaceRole.Xs
3 -> SpaceRole.Sm
4 -> SpaceRole.Md
5 -> SpaceRole.Lg
6 -> SpaceRole.Xl
7 -> SpaceRole.Xxl
else -> throw ProtocolException("unknown SpaceRole tag $tag", offset)
}
private fun textAlign(tag: Int, offset: Int): TextAlign = when (tag) {
1 -> TextAlign.Start
2 -> TextAlign.Center
3 -> TextAlign.End
4 -> TextAlign.Justify
else -> throw ProtocolException("unknown TextAlign tag $tag", offset)
}
private fun textOverflow(tag: Int, offset: Int): TextOverflow = when (tag) {
1 -> TextOverflow.Clip
2 -> TextOverflow.Ellipsis
3 -> TextOverflow.Visible
else -> throw ProtocolException("unknown TextOverflow tag $tag", offset)
}
private fun arrangement(tag: Int, offset: Int): Arrangement = when (tag) {
1 -> Arrangement.Start
2 -> Arrangement.Center
3 -> Arrangement.End
4 -> Arrangement.SpaceBetween
5 -> Arrangement.SpaceAround
6 -> Arrangement.SpaceEvenly
else -> throw ProtocolException("unknown Arrangement tag $tag", offset)
}
private fun alignment(tag: Int, offset: Int): Alignment = when (tag) {
1 -> Alignment.TopStart
2 -> Alignment.TopCenter
3 -> Alignment.TopEnd
4 -> Alignment.CenterStart
5 -> Alignment.Center
6 -> Alignment.CenterEnd
7 -> Alignment.BottomStart
8 -> Alignment.BottomCenter
9 -> Alignment.BottomEnd
else -> throw ProtocolException("unknown Alignment tag $tag", offset)
}
private fun buttonVariant(tag: Int, offset: Int): ButtonVariant = when (tag) {
1 -> ButtonVariant.Filled
2 -> ButtonVariant.Tonal
3 -> ButtonVariant.Outlined
4 -> ButtonVariant.Text
5 -> ButtonVariant.Operator
else -> throw ProtocolException("unknown ButtonVariant tag $tag", offset)
}
private fun designSystem(tag: Int, offset: Int): DesignSystem = when (tag) {
1 -> DesignSystem.Material3
2 -> DesignSystem.Cupertino
3 -> DesignSystem.Fluent
4 -> DesignSystem.Gnome
5 -> DesignSystem.Breeze
6 -> DesignSystem.Deepin
7 -> DesignSystem.LiquidGlass
else -> throw ProtocolException("unknown DesignSystem tag $tag", offset)
}
private fun colorScheme(tag: Int, offset: Int): ColorScheme = when (tag) {
1 -> ColorScheme.Light
2 -> ColorScheme.Dark
3 -> ColorScheme.FollowSystem
else -> throw ProtocolException("unknown ColorScheme tag $tag", offset)
}
private fun assetKind(tag: Int, offset: Int): AssetKind = when (tag) {
1 -> AssetKind.Png
2 -> AssetKind.Jpeg
3 -> AssetKind.Svg
4 -> AssetKind.VectorIcon
5 -> AssetKind.Font
6 -> AssetKind.Brush
else -> throw ProtocolException("unknown AssetKind tag $tag", offset)
}
private fun iconRole(tag: Int, offset: Int): IconRole = when (tag) {
1 -> IconRole.Back
2 -> IconRole.Forward
3 -> IconRole.Close
4 -> IconRole.Search
5 -> IconRole.Add
6 -> IconRole.Check
7 -> IconRole.Settings
8 -> IconRole.More
9 -> IconRole.Home
10 -> IconRole.List
11 -> IconRole.Inbox
12 -> IconRole.Menu
13 -> IconRole.History
14 -> IconRole.Send
15 -> IconRole.Mic
16 -> IconRole.Image
17 -> IconRole.Video
18 -> IconRole.Library
19 -> IconRole.Sidebar
20 -> IconRole.Compose
21 -> IconRole.Collapse
else -> throw ProtocolException("unknown IconRole tag $tag", offset)
}
private fun messageDuration(tag: Int, offset: Int): MessageDuration = when (tag) {
1 -> MessageDuration.Short
2 -> MessageDuration.Long
else -> throw ProtocolException("unknown MessageDuration tag $tag", offset)
}
private fun chrome(tag: Int, offset: Int): Chrome = when (tag) {
1 -> Chrome.Modern
2 -> Chrome.System
else -> throw ProtocolException("unknown Chrome tag $tag", offset)
}
private fun titleBar(tag: Int, offset: Int): TitleBar = when (tag) {
1 -> TitleBar.Normal
2 -> TitleBar.Simple
else -> throw ProtocolException("unknown TitleBar tag $tag", offset)
}
private fun slotRole(tag: Int, offset: Int): SlotRole = when (tag) {
1 -> SlotRole.TopBar
2 -> SlotRole.BottomBar
3 -> SlotRole.FloatingAction
4 -> SlotRole.Content
else -> throw ProtocolException("unknown SlotRole tag $tag", offset)
}
private fun notificationImportance(tag: Int, offset: Int): NotificationImportance = when (tag) {
1 -> NotificationImportance.Normal
2 -> NotificationImportance.Urgent
else -> throw ProtocolException("unknown NotificationImportance tag $tag", offset)
}
private fun notificationPresentation(tag: Int, offset: Int): NotificationPresentation = when (tag) {
1 -> NotificationPresentation.Always
2 -> NotificationPresentation.WhenInactive
else -> throw ProtocolException("unknown NotificationPresentation tag $tag", offset)
}
private fun notificationPermission(tag: Int, offset: Int): NotificationPermission = when (tag) {
1 -> NotificationPermission.NotDetermined
2 -> NotificationPermission.Granted
3 -> NotificationPermission.Denied
4 -> NotificationPermission.Unsupported
else -> throw ProtocolException("unknown NotificationPermission tag $tag", offset)
}
private fun paint(bits: Long, offset: Int): Paint {
val value = bits.toInt()
return when (val kind = (bits ushr 32).toInt()) {
1 -> Paint.Role(colorRole(value, offset))
2 -> Paint.Literal(value)
3 -> Paint.Asset(value)
else -> throw ProtocolException("unknown paint kind $kind", offset)
}
}
private fun modifier(tag: Int, first: Long, second: Long, offset: Int): Modifier = when (tag) {
0 -> Modifier.Empty
1 -> Modifier.Padding(kotlin.Float.fromBits(first.toInt()))
2 -> Modifier.FillMaxWidth
3 -> Modifier.FillMaxHeight
4 -> Modifier.Width(kotlin.Float.fromBits(first.toInt()))
5 -> Modifier.Height(kotlin.Float.fromBits(first.toInt()))
6 -> Modifier.Size(kotlin.Float.fromBits(first.toInt()), kotlin.Float.fromBits(second.toInt()))
7 -> Modifier.Background(paint(first, offset))
8 -> Modifier.Clickable(first)
9 -> Modifier.PaddingRole(spaceRole(first.toInt(), offset))
10 -> Modifier.PaddingEach(kotlin.Float.fromBits(first.toInt()), kotlin.Float.fromBits((first ushr 32).toInt()), kotlin.Float.fromBits(second.toInt()), kotlin.Float.fromBits((second ushr 32).toInt()))
11 -> Modifier.Weight(kotlin.Float.fromBits(first.toInt()))
12 -> Modifier.Shape(kotlin.Float.fromBits(first.toInt()), kotlin.Float.fromBits((first ushr 32).toInt()), kotlin.Float.fromBits(second.toInt()), kotlin.Float.fromBits((second ushr 32).toInt()))
13 -> Modifier.ShapeRole(shapeRole(first.toInt(), offset))
14 -> Modifier.Border(kotlin.Float.fromBits(first.toInt()), paint(second, offset))
15 -> Modifier.Elevation(kotlin.Float.fromBits(first.toInt()))
16 -> Modifier.ObserveSize(first.toInt())
17 -> Modifier.Motion(motionRole(first.toInt(), offset))
18 -> Modifier.Material(materialRole(first.toInt(), offset))
else -> throw ProtocolException("unknown modifier tag $tag", offset)
}
/**
* An arena range with no UTF-8 requirement, used by both a drawing command list and an
* asset's bytes. For an asset this is the one copy this side makes, and it happens at
* registration rather than per frame.
*/
private fun readBytes(batch: ByteBuffer, base: Int, available: Int, referenceOffset: Int): ByteArray {
val offsetLong = readU32(batch, base, available, referenceOffset)
val lengthLong = readU32(batch, base, available, referenceOffset + 4)
if (offsetLong > Int.MAX_VALUE || lengthLong > Int.MAX_VALUE) {
throw ProtocolException("byte range is too large", referenceOffset)
}
val offset = offsetLong.toInt()
val length = lengthLong.toInt()
requireRange(available, offset, length, referenceOffset)
val copy = ByteArray(length)
copyOut(batch, base + offset, copy, length)
return copy
}
/**
* Copies a range of the arena into an array.
*
* The absolute bulk read that says the same thing in one call, `get(index, array,
* offset, length)`, arrived in Java 13 and is not in every Android runtime this has to
* run on. So the read goes through the buffer's own position, which every version has,
* and puts it back afterwards: the arena is read from one thread and a position left
* where the last read ended would make the next one read the wrong bytes.
*
* Nothing is allocated. A `duplicate()` would avoid touching the position and costs an
* object per call on a path that runs per string in every batch.
*/
private fun copyOut(batch: ByteBuffer, at: Int, into: ByteArray, length: Int) {
val mark = batch.position()
batch.position(at)
batch.get(into, 0, length)
batch.position(mark)
}
/**
* The string Compose is handed, and the only copy of the text this side makes.
*
* Validating and building are two passes rather than one because the two things that
* do both are each wrong here. `String(bytes, UTF_8)` replaces a malformed byte with
* U+FFFD, and a malformed string means the two sides disagree about the arena, which
* has to be reported rather than drawn. A `CharsetDecoder` reports it, but assembles
* the text as `char` first, two bytes a character, and then copies that into the
* `String`, and it needs a decoder and two buffer views of its own to do it.
*
* So the bytes are checked where they lie, which allocates nothing, and then copied
* once into a buffer this object keeps and handed to `String`, which by then has
* nothing left to replace. What remains is the one allocation the string itself is.
*/
private fun readString(batch: ByteBuffer, base: Int, available: Int, referenceOffset: Int): String {
val offsetLong = readU32(batch, base, available, referenceOffset)
val lengthLong = readU32(batch, base, available, referenceOffset + 4)
if (offsetLong > Int.MAX_VALUE || lengthLong > Int.MAX_VALUE) {
throw ProtocolException("string range is too large", referenceOffset)
}
val offset = offsetLong.toInt()
val length = lengthLong.toInt()
requireRange(available, offset, length, referenceOffset)
if (length == 0) {
return ""
}
requireUtf8(batch, base + offset, length, offset)
val scratch = stringBytes(length)
copyOut(batch, base + offset, scratch, length)
return String(scratch, 0, length, StandardCharsets.UTF_8)
}
/**
* Reads a range of the arena as UTF-8 without building anything out of it.
*
* The bounds are Unicode's, and the ones that look arbitrary are the point. A two byte
* sequence starts at 0xc2 because 0xc0 and 0xc1 could only encode what one byte
* already encodes; the second byte after 0xe0 starts at 0xa0 and after 0xf0 at 0x90
* for the same reason; 0xed stops at 0x9f because the surrogate halves follow it; and
* four byte sequences stop at 0xf4 0x8f because U+10FFFF is the last code point. The
* platform's decoder refuses every one of those, and this has to refuse them too, or
* a range it called text would come out of `String` as replacement characters.
*/
private fun requireUtf8(batch: ByteBuffer, start: Int, length: Int, errorOffset: Int) {
var index = 0
while (index < length) {
val lead = batch.get(start + index).toInt() and 0xff
if (lead < 0x80) {
index += 1
// Eight bytes at a time for as long as the text stays ASCII, which most
// text is and all of a streaming append usually is: a long with no high
// bit anywhere in it is eight characters that need nothing else checked.
// The mask is the same at both ends, so which order the long is read in
// does not matter. Entered only after an ASCII byte, so text with no
// ASCII in it never pays for the attempt.
while (index + 8 <= length &&
batch.getLong(start + index) and ASCII_HIGH_BITS == 0L
) {
index += 8
}
continue
}
val width = when {
lead >= 0xc2 && lead <= 0xdf -> 2
lead >= 0xe0 && lead <= 0xef -> 3
lead >= 0xf0 && lead <= 0xf4 -> 4
else -> malformedUtf8(errorOffset)
}
if (index + width > length) {
malformedUtf8(errorOffset)
}
val lowest = when (lead) {
0xe0 -> 0xa0
0xf0 -> 0x90
else -> 0x80
}
val highest = when (lead) {
0xed -> 0x9f
0xf4 -> 0x8f
else -> 0xbf
}
val second = batch.get(start + index + 1).toInt() and 0xff
if (second < lowest || second > highest) {
malformedUtf8(errorOffset)
}
var step = 2
while (step < width) {
val continuation = batch.get(start + index + step).toInt() and 0xff
if (continuation < 0x80 || continuation > 0xbf) {
malformedUtf8(errorOffset)
}
step += 1
}
index += width
}
}
private fun malformedUtf8(errorOffset: Int): Nothing =
throw ProtocolException("string is not valid UTF-8", errorOffset)
/**
* The buffer every string is copied through, grown to fit and then kept.
*
* A frame's strings go through it one after another, so a stream of keystrokes costs
* nothing here once it is big enough. It doubles rather than fitting exactly, because
* text that grows a character at a time would otherwise reallocate on every keystroke.
*
* One buffer is enough, and it needs no lock, because a batch is decoded on the
* Renderer's UI thread and on no other: the Host's worker threads update signals and
* ask for a frame, they never call across the boundary themselves. Two threads
* decoding at once would hand each other half a string.
*/
private var stringScratch = ByteArray(256)
private fun stringBytes(length: Int): ByteArray {
val scratch = stringScratch
if (scratch.size >= length) {
return scratch
}
var size = scratch.size
while (size < length) {
size = if (size > Int.MAX_VALUE / 2) length else size + size
}
val grown = ByteArray(size)
stringScratch = grown
return grown
}
private fun readU16(batch: ByteBuffer, base: Int, available: Int, offset: Int): Int {
requireRange(available, offset, 2, offset)
return batch.getShort(base + offset).toInt() and 0xffff
}
private fun readU32(batch: ByteBuffer, base: Int, available: Int, offset: Int): Long {
requireRange(available, offset, 4, offset)
return batch.getInt(base + offset).toLong() and 0xffff_ffffL
}
private fun readU64(batch: ByteBuffer, base: Int, available: Int, offset: Int): Long {
requireRange(available, offset, 8, offset)
return batch.getLong(base + offset)
}
private fun requireRecordLength(actual: Int, expected: Int, offset: Int) {
if (actual != expected) {
throw ProtocolException("invalid record length $actual for tag", offset + 2)
}
}
private fun requireRange(available: Int, offset: Int, length: Int, errorOffset: Int) {
if (offset < 0 || length < 0 || offset.toLong() + length > available.toLong()) {
throw ProtocolException("truncated or out-of-range data", errorOffset)
}
}
}
/** One rung of the type ladder. Sizes are sp, spacing may be negative. */
data class TypeToken(
val size: Float,
val weight: Int,
val lineHeight: Float,
val letterSpacing: Float,
val monospace: Boolean,
)
/**
* The generated half of one design system's tables: colours, type, shapes and spacing.
*
* Arrays are indexed by the role's ordinal, which matches its wire tag minus one.
* The rules that consume them (elevation rendering, ButtonVariant styling, motion) are
* written by hand in the Renderer.
*/
class DesignTokenTable(
val system: DesignSystem,
/** The published guideline these values come from. */
val reference: String,
val defaultFamily: String,
val monospaceFamily: String,
private val lightColors: IntArray,
private val darkColors: IntArray,
private val typeScale: Array<TypeToken>,
private val shapeRadii: FloatArray,
private val spaces: FloatArray,
) {
/** The 0xAARRGGBB value for a role. `dark` is the scheme the Renderer resolved. */
fun color(role: ColorRole, dark: Boolean): Int =
if (dark) darkColors[role.ordinal] else lightColors[role.ordinal]
fun type(role: TypeRole): TypeToken = typeScale[role.ordinal]
/** Corner radius in dp. */
fun radius(role: ShapeRole): Float = shapeRadii[role.ordinal]
/** Spacing in dp. */
fun space(role: SpaceRole): Float = spaces[role.ordinal]
}
object DesignTokens {
val MATERIAL3: DesignTokenTable = DesignTokenTable(
DesignSystem.Material3,
"Material 3 Expressive shape, colour and type scales, m3.material.io, 2025",
"Roboto",
"Roboto Mono",
intArrayOf(
0xff6750a4.toInt(), // Primary
0xffffffff.toInt(), // OnPrimary
0xff625b71.toInt(), // Secondary
0xffffffff.toInt(), // OnSecondary
0xfffef7ff.toInt(), // Surface
0xff1d1b20.toInt(), // OnSurface
0xffe7e0ec.toInt(), // SurfaceVariant
0xff49454f.toInt(), // OnSurfaceVariant
0xfffef7ff.toInt(), // Background
0xff1d1b20.toInt(), // OnBackground
0xff79747e.toInt(), // Outline
0xffcac4d0.toInt(), // OutlineVariant
0xffb3261e.toInt(), // Error
0xffffffff.toInt(), // OnError
0xfff3edf7.toInt(), // SurfaceContainer
0xff7d5260.toInt(), // Tertiary
0xffffffff.toInt(), // OnTertiary
0xffeaddff.toInt(), // PrimaryContainer
0xff21005d.toInt(), // OnPrimaryContainer
0xffe8def8.toInt(), // SecondaryContainer
0xff1d192b.toInt(), // OnSecondaryContainer
0xffffd8e4.toInt(), // TertiaryContainer
0xff31111d.toInt(), // OnTertiaryContainer
0xff6750a4.toInt(), // SyntaxKeyword
0xff7d5260.toInt(), // SyntaxString
0xff49454f.toInt(), // SyntaxComment
0xff31111d.toInt(), // SyntaxNumber
0xff31111d.toInt(), // SyntaxConstant
0xff625b71.toInt(), // SyntaxType
0xff21005d.toInt(), // SyntaxFunction
0xff1d1b20.toInt(), // SyntaxVariable
0xff1d192b.toInt(), // SyntaxProperty
0xff49454f.toInt(), // SyntaxOperator
0xff49454f.toInt(), // SyntaxPunctuation
0xff6750a4.toInt(), // SyntaxTag
0xff625b71.toInt(), // SyntaxAttribute
0xffb3261e.toInt(), // SyntaxEscape
0xff625b71.toInt(), // SyntaxMacro
0xff006d17.toInt(), // DiffAdded
0xffb3261e.toInt(), // DiffRemoved
0xff7d5260.toInt(), // DiffModified
0xffd6dedc.toInt(), // DiffAddedContainer
0xffebd5dd.toInt(), // DiffRemovedContainer
0xffaac7b4.toInt(), // DiffAddedEmphasis
0xffe0b1b6.toInt(), // DiffRemovedEmphasis
),
intArrayOf(
0xffd0bcff.toInt(), // Primary
0xff381e72.toInt(), // OnPrimary
0xffccc2dc.toInt(), // Secondary
0xff332d41.toInt(), // OnSecondary
0xff141218.toInt(), // Surface
0xffe6e0e9.toInt(), // OnSurface
0xff49454f.toInt(), // SurfaceVariant
0xffcac4d0.toInt(), // OnSurfaceVariant
0xff141218.toInt(), // Background
0xffe6e0e9.toInt(), // OnBackground
0xff938f99.toInt(), // Outline
0xff49454f.toInt(), // OutlineVariant
0xfff2b8b5.toInt(), // Error
0xff601410.toInt(), // OnError
0xff211f26.toInt(), // SurfaceContainer
0xffefb8c8.toInt(), // Tertiary
0xff492532.toInt(), // OnTertiary
0xff4f378b.toInt(), // PrimaryContainer
0xffeaddff.toInt(), // OnPrimaryContainer
0xff4a4458.toInt(), // SecondaryContainer
0xffe8def8.toInt(), // OnSecondaryContainer
0xff633b48.toInt(), // TertiaryContainer
0xffffd8e4.toInt(), // OnTertiaryContainer
0xffd0bcff.toInt(), // SyntaxKeyword
0xffefb8c8.toInt(), // SyntaxString
0xffcac4d0.toInt(), // SyntaxComment
0xffffd8e4.toInt(), // SyntaxNumber
0xffffd8e4.toInt(), // SyntaxConstant
0xffccc2dc.toInt(), // SyntaxType
0xffeaddff.toInt(), // SyntaxFunction
0xffe6e0e9.toInt(), // SyntaxVariable
0xffe8def8.toInt(), // SyntaxProperty
0xffcac4d0.toInt(), // SyntaxOperator
0xffcac4d0.toInt(), // SyntaxPunctuation
0xffd0bcff.toInt(), // SyntaxTag
0xffccc2dc.toInt(), // SyntaxAttribute
0xfff2b8b5.toInt(), // SyntaxEscape
0xffccc2dc.toInt(), // SyntaxMacro
0xffb2ceaa.toInt(), // DiffAdded
0xfff2b8b5.toInt(), // DiffRemoved
0xffefb8c8.toInt(), // DiffModified
0xff3e4240.toInt(), // DiffAddedContainer
0xff4b3e43.toInt(), // DiffRemovedContainer
0xff5b655b.toInt(), // DiffAddedEmphasis
0xff755c5f.toInt(), // DiffRemovedEmphasis
),
arrayOf(
TypeToken(57.0f, 400, 64.0f, 0.0f, false), // Display
TypeToken(32.0f, 400, 40.0f, 0.0f, false), // Headline
TypeToken(22.0f, 400, 28.0f, 0.0f, false), // Title
TypeToken(16.0f, 500, 24.0f, 0.15f, false), // Subtitle
TypeToken(16.0f, 400, 24.0f, 0.5f, false), // Body
TypeToken(16.0f, 500, 24.0f, 0.15f, false), // BodyStrong
TypeToken(14.0f, 500, 20.0f, 0.1f, false), // Label
TypeToken(12.0f, 400, 16.0f, 0.4f, false), // Caption
TypeToken(14.0f, 400, 20.0f, 0.0f, true), // Mono
),
floatArrayOf(0.0f, 4.0f, 12.0f, 16.0f, 28.0f, 1000.0f),
floatArrayOf(0.0f, 4.0f, 8.0f, 16.0f, 24.0f, 32.0f, 48.0f),
)
val CUPERTINO: DesignTokenTable = DesignTokenTable(
DesignSystem.Cupertino,
"Apple Human Interface Guidelines, system colors and Dynamic Type, 2024",
"SF Pro",
"SF Mono",
intArrayOf(
0xff007aff.toInt(), // Primary
0xffffffff.toInt(), // OnPrimary
0xff5856d6.toInt(), // Secondary
0xffffffff.toInt(), // OnSecondary
0xffffffff.toInt(), // Surface
0xff000000.toInt(), // OnSurface
0xffe9e9eb.toInt(), // SurfaceVariant
0xff3c3c43.toInt(), // OnSurfaceVariant
0xfff2f2f7.toInt(), // Background
0xff000000.toInt(), // OnBackground
0xffc6c6c8.toInt(), // Outline
0xffe5e5ea.toInt(), // OutlineVariant
0xffff3b30.toInt(), // Error
0xffffffff.toInt(), // OnError
0xffffffff.toInt(), // SurfaceContainer
0xffaf52de.toInt(), // Tertiary
0xffffffff.toInt(), // OnTertiary
0xffd6e4ff.toInt(), // PrimaryContainer
0xff003070.toInt(), // OnPrimaryContainer
0xffe2e0ff.toInt(), // SecondaryContainer
0xff2a1b70.toInt(), // OnSecondaryContainer
0xfff3ddfb.toInt(), // TertiaryContainer
0xff3d0b52.toInt(), // OnTertiaryContainer
0xff9b2393.toInt(), // SyntaxKeyword
0xffc41a16.toInt(), // SyntaxString
0xff5d6c79.toInt(), // SyntaxComment
0xff1c00cf.toInt(), // SyntaxNumber
0xff6c36a9.toInt(), // SyntaxConstant
0xff3900a0.toInt(), // SyntaxType
0xff326d74.toInt(), // SyntaxFunction
0xff000000.toInt(), // SyntaxVariable
0xff326d74.toInt(), // SyntaxProperty
0xff000000.toInt(), // SyntaxOperator
0xff000000.toInt(), // SyntaxPunctuation
0xff0b4f79.toInt(), // SyntaxTag
0xff815f03.toInt(), // SyntaxAttribute
0xff1c00cf.toInt(), // SyntaxEscape
0xff643820.toInt(), // SyntaxMacro
0xff087a2f.toInt(), // DiffAdded
0xffce0014.toInt(), // DiffRemoved
0xff007aff.toInt(), // DiffModified
0xffe1efe6.toInt(), // DiffAddedContainer
0xfff9e0e3.toInt(), // DiffRemovedContainer
0xffb5d7c1.toInt(), // DiffAddedEmphasis
0xfff0b3b9.toInt(), // DiffRemovedEmphasis
),
intArrayOf(
0xff0a84ff.toInt(), // Primary
0xffffffff.toInt(), // OnPrimary
0xff5e5ce6.toInt(), // Secondary
0xffffffff.toInt(), // OnSecondary
0xff1c1c1e.toInt(), // Surface
0xffffffff.toInt(), // OnSurface
0xff2c2c2e.toInt(), // SurfaceVariant
0xffebebf5.toInt(), // OnSurfaceVariant
0xff000000.toInt(), // Background
0xffffffff.toInt(), // OnBackground
0xff38383a.toInt(), // Outline
0xff48484a.toInt(), // OutlineVariant
0xffff453a.toInt(), // Error
0xffffffff.toInt(), // OnError
0xff1c1c1e.toInt(), // SurfaceContainer
0xffbf5af2.toInt(), // Tertiary
0xffffffff.toInt(), // OnTertiary
0xff0a2d52.toInt(), // PrimaryContainer
0xffcfe3ff.toInt(), // OnPrimaryContainer
0xff262663.toInt(), // SecondaryContainer
0xffdedcff.toInt(), // OnSecondaryContainer
0xff3f1a52.toInt(), // TertiaryContainer
0xfff1d9fa.toInt(), // OnTertiaryContainer
0xfffc5fa3.toInt(), // SyntaxKeyword
0xfffc6a5d.toInt(), // SyntaxString
0xff798693.toInt(), // SyntaxComment
0xffd0bf69.toInt(), // SyntaxNumber
0xffa167e6.toInt(), // SyntaxConstant
0xffd0a8ff.toInt(), // SyntaxType
0xff67b7a4.toInt(), // SyntaxFunction
0xffdfdfe0.toInt(), // SyntaxVariable
0xff67b7a4.toInt(), // SyntaxProperty
0xffdfdfe0.toInt(), // SyntaxOperator
0xffdfdfe0.toInt(), // SyntaxPunctuation
0xff5dd8ff.toInt(), // SyntaxTag
0xffbf8555.toInt(), // SyntaxAttribute
0xffd0bf69.toInt(), // SyntaxEscape
0xfffd8f3f.toInt(), // SyntaxMacro
0xff30d158.toInt(), // DiffAdded
0xffff6e66.toInt(), // DiffRemoved
0xff0a84ff.toInt(), // DiffModified
0xff20402a.toInt(), // DiffAddedContainer
0xff492c2c.toInt(), // DiffRemovedContainer
0xff246435.toInt(), // DiffAddedEmphasis
0xff773d3b.toInt(), // DiffRemovedEmphasis
),
arrayOf(
TypeToken(34.0f, 700, 41.0f, 0.37f, false), // Display
TypeToken(28.0f, 400, 34.0f, 0.36f, false), // Headline
TypeToken(22.0f, 400, 28.0f, 0.35f, false), // Title
TypeToken(17.0f, 600, 22.0f, -0.41f, false), // Subtitle
TypeToken(17.0f, 400, 22.0f, -0.41f, false), // Body
TypeToken(17.0f, 600, 22.0f, -0.41f, false), // BodyStrong
TypeToken(15.0f, 400, 20.0f, -0.24f, false), // Label
TypeToken(12.0f, 400, 16.0f, 0.0f, false), // Caption
TypeToken(15.0f, 400, 20.0f, 0.0f, true), // Mono
),
floatArrayOf(0.0f, 4.0f, 8.0f, 10.0f, 14.0f, 1000.0f),
floatArrayOf(0.0f, 4.0f, 8.0f, 16.0f, 20.0f, 32.0f, 44.0f),
)
val FLUENT: DesignTokenTable = DesignTokenTable(
DesignSystem.Fluent,
"WinUI / Fluent 2 web and Windows tokens, fluent2.microsoft.design, 2024",
"Segoe UI Variable",
"Cascadia Mono",
intArrayOf(
0xff0f6cbd.toInt(), // Primary
0xffffffff.toInt(), // OnPrimary
0xffebf3fc.toInt(), // Secondary
0xff0f548c.toInt(), // OnSecondary
0xffffffff.toInt(), // Surface
0xff242424.toInt(), // OnSurface
0xffeaeaea.toInt(), // SurfaceVariant
0xff424242.toInt(), // OnSurfaceVariant
0xfff3f3f3.toInt(), // Background
0xff242424.toInt(), // OnBackground
0xffd1d1d1.toInt(), // Outline
0xffe0e0e0.toInt(), // OutlineVariant
0xffc50f1f.toInt(), // Error
0xffffffff.toInt(), // OnError
0xffffffff.toInt(), // SurfaceContainer
0xff8764b8.toInt(), // Tertiary
0xffffffff.toInt(), // OnTertiary
0xffcfe4fa.toInt(), // PrimaryContainer
0xff0c3b5e.toInt(), // OnPrimaryContainer
0xffb4d6fa.toInt(), // SecondaryContainer
0xff0b3350.toInt(), // OnSecondaryContainer
0xffe8dcf7.toInt(), // TertiaryContainer
0xff341a5e.toInt(), // OnTertiaryContainer
0xff0000ff.toInt(), // SyntaxKeyword
0xffa31515.toInt(), // SyntaxString
0xff008000.toInt(), // SyntaxComment
0xff098658.toInt(), // SyntaxNumber
0xff0070c1.toInt(), // SyntaxConstant
0xff267f99.toInt(), // SyntaxType
0xff795e26.toInt(), // SyntaxFunction
0xff001080.toInt(), // SyntaxVariable
0xff001080.toInt(), // SyntaxProperty
0xff000000.toInt(), // SyntaxOperator
0xff000000.toInt(), // SyntaxPunctuation
0xff800000.toInt(), // SyntaxTag
0xffe50000.toInt(), // SyntaxAttribute
0xffee0000.toInt(), // SyntaxEscape
0xffaf00db.toInt(), // SyntaxMacro
0xff007b50.toInt(), // DiffAdded
0xffa31515.toInt(), // DiffRemoved
0xff0451a5.toInt(), // DiffModified
0xffebf1dd.toInt(), // DiffAddedContainer
0xffffcccc.toInt(), // DiffRemovedContainer
0xffd7e8b1.toInt(), // DiffAddedEmphasis
0xffff9999.toInt(), // DiffRemovedEmphasis
),
intArrayOf(
0xff479ef5.toInt(), // Primary
0xff000000.toInt(), // OnPrimary
0xff0c3b5e.toInt(), // Secondary
0xffffffff.toInt(), // OnSecondary
0xff292929.toInt(), // Surface
0xffffffff.toInt(), // OnSurface
0xff333333.toInt(), // SurfaceVariant
0xffd6d6d6.toInt(), // OnSurfaceVariant
0xff1f1f1f.toInt(), // Background
0xffffffff.toInt(), // OnBackground
0xff666666.toInt(), // Outline
0xff3d3d3d.toInt(), // OutlineVariant
0xffdc626d.toInt(), // Error
0xff000000.toInt(), // OnError
0xff2b2b2b.toInt(), // SurfaceContainer
0xffb18cd9.toInt(), // Tertiary
0xff22103a.toInt(), // OnTertiary
0xff0c3b5e.toInt(), // PrimaryContainer
0xffcfe4fa.toInt(), // OnPrimaryContainer
0xff123d61.toInt(), // SecondaryContainer
0xffb4d6fa.toInt(), // OnSecondaryContainer
0xff3b2159.toInt(), // TertiaryContainer
0xffe8dcf7.toInt(), // OnTertiaryContainer
0xff569cd6.toInt(), // SyntaxKeyword
0xffce9178.toInt(), // SyntaxString
0xff6f9e5a.toInt(), // SyntaxComment
0xffb5cea8.toInt(), // SyntaxNumber
0xff4fc1ff.toInt(), // SyntaxConstant
0xff4ec9b0.toInt(), // SyntaxType
0xffdcdcaa.toInt(), // SyntaxFunction
0xff9cdcfe.toInt(), // SyntaxVariable
0xff9cdcfe.toInt(), // SyntaxProperty
0xffd4d4d4.toInt(), // SyntaxOperator
0xffd4d4d4.toInt(), // SyntaxPunctuation
0xff569cd6.toInt(), // SyntaxTag
0xff9cdcfe.toInt(), // SyntaxAttribute
0xffd7ba7d.toInt(), // SyntaxEscape
0xffc586c0.toInt(), // SyntaxMacro
0xffb5cea8.toInt(), // DiffAdded
0xffce9178.toInt(), // DiffRemoved
0xff569cd6.toInt(), // DiffModified
0xff414733.toInt(), // DiffAddedContainer
0xff552222.toInt(), // DiffRemovedContainer
0xff536232.toInt(), // DiffAddedEmphasis
0xff771b1b.toInt(), // DiffRemovedEmphasis
),
arrayOf(
TypeToken(40.0f, 600, 52.0f, 0.0f, false), // Display
TypeToken(28.0f, 600, 36.0f, 0.0f, false), // Headline
TypeToken(20.0f, 600, 28.0f, 0.0f, false), // Title
TypeToken(16.0f, 600, 22.0f, 0.0f, false), // Subtitle
TypeToken(14.0f, 400, 20.0f, 0.0f, false), // Body
TypeToken(14.0f, 600, 20.0f, 0.0f, false), // BodyStrong
TypeToken(12.0f, 400, 16.0f, 0.0f, false), // Label
TypeToken(12.0f, 400, 16.0f, 0.0f, false), // Caption
TypeToken(13.0f, 400, 18.0f, 0.0f, true), // Mono
),
floatArrayOf(0.0f, 2.0f, 3.0f, 4.0f, 8.0f, 1000.0f),
floatArrayOf(0.0f, 2.0f, 4.0f, 8.0f, 12.0f, 20.0f, 32.0f),
)
val GNOME: DesignTokenTable = DesignTokenTable(
DesignSystem.Gnome,
"GNOME Human Interface Guidelines and the libadwaita named colours, GNOME 50",
"Cantarell",
"Source Code Pro",
intArrayOf(
0xff3584e4.toInt(), // Primary
0xffffffff.toInt(), // OnPrimary
0xff9141ac.toInt(), // Secondary
0xffffffff.toInt(), // OnSecondary
0xffffffff.toInt(), // Surface
0xff2e3436.toInt(), // OnSurface
0xffebebeb.toInt(), // SurfaceVariant
0xff5e5c64.toInt(), // OnSurfaceVariant
0xfffafafa.toInt(), // Background
0xff2e3436.toInt(), // OnBackground
0xffcdc7c2.toInt(), // Outline
0xffe6e3e1.toInt(), // OutlineVariant
0xffe01b24.toInt(), // Error
0xffffffff.toInt(), // OnError
0xffebebeb.toInt(), // SurfaceContainer
0xff2190a4.toInt(), // Tertiary
0xffffffff.toInt(), // OnTertiary
0xffd4e5fb.toInt(), // PrimaryContainer
0xff0d3b70.toInt(), // OnPrimaryContainer
0xffecd9f1.toInt(), // SecondaryContainer
0xff45164f.toInt(), // OnSecondaryContainer
0xffcfe9ed.toInt(), // TertiaryContainer
0xff0a3d45.toInt(), // OnTertiaryContainer
0xffba4100.toInt(), // SyntaxKeyword
0xff007778.toInt(), // SyntaxString
0xff6a6a6e.toInt(), // SyntaxComment
0xff4e57ba.toInt(), // SyntaxNumber
0xff4e57ba.toInt(), // SyntaxConstant
0xff007778.toInt(), // SyntaxType
0xff0067cd.toInt(), // SyntaxFunction
0xff3b7800.toInt(), // SyntaxVariable
0xff504e55.toInt(), // SyntaxProperty
0xff504e55.toInt(), // SyntaxOperator
0xff504e55.toInt(), // SyntaxPunctuation
0xff007778.toInt(), // SyntaxTag
0xffba4100.toInt(), // SyntaxAttribute
0xffd01a2a.toInt(), // SyntaxEscape
0xffba4100.toInt(), // SyntaxMacro
0xff006a6b.toInt(), // DiffAdded
0xffae2d23.toInt(), // DiffRemoved
0xffe66100.toInt(), // DiffModified
0xffcfdcdc.toInt(), // DiffAddedContainer
0xffe4d4d3.toInt(), // DiffRemovedContainer
0xffa5c4c5.toInt(), // DiffAddedEmphasis
0xffd9b2af.toInt(), // DiffRemovedEmphasis
),
intArrayOf(
0xff3584e4.toInt(), // Primary
0xffffffff.toInt(), // OnPrimary
0xffc061cb.toInt(), // Secondary
0xffffffff.toInt(), // OnSecondary
0xff1e1e1e.toInt(), // Surface
0xffffffff.toInt(), // OnSurface
0xff303030.toInt(), // SurfaceVariant
0xffc0bfbc.toInt(), // OnSurfaceVariant
0xff242424.toInt(), // Background
0xffffffff.toInt(), // OnBackground
0xff52514f.toInt(), // Outline
0xff3a3a3a.toInt(), // OutlineVariant
0xffff7b63.toInt(), // Error
0xff2a0a06.toInt(), // OnError
0xff303030.toInt(), // SurfaceContainer
0xff2190a4.toInt(), // Tertiary
0xffffffff.toInt(), // OnTertiary
0xff1b3c5e.toInt(), // PrimaryContainer
0xffcfe0f7.toInt(), // OnPrimaryContainer
0xff44234c.toInt(), // SecondaryContainer
0xffecd9f1.toInt(), // OnSecondaryContainer
0xff134249.toInt(), // TertiaryContainer
0xffcfe9ed.toInt(), // OnTertiaryContainer
0xffffa348.toInt(), // SyntaxKeyword
0xff5bc8af.toInt(), // SyntaxString
0xff999898.toInt(), // SyntaxComment
0xff8794d2.toInt(), // SyntaxNumber
0xff8794d2.toInt(), // SyntaxConstant
0xff5bc8af.toInt(), // SyntaxType
0xff62a0ea.toInt(), // SyntaxFunction
0xff5dab1e.toInt(), // SyntaxVariable
0xffc0bfbc.toInt(), // SyntaxProperty
0xffc0bfbc.toInt(), // SyntaxOperator
0xffc0bfbc.toInt(), // SyntaxPunctuation
0xff33b2a4.toInt(), // SyntaxTag
0xffff7800.toInt(), // SyntaxAttribute
0xfffd6656.toInt(), // SyntaxEscape
0xfffb7018.toInt(), // SyntaxMacro
0xff56cebf.toInt(), // DiffAdded
0xffffa295.toInt(), // DiffRemoved
0xffff7800.toInt(), // DiffModified
0xff38504d.toInt(), // DiffAddedContainer
0xff594744.toInt(), // DiffRemovedContainer
0xff3f6f69.toInt(), // DiffAddedEmphasis
0xff835e58.toInt(), // DiffRemovedEmphasis
),
arrayOf(
TypeToken(44.0f, 800, 52.0f, -0.5f, false), // Display
TypeToken(32.0f, 800, 40.0f, -0.25f, false), // Headline
TypeToken(24.0f, 700, 32.0f, 0.0f, false), // Title
TypeToken(20.0f, 700, 28.0f, 0.0f, false), // Subtitle
TypeToken(15.0f, 400, 22.0f, 0.0f, false), // Body
TypeToken(15.0f, 700, 22.0f, 0.0f, false), // BodyStrong
TypeToken(13.0f, 700, 18.0f, 0.1f, false), // Label
TypeToken(12.0f, 400, 16.0f, 0.0f, false), // Caption
TypeToken(14.0f, 400, 20.0f, 0.0f, true), // Mono
),
floatArrayOf(0.0f, 4.0f, 6.0f, 12.0f, 15.0f, 1000.0f),
floatArrayOf(0.0f, 3.0f, 6.0f, 12.0f, 18.0f, 24.0f, 36.0f),
)
val BREEZE: DesignTokenTable = DesignTokenTable(
DesignSystem.Breeze,
"KDE Breeze colour schemes and the KDE Human Interface Guidelines, Plasma 6",
"Noto Sans",
"Hack",
intArrayOf(
0xff3daee9.toInt(), // Primary
0xff06222e.toInt(), // OnPrimary
0xff16a085.toInt(), // Secondary
0xff03201b.toInt(), // OnSecondary
0xfffcfcfc.toInt(), // Surface
0xff232629.toInt(), // OnSurface
0xffe1e3e5.toInt(), // SurfaceVariant
0xff4d5052.toInt(), // OnSurfaceVariant
0xffeff0f1.toInt(), // Background
0xff232629.toInt(), // OnBackground
0xffbdc3c7.toInt(), // Outline
0xffd8dbdd.toInt(), // OutlineVariant
0xffda4453.toInt(), // Error
0xffffffff.toInt(), // OnError
0xfffcfcfc.toInt(), // SurfaceContainer
0xfff67400.toInt(), // Tertiary
0xff2b1200.toInt(), // OnTertiary
0xffd3ecf9.toInt(), // PrimaryContainer
0xff0b3b52.toInt(), // OnPrimaryContainer
0xffd2ece5.toInt(), // SecondaryContainer
0xff083b31.toInt(), // OnSecondaryContainer
0xfffae0c4.toInt(), // TertiaryContainer
0xff4a2c00.toInt(), // OnTertiaryContainer
0xff1f1c1b.toInt(), // SyntaxKeyword
0xffbf0303.toInt(), // SyntaxString
0xff747473.toInt(), // SyntaxComment
0xff966d00.toInt(), // SyntaxNumber
0xffaa5500.toInt(), // SyntaxConstant
0xff0057ae.toInt(), // SyntaxType
0xff644a9b.toInt(), // SyntaxFunction
0xff0057ae.toInt(), // SyntaxVariable
0xff0057ae.toInt(), // SyntaxProperty
0xffaf48b1.toInt(), // SyntaxOperator
0xff1f1c1b.toInt(), // SyntaxPunctuation
0xff1f1c1b.toInt(), // SyntaxTag
0xff0057ae.toInt(), // SyntaxAttribute
0xff007bac.toInt(), // SyntaxEscape
0xff006e28.toInt(), // SyntaxMacro
0xff0057ae.toInt(), // DiffAdded
0xffbf0303.toInt(), // DiffRemoved
0xfffdbc4b.toInt(), // DiffModified
0xffdee8f3.toInt(), // DiffAddedContainer
0xfff5dede.toInt(), // DiffRemovedContainer
0xffb0cae5.toInt(), // DiffAddedEmphasis
0xffeab1b1.toInt(), // DiffRemovedEmphasis
),
intArrayOf(
0xff3daee9.toInt(), // Primary
0xff06222e.toInt(), // OnPrimary
0xff1abc9c.toInt(), // Secondary
0xff03201b.toInt(), // OnSecondary
0xff1b1e20.toInt(), // Surface
0xfffcfcfc.toInt(), // OnSurface
0xff31363b.toInt(), // SurfaceVariant
0xffbdc3c7.toInt(), // OnSurfaceVariant
0xff232629.toInt(), // Background
0xfffcfcfc.toInt(), // OnBackground
0xff4d5155.toInt(), // Outline
0xff3f4449.toInt(), // OutlineVariant
0xffed8079.toInt(), // Error
0xff2a0806.toInt(), // OnError
0xff1b1e20.toInt(), // SurfaceContainer
0xfff8a44c.toInt(), // Tertiary
0xff2b1200.toInt(), // OnTertiary
0xff123b4f.toInt(), // PrimaryContainer
0xffcde6f5.toInt(), // OnPrimaryContainer
0xff103a32.toInt(), // SecondaryContainer
0xffcfe8e0.toInt(), // OnSecondaryContainer
0xff4a3113.toInt(), // TertiaryContainer
0xfff8dfc3.toInt(), // OnTertiaryContainer
0xffcfcfc2.toInt(), // SyntaxKeyword
0xfff44f4f.toInt(), // SyntaxString
0xff848687.toInt(), // SyntaxComment
0xfff67400.toInt(), // SyntaxNumber
0xff27aeae.toInt(), // SyntaxConstant
0xff3b8dc7.toInt(), // SyntaxType
0xffb266d1.toInt(), // SyntaxFunction
0xff27aeae.toInt(), // SyntaxVariable
0xff27aeae.toInt(), // SyntaxProperty
0xff519269.toInt(), // SyntaxOperator
0xffcfcfc2.toInt(), // SyntaxPunctuation
0xffcfcfc2.toInt(), // SyntaxTag
0xff3b8dc7.toInt(), // SyntaxAttribute
0xff3daee9.toInt(), // SyntaxEscape
0xff27ae60.toInt(), // SyntaxMacro
0xff2cb1b1.toInt(), // DiffAdded
0xffff736e.toInt(), // DiffRemoved
0xffc04900.toInt(), // DiffModified
0xff1e3b3d.toInt(), // DiffAddedContainer
0xff492f30.toInt(), // DiffRemovedContainer
0xff22595a.toInt(), // DiffAddedEmphasis
0xff76403f.toInt(), // DiffRemovedEmphasis
),
arrayOf(
TypeToken(34.0f, 600, 40.0f, 0.0f, false), // Display
TypeToken(26.0f, 600, 32.0f, 0.0f, false), // Headline
TypeToken(19.0f, 600, 24.0f, 0.0f, false), // Title
TypeToken(16.0f, 500, 21.0f, 0.0f, false), // Subtitle
TypeToken(13.0f, 400, 19.0f, 0.0f, false), // Body
TypeToken(13.0f, 600, 19.0f, 0.0f, false), // BodyStrong
TypeToken(12.0f, 500, 16.0f, 0.2f, false), // Label
TypeToken(11.0f, 400, 15.0f, 0.0f, false), // Caption
TypeToken(12.0f, 400, 17.0f, 0.0f, true), // Mono
),
floatArrayOf(0.0f, 2.0f, 3.0f, 4.0f, 6.0f, 1000.0f),
floatArrayOf(0.0f, 2.0f, 4.0f, 8.0f, 12.0f, 18.0f, 24.0f),
)
val DEEPIN: DesignTokenTable = DesignTokenTable(
DesignSystem.Deepin,
"Deepin Design specification and the DTK control defaults, deepin 23",
"Noto Sans",
"Noto Sans Mono",
intArrayOf(
0xff0081ff.toInt(), // Primary
0xffffffff.toInt(), // OnPrimary
0xfff2a13c.toInt(), // Secondary
0xff2b1a05.toInt(), // OnSecondary
0xffffffff.toInt(), // Surface
0xff202020.toInt(), // OnSurface
0xffe6e6e6.toInt(), // SurfaceVariant
0xff5a5a5a.toInt(), // OnSurfaceVariant
0xffffffff.toInt(), // Background
0xff202020.toInt(), // OnBackground
0xffcdcdcd.toInt(), // Outline
0xffe0e0e0.toInt(), // OutlineVariant
0xffff5736.toInt(), // Error
0xffffffff.toInt(), // OnError
0xfff1f1f1.toInt(), // SurfaceContainer
0xff7a5bd6.toInt(), // Tertiary
0xffffffff.toInt(), // OnTertiary
0xffd8ecff.toInt(), // PrimaryContainer
0xff00407f.toInt(), // OnPrimaryContainer
0xfffdf0e1.toInt(), // SecondaryContainer
0xff79501e.toInt(), // OnSecondaryContainer
0xffebe6f8.toInt(), // TertiaryContainer
0xff3d2d6b.toInt(), // OnTertiaryContainer
0xff006bd5.toInt(), // SyntaxKeyword
0xff7758d3.toInt(), // SyntaxString
0xff5a5a5a.toInt(), // SyntaxComment
0xff3d2d6b.toInt(), // SyntaxNumber
0xff3d2d6b.toInt(), // SyntaxConstant
0xff9b5e00.toInt(), // SyntaxType
0xff00407f.toInt(), // SyntaxFunction
0xff202020.toInt(), // SyntaxVariable
0xff79501e.toInt(), // SyntaxProperty
0xff5a5a5a.toInt(), // SyntaxOperator
0xff5a5a5a.toInt(), // SyntaxPunctuation
0xff006bd5.toInt(), // SyntaxTag
0xff9b5e00.toInt(), // SyntaxAttribute
0xffcd3416.toInt(), // SyntaxEscape
0xff9b5e00.toInt(), // SyntaxMacro
0xff007118.toInt(), // DiffAdded
0xffbb2608.toInt(), // DiffRemoved
0xff7a5bd6.toInt(), // DiffModified
0xffd4e2d7.toInt(), // DiffAddedContainer
0xffebd9d5.toInt(), // DiffRemovedContainer
0xffa9cbb0.toInt(), // DiffAddedEmphasis
0xffe1b4ab.toInt(), // DiffRemovedEmphasis
),
intArrayOf(
0xff3ba2ff.toInt(), // Primary
0xff04203a.toInt(), // OnPrimary
0xffffb964.toInt(), // Secondary
0xff33200a.toInt(), // OnSecondary
0xff2a2a2a.toInt(), // Surface
0xfff0f0f0.toInt(), // OnSurface
0xff3a3a3a.toInt(), // SurfaceVariant
0xffb4b4b4.toInt(), // OnSurfaceVariant
0xff1a1a1a.toInt(), // Background
0xfff0f0f0.toInt(), // OnBackground
0xff4d4d4d.toInt(), // Outline
0xff333333.toInt(), // OutlineVariant
0xffff8a73.toInt(), // Error
0xff34110a.toInt(), // OnError
0xff2a2a2a.toInt(), // SurfaceContainer
0xff9f8ae3.toInt(), // Tertiary
0xff1d0f45.toInt(), // OnTertiary
0xff203547.toInt(), // PrimaryContainer
0xff9dd0ff.toInt(), // OnPrimaryContainer
0xff473928.toInt(), // SecondaryContainer
0xffffdcb1.toInt(), // OnSecondaryContainer
0xff343042.toInt(), // TertiaryContainer
0xffcfc4f1.toInt(), // OnTertiaryContainer
0xff3ba2ff.toInt(), // SyntaxKeyword
0xff9f8ae3.toInt(), // SyntaxString
0xffb4b4b4.toInt(), // SyntaxComment
0xffcfc4f1.toInt(), // SyntaxNumber
0xffcfc4f1.toInt(), // SyntaxConstant
0xffffb964.toInt(), // SyntaxType
0xff9dd0ff.toInt(), // SyntaxFunction
0xfff0f0f0.toInt(), // SyntaxVariable
0xffffdcb1.toInt(), // SyntaxProperty
0xffb4b4b4.toInt(), // SyntaxOperator
0xffb4b4b4.toInt(), // SyntaxPunctuation
0xff3ba2ff.toInt(), // SyntaxTag
0xffffb964.toInt(), // SyntaxAttribute
0xffff8a73.toInt(), // SyntaxEscape
0xffffb964.toInt(), // SyntaxMacro
0xff76c270.toInt(), // DiffAdded
0xffff9783.toInt(), // DiffRemoved
0xff9f8ae3.toInt(), // DiffModified
0xff394838.toInt(), // DiffAddedContainer
0xff55403c.toInt(), // DiffRemovedContainer
0xff486746.toInt(), // DiffAddedEmphasis
0xff7f564e.toInt(), // DiffRemovedEmphasis
),
arrayOf(
TypeToken(40.0f, 600, 50.0f, 0.0f, false), // Display
TypeToken(30.0f, 600, 38.0f, 0.0f, false), // Headline
TypeToken(22.0f, 500, 30.0f, 0.0f, false), // Title
TypeToken(18.0f, 500, 26.0f, 0.0f, false), // Subtitle
TypeToken(14.0f, 400, 21.0f, 0.0f, false), // Body
TypeToken(14.0f, 600, 21.0f, 0.0f, false), // BodyStrong
TypeToken(13.0f, 500, 18.0f, 0.3f, false), // Label
TypeToken(12.0f, 400, 17.0f, 0.0f, false), // Caption
TypeToken(13.0f, 400, 19.0f, 0.0f, true), // Mono
),
floatArrayOf(0.0f, 6.0f, 8.0f, 10.0f, 18.0f, 1000.0f),
floatArrayOf(0.0f, 4.0f, 10.0f, 16.0f, 20.0f, 30.0f, 40.0f),
)
val LIQUID_GLASS: DesignTokenTable = DesignTokenTable(
DesignSystem.LiquidGlass,
"Apple Human Interface Guidelines, Liquid Glass, system colors and Dynamic Type, 2026",
"SF Pro",
"SF Mono",
intArrayOf(
0xff007aff.toInt(), // Primary
0xffffffff.toInt(), // OnPrimary
0xff5856d6.toInt(), // Secondary
0xffffffff.toInt(), // OnSecondary
0xffffffff.toInt(), // Surface
0xff000000.toInt(), // OnSurface
0xffe9e9eb.toInt(), // SurfaceVariant
0xff3c3c43.toInt(), // OnSurfaceVariant
0xffffffff.toInt(), // Background
0xff000000.toInt(), // OnBackground
0xffc6c6c8.toInt(), // Outline
0xffe5e5ea.toInt(), // OutlineVariant
0xffff3b30.toInt(), // Error
0xffffffff.toInt(), // OnError
0xfff2f2f7.toInt(), // SurfaceContainer
0xffaf52de.toInt(), // Tertiary
0xffffffff.toInt(), // OnTertiary
0xffd6e4ff.toInt(), // PrimaryContainer
0xff003070.toInt(), // OnPrimaryContainer
0xffe2e0ff.toInt(), // SecondaryContainer
0xff2a1b70.toInt(), // OnSecondaryContainer
0xfff3ddfb.toInt(), // TertiaryContainer
0xff3d0b52.toInt(), // OnTertiaryContainer
0xff9b2393.toInt(), // SyntaxKeyword
0xffc41a16.toInt(), // SyntaxString
0xff5d6c79.toInt(), // SyntaxComment
0xff1c00cf.toInt(), // SyntaxNumber
0xff6c36a9.toInt(), // SyntaxConstant
0xff3900a0.toInt(), // SyntaxType
0xff326d74.toInt(), // SyntaxFunction
0xff000000.toInt(), // SyntaxVariable
0xff326d74.toInt(), // SyntaxProperty
0xff000000.toInt(), // SyntaxOperator
0xff000000.toInt(), // SyntaxPunctuation
0xff0b4f79.toInt(), // SyntaxTag
0xff815f03.toInt(), // SyntaxAttribute
0xff1c00cf.toInt(), // SyntaxEscape
0xff643820.toInt(), // SyntaxMacro
0xff00722a.toInt(), // DiffAdded
0xffc40012.toInt(), // DiffRemoved
0xff007aff.toInt(), // DiffModified
0xffd5e3de.toInt(), // DiffAddedContainer
0xffecd5dc.toInt(), // DiffRemovedContainer
0xffa9ccb9.toInt(), // DiffAddedEmphasis
0xffe4a9b2.toInt(), // DiffRemovedEmphasis
),
intArrayOf(
0xff0a84ff.toInt(), // Primary
0xffffffff.toInt(), // OnPrimary
0xff5e5ce6.toInt(), // Secondary
0xffffffff.toInt(), // OnSecondary
0xff1c1c1e.toInt(), // Surface
0xffffffff.toInt(), // OnSurface
0xff2c2c2e.toInt(), // SurfaceVariant
0xffebebf5.toInt(), // OnSurfaceVariant
0xff000000.toInt(), // Background
0xffffffff.toInt(), // OnBackground
0xff38383a.toInt(), // Outline
0xff48484a.toInt(), // OutlineVariant
0xffff453a.toInt(), // Error
0xffffffff.toInt(), // OnError
0xff1c1c1e.toInt(), // SurfaceContainer
0xffbf5af2.toInt(), // Tertiary
0xffffffff.toInt(), // OnTertiary
0xff0a2d52.toInt(), // PrimaryContainer
0xffcfe3ff.toInt(), // OnPrimaryContainer
0xff262663.toInt(), // SecondaryContainer
0xffdedcff.toInt(), // OnSecondaryContainer
0xff3f1a52.toInt(), // TertiaryContainer
0xfff1d9fa.toInt(), // OnTertiaryContainer
0xfffc5fa3.toInt(), // SyntaxKeyword
0xfffc6a5d.toInt(), // SyntaxString
0xff798693.toInt(), // SyntaxComment
0xffd0bf69.toInt(), // SyntaxNumber
0xffa167e6.toInt(), // SyntaxConstant
0xffd0a8ff.toInt(), // SyntaxType
0xff67b7a4.toInt(), // SyntaxFunction
0xffdfdfe0.toInt(), // SyntaxVariable
0xff67b7a4.toInt(), // SyntaxProperty
0xffdfdfe0.toInt(), // SyntaxOperator
0xffdfdfe0.toInt(), // SyntaxPunctuation
0xff5dd8ff.toInt(), // SyntaxTag
0xffbf8555.toInt(), // SyntaxAttribute
0xffd0bf69.toInt(), // SyntaxEscape
0xfffd8f3f.toInt(), // SyntaxMacro
0xff30d158.toInt(), // DiffAdded
0xffff6e66.toInt(), // DiffRemoved
0xff0a84ff.toInt(), // DiffModified
0xff20402a.toInt(), // DiffAddedContainer
0xff492c2c.toInt(), // DiffRemovedContainer
0xff246435.toInt(), // DiffAddedEmphasis
0xff773d3b.toInt(), // DiffRemovedEmphasis
),
arrayOf(
TypeToken(34.0f, 700, 41.0f, 0.37f, false), // Display
TypeToken(28.0f, 700, 34.0f, 0.36f, false), // Headline
TypeToken(22.0f, 700, 28.0f, 0.35f, false), // Title
TypeToken(17.0f, 600, 22.0f, -0.41f, false), // Subtitle
TypeToken(17.0f, 400, 22.0f, -0.41f, false), // Body
TypeToken(17.0f, 600, 22.0f, -0.41f, false), // BodyStrong
TypeToken(15.0f, 600, 20.0f, -0.24f, false), // Label
TypeToken(12.0f, 500, 16.0f, 0.0f, false), // Caption
TypeToken(15.0f, 400, 20.0f, 0.0f, true), // Mono
),
floatArrayOf(0.0f, 6.0f, 10.0f, 16.0f, 22.0f, 1000.0f),
floatArrayOf(0.0f, 4.0f, 10.0f, 18.0f, 24.0f, 36.0f, 48.0f),
)
fun of(system: DesignSystem): DesignTokenTable = when (system) {
DesignSystem.Material3 -> MATERIAL3
DesignSystem.Cupertino -> CUPERTINO
DesignSystem.Fluent -> FLUENT
DesignSystem.Gnome -> GNOME
DesignSystem.Breeze -> BREEZE
DesignSystem.Deepin -> DEEPIN
DesignSystem.LiquidGlass -> LIQUID_GLASS
}
}