fontist 1.18.2 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/docs/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "scripts": {
3
+ "dev": "vitepress dev",
4
+ "build": "vitepress build",
5
+ "preview": "vitepress preview",
6
+ "format": "prettier -w ."
7
+ },
8
+ "type": "module",
9
+ "devDependencies": {
10
+ "@types/node": "latest",
11
+ "prettier": "^3.2.4",
12
+ "typescript": "latest",
13
+ "vitepress": "^1.0.0-rc.44",
14
+ "vue": "^3.4.15",
15
+ "vue-tsc": "^1.8.27"
16
+ }
17
+ }
Binary file
Binary file
@@ -0,0 +1,143 @@
1
+ # Fontist CLI reference
2
+
3
+ <!-- This can be converted to a folder & multiple pages at any time. -->
4
+
5
+ ## `fontist cache`
6
+
7
+ The only subcommand available on `fontist cache` is `fontist cache clear`. It clears the `~/.fontist` cache.
8
+
9
+ ```sh
10
+ fontist cache clear
11
+ ```
12
+
13
+ ```
14
+ Cache has been successfully removed.
15
+ ```
16
+
17
+ ## `fontist config`
18
+
19
+ `fontist config` lets you edit the Fontist config file from the command line instead of opening an editor. There are four subcommands available:
20
+
21
+ - `fontist config delete <key>`
22
+ - `fontist config keys`
23
+ - `fontist config set <key> <value>`
24
+ - `fontist config show`
25
+
26
+ Here's an example of these commands being used to edit the config file:
27
+
28
+ ```sh
29
+ fontist config keys
30
+ fontist config set font_path /var/myfonts
31
+ fontist config delete font_path
32
+ fontist config show
33
+ ```
34
+
35
+ ```
36
+ $ fontist config keys
37
+ Available keys:
38
+ fonts_path (default: /home/octocat/.fontist/fonts)
39
+ open_timeout (default: 10)
40
+ read_timeout (default: 10)
41
+
42
+ $ fontist config set fonts_path /var/myfonts
43
+ 'fonts_path' set to '/var/myfonts'.
44
+
45
+ $ fontist config delete fonts_path
46
+ 'fonts_path' reset to default ('/home/octocat/.fontist/fonts').
47
+
48
+ $ fontist config show
49
+ Config is empty.
50
+ ```
51
+
52
+ ### Config reference
53
+
54
+ <!-- Move this into its own '/reference/config' page if this grows a lot. -->
55
+
56
+ - **`fonts_path`:** Where to put the `.ttf` files. Defaults to `~/.fontist/fonts`
57
+
58
+ - **`open_timeout`:** Defaults to 10.
59
+
60
+ - **`read_timeout`:** Defaults to 10.
61
+
62
+ ## `fontist status [font-name]`
63
+
64
+ Prints the paths to a particular installed font or all fonts if the `font-name` is omitted. This searches **all fonts available on your system** even those not managed by Fontist.
65
+
66
+ ```sh
67
+ fontist status
68
+ ```
69
+
70
+ ```
71
+ Fonts found at:
72
+ - /usr/share/fonts/truetype/ubuntu/UbuntuMono-B.ttf
73
+ - /usr/share/fonts/truetype/ubuntu/UbuntuMono-BI.ttf
74
+ - /usr/share/fonts/truetype/ubuntu/UbuntuMono-R.ttf
75
+ - /usr/share/fonts/truetype/ubuntu/UbuntuMono-RI.ttf
76
+ - /home/octocat/.fontist/fonts/Arial.ttf (from ms_truetype formula)
77
+ - /home/octocat/.fontist/fonts/ArialBI.ttf (from ms_truetype formula)
78
+ - /home/octocat/.fontist/fonts/ArialBd.ttf (from ms_truetype formula)
79
+ - /home/octocat/.fontist/fonts/ArialI.ttf (from ms_truetype formula)
80
+ ```
81
+
82
+ Here's an example narrowed to a specific font:
83
+
84
+ ```sh
85
+ fontist status "Open Sans"
86
+ ```
87
+
88
+ ```
89
+ Fonts found at:
90
+ - /home/octocat/.fontist/fonts/OpenSans-Bold.ttf (from open_sans formula)
91
+ - /home/octocat/.fontist/fonts/OpenSans-BoldItalic.ttf (from open_sans formula)
92
+ - /home/octocat/.fontist/fonts/OpenSans-Italic.ttf (from open_sans formula)
93
+ - /home/octocat/.fontist/fonts/OpenSans-Regular.ttf (from open_sans formula)
94
+ ```
95
+
96
+ ## `fontist list [font-name]`
97
+
98
+ Lists the installation status of `font-name` or all fonts if no font name provided.
99
+
100
+ ```sh
101
+ fontist status
102
+ ```
103
+
104
+ ```
105
+ Fonts found at:
106
+ - /usr/share/fonts/truetype/ubuntu/UbuntuMono-B.ttf
107
+ - /usr/share/fonts/truetype/ubuntu/UbuntuMono-BI.ttf
108
+ - /usr/share/fonts/truetype/ubuntu/UbuntuMono-R.ttf
109
+ - /usr/share/fonts/truetype/ubuntu/UbuntuMono-RI.ttf
110
+ - /home/octocat/.fontist/fonts/Arial.ttf (from ms_truetype formula)
111
+ - /home/octocat/.fontist/fonts/ArialBI.ttf (from ms_truetype formula)
112
+ - /home/octocat/.fontist/fonts/ArialBd.ttf (from ms_truetype formula)
113
+ - /home/octocat/.fontist/fonts/ArialI.ttf (from ms_truetype formula)
114
+ ```
115
+
116
+ Here's an example getting the status of a specific font:
117
+
118
+ ```sh
119
+ fontist status "Fira Mono"
120
+ ```
121
+
122
+ ```
123
+ Font "Fira Mono" not found locally.
124
+ 'Fira Mono' font is missing, please run `fontist install 'Fira Mono'` to download the font.
125
+ ```
126
+
127
+ ## Environment variables
128
+
129
+ ### `FONTIST_PATH`
130
+
131
+ By default Fontist uses the `~/.fontist` directory to store fonts and its files. It can be changed with the `FONTIST_PATH` environment variable.
132
+
133
+ ```sh
134
+ FONTIST_PATH=/var/fontist2 fontist update
135
+ ```
136
+
137
+ ## Excluded fonts
138
+
139
+ `fontist` excludes some fonts from usage when they break other software:
140
+
141
+ - `NISC18030.ttf` (GB18030 Bitmap) - macOS [fontist/fontist#344](https://github.com/fontist/fontist/issues/344)
142
+
143
+ [📑 View the up-to-date list of known problematic fonts on GitHub](https://github.com/fontist/fontist/blob/main/lib/fontist/exclude.yml)
data/fontist.gemspec CHANGED
@@ -31,6 +31,7 @@ Gem::Specification.new do |spec|
31
31
 
32
32
  spec.add_runtime_dependency "down", "~> 5.0"
33
33
  spec.add_runtime_dependency "extract_ttc", "~> 0.1"
34
+ spec.add_runtime_dependency "fuzzy_match", "~> 2.1"
34
35
  spec.add_runtime_dependency "json", "~> 2.0"
35
36
  spec.add_runtime_dependency "nokogiri", "~> 1.0"
36
37
  spec.add_runtime_dependency "mime-types", "~> 3.0"
@@ -50,4 +51,6 @@ Gem::Specification.new do |spec|
50
51
  spec.add_development_dependency "rubocop", "~> 1.22.1"
51
52
  spec.add_development_dependency "rubocop-rails", "~> 2.9"
52
53
  spec.add_development_dependency "rubocop-performance", "~> 1.10"
54
+ spec.add_development_dependency "vcr"
55
+ spec.add_development_dependency "webmock"
53
56
  end
@@ -22,6 +22,12 @@ module Fontist
22
22
  type: :boolean,
23
23
  desc: "Avoid using cache during download"
24
24
 
25
+ base.class_option :interactive,
26
+ aliases: :i,
27
+ type: :boolean,
28
+ default: true,
29
+ desc: "Interactive mode"
30
+
25
31
  base.class_option :formulas_path,
26
32
  type: :string,
27
33
  desc: "Path to formulas"
@@ -32,6 +38,7 @@ module Fontist
32
38
  Fontist.preferred_family = options[:preferred_family]
33
39
  Fontist.log_level = log_level(options)
34
40
  Fontist.use_cache = !options[:no_cache]
41
+ Fontist.interactive = options[:interactive]
35
42
 
36
43
  if options[:formulas_path]
37
44
  Fontist.formulas_path = Pathname.new(options[:formulas_path])
data/lib/fontist/cli.rb CHANGED
@@ -27,6 +27,7 @@ module Fontist
27
27
  STATUS_FONTCONFIG_NOT_FOUND = 14
28
28
  STATUS_FONTCONFIG_FILE_NOT_FOUND = 15
29
29
  STATUS_FONTIST_VERSION_ERROR = 15
30
+ STATUS_INVALID_CONFIG_ATTRIBUTE = 16
30
31
 
31
32
  ERROR_TO_STATUS = {
32
33
  Fontist::Errors::UnsupportedFontError => [STATUS_NON_SUPPORTED_FONT_ERROR],
@@ -15,42 +15,58 @@ module Fontist
15
15
  end
16
16
 
17
17
  def set(key, value)
18
+ attr = key.to_sym
19
+ unless default_values.key?(attr)
20
+ raise Errors::InvalidConfigAttributeError,
21
+ "No such attribute '#{attr}' exists."
22
+ end
23
+
18
24
  v = normalize_value(value)
19
- @custom_values[key.to_s] = v
25
+ if respond_to?("#{attr}=")
26
+ public_send("#{attr}=", v)
27
+ else
28
+ @custom_values[attr] = v
29
+ end
20
30
 
21
31
  persist
22
32
  end
23
33
 
24
34
  def delete(key)
25
- @custom_values.delete(key.to_s)
35
+ @custom_values.delete(key.to_sym)
26
36
 
27
37
  persist
28
38
  end
29
39
 
30
40
  def default_value(key)
31
- default_values[key.to_s]
41
+ default_values[key.to_sym]
32
42
  end
33
43
 
34
44
  def default_values
35
- { open_timeout: 10,
36
- read_timeout: 10 }.transform_keys(&:to_s)
45
+ { fonts_path: Fontist.fontist_path.join("fonts"),
46
+ open_timeout: 10,
47
+ read_timeout: 10 }
37
48
  end
38
49
 
39
50
  def persist
51
+ values = @custom_values.transform_keys(&:to_s)
40
52
  FileUtils.mkdir_p(File.dirname(Fontist.config_path))
41
- File.write(Fontist.config_path, YAML.dump(@custom_values))
53
+ File.write(Fontist.config_path, YAML.dump(values))
42
54
  end
43
55
 
44
56
  def load
45
57
  @custom_values = load_config_file
46
58
  end
47
59
 
60
+ def fonts_path=(value)
61
+ @custom_values[:fonts_path] = File.expand_path(value)
62
+ end
63
+
48
64
  private
49
65
 
50
66
  def load_config_file
51
67
  return {} unless File.exist?(Fontist.config_path)
52
68
 
53
- YAML.load_file(Fontist.config_path)
69
+ YAML.load_file(Fontist.config_path).transform_keys(&:to_sym)
54
70
  end
55
71
 
56
72
  def normalize_value(value)
@@ -2,15 +2,19 @@ module Fontist
2
2
  class ConfigCLI < Thor
3
3
  include CLI::ClassOptions
4
4
 
5
- STATUS_SUCCESS = 0
6
-
7
5
  desc "show", "Show values of the current config"
8
6
  def show
9
7
  handle_class_options(options)
10
8
  values = Config.instance.custom_values
11
- Fontist.ui.success("Current config:")
12
- Fontist.ui.success(format_hash(values))
13
- STATUS_SUCCESS
9
+
10
+ if values.empty?
11
+ Fontist.ui.success("Config is empty.")
12
+ else
13
+ Fontist.ui.success("Current config:")
14
+ Fontist.ui.success(format_hash(values))
15
+ end
16
+
17
+ CLI::STATUS_SUCCESS
14
18
  end
15
19
 
16
20
  desc "set KEY VALUE", "Set the KEY attribute to VALUE in the current config"
@@ -18,7 +22,10 @@ module Fontist
18
22
  handle_class_options(options)
19
23
  Config.instance.set(key, value)
20
24
  Fontist.ui.success("'#{key}' set to '#{value}'.")
21
- STATUS_SUCCESS
25
+ CLI::STATUS_SUCCESS
26
+ rescue Errors::InvalidConfigAttributeError => e
27
+ Fontist.ui.error(e.message)
28
+ CLI::STATUS_INVALID_CONFIG_ATTRIBUTE
22
29
  end
23
30
 
24
31
  desc "delete KEY", "Delete the KEY attribute from the current config"
@@ -28,13 +35,24 @@ module Fontist
28
35
  Fontist.ui.success(
29
36
  "'#{key}' reset to default ('#{Config.instance.default_value(key)}').",
30
37
  )
31
- STATUS_SUCCESS
38
+ CLI::STATUS_SUCCESS
39
+ end
40
+
41
+ desc "keys", "Print all available config attributes"
42
+ def keys
43
+ handle_class_options(options)
44
+ Fontist.ui.say("Available keys:")
45
+ Config.instance.default_values.each do |key, value|
46
+ Fontist.ui.say("#{key} (default: #{value})")
47
+ end
48
+ CLI::STATUS_SUCCESS
32
49
  end
33
50
 
34
51
  private
35
52
 
36
53
  def format_hash(hash)
37
- YAML.dump(hash).gsub(/^---.*$/, "").strip
54
+ h = hash.transform_keys(&:to_s)
55
+ YAML.dump(h).gsub(/^---.*$/, "").strip
38
56
  end
39
57
  end
40
58
  end
@@ -40,6 +40,8 @@ module Fontist
40
40
 
41
41
  class MainRepoNotFoundError < FormulaIndexNotFoundError; end
42
42
 
43
+ class InvalidConfigAttributeError < GeneralError; end
44
+
43
45
  class InvalidResourceError < GeneralError; end
44
46
 
45
47
  class LicensingError < GeneralError; end
data/lib/fontist/font.rb CHANGED
@@ -2,6 +2,7 @@ require "fontist/font_installer"
2
2
  require "fontist/font_path"
3
3
  require "fontist/formula_picker"
4
4
  require "fontist/fontconfig"
5
+ require "fontist/formula_suggestion"
5
6
 
6
7
  module Fontist
7
8
  class Font
@@ -109,17 +110,48 @@ module Fontist
109
110
  end
110
111
 
111
112
  def install_formula
112
- download_formula || raise_formula_not_found
113
+ download_formula || make_suggestions || raise_formula_not_found
113
114
  end
114
115
 
115
116
  def download_formula
116
- formula = Formula.find_by_key(@name)
117
+ formula = Formula.find_by_key_or_name(@name)
117
118
  return unless formula
118
119
  return unless formula.downloadable?
119
120
 
120
121
  request_formula_installation(formula)
121
122
  end
122
123
 
124
+ def make_suggestions
125
+ return unless Fontist.interactive?
126
+
127
+ suggestions = fuzzy_search_formulas
128
+ return if suggestions.empty?
129
+
130
+ choice = offer_to_choose(suggestions)
131
+ return unless choice
132
+
133
+ request_formula_installation(choice)
134
+ end
135
+
136
+ def fuzzy_search_formulas
137
+ @formula_suggestion ||= FormulaSuggestion.new
138
+ @formula_suggestion.find(@name)
139
+ end
140
+
141
+ def offer_to_choose(formulas)
142
+ Fontist.ui.say("Formula '#{@name}' not found. Did you mean?")
143
+
144
+ formulas.each_with_index do |formula, index|
145
+ Fontist.ui.say("[#{index}] #{formula.name}")
146
+ end
147
+
148
+ choice = Fontist.ui.ask("Please type number or " \
149
+ "press ENTER to skip installation:").chomp
150
+ return unless choice.to_i.to_s == choice
151
+
152
+ formulas[choice.to_i]
153
+ end
154
+
123
155
  def raise_formula_not_found
124
156
  raise Errors::FormulaNotFoundError.new(@name)
125
157
  end
@@ -193,7 +225,7 @@ module Fontist
193
225
  def check_and_confirm_required_license(formula)
194
226
  return @confirmation unless formula.license_required
195
227
 
196
- show_license(formula.license) unless @hide_licenses
228
+ show_license(formula) unless @hide_licenses
197
229
  return @confirmation if @confirmation.casecmp?("yes")
198
230
 
199
231
  confirmation = ask_for_agreement
@@ -204,8 +236,8 @@ module Fontist
204
236
  )
205
237
  end
206
238
 
207
- def show_license(license)
208
- Fontist.ui.say(license_agrement_message(license))
239
+ def show_license(formula)
240
+ Fontist.ui.say(license_agrement_message(formula))
209
241
  end
210
242
 
211
243
  def ask_for_agreement
@@ -215,20 +247,28 @@ module Fontist
215
247
  )
216
248
  end
217
249
 
218
- def license_agrement_message(license)
250
+ def license_agrement_message(formula)
251
+ human_name = human_name(formula)
252
+
219
253
  <<~MSG
220
- FONT LICENSE ACCEPTANCE REQUIRED FOR "#{name}":
254
+ FONT LICENSE ACCEPTANCE REQUIRED FOR "#{human_name}":
221
255
 
222
256
  Fontist can install this font if you accept its licensing conditions.
223
257
 
224
- FONT LICENSE BEGIN ("#{name}")
258
+ FONT LICENSE BEGIN ("#{human_name}")
225
259
  -----------------------------------------------------------------------
226
- #{license}
260
+ #{formula.license}
227
261
  -----------------------------------------------------------------------
228
- FONT LICENSE END ("#{name}")
262
+ FONT LICENSE END ("#{human_name}")
229
263
  MSG
230
264
  end
231
265
 
266
+ def human_name(formula)
267
+ return formula.name if @by_formula
268
+
269
+ formula.font_by_name(@name).name
270
+ end
271
+
232
272
  def update_fontconfig
233
273
  return unless @update_fontconfig
234
274
 
@@ -5,6 +5,11 @@ require "git"
5
5
 
6
6
  module Fontist
7
7
  class Formula
8
+ NAMESPACES = {
9
+ "sil" => "SIL",
10
+ "macos" => "macOS",
11
+ }.freeze
12
+
8
13
  def self.update_formulas_repo
9
14
  Update.call
10
15
  end
@@ -15,6 +20,12 @@ module Fontist
15
20
  end
16
21
  end
17
22
 
23
+ def self.all_keys
24
+ Dir[Fontist.formulas_path.join("**/*.yml").to_s].map do |path|
25
+ path.sub("#{Fontist.formulas_path}/", "").sub(".yml", "")
26
+ end
27
+ end
28
+
18
29
  def self.find(font_name)
19
30
  Indexes::FontIndex.from_yaml.load_formulas(font_name).first
20
31
  end
@@ -45,6 +56,10 @@ module Fontist
45
56
  end.flatten
46
57
  end
47
58
 
59
+ def self.find_by_key_or_name(name)
60
+ find_by_key(name) || find_by_name(name)
61
+ end
62
+
48
63
  def self.find_by_key(key)
49
64
  path = Fontist.formulas_path.join("#{key}.yml")
50
65
  return unless File.exist?(path)
@@ -52,6 +67,16 @@ module Fontist
52
67
  new_from_file(path)
53
68
  end
54
69
 
70
+ def self.find_by_name(name)
71
+ key = name_to_key(name)
72
+
73
+ find_by_key(key)
74
+ end
75
+
76
+ def self.name_to_key(name)
77
+ name.downcase.gsub(" ", "_")
78
+ end
79
+
55
80
  def self.find_by_font_file(font_file)
56
81
  key = Indexes::FilenameIndex
57
82
  .from_yaml
@@ -69,7 +94,7 @@ module Fontist
69
94
 
70
95
  def initialize(data, path)
71
96
  @data = data
72
- @path = path
97
+ @path = real_path(path)
73
98
  end
74
99
 
75
100
  def to_index_formula
@@ -89,7 +114,13 @@ module Fontist
89
114
  end
90
115
 
91
116
  def key
92
- key_from_path
117
+ @key ||= {}
118
+ @key[@path] ||= key_from_path
119
+ end
120
+
121
+ def name
122
+ @name ||= {}
123
+ @name[key] ||= namespace.empty? ? base_name : "#{namespace}/#{base_name}"
93
124
  end
94
125
 
95
126
  def description
@@ -142,6 +173,12 @@ module Fontist
142
173
  @data["instructions"]
143
174
  end
144
175
 
176
+ def font_by_name(name)
177
+ fonts.find do |font|
178
+ font.name.casecmp?(name)
179
+ end
180
+ end
181
+
145
182
  def fonts_by_name(name)
146
183
  fonts.select do |font|
147
184
  font.name.casecmp?(name)
@@ -166,9 +203,40 @@ module Fontist
166
203
 
167
204
  private
168
205
 
206
+ def real_path(path)
207
+ Dir.glob(path).first
208
+ end
209
+
169
210
  def key_from_path
170
211
  escaped = Regexp.escape("#{Fontist.formulas_path}/")
171
- @path.sub(Regexp.new("^#{escaped}"), "").sub(/\.yml$/, "")
212
+ @path.sub(Regexp.new("^#{escaped}"), "").sub(/\.yml$/, "").to_s
213
+ end
214
+
215
+ def namespace
216
+ namespace_from_mappings || namespace_from_key
217
+ end
218
+
219
+ def namespace_from_mappings
220
+ parts = key.split("/")
221
+ namespace_from_key = parts.take(parts.size - 1).join("/")
222
+ NAMESPACES[namespace_from_key]
223
+ end
224
+
225
+ def namespace_from_key
226
+ parts = key.downcase.gsub("_", " ").split("/")
227
+ parts.take(parts.size - 1).map do |namespace|
228
+ namespace.split.map(&:capitalize).join(" ")
229
+ end.join("/")
230
+ end
231
+
232
+ def base_name
233
+ @data["name"] || base_name_from_key
234
+ end
235
+
236
+ def base_name_from_key
237
+ key.split("/").last
238
+ .downcase.gsub("_", " ")
239
+ .split.map(&:capitalize).join(" ")
172
240
  end
173
241
 
174
242
  def fonts_by_family
@@ -0,0 +1,55 @@
1
+ require "fuzzy_match"
2
+
3
+ module Fontist
4
+ class FormulaSuggestion
5
+ MINIMUM_REQUIRED_SCORE = 0.6
6
+
7
+ def initialize
8
+ @fuzzy_match = prepare_search_engine
9
+ end
10
+
11
+ def find(name)
12
+ @fuzzy_match.find_all_with_score(normalize(name))
13
+ .tap { |res| Fontist.ui.debug(prettify_result(res)) }
14
+ .select { |_key, score, _| score >= MINIMUM_REQUIRED_SCORE }
15
+ .take(10)
16
+ .map(&:first)
17
+ .map { |x| Formula.find_by_key_or_name(x) }
18
+ .select(&:downloadable?)
19
+ end
20
+
21
+ private
22
+
23
+ def normalize(name)
24
+ name.gsub(" ", "_")
25
+ end
26
+
27
+ def prepare_search_engine
28
+ dict = Formula.all_keys
29
+ stop_words = namespaces(dict).map { |ns| /^#{Regexp.escape(ns)}/i }
30
+
31
+ FuzzyMatch.new(dict, stop_words: stop_words)
32
+ end
33
+
34
+ def namespaces(keys)
35
+ keys.map do |key|
36
+ parts = key.split("/")
37
+ parts.size
38
+ parts.take(parts.size - 1).join("/")
39
+ end.uniq
40
+ end
41
+
42
+ def prettify_result(result)
43
+ list = result.map do |key, dice, leve|
44
+ sprintf(
45
+ "%<dice>.3f %<leve>.3f %<key>s",
46
+ dice: dice,
47
+ leve: leve,
48
+ key: key,
49
+ )
50
+ end
51
+
52
+ "FuzzyMatch:\n#{list.join("\n")}"
53
+ end
54
+ end
55
+ end
@@ -1,3 +1,3 @@
1
1
  module Fontist
2
- VERSION = "1.18.2".freeze
2
+ VERSION = "1.20.0".freeze
3
3
  end
data/lib/fontist.rb CHANGED
@@ -37,7 +37,7 @@ module Fontist
37
37
  end
38
38
 
39
39
  def self.fonts_path
40
- Fontist.fontist_path.join("fonts")
40
+ Pathname.new(config[:fonts_path])
41
41
  end
42
42
 
43
43
  def self.formulas_repo_path
@@ -151,4 +151,12 @@ module Fontist
151
151
  def self.log_level=(level)
152
152
  Fontist.ui.level = level
153
153
  end
154
+
155
+ def self.interactive?
156
+ @interactive || false
157
+ end
158
+
159
+ def self.interactive=(bool)
160
+ @interactive = bool
161
+ end
154
162
  end