Class: Charming::Presentation::UI::Theme

Inherits:
Object
  • Object
show all
Defined in:
lib/charming/presentation/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.



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

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



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

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

  super
end

Instance Attribute Details

#backgroundObject (readonly)

Returns the value of attribute background.



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

def background
  @background
end

Class Method Details

.built_in_namesObject



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

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)


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

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



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

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



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

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

.from_hash(value) ⇒ Object

Raises:

  • (ArgumentError)


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

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



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

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

.load_file(path) ⇒ Object



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

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

.normalize_color(value) ⇒ Object



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

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



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

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

.resolve_background(value, palette) ⇒ Object



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

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

  deep_resolve_colors(value, normalize_colors(palette))
end

.resolve_palette_references(styles, palette) ⇒ Object



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

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)


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

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

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



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

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

  build_style(spec)
end