feat: Adds vertical and horizontal separators, renames modifier protocol.
This commit is contained in:
@@ -69,8 +69,8 @@ public struct ExampleConfiguration {
|
|||||||
|
|
||||||
// MARK: - Style
|
// MARK: - Style
|
||||||
|
|
||||||
public protocol ExampleSectionStyle: NodeModifier where Content == ExampleSectionConfiguration {}
|
public protocol ExampleSectionStyle: TextModifier where Content == ExampleSectionConfiguration {}
|
||||||
public protocol ExampleStyle: NodeModifier where Content == ExampleConfiguration {}
|
public protocol ExampleStyle: TextModifier where Content == ExampleConfiguration {}
|
||||||
|
|
||||||
public extension ExampleSection {
|
public extension ExampleSection {
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ public struct DefaultExampleStyle: ExampleStyle {
|
|||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
public func render(content: ExampleConfiguration) -> some TextNode {
|
public func render(content: ExampleConfiguration) -> some TextNode {
|
||||||
VStack(spacing: 2) {
|
VStack(separator: .newLine(count: 2)) {
|
||||||
content.examples.map { example in
|
content.examples.map { example in
|
||||||
VStack {
|
VStack {
|
||||||
Label(example.label.green.bold)
|
Label(example.label.green.bold)
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ public extension Note {
|
|||||||
|
|
||||||
// MARK: - Style
|
// MARK: - Style
|
||||||
|
|
||||||
public protocol NoteStyleModifier: NodeModifier where Content == NoteStyleConfiguration {}
|
public protocol NoteStyleModifier: TextModifier where Content == NoteStyleConfiguration {}
|
||||||
|
|
||||||
public extension NoteStyleModifier where Self == DefaultNoteStyle {
|
public extension NoteStyleModifier where Self == DefaultNoteStyle {
|
||||||
static var `default`: Self {
|
static var `default`: Self {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public extension ShellCommand {
|
|||||||
|
|
||||||
// MARK: - Style
|
// MARK: - Style
|
||||||
|
|
||||||
public protocol ShellCommandStyle: NodeModifier where Self.Content == ShellCommandConfiguration {}
|
public protocol ShellCommandStyle: TextModifier where Self.Content == ShellCommandConfiguration {}
|
||||||
|
|
||||||
public extension ShellCommandStyle where Self == DefaultShellCommandStyle {
|
public extension ShellCommandStyle where Self == DefaultShellCommandStyle {
|
||||||
static var `default`: Self { DefaultShellCommandStyle() }
|
static var `default`: Self { DefaultShellCommandStyle() }
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
import Rainbow
|
|
||||||
|
|
||||||
public protocol NodeModifier {
|
|
||||||
// swiftlint:disable type_name
|
|
||||||
associatedtype _Body: TextNode
|
|
||||||
typealias Body = _Body
|
|
||||||
// swiftlint:enable type_name
|
|
||||||
|
|
||||||
associatedtype Content
|
|
||||||
|
|
||||||
@TextBuilder
|
|
||||||
func render(content: Content) -> Body
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct ModifiedNode<Content: TextNode, Modifier: NodeModifier> {
|
|
||||||
|
|
||||||
@usableFromInline
|
|
||||||
let content: Content
|
|
||||||
|
|
||||||
@usableFromInline
|
|
||||||
let modifier: Modifier
|
|
||||||
|
|
||||||
@usableFromInline
|
|
||||||
init(content: Content, modifier: Modifier) {
|
|
||||||
self.content = content
|
|
||||||
self.modifier = modifier
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ModifiedNode: TextNode where Modifier.Content == Content {
|
|
||||||
public var body: some TextNode {
|
|
||||||
modifier.render(content: content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ModifiedNode: NodeRepresentable where Self: TextNode {
|
|
||||||
@inlinable
|
|
||||||
public func render() -> String {
|
|
||||||
body.render()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension TextNode {
|
|
||||||
@inlinable
|
|
||||||
func modifier<M: NodeModifier>(_ modifier: M) -> ModifiedNode<Self, M> {
|
|
||||||
.init(content: self, modifier: modifier)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
/// An empty text node.
|
/// An empty text node.
|
||||||
|
///
|
||||||
|
/// This gets removed from any output when rendering text nodes.
|
||||||
public struct Empty: TextNode {
|
public struct Empty: TextNode {
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
|
/// A group of text nodes.
|
||||||
|
///
|
||||||
|
/// This allows you to group content together, which can optionally be
|
||||||
|
/// styled.
|
||||||
public struct Group<Content: TextNode>: TextNode {
|
public struct Group<Content: TextNode>: TextNode {
|
||||||
|
|
||||||
@usableFromInline
|
@usableFromInline
|
||||||
var content: Content
|
var content: Content
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
|
/// A horizontal group of text nodes.
|
||||||
public struct HStack: TextNode {
|
public struct HStack: TextNode {
|
||||||
|
|
||||||
@usableFromInline
|
@usableFromInline
|
||||||
let content: [any TextNode]
|
let content: [any TextNode]
|
||||||
|
|
||||||
@usableFromInline
|
@usableFromInline
|
||||||
let separator: any TextNode
|
let separator: Separator.Horizontal
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
public init(
|
public init(
|
||||||
spacing: Int = 1,
|
separator: Separator.Horizontal = .space(count: 1),
|
||||||
@TextBuilder content: () -> any TextNode
|
@TextBuilder content: () -> any TextNode
|
||||||
) {
|
) {
|
||||||
self.content = array(from: content())
|
self.content = array(from: content())
|
||||||
self.separator = seperator(" ", count: spacing > 0 ? spacing - 1 : 0)
|
self.separator = separator
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public struct SectionConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol SectionStyle: NodeModifier where Content == SectionConfiguration {}
|
public protocol SectionStyle: TextModifier where Content == SectionConfiguration {}
|
||||||
|
|
||||||
public extension SectionStyle where Self == DefaultSectionStyle {
|
public extension SectionStyle where Self == DefaultSectionStyle {
|
||||||
static var `default`: Self { DefaultSectionStyle() }
|
static var `default`: Self { DefaultSectionStyle() }
|
||||||
@@ -87,7 +87,7 @@ public extension SectionStyle where Self == DefaultSectionStyle {
|
|||||||
public struct DefaultSectionStyle: SectionStyle {
|
public struct DefaultSectionStyle: SectionStyle {
|
||||||
|
|
||||||
public func render(content: SectionConfiguration) -> some TextNode {
|
public func render(content: SectionConfiguration) -> some TextNode {
|
||||||
VStack(spacing: 2) {
|
VStack(separator: .newLine(count: 2)) {
|
||||||
content.header
|
content.header
|
||||||
content.content
|
content.content
|
||||||
content.footer
|
content.footer
|
||||||
|
|||||||
66
Sources/CliDocCore/Nodes/Separator.swift
Normal file
66
Sources/CliDocCore/Nodes/Separator.swift
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
public enum Separator {
|
||||||
|
|
||||||
|
/// Represents a horizontal separator that can be used between text nodes, typically inside
|
||||||
|
/// an ``HStack``
|
||||||
|
public enum Horizontal: TextNode {
|
||||||
|
/// Separate nodes by spaces of the given count.
|
||||||
|
case space(count: Int = 1)
|
||||||
|
|
||||||
|
/// Separate nodes by tabs of the given count.
|
||||||
|
case tab(count: Int = 1)
|
||||||
|
|
||||||
|
/// Separate nodes by the provided string of the given count.
|
||||||
|
case custom(String, count: Int = 1)
|
||||||
|
|
||||||
|
@TextBuilder
|
||||||
|
@inlinable
|
||||||
|
public var body: some TextNode {
|
||||||
|
switch self {
|
||||||
|
case let .tab(count: count):
|
||||||
|
seperator("\t", count: count)
|
||||||
|
case let .space(count: count):
|
||||||
|
seperator(" ", count: count)
|
||||||
|
case let .custom(string, count: count):
|
||||||
|
seperator(string, count: count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a vertical separator that can be used between text nodes, typically inside
|
||||||
|
/// a ``VStack``
|
||||||
|
public enum Vertical: TextNode {
|
||||||
|
case newLine(count: Int = 1)
|
||||||
|
case custom(String, count: Int = 1)
|
||||||
|
|
||||||
|
@TextBuilder
|
||||||
|
@inlinable
|
||||||
|
public var body: some TextNode {
|
||||||
|
switch self {
|
||||||
|
case let .newLine(count: count):
|
||||||
|
seperator("\n", count: count)
|
||||||
|
case let .custom(string, count: count):
|
||||||
|
seperator(string, count: count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
func ensuredCount(_ count: Int) -> Int {
|
||||||
|
guard count >= 1 else { return 1 }
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
func seperator(_ separator: String, count: Int) -> some TextNode {
|
||||||
|
let count = ensuredCount(count)
|
||||||
|
|
||||||
|
assert(count >= 1, "Invalid count while creating a separator")
|
||||||
|
|
||||||
|
var output = ""
|
||||||
|
for _ in 1 ... count {
|
||||||
|
output += separator
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
@@ -1,18 +1,21 @@
|
|||||||
public struct VStack: TextNode {
|
/// A vertical stack of text nodes.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
public struct VStack: TextNode {
|
||||||
@usableFromInline
|
@usableFromInline
|
||||||
let content: [any TextNode]
|
let content: [any TextNode]
|
||||||
|
|
||||||
@usableFromInline
|
@usableFromInline
|
||||||
let separator: any TextNode
|
let separator: Separator.Vertical
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
public init(
|
public init(
|
||||||
spacing: Int = 1,
|
separator: Separator.Vertical = .newLine(count: 1),
|
||||||
@TextBuilder content: () -> any TextNode
|
@TextBuilder content: () -> any TextNode
|
||||||
) {
|
) {
|
||||||
self.content = array(from: content())
|
self.content = array(from: content())
|
||||||
self.separator = seperator("\n", count: spacing > 0 ? spacing - 1 : 0)
|
self.separator = separator
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
|
|||||||
11
Sources/CliDocCore/TextModifier.swift
Normal file
11
Sources/CliDocCore/TextModifier.swift
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
public protocol TextModifier {
|
||||||
|
// swiftlint:disable type_name
|
||||||
|
associatedtype _Body: TextNode
|
||||||
|
typealias Body = _Body
|
||||||
|
// swiftlint:enable type_name
|
||||||
|
|
||||||
|
associatedtype Content
|
||||||
|
|
||||||
|
@TextBuilder
|
||||||
|
func render(content: Content) -> Body
|
||||||
|
}
|
||||||
@@ -13,14 +13,29 @@ public extension TextNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
func color(red: UInt8, green: UInt8, blue: UInt8) -> some TextNode {
|
func color(_ red: UInt8, _ green: UInt8, _ blue: UInt8) -> some TextNode {
|
||||||
textStyle(.color(rgb: (red, green, blue)))
|
textStyle(.color(rgb: (red, green, blue)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
func backgroundColor(_ name: NamedBackgroundColor) -> some TextNode {
|
||||||
|
textStyle(.backgroundColor(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
func backgroundColor(_ bit8: UInt8) -> some TextNode {
|
||||||
|
textStyle(.backgroundColor(bit8: bit8))
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
func backgroundColor(_ red: UInt8, _ green: UInt8, _ blue: UInt8) -> some TextNode {
|
||||||
|
textStyle(.backgroundColor(rgb: (red, green, blue)))
|
||||||
|
}
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
func textStyle<S: TextStyle>(_ styles: S...) -> some TextNode {
|
func textStyle<S: TextStyle>(_ styles: S...) -> some TextNode {
|
||||||
styles.reduce(render()) { string, style in
|
styles.reduce(render()) { string, style in
|
||||||
style.render(content: string).render()
|
style.render(content: .init(string)).render()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,10 +59,20 @@ public extension TextNode {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove string restraint.
|
public protocol TextStyle: TextModifier where Content == TextStyleConfiguration {}
|
||||||
public protocol TextStyle: NodeModifier where Content == String {}
|
|
||||||
|
public struct TextStyleConfiguration {
|
||||||
|
@usableFromInline
|
||||||
|
let node: any TextNode
|
||||||
|
|
||||||
|
@usableFromInline
|
||||||
|
init(_ node: any TextNode) {
|
||||||
|
self.node = node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public extension TextStyle where Self == StyledText {
|
public extension TextStyle where Self == StyledText {
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
static var bold: Self { .init(.bold) }
|
static var bold: Self { .init(.bold) }
|
||||||
|
|
||||||
@@ -63,9 +88,6 @@ public extension TextStyle where Self == StyledText {
|
|||||||
@inlinable
|
@inlinable
|
||||||
static var blink: Self { .init(.blink) }
|
static var blink: Self { .init(.blink) }
|
||||||
|
|
||||||
@inlinable
|
|
||||||
static var swap: Self { .init(.swap) }
|
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
static var strikeThrough: Self { .init(.strikethrough) }
|
static var strikeThrough: Self { .init(.strikethrough) }
|
||||||
}
|
}
|
||||||
@@ -74,37 +96,58 @@ public extension TextStyle where Self == ColorTextStyle {
|
|||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
static func color(_ name: NamedColor) -> Self {
|
static func color(_ name: NamedColor) -> Self {
|
||||||
.init(.named(name))
|
.init(.foreground(.named(name)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
static func color(bit8: UInt8) -> Self {
|
static func color(bit8: UInt8) -> Self {
|
||||||
.init(.bit8(bit8))
|
.init(.foreground(.bit8(bit8)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
static func color(rgb: RGB) -> Self {
|
static func color(rgb: RGB) -> Self {
|
||||||
.init(.bit24(rgb))
|
.init(.foreground(.bit24(rgb)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
static func backgroundColor(_ name: NamedBackgroundColor) -> Self {
|
||||||
|
.init(.background(.named(name)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
static func backgroundColor(bit8: UInt8) -> Self {
|
||||||
|
.init(.background(.bit8(bit8)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@inlinable
|
||||||
|
static func backgroundColor(rgb: RGB) -> Self {
|
||||||
|
.init(.background(.bit24(rgb)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ColorTextStyle: TextStyle {
|
public struct ColorTextStyle: TextStyle {
|
||||||
enum Location {
|
@usableFromInline
|
||||||
|
enum Style {
|
||||||
case foreground(ColorType)
|
case foreground(ColorType)
|
||||||
case background(ColorType)
|
case background(BackgroundColorType)
|
||||||
}
|
}
|
||||||
|
|
||||||
@usableFromInline
|
@usableFromInline
|
||||||
let color: ColorType
|
let style: Style
|
||||||
|
|
||||||
@usableFromInline
|
@usableFromInline
|
||||||
init(_ color: ColorType) {
|
init(_ style: Style) {
|
||||||
self.color = color
|
self.style = style
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
public func render(content: String) -> some TextNode {
|
public func render(content: TextStyleConfiguration) -> some TextNode {
|
||||||
content.applyingColor(color)
|
switch style {
|
||||||
|
case let .foreground(color):
|
||||||
|
return content.node.render().applyingColor(color)
|
||||||
|
case let .background(color):
|
||||||
|
return content.node.render().applyingBackgroundColor(color)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +161,7 @@ public struct StyledText: TextStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@inlinable
|
@inlinable
|
||||||
public func render(content: String) -> some TextNode {
|
public func render(content: TextStyleConfiguration) -> some TextNode {
|
||||||
content.applyingStyle(style)
|
content.node.render().applyingStyle(style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,17 +9,6 @@ func array(from node: any TextNode) -> [any TextNode] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@usableFromInline
|
|
||||||
func seperator(_ separator: String, count: Int) -> any TextNode {
|
|
||||||
assert(count >= 0, "Invalid count while creating a separator")
|
|
||||||
|
|
||||||
var output = ""
|
|
||||||
for _ in 0 ... count {
|
|
||||||
output += separator
|
|
||||||
}
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Array where Element == (any TextNode) {
|
extension Array where Element == (any TextNode) {
|
||||||
|
|
||||||
@usableFromInline
|
@usableFromInline
|
||||||
|
|||||||
@@ -32,6 +32,18 @@ struct CliDocCoreTests {
|
|||||||
"bar"
|
"bar"
|
||||||
}
|
}
|
||||||
#expect(stack.render() == "foo bar")
|
#expect(stack.render() == "foo bar")
|
||||||
|
|
||||||
|
let tabStack = HStack(separator: .tab()) {
|
||||||
|
"foo"
|
||||||
|
"bar"
|
||||||
|
}
|
||||||
|
#expect(tabStack.render() == "foo\tbar")
|
||||||
|
|
||||||
|
let customStack = HStack(separator: .custom(":blob:")) {
|
||||||
|
"foo"
|
||||||
|
"bar"
|
||||||
|
}
|
||||||
|
#expect(customStack.render() == "foo:blob:bar")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -45,6 +57,15 @@ struct CliDocCoreTests {
|
|||||||
foo
|
foo
|
||||||
bar
|
bar
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
let customStack = VStack(separator: .custom("\n\t")) {
|
||||||
|
"foo"
|
||||||
|
"bar"
|
||||||
|
}
|
||||||
|
#expect(customStack.render() == """
|
||||||
|
foo
|
||||||
|
\tbar
|
||||||
|
""")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -65,6 +86,73 @@ struct CliDocCoreTests {
|
|||||||
#expect(array.render() == "foo bar")
|
#expect(array.render() == "foo bar")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(arguments: [
|
||||||
|
Style.bold, .italic, .dim, .underline, .blink, .strikethrough
|
||||||
|
])
|
||||||
|
func testTextStyles(style: Style) {
|
||||||
|
let node = Group { "foo" }.textStyle(StyledText(style))
|
||||||
|
let string = "foo".applyingStyle(style)
|
||||||
|
#expect(node.render() == string)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func testTextStylesDirectlyOnNode() {
|
||||||
|
let bold = Group { "foo" }.bold()
|
||||||
|
let string = "foo".bold
|
||||||
|
#expect(bold.render() == string)
|
||||||
|
|
||||||
|
let dim = Group { "foo" }.dim()
|
||||||
|
let dimString = "foo".dim
|
||||||
|
#expect(dim.render() == dimString)
|
||||||
|
|
||||||
|
let italic = Group { "foo" }.italic()
|
||||||
|
let italicString = "foo".italic
|
||||||
|
#expect(italic.render() == italicString)
|
||||||
|
|
||||||
|
let blink = Group { "foo" }.blink()
|
||||||
|
let blinkString = "foo".blink
|
||||||
|
#expect(blink.render() == blinkString)
|
||||||
|
|
||||||
|
let strikeThrough = Group { "foo" }.strikeThrough()
|
||||||
|
let strikeThroughString = "foo".applyingStyle(.strikethrough)
|
||||||
|
#expect(strikeThrough.render() == strikeThroughString)
|
||||||
|
|
||||||
|
let underline = Group { "foo" }.underline()
|
||||||
|
let underlineString = "foo".underline
|
||||||
|
#expect(underline.render() == underlineString)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(arguments: NamedColor.allCases)
|
||||||
|
func testNamedColors(color: NamedColor) {
|
||||||
|
let foregroundNode = Group { "foo" }.color(color)
|
||||||
|
let string = "foo".applyingColor(color)
|
||||||
|
#expect(foregroundNode.render() == string)
|
||||||
|
|
||||||
|
let backgroundNode = Group { "foo" }.backgroundColor(color.toBackgroundColor)
|
||||||
|
let backgroundString = "foo".applyingBackgroundColor(color.toBackgroundColor)
|
||||||
|
#expect(backgroundNode.render() == backgroundString)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func testBit8Colors() {
|
||||||
|
let color: UInt8 = 32
|
||||||
|
let foregroundNode = Group { "foo" }.color(color)
|
||||||
|
let string = "foo".applyingColor(.bit8(color))
|
||||||
|
#expect(foregroundNode.render() == string)
|
||||||
|
|
||||||
|
let backgroundNode = Group { "foo" }.backgroundColor(color)
|
||||||
|
let backgroundString = "foo".applyingBackgroundColor(.bit8(color))
|
||||||
|
#expect(backgroundNode.render() == backgroundString)
|
||||||
|
|
||||||
|
let rgbNode = Group { "foo" }.color(color, color, color)
|
||||||
|
let rgbString = "foo".applyingColor(.bit24((color, color, color)))
|
||||||
|
#expect(rgbNode.render() == rgbString)
|
||||||
|
|
||||||
|
let rgbBackgroundNode = Group { "foo" }.backgroundColor(color, color, color)
|
||||||
|
let rgbBackgroundString = "foo".applyingBackgroundColor(.bit24((color, color, color)))
|
||||||
|
#expect(rgbBackgroundNode.render() == rgbBackgroundString)
|
||||||
|
}
|
||||||
|
|
||||||
@Test(
|
@Test(
|
||||||
arguments: SectionArg.arguments
|
arguments: SectionArg.arguments
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ extension ExampleSectionStyle where Self == DefaultExampleSectionStyle<CustomExa
|
|||||||
|
|
||||||
struct CustomExampleOnlyStyle: ExampleStyle {
|
struct CustomExampleOnlyStyle: ExampleStyle {
|
||||||
func render(content: ExampleConfiguration) -> some TextNode {
|
func render(content: ExampleConfiguration) -> some TextNode {
|
||||||
VStack(spacing: 2) {
|
VStack(separator: .newLine(count: 2)) {
|
||||||
content.examples.map { example in
|
content.examples.map { example in
|
||||||
VStack {
|
VStack {
|
||||||
example.label.red
|
example.label.red
|
||||||
|
|||||||
Reference in New Issue
Block a user