narou 2.5.2 → 2.6.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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/ChangeLog.md +54 -0
  4. data/LICENSE.txt +26 -0
  5. data/README.md +70 -84
  6. data/lib/command/alias.rb +7 -0
  7. data/lib/command/convert.rb +40 -61
  8. data/lib/command/download.rb +9 -1
  9. data/lib/command/mail.rb +24 -13
  10. data/lib/command/send.rb +18 -2
  11. data/lib/command/setting.rb +152 -34
  12. data/lib/command/tag.rb +5 -0
  13. data/lib/command/update.rb +137 -4
  14. data/lib/converterbase.rb +12 -6
  15. data/lib/database.rb +1 -1
  16. data/lib/device/ibooks.rb +6 -5
  17. data/lib/downloader.rb +85 -83
  18. data/lib/helper.rb +3 -2
  19. data/lib/html.rb +3 -0
  20. data/lib/logger.rb +20 -12
  21. data/lib/narou.rb +16 -0
  22. data/lib/novelconverter.rb +209 -75
  23. data/lib/novelinfo.rb +1 -1
  24. data/lib/novelsetting.rb +53 -29
  25. data/lib/template.rb +4 -3
  26. data/lib/version.rb +1 -1
  27. data/lib/web/appserver.rb +46 -32
  28. data/lib/web/public/resources/common.ui.js +2 -4
  29. data/lib/web/public/resources/default-style.css +37 -0
  30. data/lib/web/public/resources/narou.library.js +48 -57
  31. data/lib/web/public/resources/narou.ui.js +71 -39
  32. data/lib/web/public/theme/Cerulean/bootstrap.min.css +11 -0
  33. data/lib/web/public/theme/Cerulean/style.css +49 -0
  34. data/lib/web/public/theme/Darkly/bootstrap.min.css +11 -0
  35. data/lib/web/public/theme/Darkly/style.css +53 -0
  36. data/lib/web/public/theme/Readable/bootstrap.min.css +11 -0
  37. data/lib/web/public/theme/Readable/style.css +53 -0
  38. data/lib/web/public/theme/Slate/bootstrap.min.css +11 -0
  39. data/lib/web/public/theme/Slate/style.css +40 -0
  40. data/lib/web/public/theme/Superhero/bootstrap.min.css +11 -0
  41. data/lib/web/public/theme/Superhero/style.css +40 -0
  42. data/lib/web/public/theme/United/bootstrap.min.css +11 -0
  43. data/lib/web/public/theme/United/style.css +47 -0
  44. data/lib/web/public/theme/fonts/glyphicons-halflings-regular.eot +0 -0
  45. data/lib/web/public/theme/fonts/glyphicons-halflings-regular.svg +288 -0
  46. data/lib/web/public/theme/fonts/glyphicons-halflings-regular.ttf +0 -0
  47. data/lib/web/public/theme/fonts/glyphicons-halflings-regular.woff +0 -0
  48. data/lib/web/public/theme/fonts/glyphicons-halflings-regular.woff2 +0 -0
  49. data/lib/web/settingmessages.rb +5 -0
  50. data/lib/web/views/index.haml +15 -19
  51. data/lib/web/views/layout.haml +6 -1
  52. data/lib/web/views/novels/setting.haml +76 -75
  53. data/lib/web/views/settings.haml +111 -107
  54. data/lib/web/views/style.scss +6 -21
  55. data/narou.gemspec +49 -6
  56. data/narou.rb +3 -1
  57. data/preset/ncode.syosetu.com/n7975cr/setting.ini +7 -0
  58. data/spec/README.txt +4 -0
  59. data/spec/data/convert_test/ruby/correct_test_ruby.txt +4 -4
  60. data/template/hotentry.txt.erb +28 -0
  61. data/template/ibunko_novel.txt.erb +4 -2
  62. data/template/novel.txt.erb +6 -3
  63. data/template/setting.ini.erb +18 -2
  64. metadata +73 -8
@@ -65,6 +65,7 @@ module Command
65
65
 
66
66
  def interactive_mode
67
67
  targets = []
68
+ return targets if Narou.web?
68
69
  puts "【対話モード】"
69
70
  puts "ダウンロードしたい小説のNコードもしくはURLを入力して下さい。(1行に1つ)"
70
71
  puts "連続して複数の小説を入力していきます。"
@@ -123,7 +124,14 @@ module Command
123
124
  end
124
125
  next
125
126
  end
126
- if Downloader.start(download_target, @options["force"], true).status != :ok
127
+ begin
128
+ downloader = Downloader.new(download_target, force: @options["force"], from_download: true)
129
+ rescue Downloader::InvalidTarget => e
130
+ error e.message
131
+ mistook_count += 1
132
+ next
133
+ end
134
+ if downloader.start_download.status != :ok
127
135
  mistook_count += 1
128
136
  next
129
137
  end
data/lib/command/mail.rb CHANGED
@@ -21,6 +21,7 @@ module Command
21
21
  ・<target>を省略した場合、新着があった小説を全て送信します。
22
22
  ・メールの送信設定は、#{Mailer::SETTING_FILE}ファイルを編集します。
23
23
  (初めてコマンドを使うときに自動で作成されます)
24
+ ・<target>にhotentryを指定した場合、最新のhotnetryを送信します
24
25
 
25
26
  Examples:
26
27
  narou mail 6 # 新着関係なくメール(送信済みフラグは立つ)
@@ -61,25 +62,33 @@ module Command
61
62
  end
62
63
  tagname_to_ids(argv)
63
64
  argv.each do |target|
64
- ebook_path = Narou.get_ebook_file_path(target, device ? device.ebook_file_ext : ".epub")
65
+ if target == "hotentry"
66
+ ebook_path = Update.get_newest_hotentry_file_path(device)
67
+ display_target = "hotentry"
68
+ else
69
+ ebook_path = Narou.get_ebook_file_path(target, device ? device.ebook_file_ext : ".epub")
70
+ data = Downloader.get_data_by_target(target)
71
+ if send_all && !@options["force"]
72
+ new_arrivals_date = data["new_arrivals_date"] || Time.now
73
+ if data["last_mail_date"] && new_arrivals_date < data["last_mail_date"]
74
+ next # すでに送信済みなので送信しない
75
+ end
76
+ end
77
+ end
65
78
  unless ebook_path
66
79
  error "#{target} は存在しません" unless send_all
67
80
  next
68
81
  end
69
- data = Downloader.get_data_by_target(target)
70
- if send_all && !@options["force"]
71
- new_arrivals_date = data["new_arrivals_date"] || Time.now
72
- if data["last_mail_date"] && new_arrivals_date < data["last_mail_date"]
73
- next # すでに送信済みなので送信しない
74
- end
75
- end
76
82
  unless File.exist?(ebook_path)
77
83
  error "まだファイル(#{File.basename(ebook_path)})が無いようです" unless send_all
78
84
  next
79
85
  end
80
- id = data["id"]
81
- title = data["title"]
82
- puts "<bold><green>ID:#{id} #{TermColorLight.escape(title)}</green></bold>".termcolor
86
+ if target != "hotentry"
87
+ id = data["id"]
88
+ title = data["title"]
89
+ display_target = "ID:#{id} #{TermColorLight.escape(title)}"
90
+ end
91
+ puts "<bold><green>#{display_target}</green></bold>".termcolor
83
92
  print "メールを送信しています"
84
93
  exit_mail = false
85
94
  mail_result = nil
@@ -94,7 +103,7 @@ module Command
94
103
  puts
95
104
  if mail_result
96
105
  puts File.basename(ebook_path) + " をメールで送信しました"
97
- database[id]["last_mail_date"] = Time.now
106
+ database[id]["last_mail_date"] = Time.now if target != "hotentry"
98
107
  else
99
108
  error "#{mailer.error_message}"
100
109
  exit Narou::EXIT_ERROR_CODE # next しても次も失敗する可能性が高いのでここで終了
@@ -115,7 +124,9 @@ module Command
115
124
  puts "created #{install_path}"
116
125
  puts "メールの設定用ファイルを作成しました。設定ファイルを書き換えることで mail コマンドが有効になります。"
117
126
  puts "注意:次回以降のupdateで新着があった場合に送信可能フラグが立ちます"
118
- Helper.open_directory(Narou.get_root_dir, "設定ファイルがあるフォルダを開きますか")
127
+ unless Narou.web?
128
+ Helper.open_directory(Narou.get_root_dir, "設定ファイルがあるフォルダを開きますか")
129
+ end
119
130
  end
120
131
 
121
132
  def alter_database_add_column_last_mail_date
data/lib/command/send.rb CHANGED
@@ -22,6 +22,7 @@ module Command
22
22
  ・narou setting device=<device>としておけば、<device>の入力を省略できます
23
23
  また、convertコマンドで変換時に(端末がPCに接続されていれば)自動でデータを送信するようになります
24
24
  ・<target>を省略した場合、管理している小説全てのファイルのタイムスタンプを端末のものと比べて新しければ送信します
25
+ ・<target>にhotentryを指定した場合、最新のhotnetryを送信します
25
26
 
26
27
  Examples:
27
28
  narou send kindle 6
@@ -73,6 +74,8 @@ module Command
73
74
  error "#{device.display_name} が接続されていません"
74
75
  exit Narou::EXIT_ERROR_CODE
75
76
  end
77
+
78
+ @options["hotentry"] = Inventory.load("local_setting", :local)["hotentry"]
76
79
  send_all = false
77
80
  titles = {}
78
81
  if argv.empty?
@@ -82,6 +85,10 @@ module Command
82
85
  argv << id
83
86
  titles[id] = data["title"]
84
87
  end
88
+ if @options["hotentry"]
89
+ argv << "hotentry"
90
+ titles["hotentry"] = "hotentry"
91
+ end
85
92
  end
86
93
 
87
94
  case
@@ -95,7 +102,11 @@ module Command
95
102
 
96
103
  tagname_to_ids(argv)
97
104
  argv.each do |target|
98
- ebook_path = Narou.get_ebook_file_path(target, device.ebook_file_ext)
105
+ if target == "hotentry"
106
+ ebook_path = Update.get_newest_hotentry_file_path(device)
107
+ else
108
+ ebook_path = Narou.get_ebook_file_path(target, device.ebook_file_ext)
109
+ end
99
110
  unless ebook_path
100
111
  error "#{target} は存在しません"
101
112
  next
@@ -106,7 +117,12 @@ module Command
106
117
  end
107
118
  if send_all
108
119
  if device.ebook_file_old?(ebook_path)
109
- puts "<bold><green>ID:#{target} #{TermColorLight.escape(titles[target])}</green></bold>".termcolor
120
+ if target == "hotentry"
121
+ display_target = target
122
+ else
123
+ display_target = "ID:#{target} #{TermColorLight.escape(titles[target])}"
124
+ end
125
+ puts "<bold><green>#{display_target}</green></bold>".termcolor
110
126
  else
111
127
  next
112
128
  end
@@ -18,7 +18,8 @@ module Command
18
18
  end
19
19
 
20
20
  def initialize
21
- super("[<name>=<value> ...] [options]")
21
+ super "[<name>=<value> ...] [options]\n" \
22
+ "--burn <target> [<target2> ...]"
22
23
  @opt.separator <<-EOS
23
24
 
24
25
  ・各コマンドの設定の変更が出来ます。
@@ -55,6 +56,9 @@ module Command
55
56
  display_variable_list
56
57
  exit 0
57
58
  }
59
+ @opt.on("--burn", "指定した小説の未設定項目に共通設定を焼き付ける") {
60
+ @options["burn"] = true
61
+ }
58
62
  end
59
63
 
60
64
  def get_scope_of_variable_name(name)
@@ -132,6 +136,10 @@ module Command
132
136
 
133
137
  def execute(argv)
134
138
  super
139
+ if @options["burn"]
140
+ burn_default_settings(argv)
141
+ return
142
+ end
135
143
  if argv.empty?
136
144
  puts @opt.help
137
145
  return
@@ -237,10 +245,77 @@ module Command
237
245
  puts get_variable_list_strings(:global).gsub(/^ {4}/, "")
238
246
  end
239
247
 
248
+ #
249
+ # 小説の setting.ini の未設定項目に共通設定を焼き付ける
250
+ #
251
+ # default.* が設定されているならそれを、なければオリジナル設定を
252
+ #
253
+ def burn_default_settings(argv)
254
+ if argv.empty?
255
+ error "対象小説を指定して下さい"
256
+ exit Narou::EXIT_ERROR_CODE
257
+ end
258
+ msg = "指定された小説のsetting.iniの未項目設定に共通設定を焼き付けます。\n" \
259
+ "(共通設定とはsetting.iniの項目が未設定時に使用される default.* 系設定およびNarou.rbオリジナル設定のこと)\n" \
260
+ "よろしいですか"
261
+ return unless Narou::Input.confirm(msg)
262
+
263
+ tagname_to_ids(argv)
264
+ argv.each do |arg|
265
+ data = Downloader.get_data_by_target(arg)
266
+ unless data
267
+ error "#{arg} は存在しません"
268
+ next
269
+ end
270
+ novel_setting = NovelSetting.new(arg, true, true) # 空っぽの設定を作成
271
+ novel_setting.settings = novel_setting.load_setting_ini["global"]
272
+ original_settings = NovelSetting.get_original_settings
273
+ default_settings = NovelSetting.load_default_settings
274
+
275
+ original_settings.each do |original|
276
+ name = original[:name]
277
+ unless novel_setting.settings.include?(name)
278
+ if default_settings.include?(name)
279
+ novel_setting[name] = default_settings[name]
280
+ else
281
+ novel_setting[name] = original[:value]
282
+ end
283
+ end
284
+ end
285
+
286
+ novel_setting.save_settings
287
+ puts "#{data["title"]} の設定を保存しました"
288
+ end
289
+ end
290
+
240
291
  def self.get_setting_variables
241
292
  SETTING_VARIABLES
242
293
  end
243
294
 
295
+ def self.get_setting_tab_names
296
+ SETTING_TAB_NAMES
297
+ end
298
+
299
+ def self.get_setting_tab_info
300
+ SETTING_TAB_INFO
301
+ end
302
+
303
+ SETTING_TAB_NAMES = {
304
+ general: "一般",
305
+ detail: "詳細",
306
+ global: "Global",
307
+ default: "default.*",
308
+ force: "force.*",
309
+ command: "コマンド",
310
+ }
311
+
312
+ SETTING_TAB_INFO = {
313
+ global: "Global な設定はユーザープロファイルに保存され、すべての narou コマンドで使われます",
314
+ default: "default.* 系の設定は個別の変換設定で未設定の項目の挙動を指定することが出来ます",
315
+ force: "force.* 系の設定は個別設定、default.* 等の設定を無視して反映されるようになります",
316
+ command: "default_args.* 系の設定は、各種コマンドのオプションを省略した場合に使用されるオプションを指定出来ます",
317
+ }
318
+
244
319
  SETTING_VARIABLES = {
245
320
  local: {
246
321
  # 変数名 => { type: 受け付ける型, help: 説明[, invisible: 不可視化フラグ,
@@ -250,83 +325,117 @@ module Command
250
325
  type: :select,
251
326
  help: "変換、送信対象の端末(sendの--help参照)",
252
327
  select_keys: Device::DEVICES.keys,
253
- select_summaries: Device::DEVICES.values.map { |d| d::DISPLAY_NAME }
328
+ select_summaries: Device::DEVICES.values.map { |d| d::DISPLAY_NAME },
329
+ tab: :general
330
+ },
331
+ "hotentry" => {
332
+ type: :boolean, help: "新着投稿だけをまとめたデータを作る",
333
+ tab: :general
334
+ },
335
+ "hotentry.auto-mail" => {
336
+ type: :boolean, help: "新着投稿作成時にメールを送る(mail設定済みの場合)",
337
+ tab: :detail
254
338
  },
255
339
  "update.strong" => {
256
- type: :boolean, help: "更新漏れが無い様に改稿日の分は必ずDLするか"
340
+ type: :boolean, help: "更新漏れが無い様に改稿日の分は必ずDLする",
341
+ tab: :general
257
342
  },
258
343
  "update.logging" => {
259
- type: :boolean, help: "更新時のログを保存するかどうか"
344
+ type: :boolean, help: "更新時のログを保存する",
345
+ tab: :detail
260
346
  },
261
347
  "update.convert-only-new-arrival" => {
262
- type: :boolean, help: "更新時に新着のみ変換を実行するかどうか"
348
+ type: :boolean, help: "更新時に新着のみ変換を実行する",
349
+ tab: :general
263
350
  },
264
351
  "update.sort-by" => {
265
352
  type: :select,
266
353
  help: "アップデートを指定した項目順で行う\n#{Narou.update_sort_key_summaries(40)}",
267
354
  select_keys: Narou::UPDATE_SORT_KEYS.keys,
268
- select_summaries: Narou::UPDATE_SORT_KEYS.values
355
+ select_summaries: Narou::UPDATE_SORT_KEYS.values,
356
+ tab: :general
269
357
  },
270
358
  "convert.copy-to" => {
271
359
  type: :directory,
272
360
  help: "変換したらこのフォルダにコピーする\n" \
273
- " ※注意:存在しないフォルダだとエラーになる"
274
- },
275
- "convert.copy-to-grouping" => {
276
- type: :boolean, help: "copy-toで指定したフォルダの中で更にdevice毎にフォルダを振り分ける"
361
+ " ※注意:存在しないフォルダだとエラーになる",
362
+ tab: :general
277
363
  },
278
364
  "convert.copy_to" => {
279
365
  type: :directory, help: "copy-toの昔の書き方(非推奨)", invisible: true
280
366
  },
281
367
  "convert.no-epub" => {
282
- type: :boolean, help: "EPUB変換を無効にするか", invisible: true
368
+ type: :boolean, help: "EPUB変換を無効にする", invisible: true,
369
+ tab: :detail
283
370
  },
284
371
  "convert.no-mobi" => {
285
- type: :boolean, help: "MOBI変換を無効にするか", invisible: true
372
+ type: :boolean, help: "MOBI変換を無効にする", invisible: true,
373
+ tab: :detail
286
374
  },
287
375
  "convert.no-strip" => {
288
376
  type: :boolean,
289
- help: "MOBIのstripを無効にするか\n" \
377
+ help: "MOBIのstripを無効にする\n" \
290
378
  " ※注意:KDP用のMOBIはstripしないでください",
291
- invisible: true
379
+ invisible: true,
380
+ tab: :detail
292
381
  },
293
382
  "convert.no-zip" => {
294
- type: :boolean, help: "i文庫用のzipファイル作成を無効にするか", invisible: true
383
+ type: :boolean, help: "i文庫用のzipファイル作成を無効にする", invisible: true,
384
+ tab: :detail
295
385
  },
296
386
  "convert.no-open" => {
297
- type: :boolean, help: "変換時に保存フォルダを開かないようにするか"
387
+ type: :boolean, help: "変換時に保存フォルダを開かないようにする",
388
+ tab: :general
298
389
  },
299
390
  "convert.inspect" => {
300
- type: :boolean, help: "常に変換時に調査結果を表示するか"
391
+ type: :boolean, help: "常に変換時に調査結果を表示する",
392
+ tab: :detail
301
393
  },
302
394
  "convert.multi-device" => {
303
395
  type: :multiple,
304
396
  help: "複数の端末用に同時に変換する。deviceよりも優先される。" \
305
397
  "端末名をカンマ区切りで入力。ただのEPUBを出力したい場合はepubを指定",
306
398
  select_keys: Device::DEVICES.keys,
307
- select_summaries: Device::DEVICES.values.map { |d| d::DISPLAY_NAME }
399
+ select_summaries: Device::DEVICES.values.map { |d| d::DISPLAY_NAME },
400
+ tab: :general
401
+ },
402
+ "convert.copy-to-grouping" => {
403
+ type: :boolean, help: "copy-toで指定したフォルダの中で更にdevice毎にフォルダを振り分ける",
404
+ tab: :general
308
405
  },
309
406
  "download.interval" => {
310
- type: :float, help: "各話DL時に指定した秒数待機する。デフォルト0"
407
+ type: :float, help: "各話DL時に指定した秒数待機する。デフォルト0",
408
+ tab: :detail
311
409
  },
312
410
  "download.wait-steps" => {
313
411
  type: :integer,
314
412
  help: "この値で指定した話数ごとにウェイトを入れる\n" \
315
- " ※注意:11以上を設定してもなろうの場合は10話ごとにウェイトが入ります"
413
+ " ※注意:11以上を設定してもなろうの場合は10話ごとにウェイトが入ります",
414
+ tab: :detail
316
415
  },
317
416
  "download.use-subdirectory" => {
318
417
  type: :boolean,
319
- help: "小説を一定数ごとにサブフォルダへ分けて保存するか\n" \
320
- " ※注意:小説を大量に同一フォルダに保存するとパフォーマンスが劣化する回避策"
418
+ help: "小説を一定数ごとにサブフォルダへ分けて保存する\n" \
419
+ " ※注意:小説を大量に同一フォルダに保存するとパフォーマンスが劣化する回避策",
420
+ tab: :detail
321
421
  },
322
422
  "send.without-freeze" => {
323
- type: :boolean, help: "一括送信時に凍結された小説は対象外に"
423
+ type: :boolean, help: "一括送信時に凍結された小説は対象外にする",
424
+ tab: :general
324
425
  },
325
426
  "send.backup-bookmark" => {
326
- type: :boolean, help: "一括送信時に栞データを自動でバックアップするか(KindlePW系用)"
427
+ type: :boolean, help: "一括送信時に栞データを自動でバックアップする(KindlePW系用)",
428
+ tab: :detail
327
429
  },
328
430
  "multiple-delimiter" => {
329
- type: :string, help: "--multiple指定時の区切り文字"
431
+ type: :string, help: "--multiple指定時の区切り文字",
432
+ tab: :detail
433
+ },
434
+ "theme" => {
435
+ type: :select, help: "WEB UI 用テーマ選択",
436
+ select_keys: Narou.get_theme_names,
437
+ select_summaries: Narou.get_theme_names,
438
+ tab: :general
330
439
  },
331
440
  },
332
441
  global: {
@@ -334,25 +443,32 @@ module Command
334
443
  type: :directory, help: "AozoraEpub3のあるフォルダを指定", invisible: true
335
444
  },
336
445
  "difftool" => {
337
- type: :string, help: "Diffで使うツールのパスを指定する"
446
+ type: :string, help: "Diffで使うツールのパスを指定する",
447
+ tab: :global
338
448
  },
339
449
  "difftool.arg" => {
340
- type: :string, help: "difftoolで使う引数を設定(オプション)"
450
+ type: :string, help: "difftoolで使う引数を設定(オプション)",
451
+ tab: :global
341
452
  },
342
453
  "no-color" => {
343
- type: :boolean, help: "カラー表示を無効にするか"
454
+ type: :boolean, help: "カラー表示を無効にする",
455
+ tab: :global
344
456
  },
345
457
  "server-port" => {
346
- type: :integer, help: "WEBサーバ起動時のポート"
458
+ type: :integer, help: "WEBサーバ起動時のポート",
459
+ tab: :global
347
460
  },
348
461
  "server-bind" => {
349
- type: :string, help: "WEBサーバのホスト制限(未設定時:起動PCのIP)", invisible: true
462
+ type: :string, help: "WEBサーバのホスト制限(未設定時:起動PCのIP)", invisible: true,
463
+ tab: :global
350
464
  },
351
465
  "over18" => {
352
- type: :boolean, help: "18歳以上かどうか", invisible: true
466
+ type: :boolean, help: "18歳以上かどうか", invisible: true,
467
+ tab: :global
353
468
  },
354
469
  "dismiss-notice" => {
355
- type: :boolean, help: "お知らせを消すかどうか", invisible: true
470
+ type: :boolean, help: "お知らせを消す", invisible: true,
471
+ tab: :global
356
472
  },
357
473
  }
358
474
  }
@@ -364,7 +480,8 @@ module Command
364
480
  help: "\n " + default[:help],
365
481
  invisible: true,
366
482
  select_keys: default[:select_keys],
367
- select_summaries: default[:select_summaries]
483
+ select_summaries: default[:select_summaries],
484
+ tab: kind.intern
368
485
  }
369
486
  SETTING_VARIABLES[:local]["#{kind}." + default[:name]] = value
370
487
  end
@@ -373,7 +490,8 @@ module Command
373
490
  Dir.glob(File.expand_path(File.join(File.dirname(__FILE__), "*.rb"))) do |path|
374
491
  cmd_name = File.basename(path, ".rb")
375
492
  SETTING_VARIABLES[:local]["default_args." + cmd_name] = {
376
- type: :string, help: "#{cmd_name} コマンドのデフォルトオプション", invisible: true
493
+ type: :string, help: "#{cmd_name} コマンドのデフォルトオプション", invisible: true,
494
+ tab: :command
377
495
  }
378
496
  end
379
497