#!/usr/bin/env dub
/+ dub.sdl:
name "platform_ui_derive_palette"
targetPath "build"
dependency "sparkles:base" path="../../../../.."
dependency "sparkles:ui" path="../../../../.."
dflags "-preview=in" "-preview=dip1000"
buildType "checked" {
buildOptions "optimize" "inline" "debugInfo"
}
+/
/**
* Deriving a `sparkles:ui` palette from an OS appearance triple.
*
* The platforms surveyed in this tree all hand an application the same three
* scalars — a **color scheme**, an **accent color**, and a **contrast level**
* (see [concepts.md](../../concepts.md)) — and every one of them leaves the
* actual palette construction to the app. This program is that construction,
* done the way [color-derivation/index.md](../index.md) recommends:
*
* 1. **Tone, not lightness.** Colors are placed on the CIE `L*` "tone" axis
* (0 = black, 100 = white), which is what Material's HCT uses for its
* third dimension. `L*` is perceptually uniform, so a fixed tone *delta*
* buys a predictable contrast ratio at any hue.
* 2. **The tone-delta rule is verified, not asserted.** Material documents
* that "a difference of 40 in HCT tone guarantees a contrast ratio >= 3.0,
* and a difference of 50 guarantees a contrast ratio >= 4.5". Step 2 below
* sweeps every tone pair at those deltas and reports the worst case, so the
* rule this tree repeats is checked by the build rather than trusted.
* 3. **Scheme flips the direction, not the recipe.** A light scheme puts text
* at a low tone on a high-tone surface; a dark scheme mirrors it. Contrast
* level shifts the deltas. The accent hue is preserved in both.
*
* Companion to docs/research/platform-ui-guidelines/color-derivation/index.md
* § "The tone-delta rule" and § "Deriving the Sparkles palette".
*
* Run with: dub run --single derive-palette.d
*
* Portability: pure computation — no OS, no terminal, no display. Green
* everywhere. The live OS reads are the sibling examples
* ([portal-appearance.d](../../gnome/examples/portal-appearance.d),
* [kdeglobals-appearance.d](../../kde/examples/kdeglobals-appearance.d),
* [color-scheme-probe.d](../../terminal/examples/color-scheme-probe.d)).
*/
module (module) platform_ui_derive_paletteDeriving a sparkles:ui palette from an OS appearance triple.
The platforms surveyed in this tree all hand an application the same three
scalars — a color scheme, an accent color, and a contrast level
(see concepts.md) — and every one of them leaves the
actual palette construction to the app. This program is that construction,
done the way color-derivation/index.md recommends:
Tone, not lightness. Colors are placed on the CIE L* "tone" axis
(0 = black, 100 = white), which is what Material's HCT uses for its
third dimension. L* is perceptually uniform, so a fixed tone delta
buys a predictable contrast ratio at any hue.
The tone-delta rule is verified, not asserted. Material documents
that "a difference of 40 in HCT tone guarantees a contrast ratio >= 3.0,
and a difference of 50 guarantees a contrast ratio >= 4.5". Step 2 below
sweeps every tone pair at those deltas and reports the worst case, so the
rule this tree repeats is checked by the build rather than trusted.
Scheme flips the direction, not the recipe. A light scheme puts text
at a low tone on a high-tone surface; a dark scheme mirrors it. Contrast
level shifts the deltas. The accent hue is preserved in both.
Companion to docs/research/platform-ui-guidelines/color-derivation/index.md
§ "The tone-delta rule" and § "Deriving the Sparkles palette".
Run with: dub run --single derive-palette.d
Portability
pure computation — no OS, no terminal, no display. Green
everywhere. The live OS reads are the sibling examples
(portal-appearance.d,
kdeglobals-appearance.d,
color-scheme-probe.d).
platform_ui_derive_palette;
import (package) stdstd.(module) std.mathContains the elementary mathematical functions (powers, roots,
and trigonometric functions), and low-level floating-point operations.
Mathematical special functions are available in std.mathspecial.
Category Members Constants E PI PI_2 PI4 M1_PI M2_PI M2_SQRTPI LN10 LN2 LOG2 LOG2E LOG2T LOG10E SQRT2 SQRT1_2 Algebraic abs fabs sqrt cbrt hypot poly nextPow2 truncPow2 Trigonometry sin cos tan asin acos atan atan2 sinh cosh tanh asinh acosh atanh Rounding ceil floor round lround trunc rint lrint nearbyint rndtol quantize Exponentiation & Logarithms pow powmod exp exp2 expm1 ldexp frexp log log2 log10 logb ilogb log1p scalbn Remainder fmod modf remainder remquo Floating-point operations approxEqual feqrel fdim fmax fmin fma isClose nextDown nextUp nextafter NaN getNaNPayload cmp Introspection isFinite isIdentical isInfinity isNaN isNormal isSubnormal signbit sgn copysign isPowerOf2 Hardware Control IeeeFlags ieeeFlags resetIeeeFlags FloatingPointControl
The functionality closely follows the IEEE754-2008 standard for
floating-point arithmetic, including the use of camelCase names rather
than C99-style lower case names. All of these functions behave correctly
when presented with an infinity or NaN.
The following IEEE 'real' formats are currently supported:
64 bit Big-endian 'double' (eg PowerPC)
128 bit Big-endian 'quadruple' (eg SPARC)
64 bit Little-endian 'double' (eg x86-SSE2)
80 bit Little-endian, with implied bit 'real80' (eg x87, Itanium)
128 bit Little-endian 'quadruple' (not implemented on any known processor!)
Non-IEEE 128 bit Big-endian 'doubledouble' (eg PowerPC) has partial support
Unlike C, there is no global 'errno' variable. Consequently, almost all of
these functions are pure nothrow.
Source
std/math/package.d
math : (alias template) platform_ui_derive_palette.pow = std.math.exponential.pow(F, G)(F x, G n) if (isFloatingPoint!F && isIntegral!G)Compute the value of x n, where n is an integer
pow, (alias) platform_ui_derive_palette.round = real std.math.rounding.round(real x) pure nothrow @nogc @trustedReturn the value of x rounded to the nearest integer.
If the fractional part of x is exactly 0.5, the return value is
rounded away from zero.
round;
import (package) stdstd.(module) std.stdioCategory Symbols File handles _popen File isFileHandle openNetwork stderr stdin stdout Reading chunks lines readf readfln readln Writing toFile write writef writefln writeln Misc KeepTerminator LockType StdioException
Standard I/O functions that extend core.stdc.stdio. core.stdc.stdio
is publically imported when importing std.stdio.
There are three layers of I/O:
The lowest layer is the operating system layer. The two main schemes are Windows and Posix.
C's stdio.h which unifies the two operating system schemes.
std.stdio, this module, unifies the various stdio.h implementations into
a high level package for D programs.
Source
std/stdio.d
stdio : (alias template) platform_ui_derive_palette.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))Equivalent to writef(fmt, args, '\n').
writefln, (alias template) platform_ui_derive_palette.writeln = std.stdio.writeln(T...)(T args)Equivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
import (package) sparklessparkles.(package) sparkles.basebase.(module) sparkles.base.term_colorTerminal color foundation: the Color type, capability tiers, depth folding,
and SGR color-parameter emission.
This is the single home for how sparkles models a terminal color and turns it
into SGR bytes.
Color is a four-case value (unset, default_, palette, rgb)
covering everything a theme or style expresses; unset (Color.init) is "not
specified", default_ is "the terminal's own default". It is the shared type
consumed by sparkles.base.styled_template, sparkles.syntax (re-exported as
sparkles.syntax.color.Color), and any future cell-grid backend.
ColorDepth + classifyColorDepth name the capability tiers and
their pure, CTFE-able classifier; detectColorDepth is the thin
environment-reading edge for standalone use. ansi256FromRgb,
ansi16FromRgb, and xterm256ToRgb are the depth fold — themes
author in 24-bit RGB and terminals that speak only 256 or 16 colors get the
nearest approximation.
writeSgrColor emits the SGR parameters selecting a color on a
ColorChannel (foreground/background/underline), depth-folded. The escape
ESC[/m wrapper and the transition diff live in
sparkles.base.term_style.writeStyleTransition.
#RRGGBBAA hex parsing (parseHexColor) understands bat's alpha
convention (alpha 0 ⇒ palette index, alpha 1 ⇒ terminal default) at the
boundary, turning the encoding trick into structure.
term_color : (struct) sparkles.base.term_color.ColorA terminal color: one of four cases.
unset — not specified (the default, ``Color.init);
default_ — use the terminal's default fore-/background;
palette — a terminal-palette index (0–255) in index;
rgb — a 24-bit value in rgb.
Examples
assert(!Color.init.isSet);
assert(Color.defaultColor.kind == Color.Kind.default_);
const c = Color.fromRgb(0x1e, 0x1e, 0x2e);
assert(c.kind == Color.Kind.rgb && c.rgb == RgbColor(0x1e, 0x1e, 0x2e));
// A hex-string literal is a `ubyte[3]` (2.108) — same as three bytes.
assert(Color.fromRgb(x"1e1e2e") == c);
const p = Color.fromPalette(4);
assert(p.kind == Color.Kind.palette && p.index == 4);
Color, (struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor;
import (package) sparklessparkles.(package) sparkles.uiui.(module) sparkles.ui.styleThe style layer for sparkles.ui — the single source of truth the three
twoslash backends (CSS, raylib GUI, ANSI) had triplicated.
A widget names a semantic Slot (error, warn, surface, …), never a
concrete color. A Palette maps every slot to a foreground/background
Color plus per-channel alpha and a handful of
scalar chrome knobs (popup radius/padding, detach gap, chrome glyphs).
defaultTwoslashPalette authors the canonical twoslash hexes once.
Three generators consume a palette:
resolveSlot → a concrete Visual (RGB + alpha) for the GUI
and the display list, deferring "inherit" to the page fg/bg;
writeTwoslashVars → the CSS :root { --twoslash-* } block, kept in
lockstep with views/twoslash.css by a unittest;
writeSlotSgr → the SGR parameters for the terminal renderer.
style : (enum) sparkles.ui.style.ColorSchemeLight or dark color scheme — only the popup surface and docs text differ (the
brand colors are shared), matching views/twoslash.css's dark @media block.
ColorScheme, (struct) sparkles.ui.style.PaletteMaps every Slot to a fore/background color and alpha, plus scalar chrome
constants shared by the backends. fg/bg are Colors:
an unset fg means "inherit the page foreground", an unset bg means
"no background". Alpha is stored separately because Color carries none.
Palette, (enum) sparkles.ui.style.SlotA semantic style role. Widgets and display-list ops carry a Slot, and a
Palette turns it into a concrete Visual. Roles are intentionally
generic (an app palette can reuse them) even though the seed values come from
twoslash.
Slot;
import (package) sparklessparkles.(package) sparkles.uiui.(module) sparkles.ui.themeThe theme level of sparkles.ui: an application's whole design language as
one runtime-swappable value.
A theme has four channels, and the point of unifying them is that they are all
the same kind of decision — whether a keyword is mauve, whether a popup border is
rounded, and whether a table draws with heavy or light box-drawing are choices a
user should be able to make together:
syntax — ordered ThemeRules mapping dotted label
selectors to StyleSpecs.
slots — the semantic Palette: a
Slot resolves to a concrete appearance.
metrics — scalar chrome (radii, paddings, border widths, font
scales), carried by the same Palette.
glyphs — GlyphSet: box-drawing charsets, status marks
and the like.
The syntax channel is opaque here. This module carries the rules but cannot
resolve them: resolution needs a label vocabulary, which is a syntax-highlighting
concept, so LabelSet, ResolvedTheme and resolveTheme stay in
sparkles.syntax.theme``. Moving them here would invert the dependency —
sparkles:syntax consumes this toolkit, never the reverse. That also keeps this
module free of every syntax type: a rule is a string and a
TermStyle, both of which sparkles:base owns.
theme : (struct) sparkles.ui.theme.ThemeA theme as plain data: all four channels in one value.
Every field defaults, so a theme that only sets syntax rules is valid and
effectivePalette derives the rest from the page background.
Theme;
// ---------------------------------------------------------------------------
// sRGB ⇄ linear ⇄ luminance ⇄ tone (CIE L*)
// ---------------------------------------------------------------------------
/// One sRGB channel (0..1) linearized, per IEC 61966-2-1 — the transfer
/// function WCAG 2.x's relative-luminance definition uses verbatim.
double double platform_ui_derive_palette.linearize(double channel) pure nothrow @nogc @safeOne sRGB channel (0..1) linearized, per IEC 61966-2-1 — the transfer
function WCAG 2.x's relative-luminance definition uses verbatim.
linearize(double (parameter) double channelchannel) @safe pure nothrow @nogc
=> (parameter) double channelchannel <= 0.040_45 ? (parameter) double channelchannel / 12.92 : double std.math.exponential.pow!(double, double)(double x, double y) pure nothrow @nogc @trustedCalculates x`y`.
x y pow(x, y)
div 0 invalid? anything 0.0 1.0 no no x> 1 + + no no x< 1 + +0.0 no no x> 1 - +0.0 no no x< 1 - + no no + > 0.0 + no no + < 0.0 +0.0 no no - odd integer > 0.0 - no no - > 0.0, not odd integer + no no - odd integer < 0.0 -0.0 no no - < 0.0, not odd integer +0.0 no no 1.0 - no yes < 0.0 finite, nonintegral no yes 0.0 odd integer < 0.0 yes no 0.0 < 0.0, not odd integer + yes no 0.0 odd integer > 0.0 0.0 no no 0.0 > 0.0, not odd integer +0.0 no no
Examples
import std.math.operations : isClose;
assert(isClose(pow(2.0, 3.0), 8.0));
assert(isClose(pow(1.5, 10.0), 57.6650390625));
// square root of 9
assert(isClose(pow(9.0, 0.5), 3.0));
// 10th root of 1024
assert(isClose(pow(1024.0, 0.1), 2.0));
assert(isClose(pow(-4.0, 3.0), -64.0));
// reciprocal of 4 ^^ 2
assert(isClose(pow(4.0, -2.0), 0.0625));
// reciprocal of (-2) ^^ 3
assert(isClose(pow(-2.0, -3.0), -0.125));
assert(isClose(pow(-2.5, 3.0), -15.625));
// reciprocal of 2.5 ^^ 3
assert(isClose(pow(2.5, -3.0), 0.064));
// reciprocal of (-2.5) ^^ 3
assert(isClose(pow(-2.5, -3.0), -0.064));
// reciprocal of square root of 4
assert(isClose(pow(4.0, -0.5), 0.5));
// per definition
assert(isClose(pow(0.0, 0.0), 1.0));
import std.math.operations : isClose;
// the result is a complex number
// which cannot be represented as floating point number
import std.math.traits : isNaN;
assert(isNaN(pow(-2.5, -1.5)));
// use the ^^-operator of std.complex instead
import std.complex : complex;
auto c1 = complex(-2.5, 0.0);
auto c2 = complex(-1.5, 0.0);
auto result = c1 ^^ c2;
// exact result apparently depends on `real` precision => increased tolerance
assert(isClose(result.re, -4.64705438e-17, 2e-4));
assert(isClose(result.im, 2.52982e-1, 2e-4));
pow(((parameter) double channelchannel + 0.055) / 1.055, 2.4);
/// WCAG relative luminance `Y` (0..1) of an sRGB color.
double double platform_ui_derive_palette.luminance(in sparkles.base.term_color.RgbColor c) pure nothrow @nogc @safeWCAG relative luminance Y (0..1) of an sRGB color.
luminance(in (struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor (parameter) const(sparkles.base.term_color.RgbColor) cc) @safe pure nothrow @nogc
=> 0.2126 * double platform_ui_derive_palette.linearize(double channel) pure nothrow @nogc @safeOne sRGB channel (0..1) linearized, per IEC 61966-2-1 — the transfer
function WCAG 2.x's relative-luminance definition uses verbatim.
linearize((parameter) const(sparkles.base.term_color.RgbColor) cc.(field) ubyte sparkles.base.term_color.RgbColor.rr / 255.0)
+ 0.7152 * double platform_ui_derive_palette.linearize(double channel) pure nothrow @nogc @safeOne sRGB channel (0..1) linearized, per IEC 61966-2-1 — the transfer
function WCAG 2.x's relative-luminance definition uses verbatim.
linearize((parameter) const(sparkles.base.term_color.RgbColor) cc.(field) ubyte sparkles.base.term_color.RgbColor.gg / 255.0)
+ 0.0722 * double platform_ui_derive_palette.linearize(double channel) pure nothrow @nogc @safeOne sRGB channel (0..1) linearized, per IEC 61966-2-1 — the transfer
function WCAG 2.x's relative-luminance definition uses verbatim.
linearize((parameter) const(sparkles.base.term_color.RgbColor) cc.(field) ubyte sparkles.base.term_color.RgbColor.bb / 255.0);
/// The WCAG 2.x contrast ratio between two colors, in `[1, 21]`.
double double platform_ui_derive_palette.contrastRatio(in sparkles.base.term_color.RgbColor a, in sparkles.base.term_color.RgbColor b) pure nothrow @nogc @safeThe WCAG 2.x contrast ratio between two colors, in [1, 21].
contrastRatio(in (struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor (parameter) const(sparkles.base.term_color.RgbColor) aa, in (struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor (parameter) const(sparkles.base.term_color.RgbColor) bb) @safe pure nothrow @nogc
{
const (local variable) const(double) lala = double platform_ui_derive_palette.luminance(in sparkles.base.term_color.RgbColor c) pure nothrow @nogc @safeWCAG relative luminance Y (0..1) of an sRGB color.
luminance((parameter) const(sparkles.base.term_color.RgbColor) aa), (local variable) const(double) lblb = double platform_ui_derive_palette.luminance(in sparkles.base.term_color.RgbColor c) pure nothrow @nogc @safeWCAG relative luminance Y (0..1) of an sRGB color.
luminance((parameter) const(sparkles.base.term_color.RgbColor) bb);
const (local variable) const(double) hihi = (local variable) const(double) lala > (local variable) const(double) lblb ? (local variable) const(double) lala : (local variable) const(double) lblb, (local variable) const(double) lolo = (local variable) const(double) lala > (local variable) const(double) lblb ? (local variable) const(double) lblb : (local variable) const(double) lala;
return ((local variable) const(double) hihi + 0.05) / ((local variable) const(double) lolo + 0.05);
}
/// CIE `L*` ("tone" in HCT terms) from a relative luminance, 0..100.
double double platform_ui_derive_palette.toneFromLuminance(double y) pure nothrow @nogc @safeCIE L* ("tone" in HCT terms) from a relative luminance, 0..100.
toneFromLuminance(double (parameter) double yy) @safe pure nothrow @nogc
=> (parameter) double yy <= 216.0 / 24_389.0 ? (parameter) double yy * 24_389.0 / 27.0 : 116.0 * double std.math.exponential.pow!(double, double)(double x, double y) pure nothrow @nogc @trustedCalculates x`y`.
x y pow(x, y)
div 0 invalid? anything 0.0 1.0 no no x> 1 + + no no x< 1 + +0.0 no no x> 1 - +0.0 no no x< 1 - + no no + > 0.0 + no no + < 0.0 +0.0 no no - odd integer > 0.0 - no no - > 0.0, not odd integer + no no - odd integer < 0.0 -0.0 no no - < 0.0, not odd integer +0.0 no no 1.0 - no yes < 0.0 finite, nonintegral no yes 0.0 odd integer < 0.0 yes no 0.0 < 0.0, not odd integer + yes no 0.0 odd integer > 0.0 0.0 no no 0.0 > 0.0, not odd integer +0.0 no no
Examples
import std.math.operations : isClose;
assert(isClose(pow(2.0, 3.0), 8.0));
assert(isClose(pow(1.5, 10.0), 57.6650390625));
// square root of 9
assert(isClose(pow(9.0, 0.5), 3.0));
// 10th root of 1024
assert(isClose(pow(1024.0, 0.1), 2.0));
assert(isClose(pow(-4.0, 3.0), -64.0));
// reciprocal of 4 ^^ 2
assert(isClose(pow(4.0, -2.0), 0.0625));
// reciprocal of (-2) ^^ 3
assert(isClose(pow(-2.0, -3.0), -0.125));
assert(isClose(pow(-2.5, 3.0), -15.625));
// reciprocal of 2.5 ^^ 3
assert(isClose(pow(2.5, -3.0), 0.064));
// reciprocal of (-2.5) ^^ 3
assert(isClose(pow(-2.5, -3.0), -0.064));
// reciprocal of square root of 4
assert(isClose(pow(4.0, -0.5), 0.5));
// per definition
assert(isClose(pow(0.0, 0.0), 1.0));
import std.math.operations : isClose;
// the result is a complex number
// which cannot be represented as floating point number
import std.math.traits : isNaN;
assert(isNaN(pow(-2.5, -1.5)));
// use the ^^-operator of std.complex instead
import std.complex : complex;
auto c1 = complex(-2.5, 0.0);
auto c2 = complex(-1.5, 0.0);
auto result = c1 ^^ c2;
// exact result apparently depends on `real` precision => increased tolerance
assert(isClose(result.re, -4.64705438e-17, 2e-4));
assert(isClose(result.im, 2.52982e-1, 2e-4));
pow((parameter) double yy, 1.0 / 3.0) - 16.0;
/// The inverse: the luminance a given tone sits at.
double double platform_ui_derive_palette.luminanceFromTone(double tone) pure nothrow @nogc @safeThe inverse: the luminance a given tone sits at.
luminanceFromTone(double (parameter) double tonetone) @safe pure nothrow @nogc
{
if ((parameter) double tonetone <= 8.0)
return (parameter) double tonetone * 27.0 / 24_389.0;
const (local variable) const(double) ff = ((parameter) double tonetone + 16.0) / 116.0;
return (local variable) const(double) ff * (local variable) const(double) ff * (local variable) const(double) ff;
}
/// The neutral grey at a given tone — the reference point a tone delta is
/// measured against when the hue does not matter (surfaces, dividers, text).
(struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor sparkles.base.term_color.RgbColor platform_ui_derive_palette.greyAtTone(double tone) pure nothrow @nogc @safeThe neutral grey at a given tone — the reference point a tone delta is
measured against when the hue does not matter (surfaces, dividers, text).
greyAtTone(double (parameter) double tonetone) @safe pure nothrow @nogc
{
const (local variable) const(double) yy = double platform_ui_derive_palette.luminanceFromTone(double tone) pure nothrow @nogc @safeThe inverse: the luminance a given tone sits at.
luminanceFromTone((parameter) double tonetone);
// Invert the sRGB transfer function back to an 8-bit channel.
const (local variable) const(double) ss = (local variable) const(double) yy <= 0.003_130_8 ? (local variable) const(double) yy * 12.92 : 1.055 * double std.math.exponential.pow!(const(double), double)(const(double) x, double y) pure nothrow @nogc @trustedCalculates x`y`.
x y pow(x, y)
div 0 invalid? anything 0.0 1.0 no no x> 1 + + no no x< 1 + +0.0 no no x> 1 - +0.0 no no x< 1 - + no no + > 0.0 + no no + < 0.0 +0.0 no no - odd integer > 0.0 - no no - > 0.0, not odd integer + no no - odd integer < 0.0 -0.0 no no - < 0.0, not odd integer +0.0 no no 1.0 - no yes < 0.0 finite, nonintegral no yes 0.0 odd integer < 0.0 yes no 0.0 < 0.0, not odd integer + yes no 0.0 odd integer > 0.0 0.0 no no 0.0 > 0.0, not odd integer +0.0 no no
Examples
import std.math.operations : isClose;
assert(isClose(pow(2.0, 3.0), 8.0));
assert(isClose(pow(1.5, 10.0), 57.6650390625));
// square root of 9
assert(isClose(pow(9.0, 0.5), 3.0));
// 10th root of 1024
assert(isClose(pow(1024.0, 0.1), 2.0));
assert(isClose(pow(-4.0, 3.0), -64.0));
// reciprocal of 4 ^^ 2
assert(isClose(pow(4.0, -2.0), 0.0625));
// reciprocal of (-2) ^^ 3
assert(isClose(pow(-2.0, -3.0), -0.125));
assert(isClose(pow(-2.5, 3.0), -15.625));
// reciprocal of 2.5 ^^ 3
assert(isClose(pow(2.5, -3.0), 0.064));
// reciprocal of (-2.5) ^^ 3
assert(isClose(pow(-2.5, -3.0), -0.064));
// reciprocal of square root of 4
assert(isClose(pow(4.0, -0.5), 0.5));
// per definition
assert(isClose(pow(0.0, 0.0), 1.0));
import std.math.operations : isClose;
// the result is a complex number
// which cannot be represented as floating point number
import std.math.traits : isNaN;
assert(isNaN(pow(-2.5, -1.5)));
// use the ^^-operator of std.complex instead
import std.complex : complex;
auto c1 = complex(-2.5, 0.0);
auto c2 = complex(-1.5, 0.0);
auto result = c1 ^^ c2;
// exact result apparently depends on `real` precision => increased tolerance
assert(isClose(result.re, -4.64705438e-17, 2e-4));
assert(isClose(result.im, 2.52982e-1, 2e-4));
pow((local variable) const(double) yy, 1.0 / 2.4) - 0.055;
const (local variable) const(ubyte) vv = cast(ubyte) real std.math.rounding.round(real x) pure nothrow @nogc @trustedReturn the value of x rounded to the nearest integer.
If the fractional part of x is exactly 0.5, the return value is
rounded away from zero.
Examples
assert(round(4.5) == 5);
assert(round(5.4) == 5);
assert(round(-4.5) == -5);
assert(round(-5.1) == -5);
round((local variable) const(double) ss * 255.0);
return (struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor((local variable) const(ubyte) vv, (local variable) const(ubyte) vv, (local variable) const(ubyte) vv);
}
/// The same color moved to a target tone, keeping its hue and relative
/// chroma. A cheap stand-in for a full CAM16 round-trip: it scales the channel
/// *ratios* so the hue survives, then corrects the result onto the target tone.
/// Good enough for chrome accents; a real HCT solve would iterate.
(struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor sparkles.base.term_color.RgbColor platform_ui_derive_palette.atTone(in sparkles.base.term_color.RgbColor c, double targetTone) pure nothrow @nogc @safeThe same color moved to a target tone, keeping its hue and relative
chroma. A cheap stand-in for a full CAM16 round-trip: it scales the channel
ratios so the hue survives, then corrects the result onto the target tone.
Good enough for chrome accents; a real HCT solve would iterate.
atTone(in (struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor (parameter) const(sparkles.base.term_color.RgbColor) cc, double (parameter) double targetTonetargetTone) @safe pure nothrow @nogc
{
const (local variable) const(double) currentcurrent = double platform_ui_derive_palette.toneFromLuminance(double y) pure nothrow @nogc @safeCIE L* ("tone" in HCT terms) from a relative luminance, 0..100.
toneFromLuminance(double platform_ui_derive_palette.luminance(in sparkles.base.term_color.RgbColor c) pure nothrow @nogc @safeWCAG relative luminance Y (0..1) of an sRGB color.
luminance((parameter) const(sparkles.base.term_color.RgbColor) cc));
if ((local variable) const(double) currentcurrent <= 0.01)
return sparkles.base.term_color.RgbColor platform_ui_derive_palette.greyAtTone(double tone) pure nothrow @nogc @safeThe neutral grey at a given tone — the reference point a tone delta is
measured against when the hue does not matter (surfaces, dividers, text).
greyAtTone((parameter) double targetTonetargetTone);
// Scale in linear light so the ratio between channels — and thus the hue —
// is preserved, then clamp.
const (local variable) const(double) wantedwanted = double platform_ui_derive_palette.luminanceFromTone(double tone) pure nothrow @nogc @safeThe inverse: the luminance a given tone sits at.
luminanceFromTone((parameter) double targetTonetargetTone);
const (local variable) const(double) havehave = double platform_ui_derive_palette.luminance(in sparkles.base.term_color.RgbColor c) pure nothrow @nogc @safeWCAG relative luminance Y (0..1) of an sRGB color.
luminance((parameter) const(sparkles.base.term_color.RgbColor) cc);
const (local variable) const(double) kk = (local variable) const(double) wantedwanted / (local variable) const(double) havehave;
ubyte ubyte platform_ui_derive_palette.atTone.chan(ubyte v) pure nothrow @nogc @safechan(ubyte (parameter) ubyte vv) @safe pure nothrow @nogc
{
const (local variable) const(double) linlin = double platform_ui_derive_palette.linearize(double channel) pure nothrow @nogc @safeOne sRGB channel (0..1) linearized, per IEC 61966-2-1 — the transfer
function WCAG 2.x's relative-luminance definition uses verbatim.
linearize((parameter) ubyte vv / 255.0) * (local variable) const(double) kk;
const (local variable) const(double) clampedclamped = (local variable) const(double) linlin < 0 ? 0.0 : ((local variable) const(double) linlin > 1 ? 1.0 : (local variable) const(double) linlin);
const (local variable) const(double) ss = (local variable) const(double) clampedclamped <= 0.003_130_8
? (local variable) const(double) clampedclamped * 12.92
: 1.055 * double std.math.exponential.pow!(const(double), double)(const(double) x, double y) pure nothrow @nogc @trustedCalculates x`y`.
x y pow(x, y)
div 0 invalid? anything 0.0 1.0 no no x> 1 + + no no x< 1 + +0.0 no no x> 1 - +0.0 no no x< 1 - + no no + > 0.0 + no no + < 0.0 +0.0 no no - odd integer > 0.0 - no no - > 0.0, not odd integer + no no - odd integer < 0.0 -0.0 no no - < 0.0, not odd integer +0.0 no no 1.0 - no yes < 0.0 finite, nonintegral no yes 0.0 odd integer < 0.0 yes no 0.0 < 0.0, not odd integer + yes no 0.0 odd integer > 0.0 0.0 no no 0.0 > 0.0, not odd integer +0.0 no no
Examples
import std.math.operations : isClose;
assert(isClose(pow(2.0, 3.0), 8.0));
assert(isClose(pow(1.5, 10.0), 57.6650390625));
// square root of 9
assert(isClose(pow(9.0, 0.5), 3.0));
// 10th root of 1024
assert(isClose(pow(1024.0, 0.1), 2.0));
assert(isClose(pow(-4.0, 3.0), -64.0));
// reciprocal of 4 ^^ 2
assert(isClose(pow(4.0, -2.0), 0.0625));
// reciprocal of (-2) ^^ 3
assert(isClose(pow(-2.0, -3.0), -0.125));
assert(isClose(pow(-2.5, 3.0), -15.625));
// reciprocal of 2.5 ^^ 3
assert(isClose(pow(2.5, -3.0), 0.064));
// reciprocal of (-2.5) ^^ 3
assert(isClose(pow(-2.5, -3.0), -0.064));
// reciprocal of square root of 4
assert(isClose(pow(4.0, -0.5), 0.5));
// per definition
assert(isClose(pow(0.0, 0.0), 1.0));
import std.math.operations : isClose;
// the result is a complex number
// which cannot be represented as floating point number
import std.math.traits : isNaN;
assert(isNaN(pow(-2.5, -1.5)));
// use the ^^-operator of std.complex instead
import std.complex : complex;
auto c1 = complex(-2.5, 0.0);
auto c2 = complex(-1.5, 0.0);
auto result = c1 ^^ c2;
// exact result apparently depends on `real` precision => increased tolerance
assert(isClose(result.re, -4.64705438e-17, 2e-4));
assert(isClose(result.im, 2.52982e-1, 2e-4));
pow((local variable) const(double) clampedclamped, 1.0 / 2.4) - 0.055;
return cast(ubyte) real std.math.rounding.round(real x) pure nothrow @nogc @trustedReturn the value of x rounded to the nearest integer.
If the fractional part of x is exactly 0.5, the return value is
rounded away from zero.
Examples
assert(round(4.5) == 5);
assert(round(5.4) == 5);
assert(round(-4.5) == -5);
assert(round(-5.1) == -5);
round((local variable) const(double) ss * 255.0);
}
return (struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor(ubyte platform_ui_derive_palette.atTone.chan(ubyte v) pure nothrow @nogc @safechan((parameter) const(sparkles.base.term_color.RgbColor) cc.(field) ubyte sparkles.base.term_color.RgbColor.rr), ubyte platform_ui_derive_palette.atTone.chan(ubyte v) pure nothrow @nogc @safechan((parameter) const(sparkles.base.term_color.RgbColor) cc.(field) ubyte sparkles.base.term_color.RgbColor.gg), ubyte platform_ui_derive_palette.atTone.chan(ubyte v) pure nothrow @nogc @safechan((parameter) const(sparkles.base.term_color.RgbColor) cc.(field) ubyte sparkles.base.term_color.RgbColor.bb));
}
// ---------------------------------------------------------------------------
// The OS appearance triple
// ---------------------------------------------------------------------------
/// The contrast levels the surveyed platforms expose. GNOME's portal and
/// Windows report a boolean; Android 14 and Apple report a third, middle step.
/// See [comparison.md](../../comparison.md) § "Dimension 3".
enum (enum) platform_ui_derive_palette.ContrastLevelThe contrast levels the surveyed platforms expose. GNOME's portal and
Windows report a boolean; Android 14 and Apple report a third, middle step.
See comparison.md § "Dimension 3".
ContrastLevel : ubyte
{
(enum value) platform_ui_derive_palette.ContrastLevel.standard = cast(ubyte)0uno preference
standard, /// no preference
(enum value) platform_ui_derive_palette.ContrastLevel.medium = 1Android 14's middle step; Apple's accessibilityContrast.high
medium, /// Android 14's middle step; Apple's `accessibilityContrast.high`
(enum value) platform_ui_derive_palette.ContrastLevel.high = 2GNOME contrast: 1, Windows HCF_HIGHCONTRASTON
high, /// GNOME `contrast: 1`, Windows `HCF_HIGHCONTRASTON`
}
/// Exactly what every surveyed platform can be reduced to.
struct (struct) platform_ui_derive_palette.SystemAppearanceExactly what every surveyed platform can be reduced to.
SystemAppearance
{
(enum) sparkles.ui.style.ColorSchemeLight or dark color scheme — only the popup surface and docs text differ (the
brand colors are shared), matching views/twoslash.css's dark @media block.
ColorScheme (field) sparkles.ui.style.ColorScheme platform_ui_derive_palette.SystemAppearance.schemescheme;
(struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor (field) sparkles.base.term_color.RgbColor platform_ui_derive_palette.SystemAppearance.accentaccent;
(enum) platform_ui_derive_palette.ContrastLevelThe contrast levels the surveyed platforms expose. GNOME's portal and
Windows report a boolean; Android 14 and Apple report a third, middle step.
See comparison.md § "Dimension 3".
ContrastLevel (field) platform_ui_derive_palette.ContrastLevel platform_ui_derive_palette.SystemAppearance.contrastcontrast;
}
/// The tone pair (text, surface) a scheme and contrast level call for. The
/// deltas come from the Material rule verified in step 2: 50 at standard, wider
/// as contrast rises. A dark scheme mirrors the axis rather than changing it.
void void platform_ui_derive_palette.tonesFor(in platform_ui_derive_palette.SystemAppearance a, out double textTone, out double surfaceTone) pure nothrow @nogc @safeThe tone pair (text, surface) a scheme and contrast level call for. The
deltas come from the Material rule verified in step 2: 50 at standard, wider
as contrast rises. A dark scheme mirrors the axis rather than changing it.
tonesFor(in (struct) platform_ui_derive_palette.SystemAppearanceExactly what every surveyed platform can be reduced to.
SystemAppearance (parameter) const(platform_ui_derive_palette.SystemAppearance) aa, out double (parameter) double textTonetextTone, out double (parameter) double surfaceTonesurfaceTone)
@safe pure nothrow @nogc
{
// Standard = ΔT 50 (≥ 4.5:1); medium = 60; high = 75 (near the extremes).
const (local variable) const(double) deltadelta = (parameter) const(platform_ui_derive_palette.SystemAppearance) aa.(field) platform_ui_derive_palette.ContrastLevel platform_ui_derive_palette.SystemAppearance.contrastcontrast == (enum) platform_ui_derive_palette.ContrastLevelThe contrast levels the surveyed platforms expose. GNOME's portal and
Windows report a boolean; Android 14 and Apple report a third, middle step.
See comparison.md § "Dimension 3".
ContrastLevel.(enum value) platform_ui_derive_palette.ContrastLevel.high = 2GNOME contrast: 1, Windows HCF_HIGHCONTRASTON
high
? 75.0
: ((parameter) const(platform_ui_derive_palette.SystemAppearance) aa.(field) platform_ui_derive_palette.ContrastLevel platform_ui_derive_palette.SystemAppearance.contrastcontrast == (enum) platform_ui_derive_palette.ContrastLevelThe contrast levels the surveyed platforms expose. GNOME's portal and
Windows report a boolean; Android 14 and Apple report a third, middle step.
See comparison.md § "Dimension 3".
ContrastLevel.(enum value) platform_ui_derive_palette.ContrastLevel.medium = 1Android 14's middle step; Apple's accessibilityContrast.high
medium ? 60.0 : 50.0);
if ((parameter) const(platform_ui_derive_palette.SystemAppearance) aa.(field) sparkles.ui.style.ColorScheme platform_ui_derive_palette.SystemAppearance.schemescheme == (enum) sparkles.ui.style.ColorSchemeLight or dark color scheme — only the popup surface and docs text differ (the
brand colors are shared), matching views/twoslash.css's dark @media block.
ColorScheme.(enum value) sparkles.ui.style.ColorScheme.light = cast(ubyte)0uthe default :root
light)
{
(parameter) double surfaceTonesurfaceTone = 98.0;
(parameter) double textTonetextTone = (parameter) double surfaceTonesurfaceTone - (local variable) const(double) deltadelta;
}
else
{
(parameter) double surfaceTonesurfaceTone = 12.0;
(parameter) double textTonetextTone = (parameter) double surfaceTonesurfaceTone + (local variable) const(double) deltadelta;
}
}
/// Derive a whole `sparkles:ui` theme from the OS triple.
///
/// Only the slots whose appearance genuinely follows the system are touched:
/// the page fore/background, the chrome band, the focused-pane accent and the
/// selection tint. The semantic status slots (`error`/`warn`/`info`) keep the
/// theme's authored hues — a red that follows the desktop accent stops meaning
/// "error", which is the trap [comparison.md](../../comparison.md)
/// § "What follows the system, and what must not" describes.
(struct) sparkles.ui.theme.ThemeA theme as plain data: all four channels in one value.
Every field defaults, so a theme that only sets syntax rules is valid and
effectivePalette derives the rest from the page background.
Theme sparkles.ui.theme.Theme platform_ui_derive_palette.deriveTheme(in platform_ui_derive_palette.SystemAppearance a, string name) pure nothrow @safeDerive a whole sparkles:ui theme from the OS triple.
Only the slots whose appearance genuinely follows the system are touched:
the page fore/background, the chrome band, the focused-pane accent and the
selection tint. The semantic status slots (error/warn/info) keep the
theme's authored hues — a red that follows the desktop accent stops meaning
"error", which is the trap comparison.md
§ "What follows the system, and what must not" describes.
deriveTheme(in (struct) platform_ui_derive_palette.SystemAppearanceExactly what every surveyed platform can be reduced to.
SystemAppearance (parameter) const(platform_ui_derive_palette.SystemAppearance) aa, (alias) object.string = stringstring (parameter) string namename) @safe pure nothrow
{
double (local variable) double textTonetextTone, (local variable) double surfaceTonesurfaceTone;
void platform_ui_derive_palette.tonesFor(in platform_ui_derive_palette.SystemAppearance a, out double textTone, out double surfaceTone) pure nothrow @nogc @safeThe tone pair (text, surface) a scheme and contrast level call for. The
deltas come from the Material rule verified in step 2: 50 at standard, wider
as contrast rises. A dark scheme mirrors the axis rather than changing it.
tonesFor((parameter) const(platform_ui_derive_palette.SystemAppearance) aa, (local variable) double textTonetextTone, (local variable) double surfaceTonesurfaceTone);
const (local variable) const(sparkles.base.term_color.RgbColor) fgfg = sparkles.base.term_color.RgbColor platform_ui_derive_palette.greyAtTone(double tone) pure nothrow @nogc @safeThe neutral grey at a given tone — the reference point a tone delta is
measured against when the hue does not matter (surfaces, dividers, text).
greyAtTone((local variable) double textTonetextTone);
const (local variable) const(sparkles.base.term_color.RgbColor) bgbg = sparkles.base.term_color.RgbColor platform_ui_derive_palette.greyAtTone(double tone) pure nothrow @nogc @safeThe neutral grey at a given tone — the reference point a tone delta is
measured against when the hue does not matter (surfaces, dividers, text).
greyAtTone((local variable) double surfaceTonesurfaceTone);
// The accent has to land on the *opposite* side of the surface to stay
// legible: a dark scheme wants a light accent and vice versa.
const (local variable) const(double) accentToneaccentTone = (parameter) const(platform_ui_derive_palette.SystemAppearance) aa.(field) sparkles.ui.style.ColorScheme platform_ui_derive_palette.SystemAppearance.schemescheme == (enum) sparkles.ui.style.ColorSchemeLight or dark color scheme — only the popup surface and docs text differ (the
brand colors are shared), matching views/twoslash.css's dark @media block.
ColorScheme.(enum value) sparkles.ui.style.ColorScheme.light = cast(ubyte)0uthe default :root
light ? 40.0 : 80.0;
const (local variable) const(sparkles.base.term_color.RgbColor) accentaccent = sparkles.base.term_color.RgbColor platform_ui_derive_palette.atTone(in sparkles.base.term_color.RgbColor c, double targetTone) pure nothrow @nogc @safeThe same color moved to a target tone, keeping its hue and relative
chroma. A cheap stand-in for a full CAM16 round-trip: it scales the channel
ratios so the hue survives, then corrects the result onto the target tone.
Good enough for chrome accents; a real HCT solve would iterate.
atTone((parameter) const(platform_ui_derive_palette.SystemAppearance) aa.(field) sparkles.base.term_color.RgbColor platform_ui_derive_palette.SystemAppearance.accentaccent, (local variable) const(double) accentToneaccentTone);
auto (local variable) sparkles.ui.theme.Theme themetheme = (struct) sparkles.ui.theme.ThemeA theme as plain data: all four channels in one value.
Every field defaults, so a theme that only sets syntax rules is valid and
effectivePalette derives the rest from the page background.
Theme(
name: (parameter) string namename,
defaultFg: (struct) sparkles.base.term_color.ColorA terminal color: one of four cases.
unset — not specified (the default, ``Color.init);
default_ — use the terminal's default fore-/background;
palette — a terminal-palette index (0–255) in index;
rgb — a 24-bit value in rgb.
Examples
assert(!Color.init.isSet);
assert(Color.defaultColor.kind == Color.Kind.default_);
const c = Color.fromRgb(0x1e, 0x1e, 0x2e);
assert(c.kind == Color.Kind.rgb && c.rgb == RgbColor(0x1e, 0x1e, 0x2e));
// A hex-string literal is a `ubyte[3]` (2.108) — same as three bytes.
assert(Color.fromRgb(x"1e1e2e") == c);
const p = Color.fromPalette(4);
assert(p.kind == Color.Kind.palette && p.index == 4);
Color.sparkles.base.term_color.Color sparkles.base.term_color.Color.fromRgb(sparkles.base.term_color.RgbColor c) pure nothrow @nogc @safeConstructs an RGB color from three bytes, or a single ubyte[3] — which a
x"…" hex-string literal converts to, giving a lightweight hex spelling.
auto mauve = Color.fromRgb(0xcb, 0xa6, 0xf7);
auto same = Color.fromRgb(x"cba6f7");
assert(mauve == same);
fromRgb((local variable) const(sparkles.base.term_color.RgbColor) fgfg),
defaultBg: (struct) sparkles.base.term_color.ColorA terminal color: one of four cases.
unset — not specified (the default, ``Color.init);
default_ — use the terminal's default fore-/background;
palette — a terminal-palette index (0–255) in index;
rgb — a 24-bit value in rgb.
Examples
assert(!Color.init.isSet);
assert(Color.defaultColor.kind == Color.Kind.default_);
const c = Color.fromRgb(0x1e, 0x1e, 0x2e);
assert(c.kind == Color.Kind.rgb && c.rgb == RgbColor(0x1e, 0x1e, 0x2e));
// A hex-string literal is a `ubyte[3]` (2.108) — same as three bytes.
assert(Color.fromRgb(x"1e1e2e") == c);
const p = Color.fromPalette(4);
assert(p.kind == Color.Kind.palette && p.index == 4);
Color.sparkles.base.term_color.Color sparkles.base.term_color.Color.fromRgb(sparkles.base.term_color.RgbColor c) pure nothrow @nogc @safeConstructs an RGB color from three bytes, or a single ubyte[3] — which a
x"…" hex-string literal converts to, giving a lightweight hex spelling.
auto mauve = Color.fromRgb(0xcb, 0xa6, 0xf7);
auto same = Color.fromRgb(x"cba6f7");
assert(mauve == same);
fromRgb((local variable) const(sparkles.base.term_color.RgbColor) bgbg),
);
(struct) sparkles.ui.style.PaletteMaps every Slot to a fore/background color and alpha, plus scalar chrome
constants shared by the backends. fg/bg are Colors:
an unset fg means "inherit the page foreground", an unset bg means
"no background". Alpha is stored separately because Color carries none.
Palette (local variable) sparkles.ui.style.Palette pp = (local variable) sparkles.ui.theme.Theme themetheme.sparkles.ui.style.Palette sparkles.ui.theme.Theme.effectivePalette() const pure nothrow @nogc @safeThe palette to resolve slots against: the explicitly configured one, or —
when a theme carries only syntax rules — one derived from defaultBg, so
light themes get light surfaces without every theme restating the whole slot
table.
effectivePalette();
(local variable) sparkles.ui.style.Palette pp.(field) sparkles.base.term_color.Color[35] sparkles.ui.style.Palette.fgPer-slot foreground; Color.init (unset) ⇒ inherit page fg.
fg[(enum) sparkles.ui.style.SlotA semantic style role. Widgets and display-list ops carry a Slot, and a
Palette turns it into a concrete Visual. Roles are intentionally
generic (an app palette can reuse them) even though the seed values come from
twoslash.
Slot.(enum value) sparkles.ui.style.Slot.chromeAccent = 19emphasized chrome text (title, active segment, key hints)
chromeAccent] = (struct) sparkles.base.term_color.ColorA terminal color: one of four cases.
unset — not specified (the default, ``Color.init);
default_ — use the terminal's default fore-/background;
palette — a terminal-palette index (0–255) in index;
rgb — a 24-bit value in rgb.
Examples
assert(!Color.init.isSet);
assert(Color.defaultColor.kind == Color.Kind.default_);
const c = Color.fromRgb(0x1e, 0x1e, 0x2e);
assert(c.kind == Color.Kind.rgb && c.rgb == RgbColor(0x1e, 0x1e, 0x2e));
// A hex-string literal is a `ubyte[3]` (2.108) — same as three bytes.
assert(Color.fromRgb(x"1e1e2e") == c);
const p = Color.fromPalette(4);
assert(p.kind == Color.Kind.palette && p.index == 4);
Color.sparkles.base.term_color.Color sparkles.base.term_color.Color.fromRgb(sparkles.base.term_color.RgbColor c) pure nothrow @nogc @safeConstructs an RGB color from three bytes, or a single ubyte[3] — which a
x"…" hex-string literal converts to, giving a lightweight hex spelling.
auto mauve = Color.fromRgb(0xcb, 0xa6, 0xf7);
auto same = Color.fromRgb(x"cba6f7");
assert(mauve == same);
fromRgb((local variable) const(sparkles.base.term_color.RgbColor) accentaccent);
(local variable) sparkles.ui.style.Palette pp.(field) sparkles.base.term_color.Color[35] sparkles.ui.style.Palette.fgPer-slot foreground; Color.init (unset) ⇒ inherit page fg.
fg[(enum) sparkles.ui.style.SlotA semantic style role. Widgets and display-list ops carry a Slot, and a
Palette turns it into a concrete Visual. Roles are intentionally
generic (an app palette can reuse them) even though the seed values come from
twoslash.
Slot.(enum value) sparkles.ui.style.Slot.caret = 15query caret / cursor marker
caret] = (struct) sparkles.base.term_color.ColorA terminal color: one of four cases.
unset — not specified (the default, ``Color.init);
default_ — use the terminal's default fore-/background;
palette — a terminal-palette index (0–255) in index;
rgb — a 24-bit value in rgb.
Examples
assert(!Color.init.isSet);
assert(Color.defaultColor.kind == Color.Kind.default_);
const c = Color.fromRgb(0x1e, 0x1e, 0x2e);
assert(c.kind == Color.Kind.rgb && c.rgb == RgbColor(0x1e, 0x1e, 0x2e));
// A hex-string literal is a `ubyte[3]` (2.108) — same as three bytes.
assert(Color.fromRgb(x"1e1e2e") == c);
const p = Color.fromPalette(4);
assert(p.kind == Color.Kind.palette && p.index == 4);
Color.sparkles.base.term_color.Color sparkles.base.term_color.Color.fromRgb(sparkles.base.term_color.RgbColor c) pure nothrow @nogc @safeConstructs an RGB color from three bytes, or a single ubyte[3] — which a
x"…" hex-string literal converts to, giving a lightweight hex spelling.
auto mauve = Color.fromRgb(0xcb, 0xa6, 0xf7);
auto same = Color.fromRgb(x"cba6f7");
assert(mauve == same);
fromRgb((local variable) const(sparkles.base.term_color.RgbColor) accentaccent);
(local variable) sparkles.ui.style.Palette pp.(field) sparkles.base.term_color.Color[35] sparkles.ui.style.Palette.bgPer-slot background; Color.init (unset) ⇒ no background.
bg[(enum) sparkles.ui.style.SlotA semantic style role. Widgets and display-list ops carry a Slot, and a
Palette turns it into a concrete Visual. Roles are intentionally
generic (an app palette can reuse them) even though the seed values come from
twoslash.
Slot.(enum value) sparkles.ui.style.Slot.chromeFocused = 25the focused pane's header band (accented background)
chromeFocused] = (struct) sparkles.base.term_color.ColorA terminal color: one of four cases.
unset — not specified (the default, ``Color.init);
default_ — use the terminal's default fore-/background;
palette — a terminal-palette index (0–255) in index;
rgb — a 24-bit value in rgb.
Examples
assert(!Color.init.isSet);
assert(Color.defaultColor.kind == Color.Kind.default_);
const c = Color.fromRgb(0x1e, 0x1e, 0x2e);
assert(c.kind == Color.Kind.rgb && c.rgb == RgbColor(0x1e, 0x1e, 0x2e));
// A hex-string literal is a `ubyte[3]` (2.108) — same as three bytes.
assert(Color.fromRgb(x"1e1e2e") == c);
const p = Color.fromPalette(4);
assert(p.kind == Color.Kind.palette && p.index == 4);
Color.sparkles.base.term_color.Color sparkles.base.term_color.Color.fromRgb(sparkles.base.term_color.RgbColor c) pure nothrow @nogc @safeConstructs an RGB color from three bytes, or a single ubyte[3] — which a
x"…" hex-string literal converts to, giving a lightweight hex spelling.
auto mauve = Color.fromRgb(0xcb, 0xa6, 0xf7);
auto same = Color.fromRgb(x"cba6f7");
assert(mauve == same);
fromRgb(sparkles.base.term_color.RgbColor platform_ui_derive_palette.atTone(in sparkles.base.term_color.RgbColor c, double targetTone) pure nothrow @nogc @safeThe same color moved to a target tone, keeping its hue and relative
chroma. A cheap stand-in for a full CAM16 round-trip: it scales the channel
ratios so the hue survives, then corrects the result onto the target tone.
Good enough for chrome accents; a real HCT solve would iterate.
atTone((parameter) const(platform_ui_derive_palette.SystemAppearance) aa.(field) sparkles.base.term_color.RgbColor platform_ui_derive_palette.SystemAppearance.accentaccent,
(parameter) const(platform_ui_derive_palette.SystemAppearance) aa.(field) sparkles.ui.style.ColorScheme platform_ui_derive_palette.SystemAppearance.schemescheme == (enum) sparkles.ui.style.ColorSchemeLight or dark color scheme — only the popup surface and docs text differ (the
brand colors are shared), matching views/twoslash.css's dark @media block.
ColorScheme.(enum value) sparkles.ui.style.ColorScheme.light = cast(ubyte)0uthe default :root
light ? 90.0 : 30.0));
(local variable) sparkles.ui.style.Palette pp.(field) sparkles.base.term_color.Color[35] sparkles.ui.style.Palette.bgPer-slot background; Color.init (unset) ⇒ no background.
bg[(enum) sparkles.ui.style.SlotA semantic style role. Widgets and display-list ops carry a Slot, and a
Palette turns it into a concrete Visual. Roles are intentionally
generic (an app palette can reuse them) even though the seed values come from
twoslash.
Slot.(enum value) sparkles.ui.style.Slot.selection = 24selected-content tint (background only)
selection] = (struct) sparkles.base.term_color.ColorA terminal color: one of four cases.
unset — not specified (the default, ``Color.init);
default_ — use the terminal's default fore-/background;
palette — a terminal-palette index (0–255) in index;
rgb — a 24-bit value in rgb.
Examples
assert(!Color.init.isSet);
assert(Color.defaultColor.kind == Color.Kind.default_);
const c = Color.fromRgb(0x1e, 0x1e, 0x2e);
assert(c.kind == Color.Kind.rgb && c.rgb == RgbColor(0x1e, 0x1e, 0x2e));
// A hex-string literal is a `ubyte[3]` (2.108) — same as three bytes.
assert(Color.fromRgb(x"1e1e2e") == c);
const p = Color.fromPalette(4);
assert(p.kind == Color.Kind.palette && p.index == 4);
Color.sparkles.base.term_color.Color sparkles.base.term_color.Color.fromRgb(sparkles.base.term_color.RgbColor c) pure nothrow @nogc @safeConstructs an RGB color from three bytes, or a single ubyte[3] — which a
x"…" hex-string literal converts to, giving a lightweight hex spelling.
auto mauve = Color.fromRgb(0xcb, 0xa6, 0xf7);
auto same = Color.fromRgb(x"cba6f7");
assert(mauve == same);
fromRgb(sparkles.base.term_color.RgbColor platform_ui_derive_palette.atTone(in sparkles.base.term_color.RgbColor c, double targetTone) pure nothrow @nogc @safeThe same color moved to a target tone, keeping its hue and relative
chroma. A cheap stand-in for a full CAM16 round-trip: it scales the channel
ratios so the hue survives, then corrects the result onto the target tone.
Good enough for chrome accents; a real HCT solve would iterate.
atTone((parameter) const(platform_ui_derive_palette.SystemAppearance) aa.(field) sparkles.base.term_color.RgbColor platform_ui_derive_palette.SystemAppearance.accentaccent,
(parameter) const(platform_ui_derive_palette.SystemAppearance) aa.(field) sparkles.ui.style.ColorScheme platform_ui_derive_palette.SystemAppearance.schemescheme == (enum) sparkles.ui.style.ColorSchemeLight or dark color scheme — only the popup surface and docs text differ (the
brand colors are shared), matching views/twoslash.css's dark @media block.
ColorScheme.(enum value) sparkles.ui.style.ColorScheme.light = cast(ubyte)0uthe default :root
light ? 85.0 : 35.0));
// A surface one step off the page, the way every platform separates a
// panel from the document beneath it.
(local variable) sparkles.ui.style.Palette pp.(field) sparkles.base.term_color.Color[35] sparkles.ui.style.Palette.bgPer-slot background; Color.init (unset) ⇒ no background.
bg[(enum) sparkles.ui.style.SlotA semantic style role. Widgets and display-list ops carry a Slot, and a
Palette turns it into a concrete Visual. Roles are intentionally
generic (an app palette can reuse them) even though the seed values come from
twoslash.
Slot.(enum value) sparkles.ui.style.Slot.surface = 9popup / panel background (opaque)
surface] = (struct) sparkles.base.term_color.ColorA terminal color: one of four cases.
unset — not specified (the default, ``Color.init);
default_ — use the terminal's default fore-/background;
palette — a terminal-palette index (0–255) in index;
rgb — a 24-bit value in rgb.
Examples
assert(!Color.init.isSet);
assert(Color.defaultColor.kind == Color.Kind.default_);
const c = Color.fromRgb(0x1e, 0x1e, 0x2e);
assert(c.kind == Color.Kind.rgb && c.rgb == RgbColor(0x1e, 0x1e, 0x2e));
// A hex-string literal is a `ubyte[3]` (2.108) — same as three bytes.
assert(Color.fromRgb(x"1e1e2e") == c);
const p = Color.fromPalette(4);
assert(p.kind == Color.Kind.palette && p.index == 4);
Color.sparkles.base.term_color.Color sparkles.base.term_color.Color.fromRgb(sparkles.base.term_color.RgbColor c) pure nothrow @nogc @safeConstructs an RGB color from three bytes, or a single ubyte[3] — which a
x"…" hex-string literal converts to, giving a lightweight hex spelling.
auto mauve = Color.fromRgb(0xcb, 0xa6, 0xf7);
auto same = Color.fromRgb(x"cba6f7");
assert(mauve == same);
fromRgb(sparkles.base.term_color.RgbColor platform_ui_derive_palette.greyAtTone(double tone) pure nothrow @nogc @safeThe neutral grey at a given tone — the reference point a tone delta is
measured against when the hue does not matter (surfaces, dividers, text).
greyAtTone(
(parameter) const(platform_ui_derive_palette.SystemAppearance) aa.(field) sparkles.ui.style.ColorScheme platform_ui_derive_palette.SystemAppearance.schemescheme == (enum) sparkles.ui.style.ColorSchemeLight or dark color scheme — only the popup surface and docs text differ (the
brand colors are shared), matching views/twoslash.css's dark @media block.
ColorScheme.(enum value) sparkles.ui.style.ColorScheme.light = cast(ubyte)0uthe default :root
light ? (local variable) double surfaceTonesurfaceTone - 4.0 : (local variable) double surfaceTonesurfaceTone + 6.0));
(local variable) sparkles.ui.theme.Theme themetheme.(field) sparkles.ui.style.Palette sparkles.ui.theme.Theme.paletteSlot + metric channels. Left at .init a theme derives them from
defaultBg — see effectivePalette.
palette = (local variable) sparkles.ui.style.Palette pp;
(local variable) sparkles.ui.theme.Theme themetheme.(field) bool sparkles.ui.theme.Theme.hasPaletteditto — true once palette was set explicitly
hasPalette = true;
return (local variable) sparkles.ui.theme.Theme themetheme;
}
// ---------------------------------------------------------------------------
(alias) object.string = stringstring string platform_ui_derive_palette.hex(in sparkles.base.term_color.RgbColor c) pure @safehex(in (struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor (parameter) const(sparkles.base.term_color.RgbColor) cc) @safe pure
{
import (package) stdstd.(module) std.formatThis package provides string formatting functionality using
printf style format strings.
Submodule Function Name Description package format Converts its arguments according to a format string into a string.
| package |
sformat |
Converts its arguments according to a format string into a buffer. |
| package |
FormatException |
Signals a problem while formatting. |
| write |
formattedWrite |
Converts its arguments according to a format string and writes
the result to an output range. |
| write |
formatValue |
Formats a value of any type according to a format specifier and
writes the result to an output range. |
| read |
formattedRead |
Reads an input range according to a format string and stores the read
values into its arguments. |
| read |
unformatValue |
Reads a value from the given input range and converts it according to
a format specifier. |
| spec |
FormatSpec |
A general handler for format strings. |
| spec |
singleSpec |
Helper function that returns a FormatSpec for a single format specifier. |
Limitation
This package does not support localization, but
adheres to the rounding mode of the floating point unit, if
available.
Format Strings
The functions contained in this package use format strings. A
format string describes the layout of another string for reading or
writing purposes. A format string is composed of normal text
interspersed with format specifiers. A format specifier starts
with a percentage sign '%', optionally followed by one or more
parameters and ends with a format indicator. A format
indicator may be a simple format character or a compound
indicator.
Format strings are composed according to the following grammar:
FormatString:
FormatStringItem FormatString
FormatStringItem:
Character
FormatSpecifier
FormatSpecifier:
'%' Parameters FormatIndicator
FormatIndicator:
FormatCharacter
CompoundIndicator
FormatCharacter:
see remark below
CompoundIndicator:
'(' FormatString '%)'
'(' FormatString '%|' Delimiter '%)'
Delimiter
empty
Character Delimiter
Parameters:
Position Flags Width Precision Separator
Position:
empty
Integer '$'**
*Integer* **':'** *Integer* **'$'
Integer ':' '$'**
*Flags*:
*empty*
*Flag* *Flags*
*Flag*:
**'-'**|**'+'**|**' '**|**'0'**|**'#'**|**'='**
*Width*:
*OptionalPositionalInteger*
*Precision*:
*empty*
**'.'** *OptionalPositionalInteger*
*Separator*:
*empty*
**','** *OptionalInteger*
**','** *OptionalInteger* **'?'**
*OptionalInteger*:
*empty*
*Integer*
**'*'**
*OptionalPositionalInteger*:
*OptionalInteger*
**'*'** *Integer* **'$'
Character
'%%'
AnyCharacterExceptPercent
Integer:
NonZeroDigit Digits
Digits:
empty
Digit Digits
NonZeroDigit:
'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'
Digit:
'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'
Note
FormatCharacter is unspecified. It can be any character
that has no other purpose in this grammar, but it is
recommended to assign (lower- and uppercase) letters.
Note
The Parameters of a CompoundIndicator are currently
limited to a '-' flag.
Format Indicator
The format indicator can either be a single character or an
expression surrounded by '%(' and '%)'. It specifies the
basic manner in which a value will be formatted and is the minimum
requirement to format a value.
The following characters can be used as format characters:
FormatCharacter Semantics 's' To be formatted in a human readable format. Can be used with all types. 'c' To be formatted as a character. 'd' To be formatted as a signed decimal integer. 'u' To be formatted as a decimal image of the underlying bit representation. 'b' To be formatted as a binary image of the underlying bit representation. 'o' To be formatted as an octal image of the underlying bit representation. 'x' / 'X' To be formatted as a hexadecimal image of the underlying bit representation. 'e' / 'E' To be formatted as a real number in decimal scientific notation. 'f' / 'F' To be formatted as a real number in decimal natural notation. 'g' / 'G' To be formatted as a real number in decimal short notation. Depending on the number, a scientific notation or a natural notation is used. 'a' / 'A' To be formatted as a real number in hexadecimal scientific notation. 'r' To be formatted as raw bytes. The output may not be printable and depends on endianness.
The compound indicator can be used to describe compound types
like arrays or structs in more detail. A compound type is enclosed
within '%(' and '%)'. The enclosed sub-format string is
applied to individual elements. The trailing portion of the
sub-format string following the specifier for the element is
interpreted as the delimiter, and is therefore omitted following the
last element. The '%|' specifier may be used to explicitly
indicate the start of the delimiter, so that the preceding portion of
the string will be included following the last element.
The format string inside of the compound indicator should
contain exactly one format specifier (two in case of associative
arrays), which specifies the formatting mode of the elements of the
compound type. This format specifier can be a compound
indicator itself.
Note
Inside a compound indicator, strings and characters are
escaped automatically. To avoid this behavior, use "%-("
instead of "%(".
Flags
There are several flags that affect the outcome of the formatting.
Flag Semantics '-' When the formatted result is shorter than the value given by the width parameter, the output is left justified. Without the '-' flag, the output remains right justified.
There are two exceptions where the '-' flag has a
different meaning: (1) with 'r' it denotes to use little
endian and (2) in case of a compound indicator it means that
no special handling of the members is applied. |
| '=' |
When the formatted result is shorter than the value
given by the width parameter, the output is centered.
If the central position is not possible it is moved slightly
to the right. In this case, if '-' flag is present in
addition to the '=' flag, it is moved slightly to the left. |
| '+' / *' '* |
Applies to numerical values. By default, positive numbers are not
formatted to include the + sign. With one of these two flags present,
positive numbers are preceded by a plus sign or a space.
When both flags are present, a plus sign is used.
In case of 'r', a big endian format is used. |
| '0' |
Is applied to numerical values that are printed right justified.
If the zero flag is present, the space left to the number is
filled with zeros instead of spaces. |
| '#' |
Denotes that an alternative output must be used. This depends on the type
to be formatted and the format character used. See the
sections below for more information. |
Width, Precision and Separator
The width parameter specifies the minimum width of the result.
The meaning of precision depends on the format indicator. For
integers it denotes the minimum number of digits printed, for
real numbers it denotes the number of fractional digits and for
strings and compound types it denotes the maximum number of elements
that are included in the output.
A separator is used for formatting numbers. If it is specified,
the output is divided into chunks of three digits, separated by a ','. The number of digits in a chunk can be given explicitly by
providing a number or a ''* after the ','.
In all three cases the number of digits can be replaced by a ''*. In this scenario, the next argument is used as the number of
digits. If the argument is a negative number, the precision and
separator parameters are considered unspecified. For width,
the absolute value is used and the '-' flag is set.
The separator can also be followed by a '?'. In that case,
an additional argument is used to specify the symbol that should be
used to separate the chunks.
Position
By default, the arguments are processed in the provided order. With
the position parameter it is possible to address arguments
directly. It is also possible to denote a series of arguments with
two numbers separated by ':', that are all processed in the same
way. The second number can be omitted. In that case the series ends
with the last argument.
It's also possible to use positional arguments for width, precision and separator by adding a number and a '$' after the ''*.
Types
This section describes the result of combining types with format
characters. It is organized in 2 subsections: a list of general
information regarding the formatting of types in the presence of
format characters and a table that contains details for every
available combination of type and format character.
When formatting types, the following rules apply:
If the format character is upper case, the resulting string will
be formatted using upper case letters.
The default precision for floating point numbers is 6 digits.
Rounding of floating point numbers adheres to the rounding mode
of the floating point unit, if available.
The floating point values NaN and Infinity are formatted as
nan and inf, possibly preceded by '+' or '-' sign.
Formatting reals is only supported for 64 bit reals and 80 bit reals.
All other reals are cast to double before they are formatted. This will
cause the result to be inf for very large numbers.
Characters and strings formatted with the 's' format character
inside of compound types are surrounded by single and double quotes
and unprintable characters are escaped. To avoid this, a '-'
flag can be specified for the compound specifier
(e.g. "%-(%s%)" instead of "%(%s%)" ).
Structs, unions, classes and interfaces are formatted by calling a
toString method if available.
See module std.format.write for more
details.
Only part of these combinations can be used for reading. See
module std.format.read for more
detailed information.
This table contains descriptions for every possible combination of
type and format character:
<th scope="col" width="20%">Type</th> <th scope="col" width="20%">Format Character</th> Formatted as... <td rowspan="1">null</td> 's' null
|<td rowspan="3">bool</td> 's' |
false or true |
| 'b', 'd', 'o', 'u', 'x', 'X' |
As the integrals 0 or 1 with the same format character.
Please note, that 'o' and 'x' with '#' flag
might produce unexpected results due to special handling of
the value 0. |
| 'r' |
\0 or \1 |
|<td rowspan="4">Integral</td> 's', 'd' |
A signed decimal number. The '#' flag is ignored. |
| 'b', 'o', 'u', 'x', 'X' |
An unsigned binary, decimal, octal or hexadecimal number.
In case of 'o' and 'x', the '#' flag
denotes that the number must be preceded by 0 and 0x, with
the exception of the value 0, where this does not apply. For
'b' and 'u' the '#' flag has no effect. |
| 'e', 'E', 'f', 'F', 'g', 'G', 'a', 'A' |
As a floating point value with the same specifier.
Default precision is large enough to add all digits
of the integral value.
In case of 'a' and 'A', the integral digit can be
any hexadecimal digit.
|
| 'r' |
Characters taken directly from the binary representation. |
|<td rowspan="5">Floating Point</td> 'e', 'E' |
Scientific notation: Exactly one integral digit followed by a dot
and fractional digits, followed by the exponent.
The exponent is formatted as 'e' followed by
a '+' or '-' sign, followed by at least
two digits.
When there are no fractional digits and the '#' flag
is not present, the dot is omitted. |
| 'f', 'F' |
Natural notation: Integral digits followed by a dot and
fractional digits.
When there are no fractional digits and the '#' flag
is not present, the dot is omitted.
Please note: the difference between 'f' and 'F'
is only visible for NaN and Infinity. |
| 's', 'g', 'G' |
Short notation: If the absolute value is larger than 10 ^^ precision
or smaller than 0.0001, the scientific notation is used.
If not, the natural notation is applied.
In both cases precision denotes the count of all digits, including
the integral digits. Trailing zeros (including a trailing dot) are removed.
If '#' flag is present, trailing zeros are not removed. |
| 'a', 'A' |
Hexadecimal scientific notation: 0x followed by 1
(or 0 in case of value zero or denormalized number)
followed by a dot, fractional digits in hexadecimal
notation and an exponent. The exponent is build by p,
followed by a sign and the exponent in decimal notation.
When there are no fractional digits and the '#' flag
is not present, the dot is omitted. |
| 'r' |
Characters taken directly from the binary representation. |
|<td rowspan="3">Character</td> 's', 'c' |
As the character.
Inside of a compound indicator 's' is treated differently: The
character is surrounded by single quotes and non printable
characters are escaped. This can be avoided by preceding
the compound indicator with a '-' flag
(e.g. "%-(%s%)"). |
| 'b', 'd', 'o', 'u', 'x', 'X' |
As the integral that represents the character. |
| 'r' |
Characters taken directly from the binary representation. |
|<td rowspan="3">String</td> 's' |
The sequence of characters that form the string.
Inside of a compound indicator the string is surrounded by double quotes
and non printable characters are escaped. This can be avoided
by preceding the compound indicator with a '-' flag
(e.g. "%-(%s%)"). |
| 'r' |
The sequence of characters, each formatted with 'r'. |
| compound |
As an array of characters. |
|<td rowspan="3">Array</td> 's' |
When the elements are characters, the array is formatted as
a string. In all other cases the array is surrounded by square brackets
and the elements are separated by a comma and a space. If the elements
are strings, they are surrounded by double quotes and non
printable characters are escaped. |
| 'r' |
The sequence of the elements, each formatted with 'r'. |
| compound |
The sequence of the elements, each formatted according to the specifications
given inside of the compound specifier. |
|<td rowspan="2">Associative Array</td> 's' |
As a sequence of the elements in unpredictable order. The output is
surrounded by square brackets. The elements are separated by a
comma and a space. The elements are formatted as key:value. |
| compound |
As a sequence of the elements in unpredictable order. Each element
is formatted according to the specifications given inside of the
compound specifier. The first specifier is used for formatting
the key and the second specifier is used for formatting the value.
The order can be changed with positional arguments. For example
"%(%2$s (%1$s), %)" will write the value, followed by the key in
parenthesis. |
|<td rowspan="2">Enum</td> 's' |
The name of the value. If the name is not available, the base value
is used, preceeded by a cast. |
| All, but 's' |
Enums can be formatted with all format characters that can be used
with the base value. In that case they are formatted like the base value. |
|<td rowspan="3">Input Range</td> 's' |
When the elements of the range are characters, they are written like a string.
In all other cases, the elements are enclosed by square brackets and separated
by a comma and a space. |
| 'r' |
The sequence of the elements, each formatted with 'r'. |
| compound |
The sequence of the elements, each formatted according to the specifications
given inside of the compound specifier. |
|<td rowspan="1">Struct</td> 's' |
When the struct has neither an applicable toString
nor is an input range, it is formatted as follows:
StructType(field1, field2, ...). |
|<td rowspan="1">Class</td> 's' |
When the class has neither an applicable toString
nor is an input range, it is formatted as the
fully qualified name of the class. |
|<td rowspan="1">Union</td> 's' |
When the union has neither an applicable toString
nor is an input range, it is formatted as its base name. |
|<td rowspan="2">Pointer</td> 's' |
A null pointer is formatted as 'null'. All other pointers are
formatted as hexadecimal numbers with the format character 'X'. |
| 'x', 'X' |
Formatted as a hexadecimal number. |
|<td rowspan="3">SIMD vector</td> 's' |
The array is surrounded by square brackets
and the elements are separated by a comma and a space. |
| 'r' |
The sequence of the elements, each formatted with 'r'. |
| compound |
The sequence of the elements, each formatted according to the specifications
given inside of the compound specifier. |
|<td rowspan="1">Delegate</td> 's', 'r', compound |
As the .stringof of this delegate treated as a string.
Please note: The implementation is currently buggy
and its use is discouraged. |
Source
std/format/package.d
Examples
Simple use:
// Easiest way is to use `%s` everywhere:
assert(format("I got %s %s for %s euros.", 30, "eggs", 5.27) == "I got 30 eggs for 5.27 euros.");
// Other format characters provide more control:
assert(format("I got %b %(%X%) for %f euros.", 30, "eggs", 5.27) == "I got 11110 65676773 for 5.270000 euros.");
Compound specifiers allow formatting arrays and other compound types:
/*
The trailing end of the sub-format string following the specifier for
each item is interpreted as the array delimiter, and is therefore
omitted following the last array item:
*/
assert(format("My items are %(%s %).", [1,2,3]) == "My items are 1 2 3.");
assert(format("My items are %(%s, %).", [1,2,3]) == "My items are 1, 2, 3.");
/*
The "%|" delimiter specifier may be used to indicate where the
delimiter begins, so that the portion of the format string prior to
it will be retained in the last array element:
*/
assert(format("My items are %(-%s-%|, %).", [1,2,3]) == "My items are -1-, -2-, -3-.");
/*
These compound format specifiers may be nested in the case of a
nested array argument:
*/
auto mat = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]];
assert(format("%(%(%d %) - %)", mat), "1 2 3 - 4 5 6 - 7 8 9");
assert(format("[%(%(%d %) - %)]", mat), "[1 2 3 - 4 5 6 - 7 8 9]");
assert(format("[%([%(%d %)]%| - %)]", mat), "[1 2 3] - [4 5 6] - [7 8 9]");
/*
Strings and characters are escaped automatically inside compound
format specifiers. To avoid this behavior, use "%-(" instead of "%(":
*/
assert(format("My friends are %s.", ["John", "Nancy"]) == `My friends are ["John", "Nancy"].`);
assert(format("My friends are %(%s, %).", ["John", "Nancy"]) == `My friends are "John", "Nancy".`);
assert(format("My friends are %-(%s, %).", ["John", "Nancy"]) == `My friends are John, Nancy.`);
Using parameters:
// Flags can be used to influence to outcome:
assert(format("%g != %+#g", 3.14, 3.14) == "3.14 != +3.14000");
// Width and precision help to arrange the formatted result:
assert(format(">%10.2f<", 1234.56789) == "> 1234.57<");
// Numbers can be grouped:
assert(format("%,4d", int.max) == "21,4748,3647");
// It's possible to specify the position of an argument:
assert(format("%3$s %1$s", 3, 17, 5) == "5 3");
Providing parameters as arguments:
// Width as argument
assert(format(">%*s<", 10, "abc") == "> abc<");
// Precision as argument
assert(format(">%.*f<", 5, 123.2) == ">123.20000<");
// Grouping as argument
assert(format("%,*d", 1, int.max) == "2,1,4,7,4,8,3,6,4,7");
// Grouping separator as argument
assert(format("%,3?d", '_', int.max) == "2_147_483_647");
// All at once
assert(format("%*.*,*?d", 20, 15, 6, '/', int.max) == " 000/002147/483647");
format : (alias template) format = std.format.format(Char, Args...)(in Char[] fmt, Args args) if (isSomeChar!Char)Converts its arguments according to a format string into a string.
The second version of format takes the format string as template
argument. In this case, it is checked for consistency at
compile-time and produces slightly faster code, because the length of
the output buffer can be estimated in advance.
Params:
fmt = a $(MREF_ALTTEXT format string, std,format)
args = a variadic list of arguments to be formatted
Char = character type of fmt
Args = a variadic list of types of the arguments
Returns:
The formatted string.
Throws:
A $(LREF FormatException) if formatting did not succeed.
See_Also:
$(LREF sformat) for a variant, that tries to avoid garbage collection.
format;
return string std.format.format!("#%02X%02X%02X", const(ubyte), const(ubyte), const(ubyte))(const(ubyte) __param_0, const(ubyte) __param_1, const(ubyte) __param_2) pure @safeExamples
The format string can be checked at compile-time:
auto s = format!"%s is %s"("Pi", 3.14);
assert(s == "Pi is 3.14");
// This line doesn't compile, because 3.14 cannot be formatted with %d:
// s = format!"%s is %d"("Pi", 3.14);
format!"#%02X%02X%02X"((parameter) const(sparkles.base.term_color.RgbColor) cc.(field) ubyte sparkles.base.term_color.RgbColor.rr, (parameter) const(sparkles.base.term_color.RgbColor) cc.(field) ubyte sparkles.base.term_color.RgbColor.gg, (parameter) const(sparkles.base.term_color.RgbColor) cc.(field) ubyte sparkles.base.term_color.RgbColor.bb);
}
/// Two decimals, for splicing a shortfall into a message.
(alias) object.string = stringstring string platform_ui_derive_palette.fmt(double v) pure @safeTwo decimals, for splicing a shortfall into a message.
fmt(double (parameter) double vv) @safe pure
{
import (package) stdstd.(module) std.formatThis package provides string formatting functionality using
printf style format strings.
Submodule Function Name Description package format Converts its arguments according to a format string into a string.
| package |
sformat |
Converts its arguments according to a format string into a buffer. |
| package |
FormatException |
Signals a problem while formatting. |
| write |
formattedWrite |
Converts its arguments according to a format string and writes
the result to an output range. |
| write |
formatValue |
Formats a value of any type according to a format specifier and
writes the result to an output range. |
| read |
formattedRead |
Reads an input range according to a format string and stores the read
values into its arguments. |
| read |
unformatValue |
Reads a value from the given input range and converts it according to
a format specifier. |
| spec |
FormatSpec |
A general handler for format strings. |
| spec |
singleSpec |
Helper function that returns a FormatSpec for a single format specifier. |
Limitation
This package does not support localization, but
adheres to the rounding mode of the floating point unit, if
available.
Format Strings
The functions contained in this package use format strings. A
format string describes the layout of another string for reading or
writing purposes. A format string is composed of normal text
interspersed with format specifiers. A format specifier starts
with a percentage sign '%', optionally followed by one or more
parameters and ends with a format indicator. A format
indicator may be a simple format character or a compound
indicator.
Format strings are composed according to the following grammar:
FormatString:
FormatStringItem FormatString
FormatStringItem:
Character
FormatSpecifier
FormatSpecifier:
'%' Parameters FormatIndicator
FormatIndicator:
FormatCharacter
CompoundIndicator
FormatCharacter:
see remark below
CompoundIndicator:
'(' FormatString '%)'
'(' FormatString '%|' Delimiter '%)'
Delimiter
empty
Character Delimiter
Parameters:
Position Flags Width Precision Separator
Position:
empty
Integer '$'**
*Integer* **':'** *Integer* **'$'
Integer ':' '$'**
*Flags*:
*empty*
*Flag* *Flags*
*Flag*:
**'-'**|**'+'**|**' '**|**'0'**|**'#'**|**'='**
*Width*:
*OptionalPositionalInteger*
*Precision*:
*empty*
**'.'** *OptionalPositionalInteger*
*Separator*:
*empty*
**','** *OptionalInteger*
**','** *OptionalInteger* **'?'**
*OptionalInteger*:
*empty*
*Integer*
**'*'**
*OptionalPositionalInteger*:
*OptionalInteger*
**'*'** *Integer* **'$'
Character
'%%'
AnyCharacterExceptPercent
Integer:
NonZeroDigit Digits
Digits:
empty
Digit Digits
NonZeroDigit:
'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'
Digit:
'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'
Note
FormatCharacter is unspecified. It can be any character
that has no other purpose in this grammar, but it is
recommended to assign (lower- and uppercase) letters.
Note
The Parameters of a CompoundIndicator are currently
limited to a '-' flag.
Format Indicator
The format indicator can either be a single character or an
expression surrounded by '%(' and '%)'. It specifies the
basic manner in which a value will be formatted and is the minimum
requirement to format a value.
The following characters can be used as format characters:
FormatCharacter Semantics 's' To be formatted in a human readable format. Can be used with all types. 'c' To be formatted as a character. 'd' To be formatted as a signed decimal integer. 'u' To be formatted as a decimal image of the underlying bit representation. 'b' To be formatted as a binary image of the underlying bit representation. 'o' To be formatted as an octal image of the underlying bit representation. 'x' / 'X' To be formatted as a hexadecimal image of the underlying bit representation. 'e' / 'E' To be formatted as a real number in decimal scientific notation. 'f' / 'F' To be formatted as a real number in decimal natural notation. 'g' / 'G' To be formatted as a real number in decimal short notation. Depending on the number, a scientific notation or a natural notation is used. 'a' / 'A' To be formatted as a real number in hexadecimal scientific notation. 'r' To be formatted as raw bytes. The output may not be printable and depends on endianness.
The compound indicator can be used to describe compound types
like arrays or structs in more detail. A compound type is enclosed
within '%(' and '%)'. The enclosed sub-format string is
applied to individual elements. The trailing portion of the
sub-format string following the specifier for the element is
interpreted as the delimiter, and is therefore omitted following the
last element. The '%|' specifier may be used to explicitly
indicate the start of the delimiter, so that the preceding portion of
the string will be included following the last element.
The format string inside of the compound indicator should
contain exactly one format specifier (two in case of associative
arrays), which specifies the formatting mode of the elements of the
compound type. This format specifier can be a compound
indicator itself.
Note
Inside a compound indicator, strings and characters are
escaped automatically. To avoid this behavior, use "%-("
instead of "%(".
Flags
There are several flags that affect the outcome of the formatting.
Flag Semantics '-' When the formatted result is shorter than the value given by the width parameter, the output is left justified. Without the '-' flag, the output remains right justified.
There are two exceptions where the '-' flag has a
different meaning: (1) with 'r' it denotes to use little
endian and (2) in case of a compound indicator it means that
no special handling of the members is applied. |
| '=' |
When the formatted result is shorter than the value
given by the width parameter, the output is centered.
If the central position is not possible it is moved slightly
to the right. In this case, if '-' flag is present in
addition to the '=' flag, it is moved slightly to the left. |
| '+' / *' '* |
Applies to numerical values. By default, positive numbers are not
formatted to include the + sign. With one of these two flags present,
positive numbers are preceded by a plus sign or a space.
When both flags are present, a plus sign is used.
In case of 'r', a big endian format is used. |
| '0' |
Is applied to numerical values that are printed right justified.
If the zero flag is present, the space left to the number is
filled with zeros instead of spaces. |
| '#' |
Denotes that an alternative output must be used. This depends on the type
to be formatted and the format character used. See the
sections below for more information. |
Width, Precision and Separator
The width parameter specifies the minimum width of the result.
The meaning of precision depends on the format indicator. For
integers it denotes the minimum number of digits printed, for
real numbers it denotes the number of fractional digits and for
strings and compound types it denotes the maximum number of elements
that are included in the output.
A separator is used for formatting numbers. If it is specified,
the output is divided into chunks of three digits, separated by a ','. The number of digits in a chunk can be given explicitly by
providing a number or a ''* after the ','.
In all three cases the number of digits can be replaced by a ''*. In this scenario, the next argument is used as the number of
digits. If the argument is a negative number, the precision and
separator parameters are considered unspecified. For width,
the absolute value is used and the '-' flag is set.
The separator can also be followed by a '?'. In that case,
an additional argument is used to specify the symbol that should be
used to separate the chunks.
Position
By default, the arguments are processed in the provided order. With
the position parameter it is possible to address arguments
directly. It is also possible to denote a series of arguments with
two numbers separated by ':', that are all processed in the same
way. The second number can be omitted. In that case the series ends
with the last argument.
It's also possible to use positional arguments for width, precision and separator by adding a number and a '$' after the ''*.
Types
This section describes the result of combining types with format
characters. It is organized in 2 subsections: a list of general
information regarding the formatting of types in the presence of
format characters and a table that contains details for every
available combination of type and format character.
When formatting types, the following rules apply:
If the format character is upper case, the resulting string will
be formatted using upper case letters.
The default precision for floating point numbers is 6 digits.
Rounding of floating point numbers adheres to the rounding mode
of the floating point unit, if available.
The floating point values NaN and Infinity are formatted as
nan and inf, possibly preceded by '+' or '-' sign.
Formatting reals is only supported for 64 bit reals and 80 bit reals.
All other reals are cast to double before they are formatted. This will
cause the result to be inf for very large numbers.
Characters and strings formatted with the 's' format character
inside of compound types are surrounded by single and double quotes
and unprintable characters are escaped. To avoid this, a '-'
flag can be specified for the compound specifier
(e.g. "%-(%s%)" instead of "%(%s%)" ).
Structs, unions, classes and interfaces are formatted by calling a
toString method if available.
See module std.format.write for more
details.
Only part of these combinations can be used for reading. See
module std.format.read for more
detailed information.
This table contains descriptions for every possible combination of
type and format character:
<th scope="col" width="20%">Type</th> <th scope="col" width="20%">Format Character</th> Formatted as... <td rowspan="1">null</td> 's' null
|<td rowspan="3">bool</td> 's' |
false or true |
| 'b', 'd', 'o', 'u', 'x', 'X' |
As the integrals 0 or 1 with the same format character.
Please note, that 'o' and 'x' with '#' flag
might produce unexpected results due to special handling of
the value 0. |
| 'r' |
\0 or \1 |
|<td rowspan="4">Integral</td> 's', 'd' |
A signed decimal number. The '#' flag is ignored. |
| 'b', 'o', 'u', 'x', 'X' |
An unsigned binary, decimal, octal or hexadecimal number.
In case of 'o' and 'x', the '#' flag
denotes that the number must be preceded by 0 and 0x, with
the exception of the value 0, where this does not apply. For
'b' and 'u' the '#' flag has no effect. |
| 'e', 'E', 'f', 'F', 'g', 'G', 'a', 'A' |
As a floating point value with the same specifier.
Default precision is large enough to add all digits
of the integral value.
In case of 'a' and 'A', the integral digit can be
any hexadecimal digit.
|
| 'r' |
Characters taken directly from the binary representation. |
|<td rowspan="5">Floating Point</td> 'e', 'E' |
Scientific notation: Exactly one integral digit followed by a dot
and fractional digits, followed by the exponent.
The exponent is formatted as 'e' followed by
a '+' or '-' sign, followed by at least
two digits.
When there are no fractional digits and the '#' flag
is not present, the dot is omitted. |
| 'f', 'F' |
Natural notation: Integral digits followed by a dot and
fractional digits.
When there are no fractional digits and the '#' flag
is not present, the dot is omitted.
Please note: the difference between 'f' and 'F'
is only visible for NaN and Infinity. |
| 's', 'g', 'G' |
Short notation: If the absolute value is larger than 10 ^^ precision
or smaller than 0.0001, the scientific notation is used.
If not, the natural notation is applied.
In both cases precision denotes the count of all digits, including
the integral digits. Trailing zeros (including a trailing dot) are removed.
If '#' flag is present, trailing zeros are not removed. |
| 'a', 'A' |
Hexadecimal scientific notation: 0x followed by 1
(or 0 in case of value zero or denormalized number)
followed by a dot, fractional digits in hexadecimal
notation and an exponent. The exponent is build by p,
followed by a sign and the exponent in decimal notation.
When there are no fractional digits and the '#' flag
is not present, the dot is omitted. |
| 'r' |
Characters taken directly from the binary representation. |
|<td rowspan="3">Character</td> 's', 'c' |
As the character.
Inside of a compound indicator 's' is treated differently: The
character is surrounded by single quotes and non printable
characters are escaped. This can be avoided by preceding
the compound indicator with a '-' flag
(e.g. "%-(%s%)"). |
| 'b', 'd', 'o', 'u', 'x', 'X' |
As the integral that represents the character. |
| 'r' |
Characters taken directly from the binary representation. |
|<td rowspan="3">String</td> 's' |
The sequence of characters that form the string.
Inside of a compound indicator the string is surrounded by double quotes
and non printable characters are escaped. This can be avoided
by preceding the compound indicator with a '-' flag
(e.g. "%-(%s%)"). |
| 'r' |
The sequence of characters, each formatted with 'r'. |
| compound |
As an array of characters. |
|<td rowspan="3">Array</td> 's' |
When the elements are characters, the array is formatted as
a string. In all other cases the array is surrounded by square brackets
and the elements are separated by a comma and a space. If the elements
are strings, they are surrounded by double quotes and non
printable characters are escaped. |
| 'r' |
The sequence of the elements, each formatted with 'r'. |
| compound |
The sequence of the elements, each formatted according to the specifications
given inside of the compound specifier. |
|<td rowspan="2">Associative Array</td> 's' |
As a sequence of the elements in unpredictable order. The output is
surrounded by square brackets. The elements are separated by a
comma and a space. The elements are formatted as key:value. |
| compound |
As a sequence of the elements in unpredictable order. Each element
is formatted according to the specifications given inside of the
compound specifier. The first specifier is used for formatting
the key and the second specifier is used for formatting the value.
The order can be changed with positional arguments. For example
"%(%2$s (%1$s), %)" will write the value, followed by the key in
parenthesis. |
|<td rowspan="2">Enum</td> 's' |
The name of the value. If the name is not available, the base value
is used, preceeded by a cast. |
| All, but 's' |
Enums can be formatted with all format characters that can be used
with the base value. In that case they are formatted like the base value. |
|<td rowspan="3">Input Range</td> 's' |
When the elements of the range are characters, they are written like a string.
In all other cases, the elements are enclosed by square brackets and separated
by a comma and a space. |
| 'r' |
The sequence of the elements, each formatted with 'r'. |
| compound |
The sequence of the elements, each formatted according to the specifications
given inside of the compound specifier. |
|<td rowspan="1">Struct</td> 's' |
When the struct has neither an applicable toString
nor is an input range, it is formatted as follows:
StructType(field1, field2, ...). |
|<td rowspan="1">Class</td> 's' |
When the class has neither an applicable toString
nor is an input range, it is formatted as the
fully qualified name of the class. |
|<td rowspan="1">Union</td> 's' |
When the union has neither an applicable toString
nor is an input range, it is formatted as its base name. |
|<td rowspan="2">Pointer</td> 's' |
A null pointer is formatted as 'null'. All other pointers are
formatted as hexadecimal numbers with the format character 'X'. |
| 'x', 'X' |
Formatted as a hexadecimal number. |
|<td rowspan="3">SIMD vector</td> 's' |
The array is surrounded by square brackets
and the elements are separated by a comma and a space. |
| 'r' |
The sequence of the elements, each formatted with 'r'. |
| compound |
The sequence of the elements, each formatted according to the specifications
given inside of the compound specifier. |
|<td rowspan="1">Delegate</td> 's', 'r', compound |
As the .stringof of this delegate treated as a string.
Please note: The implementation is currently buggy
and its use is discouraged. |
Source
std/format/package.d
Examples
Simple use:
// Easiest way is to use `%s` everywhere:
assert(format("I got %s %s for %s euros.", 30, "eggs", 5.27) == "I got 30 eggs for 5.27 euros.");
// Other format characters provide more control:
assert(format("I got %b %(%X%) for %f euros.", 30, "eggs", 5.27) == "I got 11110 65676773 for 5.270000 euros.");
Compound specifiers allow formatting arrays and other compound types:
/*
The trailing end of the sub-format string following the specifier for
each item is interpreted as the array delimiter, and is therefore
omitted following the last array item:
*/
assert(format("My items are %(%s %).", [1,2,3]) == "My items are 1 2 3.");
assert(format("My items are %(%s, %).", [1,2,3]) == "My items are 1, 2, 3.");
/*
The "%|" delimiter specifier may be used to indicate where the
delimiter begins, so that the portion of the format string prior to
it will be retained in the last array element:
*/
assert(format("My items are %(-%s-%|, %).", [1,2,3]) == "My items are -1-, -2-, -3-.");
/*
These compound format specifiers may be nested in the case of a
nested array argument:
*/
auto mat = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]];
assert(format("%(%(%d %) - %)", mat), "1 2 3 - 4 5 6 - 7 8 9");
assert(format("[%(%(%d %) - %)]", mat), "[1 2 3 - 4 5 6 - 7 8 9]");
assert(format("[%([%(%d %)]%| - %)]", mat), "[1 2 3] - [4 5 6] - [7 8 9]");
/*
Strings and characters are escaped automatically inside compound
format specifiers. To avoid this behavior, use "%-(" instead of "%(":
*/
assert(format("My friends are %s.", ["John", "Nancy"]) == `My friends are ["John", "Nancy"].`);
assert(format("My friends are %(%s, %).", ["John", "Nancy"]) == `My friends are "John", "Nancy".`);
assert(format("My friends are %-(%s, %).", ["John", "Nancy"]) == `My friends are John, Nancy.`);
Using parameters:
// Flags can be used to influence to outcome:
assert(format("%g != %+#g", 3.14, 3.14) == "3.14 != +3.14000");
// Width and precision help to arrange the formatted result:
assert(format(">%10.2f<", 1234.56789) == "> 1234.57<");
// Numbers can be grouped:
assert(format("%,4d", int.max) == "21,4748,3647");
// It's possible to specify the position of an argument:
assert(format("%3$s %1$s", 3, 17, 5) == "5 3");
Providing parameters as arguments:
// Width as argument
assert(format(">%*s<", 10, "abc") == "> abc<");
// Precision as argument
assert(format(">%.*f<", 5, 123.2) == ">123.20000<");
// Grouping as argument
assert(format("%,*d", 1, int.max) == "2,1,4,7,4,8,3,6,4,7");
// Grouping separator as argument
assert(format("%,3?d", '_', int.max) == "2_147_483_647");
// All at once
assert(format("%*.*,*?d", 20, 15, 6, '/', int.max) == " 000/002147/483647");
format : (alias template) format = std.format.format(Char, Args...)(in Char[] fmt, Args args) if (isSomeChar!Char)Converts its arguments according to a format string into a string.
The second version of format takes the format string as template
argument. In this case, it is checked for consistency at
compile-time and produces slightly faster code, because the length of
the output buffer can be estimated in advance.
Params:
fmt = a $(MREF_ALTTEXT format string, std,format)
args = a variadic list of arguments to be formatted
Char = character type of fmt
Args = a variadic list of types of the arguments
Returns:
The formatted string.
Throws:
A $(LREF FormatException) if formatting did not succeed.
See_Also:
$(LREF sformat) for a variant, that tries to avoid garbage collection.
format;
return string std.format.format!("%.3f", double)(double __param_0) pure @safeExamples
The format string can be checked at compile-time:
auto s = format!"%s is %s"("Pi", 3.14);
assert(s == "Pi is 3.14");
// This line doesn't compile, because 3.14 cannot be formatted with %d:
// s = format!"%s is %d"("Pi", 3.14);
format!"%.3f"((parameter) double vv);
}
void void D main() @safemain() @safe
{
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("=== 1. The tone axis ===");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("tone grey luminance");
foreach ((parameter) int tt; [0, 10, 20, 40, 50, 60, 80, 90, 100])
{
const (local variable) const(sparkles.base.term_color.RgbColor) gg = sparkles.base.term_color.RgbColor platform_ui_derive_palette.greyAtTone(double tone) pure nothrow @nogc @safeThe neutral grey at a given tone — the reference point a tone delta is
measured against when the hue does not matter (surfaces, dividers, text).
greyAtTone((local variable) int tt);
void std.stdio.writefln!("%4d %s %.4f", int, string, double)(int __param_0, string __param_1, double __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln!"%4d %s %.4f"((local variable) int tt, (local variable) const(sparkles.base.term_color.RgbColor) gg.string platform_ui_derive_palette.hex(in sparkles.base.term_color.RgbColor c) pure @safehex, double platform_ui_derive_palette.luminance(in sparkles.base.term_color.RgbColor c) pure nothrow @nogc @safeWCAG relative luminance Y (0..1) of an sRGB color.
luminance((local variable) const(sparkles.base.term_color.RgbColor) gg));
}
// Material's claim, checked rather than repeated. Sweep every tone pair at
// a given delta and report the WORST contrast ratio it achieves.
double double platform_ui_derive_palette.main.worstRatioAt(double delta, out double worstLo) pure nothrow @nogc @safeworstRatioAt(double (parameter) double deltadelta, out double (parameter) double worstLoworstLo) @safe
{
double (local variable) double worstworst = double.(constant) double double.infinity = infinfinity;
(parameter) double worstLoworstLo = 0;
for (double (local variable) double lolo = 0; lo + delta <= 100.0; lo += 0.25)
{
const (local variable) const(double) rr = double platform_ui_derive_palette.contrastRatio(in sparkles.base.term_color.RgbColor a, in sparkles.base.term_color.RgbColor b) pure nothrow @nogc @safeThe WCAG 2.x contrast ratio between two colors, in [1, 21].
contrastRatio(sparkles.base.term_color.RgbColor platform_ui_derive_palette.greyAtTone(double tone) pure nothrow @nogc @safeThe neutral grey at a given tone — the reference point a tone delta is
measured against when the hue does not matter (surfaces, dividers, text).
greyAtTone((local variable) double lolo), sparkles.base.term_color.RgbColor platform_ui_derive_palette.greyAtTone(double tone) pure nothrow @nogc @safeThe neutral grey at a given tone — the reference point a tone delta is
measured against when the hue does not matter (surfaces, dividers, text).
greyAtTone((local variable) double lolo + (parameter) double deltadelta));
if ((local variable) const(double) rr < (local variable) double worstworst)
{
(local variable) double worstworst = (local variable) const(double) rr;
(parameter) double worstLoworstLo = (local variable) double lolo;
}
}
return (local variable) double worstworst;
}
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("\n=== 2. The tone-delta rule, measured ===");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("Material states: \"a difference of 40 in HCT tone guarantees a contrast");
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("ratio >= 3.0, and a difference of 50 guarantees a contrast ratio >= 4.5\".");
void std.stdio.writeln!()() @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln();
foreach ((parameter) double deltadelta; [40.0, 50.0])
{
double (local variable) double atat;
const (local variable) const(double) worstworst = double platform_ui_derive_palette.main.worstRatioAt(double delta, out double worstLo) pure nothrow @nogc @safeworstRatioAt((local variable) double deltadelta, (local variable) double atat);
const (local variable) const(double) claimclaim = (local variable) double deltadelta == 40.0 ? 3.0 : 4.5;
void std.stdio.writefln!("\xce\x94T %.0f: worst %.3f:1 (tone %.0f\xe2\x86\x92%.0f) vs claimed \xe2\x89\xa5 %.1f:1 %s", double, const(double), double, double, const(double), string)(double __param_0, const(double) __param_1, double __param_2, double __param_3, const(double) __param_4, string __param_5) @safeEquivalent to writef(fmt, args, '\n').
writefln!"ΔT %.0f: worst %.3f:1 (tone %.0f→%.0f) vs claimed ≥ %.1f:1 %s"(
(local variable) double deltadelta, (local variable) const(double) worstworst, (local variable) double atat, (local variable) double atat + (local variable) double deltadelta, (local variable) const(double) claimclaim,
(local variable) const(double) worstworst >= (local variable) const(double) claimclaim ? "holds" : "MISSES by " ~ string platform_ui_derive_palette.fmt(double v) pure @safeTwo decimals, for splicing a shortfall into a message.
fmt((local variable) const(double) claimclaim - (local variable) const(double) worstworst));
}
// ΔT 50 misses 4.5:1 at the very top of the tone axis. The shortfall is
// ~0.4%, so the rule is a sound design heuristic — but it is an
// approximation, not the guarantee the wording claims, and a palette that
// must *certify* WCAG AA has to check the ratio rather than trust the delta.
// Solve for the delta that actually clears each threshold everywhere.
void std.stdio.writeln!()() @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln();
foreach ((parameter) double targettarget; [3.0, 4.5])
{
double (local variable) double needneed = 0;
for (double (local variable) double dd = 1; d <= 100.0; d += 0.25)
{
double (local variable) double atat;
if (double platform_ui_derive_palette.main.worstRatioAt(double delta, out double worstLo) pure nothrow @nogc @safeworstRatioAt((local variable) double dd, (local variable) double atat) >= (local variable) double targettarget)
{
(local variable) double needneed = (local variable) double dd;
break;
}
}
void std.stdio.writefln!("smallest \xce\x94T that clears %.1f:1 at every tone: %.2f", double, double)(double __param_0, double __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln!"smallest ΔT that clears %.1f:1 at every tone: %.2f"((local variable) double targettarget, (local variable) double needneed);
}
// What the build actually guarantees, as opposed to what the docs claim.
{
double (local variable) double atat;
assert(double platform_ui_derive_palette.main.worstRatioAt(double delta, out double worstLo) pure nothrow @nogc @safeworstRatioAt(40.0, (local variable) double atat) >= 3.0, "ΔT 40 no longer clears 3.0:1");
assert(double platform_ui_derive_palette.main.worstRatioAt(double delta, out double worstLo) pure nothrow @nogc @safeworstRatioAt(50.0, (local variable) double atat) >= 4.47, "ΔT 50 fell below its measured floor");
}
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("\n=== 3. Derived themes from one accent ===");
// The accent GNOME actually reported on the machine this example was
// written on: portal `accent-color` (0.2078, 0.5176, 0.8941) = #3584E4.
const (local variable) const(sparkles.base.term_color.RgbColor) gnomeBluegnomeBlue = (struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor(0x35, 0x84, 0xE4);
void std.stdio.writefln!("OS accent: %s (GNOME 'blue')", string)(string __param_0) @safeEquivalent to writef(fmt, args, '\n').
writefln!"OS accent: %s (GNOME 'blue')"((local variable) const(sparkles.base.term_color.RgbColor) gnomeBluegnomeBlue.string platform_ui_derive_palette.hex(in sparkles.base.term_color.RgbColor c) pure @safehex);
foreach ((parameter) sparkles.ui.style.ColorScheme schemescheme; [(enum) sparkles.ui.style.ColorSchemeLight or dark color scheme — only the popup surface and docs text differ (the
brand colors are shared), matching views/twoslash.css's dark @media block.
ColorScheme.(enum value) sparkles.ui.style.ColorScheme.light = cast(ubyte)0uthe default :root
light, (enum) sparkles.ui.style.ColorSchemeLight or dark color scheme — only the popup surface and docs text differ (the
brand colors are shared), matching views/twoslash.css's dark @media block.
ColorScheme.(enum value) sparkles.ui.style.ColorScheme.dark = 1the @media (prefers-color-scheme: dark) overrides
dark])
foreach ((parameter) platform_ui_derive_palette.ContrastLevel levellevel; [(enum) platform_ui_derive_palette.ContrastLevelThe contrast levels the surveyed platforms expose. GNOME's portal and
Windows report a boolean; Android 14 and Apple report a third, middle step.
See comparison.md § "Dimension 3".
ContrastLevel.(enum value) platform_ui_derive_palette.ContrastLevel.standard = cast(ubyte)0uno preference
standard, (enum) platform_ui_derive_palette.ContrastLevelThe contrast levels the surveyed platforms expose. GNOME's portal and
Windows report a boolean; Android 14 and Apple report a third, middle step.
See comparison.md § "Dimension 3".
ContrastLevel.(enum value) platform_ui_derive_palette.ContrastLevel.high = 2GNOME contrast: 1, Windows HCF_HIGHCONTRASTON
high])
{
const (local variable) const(platform_ui_derive_palette.SystemAppearance) appapp = (struct) platform_ui_derive_palette.SystemAppearanceExactly what every surveyed platform can be reduced to.
SystemAppearance((local variable) sparkles.ui.style.ColorScheme schemescheme, (local variable) const(sparkles.base.term_color.RgbColor) gnomeBluegnomeBlue, (local variable) platform_ui_derive_palette.ContrastLevel levellevel);
const (local variable) const(sparkles.ui.theme.Theme) tt = sparkles.ui.theme.Theme platform_ui_derive_palette.deriveTheme(in platform_ui_derive_palette.SystemAppearance a, string name) pure nothrow @safeDerive a whole sparkles:ui theme from the OS triple.
Only the slots whose appearance genuinely follows the system are touched:
the page fore/background, the chrome band, the focused-pane accent and the
selection tint. The semantic status slots (error/warn/info) keep the
theme's authored hues — a red that follows the desktop accent stops meaning
"error", which is the trap comparison.md
§ "What follows the system, and what must not" describes.
deriveTheme((local variable) const(platform_ui_derive_palette.SystemAppearance) appapp, "derived");
const (local variable) const(sparkles.base.term_color.RgbColor) fgfg = (local variable) const(sparkles.ui.theme.Theme) tt.(field) sparkles.base.term_color.Color sparkles.ui.theme.Theme.defaultFgunlabeled-text foreground
defaultFg.(field) sparkles.base.term_color.RgbColor sparkles.base.term_color.Color.rgb24-bit value (kind == rgb)
rgb, (local variable) const(sparkles.base.term_color.RgbColor) bgbg = (local variable) const(sparkles.ui.theme.Theme) tt.(field) sparkles.base.term_color.Color sparkles.ui.theme.Theme.defaultBgdocument background
defaultBg.(field) sparkles.base.term_color.RgbColor sparkles.base.term_color.Color.rgb24-bit value (kind == rgb)
rgb;
const (local variable) const(sparkles.ui.style.Palette) palpal = (local variable) const(sparkles.ui.theme.Theme) tt.sparkles.ui.style.Palette sparkles.ui.theme.Theme.effectivePalette() const pure nothrow @nogc @safeThe palette to resolve slots against: the explicitly configured one, or —
when a theme carries only syntax rules — one derived from defaultBg, so
light themes get light surfaces without every theme restating the whole slot
table.
effectivePalette();
const (local variable) const(sparkles.base.term_color.RgbColor) accacc = (local variable) const(sparkles.ui.style.Palette) palpal.(field) sparkles.base.term_color.Color[35] sparkles.ui.style.Palette.fgPer-slot foreground; Color.init (unset) ⇒ inherit page fg.
fg[(enum) sparkles.ui.style.SlotA semantic style role. Widgets and display-list ops carry a Slot, and a
Palette turns it into a concrete Visual. Roles are intentionally
generic (an app palette can reuse them) even though the seed values come from
twoslash.
Slot.(enum value) sparkles.ui.style.Slot.chromeAccent = 19emphasized chrome text (title, active segment, key hints)
chromeAccent].(field) sparkles.base.term_color.RgbColor sparkles.base.term_color.Color.rgb24-bit value (kind == rgb)
rgb;
void std.stdio.writefln!("\n%-5s / %-8s fg %s bg %s accent %s", sparkles.ui.style.ColorScheme, platform_ui_derive_palette.ContrastLevel, string, string, string)(sparkles.ui.style.ColorScheme __param_0, platform_ui_derive_palette.ContrastLevel __param_1, string __param_2, string __param_3, string __param_4) @safeEquivalent to writef(fmt, args, '\n').
writefln!"\n%-5s / %-8s fg %s bg %s accent %s"(
(local variable) sparkles.ui.style.ColorScheme schemescheme, (local variable) platform_ui_derive_palette.ContrastLevel levellevel, (local variable) const(sparkles.base.term_color.RgbColor) fgfg.string platform_ui_derive_palette.hex(in sparkles.base.term_color.RgbColor c) pure @safehex, (local variable) const(sparkles.base.term_color.RgbColor) bgbg.string platform_ui_derive_palette.hex(in sparkles.base.term_color.RgbColor c) pure @safehex, (local variable) const(sparkles.base.term_color.RgbColor) accacc.string platform_ui_derive_palette.hex(in sparkles.base.term_color.RgbColor c) pure @safehex);
void std.stdio.writefln!(" text/bg %5.2f:1 accent/bg %5.2f:1", double, double)(double __param_0, double __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln!" text/bg %5.2f:1 accent/bg %5.2f:1"(
double platform_ui_derive_palette.contrastRatio(in sparkles.base.term_color.RgbColor a, in sparkles.base.term_color.RgbColor b) pure nothrow @nogc @safeThe WCAG 2.x contrast ratio between two colors, in [1, 21].
contrastRatio((local variable) const(sparkles.base.term_color.RgbColor) fgfg, (local variable) const(sparkles.base.term_color.RgbColor) bgbg), double platform_ui_derive_palette.contrastRatio(in sparkles.base.term_color.RgbColor a, in sparkles.base.term_color.RgbColor b) pure nothrow @nogc @safeThe WCAG 2.x contrast ratio between two colors, in [1, 21].
contrastRatio((local variable) const(sparkles.base.term_color.RgbColor) accacc, (local variable) const(sparkles.base.term_color.RgbColor) bgbg));
// Body text must clear WCAG AA (4.5:1) in every combination — this
// is the property the whole derivation exists to guarantee.
assert(double platform_ui_derive_palette.contrastRatio(in sparkles.base.term_color.RgbColor a, in sparkles.base.term_color.RgbColor b) pure nothrow @nogc @safeThe WCAG 2.x contrast ratio between two colors, in [1, 21].
contrastRatio((local variable) const(sparkles.base.term_color.RgbColor) fgfg, (local variable) const(sparkles.base.term_color.RgbColor) bgbg) >= 4.5,
"derived body text fails WCAG AA");
// The accent is chrome, not body text, so AA-large (3:1) applies.
assert(double platform_ui_derive_palette.contrastRatio(in sparkles.base.term_color.RgbColor a, in sparkles.base.term_color.RgbColor b) pure nothrow @nogc @safeThe WCAG 2.x contrast ratio between two colors, in [1, 21].
contrastRatio((local variable) const(sparkles.base.term_color.RgbColor) accacc, (local variable) const(sparkles.base.term_color.RgbColor) bgbg) >= 3.0,
"derived accent fails WCAG AA-large");
}
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("\n=== 4. Scheme inference vs. the OS answer ===");
// What `schemeForBackground` in libs/ui/src/sparkles/ui/style.d does today:
// Rec. 601 luma < 110 ⇒ dark. Compare it with the tone the same color sits
// at. The two disagree in a band around the midpoint — which is exactly why
// asking the OS beats inferring from a background color.
import (package) sparklessparkles.(package) sparkles.uiui.(module) sparkles.ui.styleThe style layer for sparkles.ui — the single source of truth the three
twoslash backends (CSS, raylib GUI, ANSI) had triplicated.
A widget names a semantic Slot (error, warn, surface, …), never a
concrete color. A Palette maps every slot to a foreground/background
Color plus per-channel alpha and a handful of
scalar chrome knobs (popup radius/padding, detach gap, chrome glyphs).
defaultTwoslashPalette authors the canonical twoslash hexes once.
Three generators consume a palette:
resolveSlot → a concrete Visual (RGB + alpha) for the GUI
and the display list, deferring "inherit" to the page fg/bg;
writeTwoslashVars → the CSS :root { --twoslash-* } block, kept in
lockstep with views/twoslash.css by a unittest;
writeSlotSgr → the SGR parameters for the terminal renderer.
style : (alias) schemeForBackground = sparkles.ui.style.ColorScheme sparkles.ui.style.schemeForBackground(in sparkles.base.term_color.RgbColor bg) pure nothrow @nogc @safePicks the scheme a page background implies (dark bg ⇒ dark scheme), by
perceptual luminance. Backends resolve the popup surface against their theme.
schemeForBackground;
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("color Rec601 tone schemeForBackground tone < 50");
void void platform_ui_derive_palette.main.row(in sparkles.base.term_color.RgbColor c, string note) @saferow(in (struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor (parameter) const(sparkles.base.term_color.RgbColor) cc, (alias) object.string = stringstring (parameter) string notenote) @safe
{
const (local variable) const(int) lumaluma = ((parameter) const(sparkles.base.term_color.RgbColor) cc.(field) ubyte sparkles.base.term_color.RgbColor.rr * 299 + (parameter) const(sparkles.base.term_color.RgbColor) cc.(field) ubyte sparkles.base.term_color.RgbColor.gg * 587 + (parameter) const(sparkles.base.term_color.RgbColor) cc.(field) ubyte sparkles.base.term_color.RgbColor.bb * 114) / 1000;
const (local variable) const(double) tonetone = double platform_ui_derive_palette.toneFromLuminance(double y) pure nothrow @nogc @safeCIE L* ("tone" in HCT terms) from a relative luminance, 0..100.
toneFromLuminance(double platform_ui_derive_palette.luminance(in sparkles.base.term_color.RgbColor c) pure nothrow @nogc @safeWCAG relative luminance Y (0..1) of an sRGB color.
luminance((parameter) const(sparkles.base.term_color.RgbColor) cc));
const (local variable) const(string) byTonebyTone = (local variable) const(double) tonetone < 50.0 ? "dark" : "light";
const (local variable) const(string) byLumabyLuma = sparkles.ui.style.ColorScheme sparkles.ui.style.schemeForBackground(in sparkles.base.term_color.RgbColor bg) pure nothrow @nogc @safePicks the scheme a page background implies (dark bg ⇒ dark scheme), by
perceptual luminance. Backends resolve the popup surface against their theme.
schemeForBackground((parameter) const(sparkles.base.term_color.RgbColor) cc) == (enum) sparkles.ui.style.ColorSchemeLight or dark color scheme — only the popup surface and docs text differ (the
brand colors are shared), matching views/twoslash.css's dark @media block.
ColorScheme.(enum value) sparkles.ui.style.ColorScheme.dark = 1the @media (prefers-color-scheme: dark) overrides
dark ? "dark" : "light";
void std.stdio.writefln!("%s %6d %5.1f %-19s %-5s %s%s", string, const(int), const(double), string, string, string, string)(string __param_0, const(int) __param_1, const(double) __param_2, string __param_3, string __param_4, string __param_5, string __param_6) @safeEquivalent to writef(fmt, args, '\n').
writefln!"%s %6d %5.1f %-19s %-5s %s%s"(
(parameter) const(sparkles.base.term_color.RgbColor) cc.string platform_ui_derive_palette.hex(in sparkles.base.term_color.RgbColor c) pure @safehex, (local variable) const(int) lumaluma, (local variable) const(double) tonetone, (local variable) const(string) byLumabyLuma, (local variable) const(string) byTonebyTone,
(local variable) const(string) byLumabyLuma != (local variable) const(string) byTonebyTone ? "<-- disagree " : "", (parameter) string notenote);
}
// Neutral greys: the two agree almost everywhere, then part company in a
// narrow band — luma 110 lands at tone ≈ 46.5, not at tone 50.
foreach ((parameter) int tt; [44, 46, 47, 48, 49])
void platform_ui_derive_palette.main.row(in sparkles.base.term_color.RgbColor c, string note) @saferow(sparkles.base.term_color.RgbColor platform_ui_derive_palette.greyAtTone(double tone) pure nothrow @nogc @safeThe neutral grey at a given tone — the reference point a tone delta is
measured against when the hue does not matter (surfaces, dividers, text).
greyAtTone((local variable) int tt), "grey");
// Saturated backgrounds are where the split is real rather than marginal.
// Rec. 601 is computed on *gamma-encoded* channels and weights green at
// 0.587; relative luminance is computed on *linear* light and weights it at
// 0.7152. A saturated mid green reads light to one and dark to the other.
void std.stdio.writeln!()() @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln();
void platform_ui_derive_palette.main.row(in sparkles.base.term_color.RgbColor c, string note) @saferow((struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor(0x00, 0x80, 0x00), "saturated green");
void platform_ui_derive_palette.main.row(in sparkles.base.term_color.RgbColor c, string note) @saferow((struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor(0x1E, 0x1E, 0x2E), "catppuccin-mocha base");
void platform_ui_derive_palette.main.row(in sparkles.base.term_color.RgbColor c, string note) @saferow((struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor(0x28, 0x2C, 0x34), "one-dark base");
void platform_ui_derive_palette.main.row(in sparkles.base.term_color.RgbColor c, string note) @saferow((struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor(0x00, 0x2B, 0x36), "solarized-dark base");
void platform_ui_derive_palette.main.row(in sparkles.base.term_color.RgbColor c, string note) @saferow((struct) sparkles.base.term_color.RgbColorA 24-bit RGB color value.
RgbColor(0xFD, 0xF6, 0xE3), "solarized-light base");
}