Class: Charming::UI::Theme

Inherits:
Object
  • Object
show all
Defined in:
lib/charming/ui/theme.rb

Constant Summary collapse

BUILT_IN_ROOT =
File.expand_path("themes", __dir__)
DEFAULT_TOKENS =
{
  text: {foreground: :bright_white},
  title: {foreground: :bright_cyan, bold: true},
  muted: {foreground: :bright_black},
  border: {foreground: :bright_magenta},
  selected: {reverse: true},
  info: {foreground: :bright_cyan},
  warn: {foreground: :yellow}
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tokens = {}, background: nil) ⇒ Theme

Returns a new instance of Theme.



98
99
100
101
# File 'lib/charming/ui/theme.rb', line 98

def initialize(tokens = {}, background: nil)
  @tokens = symbolize_keys(tokens)
  @background = background
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name) ⇒ Object



112
113
114
115
116
# File 'lib/charming/ui/theme.rb', line 112

def method_missing(name, ...)
  return style(name) if @tokens.key?(name)

  super
end

Instance Attribute Details

#backgroundObject (readonly)

Returns the value of attribute background.



96
97
98
# File 'lib/charming/ui/theme.rb', line 96

def background
  @background
end

Class Method Details

.built_in_namesObject



32
33
34
# File 'lib/charming/ui/theme.rb', line 32

def self.built_in_names
  Dir.glob(File.join(BUILT_IN_ROOT, "*.json")).map { |path| File.basename(path, ".json") }.sort
end

.built_in_path(name) ⇒ Object

Raises:

  • (ArgumentError)


56
57
58
59
60
61
# File 'lib/charming/ui/theme.rb', line 56

def self.built_in_path(name)
  slug = name.to_s
  raise ArgumentError, "unknown built-in theme: #{name.inspect}" unless built_in_names.include?(slug)

  File.join(BUILT_IN_ROOT, "#{slug}.json")
end

.deep_resolve_colors(value, palette) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/charming/ui/theme.rb', line 68

def self.deep_resolve_colors(value, palette)
  case value
  when Hash
    value.transform_values { |item| deep_resolve_colors(item, palette) }
  when Array
    value.map { |item| deep_resolve_colors(item, palette) }
  when String
    palette.fetch(value, normalize_color(value) || value)
  else
    value
  end
end

.defaultObject



20
21
22
# File 'lib/charming/ui/theme.rb', line 20

def self.default
  @default ||= load_builtin("phosphor")
end

.from_hash(value) ⇒ Object

Raises:

  • (ArgumentError)


36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/charming/ui/theme.rb', line 36

def self.from_hash(value)
  raise ArgumentError, "theme file must contain an object" unless value.is_a?(Hash)

  styles = value.fetch("styles") do
    raise ArgumentError, "theme file must contain styles"
  end

  palette = value.fetch("palette", {})
  new(
    resolve_palette_references(styles, palette),
    background: resolve_background(value["background"], palette)
  )
end

.load_builtin(name) ⇒ Object



28
29
30
# File 'lib/charming/ui/theme.rb', line 28

def self.load_builtin(name)
  load_file(built_in_path(name))
end

.load_file(path) ⇒ Object



24
25
26
# File 'lib/charming/ui/theme.rb', line 24

def self.load_file(path)
  from_hash(JSON.parse(File.read(path)))
end

.normalize_color(value) ⇒ Object



85
86
87
88
89
90
91
92
93
94
# File 'lib/charming/ui/theme.rb', line 85

def self.normalize_color(value)
  return unless value.is_a?(String)

  case value
  when /\A#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])(?:[0-9a-fA-F])?\z/
    "#{$1 * 2}#{$2 * 2}#{$3 * 2}".prepend("#")
  when /\A#[0-9a-fA-F]{6}(?:[0-9a-fA-F]{2})?\z/
    value[0, 7]
  end
end

.normalize_colors(values) ⇒ Object



81
82
83
# File 'lib/charming/ui/theme.rb', line 81

def self.normalize_colors(values)
  values.transform_values { |value| normalize_color(value) }.compact
end

.resolve_background(value, palette) ⇒ Object



50
51
52
53
54
# File 'lib/charming/ui/theme.rb', line 50

def self.resolve_background(value, palette)
  return unless value

  deep_resolve_colors(value, normalize_colors(palette))
end

.resolve_palette_references(styles, palette) ⇒ Object



63
64
65
66
# File 'lib/charming/ui/theme.rb', line 63

def self.resolve_palette_references(styles, palette)
  palette = normalize_colors(palette)
  deep_resolve_colors(styles, palette)
end

Instance Method Details

#respond_to_missing?(name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


118
119
120
# File 'lib/charming/ui/theme.rb', line 118

def respond_to_missing?(name, include_private = false)
  @tokens.key?(name) || super
end

#style(name) ⇒ Object Also known as: []



103
104
105
106
107
108
109
# File 'lib/charming/ui/theme.rb', line 103

def style(name)
  spec = @tokens.fetch(name.to_sym) do
    raise ArgumentError, "unknown theme token: #{name.inspect}"
  end

  build_style(spec)
end