omaship 0.2.2 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f76522b3b89f67ab222e30cbd3055791f747ccd84a3c1121325a2b881b23f88
4
- data.tar.gz: ed00630e44a0691758648ef290d9ade09d2633f1df723cd526268b1a8f103395
3
+ metadata.gz: eb563c444317757eeb0a2a8907d0c6b6b26bd1dc03e2373124270430e8565949
4
+ data.tar.gz: cbee20a1cd5b4e9e7c3e6af65246f632e4c024522d0caff7a68027bc8c43a0c5
5
5
  SHA512:
6
- metadata.gz: 21cf9fbd52bfeb7cb833f91894018ffe9c4d22a26efebff655ae609e28ab7ce05256af8ccaf9e767844427f9a6942165c4e5a96e64253a7c045ec9f336234c8d
7
- data.tar.gz: be9499fdf4ca57543abaf8411b5a90ec4495dc5aea79a8fbcaf78a3fc7c7b837fd1c41e6d61abd0b31c2ef879a9a3348c32c137919a65c32fb3adafc0b2987c1
6
+ metadata.gz: 70ac4ca7b97b49bdfd46b0ac34d9193f183c57cad9c72a695434c705f92e342246d28ff2cf27a5d5b321bad22fe2ada28102c9277de888b131689c39bf66f147
7
+ data.tar.gz: 0432cb652ead9e22d92898bb17bd8d9babf45525b6f2e273248fdd55d47ce2deadf54a014002c9184dce302c024d4bad59abc4d7f2c371aa73217b964e981534
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.0](https://github.com/bloomedai/omaship/compare/omaship/v0.3.0...omaship/v0.4.0) (2026-03-14)
4
+
5
+
6
+ ### Features
7
+
8
+ * ✨ redesign onboarding flow with progressive CLI terminal path ([#324](https://github.com/bloomedai/omaship/issues/324)) ([872b145](https://github.com/bloomedai/omaship/commit/872b145a2f72775448375557246b4b31fe194459))
9
+
10
+ ## [0.3.0](https://github.com/bloomedai/omaship/compare/omaship/v0.2.2...omaship/v0.3.0) (2026-03-09)
11
+
12
+
13
+ ### Features
14
+
15
+ * ✨ ship the free landing-page flow end to end ([#290](https://github.com/bloomedai/omaship/issues/290)) ([352c088](https://github.com/bloomedai/omaship/commit/352c088219acff3a24a72cda965d12bf1767af1e))
16
+
3
17
  ## [0.2.2](https://github.com/bloomedai/omaship/compare/omaship/v0.2.1...omaship/v0.2.2) (2026-03-06)
4
18
 
5
19
 
data/README.md CHANGED
@@ -18,7 +18,7 @@ brew install omaship
18
18
  ## Quick Start
19
19
 
20
20
  ```bash
21
- omaship login --token <token> --host https://build.omaship.com
21
+ omaship login --token <token>
22
22
  omaship list
23
23
  omaship use <ship-ref>
24
24
  omaship info
@@ -33,7 +33,7 @@ omaship use 17
33
33
 
34
34
  ## Ship Selection
35
35
 
36
- `info`, `configure`, and `deploy` resolve the target ship in this order:
36
+ `info` and `deploy` resolve the target ship in this order:
37
37
 
38
38
  1. `--ship <ship-ref>`
39
39
  2. saved default from `omaship use <ship-ref>`
@@ -51,7 +51,6 @@ Numeric ids are also accepted.
51
51
  - `omaship use <ship-ref>`
52
52
  - `omaship info [--ship <ship-ref>]` (`status` and `ship` are aliases)
53
53
  - `omaship new <name>` (requires Full CLI access)
54
- - `omaship configure --payments <provider> [--ship <ship-ref>]` (requires Full CLI access)
55
54
  - `omaship deploy [--ship <ship-ref>]` (requires Full CLI access)
56
55
  - `omaship complete <bash|zsh|fish>` (print shell completion script)
57
56
  - `omaship logout`
@@ -94,7 +93,7 @@ omaship complete fish > ~/.config/fish/completions/omaship.fish
94
93
 
95
94
  ## Environment
96
95
 
97
- - `OMASHIP_HOST` (default: `http://localhost:3000`)
96
+ - `OMASHIP_HOST` (default: `https://omaship.com`)
98
97
  - `OMASHIP_TOKEN` (optional alternative for `login`)
99
98
 
100
99
  ## Credentials
@@ -26,16 +26,15 @@ module Omaship
26
26
  post_json("/api/v1/cli/ships", { ship: { root_domain: root_domain, visibility: visibility } })
27
27
  end
28
28
 
29
- def ship(ship_id:)
30
- get_json("/api/v1/cli/ships/#{ship_id}")
31
- end
32
-
33
- def ship_logs(ship_id:)
34
- get_json("/api/v1/cli/ships/#{ship_id}/logs")
29
+ def create_landing_page(name:, color_scheme: "mono-dark", purpose_profile: {})
30
+ post_json("/api/v1/cli/ships", {
31
+ ship: { name: name, landing_page: true, color_scheme: color_scheme },
32
+ purpose_profile: purpose_profile
33
+ })
35
34
  end
36
35
 
37
- def create_configuration(ship_id:, package:)
38
- post_json("/api/v1/cli/ships/#{ship_id}/configurations", { configuration: { package: package } })
36
+ def ship(ship_id:)
37
+ get_json("/api/v1/cli/ships/#{ship_id}")
39
38
  end
40
39
 
41
40
  def create_deploy(ship_id:)
data/lib/omaship/cli.rb CHANGED
@@ -51,8 +51,8 @@ module Omaship
51
51
  shell.say " use Set default ship from `omaship list` (for example: `omaship use omaship/acme`)"
52
52
  shell.say " info Show details for a ship (aliases: ship, status)"
53
53
  shell.say " new Create and provision a new ship"
54
- shell.say " configure Configure a ship (requires Full CLI access)"
55
54
  shell.say " deploy Deploy a ship (requires Full CLI access)"
55
+ shell.say " upgrade Open browser to upgrade your plan"
56
56
  shell.say " complete Print shell completion script (bash, zsh, fish)"
57
57
  shell.say " logout Remove local CLI credentials"
58
58
  shell.say
@@ -206,43 +206,20 @@ module Omaship
206
206
 
207
207
  desc "new NAME", "Create and provision a new ship"
208
208
  method_option :domain, type: :string, desc: "Root domain (defaults to NAME.com)"
209
+ method_option :skip_purpose, type: :boolean, default: false, desc: "Skip purpose profile questions"
209
210
  def new_ship(name)
210
211
  with_api_error_handling(command: :new_ship) do
211
212
  token = current_token
212
- root_domain = options[:domain] || "#{name}.com"
213
- payload = build_api_client(token: token).create_ship(root_domain: root_domain)
214
- ship = payload.fetch("ship")
213
+ plan = fetch_plan(token: token)
215
214
 
216
- progress_renderer.step("Setting up your codebase")
217
- final_ship = poll_until_terminal(ship_id: ship.fetch("id"), token: token)
218
-
219
- if final_ship.fetch("status") == "live"
220
- progress_renderer.step("Ready. Customers can sign up and pay at #{final_ship.fetch("root_domain")}")
215
+ if plan == "free"
216
+ create_free_ship(name: name, token: token)
221
217
  else
222
- message = final_ship["error_message"] || "Provisioning failed"
223
- raise Thor::Error, message
218
+ create_paid_ship(name: name, token: token)
224
219
  end
225
220
  end
226
221
  end
227
222
 
228
- desc "configure --payments PROVIDER", "Configure a ship"
229
- method_option :payments, type: :string, required: true
230
- method_option :ship, type: :string, desc: "Ship from `omaship list` (preferred: omaship/acme; id also works)"
231
- def configure
232
- with_api_error_handling(command: :configure) do
233
- payments_provider = options[:payments].to_s.strip.downcase
234
-
235
- token = current_token
236
- ship = resolve_ship(token: token)
237
- build_api_client(token: token).create_configuration(ship_id: ship.fetch("id"), package: payments_provider)
238
-
239
- progress_renderer.step("Configuration queued.")
240
-
241
- poll_until_configuration_complete(ship_id: ship.fetch("id"), token: token)
242
- progress_renderer.step("Configuration applied.")
243
- end
244
- end
245
-
246
223
  desc "deploy", "Deploy a ship (requires Full CLI access)"
247
224
  method_option :ship, type: :string, desc: "Ship from `omaship list` (preferred: omaship/acme; id also works)"
248
225
  def deploy
@@ -260,6 +237,17 @@ module Omaship
260
237
  end
261
238
  end
262
239
 
240
+ desc "upgrade [SHIP]", "Open browser to upgrade your plan"
241
+ def upgrade(ship_name = nil)
242
+ url = "#{resolved_host}/settings/license"
243
+ if ship_name
244
+ url = "#{url}?ship=#{ship_name}"
245
+ end
246
+
247
+ say "Opening #{url}"
248
+ open_browser(url)
249
+ end
250
+
263
251
  desc "complete SHELL", "Print shell completion script (bash, zsh, fish)"
264
252
  long_desc <<~DESC
265
253
  Print a shell completion script to stdout.
@@ -307,7 +295,7 @@ module Omaship
307
295
  fi
308
296
 
309
297
  if [[ ${COMP_CWORD} -eq 1 ]]; then
310
- COMPREPLY=( $(compgen -W "login whoami list use info status ship new configure deploy logout complete help" -- "${cur}") )
298
+ COMPREPLY=( $(compgen -W "login whoami list use info status ship new deploy upgrade logout complete help" -- "${cur}") )
311
299
  return 0
312
300
  fi
313
301
 
@@ -316,11 +304,6 @@ module Omaship
316
304
  return 0
317
305
  fi
318
306
 
319
- if [[ "${prev}" == "--payments" ]]; then
320
- COMPREPLY=( $(compgen -W "stripe" -- "${cur}") )
321
- return 0
322
- fi
323
-
324
307
  if [[ "${prev}" == "--ship" ]]; then
325
308
  COMPREPLY=( $(compgen -W "$(omaship list 2>/dev/null | awk '/^[* ] [0-9]+ / { print $3 }')" -- "${cur}") )
326
309
  return 0
@@ -345,10 +328,7 @@ module Omaship
345
328
  COMPREPLY=( $(compgen -W "--ship --host -h --help" -- "${cur}") )
346
329
  ;;
347
330
  new)
348
- COMPREPLY=( $(compgen -W "--domain --host -h --help" -- "${cur}") )
349
- ;;
350
- configure)
351
- COMPREPLY=( $(compgen -W "--payments --ship --host -h --help" -- "${cur}") )
331
+ COMPREPLY=( $(compgen -W "--domain --skip-purpose --host -h --help" -- "${cur}") )
352
332
  ;;
353
333
  complete)
354
334
  COMPREPLY=( $(compgen -W "bash zsh fish" -- "${cur}") )
@@ -386,8 +366,8 @@ module Omaship
386
366
  'status:Alias for info'
387
367
  'ship:Alias for info'
388
368
  'new:Create and provision a new ship'
389
- 'configure:Configure a ship'
390
369
  'deploy:Deploy a ship'
370
+ 'upgrade:Open browser to upgrade your plan'
391
371
  'logout:Remove local CLI credentials'
392
372
  'complete:Print shell completion script'
393
373
  'help:Describe available commands'
@@ -417,10 +397,7 @@ module Omaship
417
397
  _arguments '--ship[Ship from omaship list]:ship:_omaship_ship_refs' '--host[API host]:host:'
418
398
  ;;
419
399
  new)
420
- _arguments '--domain[Root domain]:domain:' '--host[API host]:host:' '1:name:'
421
- ;;
422
- configure)
423
- _arguments '--payments[Payments provider]:provider:(stripe)' '--ship[Ship from omaship list]:ship:_omaship_ship_refs' '--host[API host]:host:'
400
+ _arguments '--domain[Root domain]:domain:' '--skip-purpose[Skip purpose questions]' '--host[API host]:host:' '1:name:'
424
401
  ;;
425
402
  complete)
426
403
  _arguments '1:shell:(bash zsh fish)'
@@ -444,19 +421,149 @@ module Omaship
444
421
  end
445
422
 
446
423
  complete -c omaship -f
447
- complete -c omaship -n '__fish_use_subcommand' -a 'login whoami list use info status ship new configure deploy logout complete help'
424
+ complete -c omaship -n '__fish_use_subcommand' -a 'login whoami list use info status ship new deploy upgrade logout complete help'
448
425
  complete -c omaship -l host -d 'API host'
449
426
 
450
427
  complete -c omaship -n '__fish_seen_subcommand_from login' -l token -d 'API token from omaship settings'
451
428
  complete -c omaship -n '__fish_seen_subcommand_from new' -l domain -d 'Root domain'
452
- complete -c omaship -n '__fish_seen_subcommand_from configure' -l payments -d 'Payments provider' -a 'stripe'
453
- complete -c omaship -n '__fish_seen_subcommand_from configure' -l ship -d 'Ship from omaship list' -a '(__fish_omaship_ship_refs)'
429
+ complete -c omaship -n '__fish_seen_subcommand_from new' -l skip-purpose -d 'Skip purpose questions'
454
430
  complete -c omaship -n '__fish_seen_subcommand_from info status ship deploy' -l ship -d 'Ship from omaship list' -a '(__fish_omaship_ship_refs)'
455
431
  complete -c omaship -n '__fish_seen_subcommand_from use' -f -a '(__fish_omaship_ship_refs)'
456
432
  complete -c omaship -n '__fish_seen_subcommand_from complete' -f -a 'bash zsh fish'
457
433
  FISH
458
434
  end
459
435
 
436
+ def fetch_plan(token:)
437
+ auth = build_api_client(token: token).authenticate
438
+ auth.dig("user", "plan") || "free"
439
+ end
440
+
441
+ def create_paid_ship(name:, token:)
442
+ root_domain = options[:domain] || "#{name}.com"
443
+ payload = build_api_client(token: token).create_ship(root_domain: root_domain)
444
+ ship = payload.fetch("ship")
445
+
446
+ progress_renderer.step("Setting up your codebase")
447
+ final_ship = poll_until_terminal(ship_id: ship.fetch("id"), token: token)
448
+
449
+ if final_ship.fetch("status") == "live"
450
+ progress_renderer.step("Ready. Customers can sign up and pay at #{final_ship.fetch("root_domain")}")
451
+ else
452
+ message = final_ship["error_message"] || "Provisioning failed"
453
+ raise Thor::Error, message
454
+ end
455
+ end
456
+
457
+ def create_free_ship(name:, token:)
458
+ print_free_banner
459
+
460
+ purpose_profile = {}
461
+ color_scheme = "mono-dark"
462
+
463
+ unless options[:skip_purpose]
464
+ if ask_purpose_profile?
465
+ purpose_profile = collect_purpose_profile
466
+ end
467
+ color_scheme = pick_color_scheme
468
+ end
469
+
470
+ say
471
+ progress_renderer.step("Creating your landing page...")
472
+
473
+ payload = build_api_client(token: token).create_landing_page(
474
+ name: name,
475
+ color_scheme: color_scheme,
476
+ purpose_profile: purpose_profile
477
+ )
478
+ ship = payload.fetch("ship")
479
+
480
+ if ship["distillation_status"] == "pending"
481
+ progress_renderer.step("Sokrates is distilling your answers...")
482
+ poll_until_distilled(ship_id: ship.fetch("id"), token: token)
483
+ end
484
+
485
+ say
486
+ say "Live at: https://#{ship.fetch("root_domain")}"
487
+ say "Review and refine at: #{resolved_host}/ships/#{ship.fetch("id")}/purpose_profile"
488
+ end
489
+
490
+ def print_free_banner
491
+ lines = [
492
+ "FREE PLAN",
493
+ "",
494
+ "You'll get a static landing page with a waitlist",
495
+ "on *.omaship.app",
496
+ "",
497
+ "Want the full Rails app on your own server?",
498
+ "Run: omaship upgrade"
499
+ ]
500
+ width = lines.map(&:length).max + 4
501
+ bar = "+#{"-" * (width + 2)}+"
502
+
503
+ say
504
+ say bar
505
+ lines.each do |line|
506
+ say "| #{line.ljust(width)}|"
507
+ end
508
+ say bar
509
+ say
510
+ end
511
+
512
+ def ask_purpose_profile?
513
+ say "Would you like to create a purpose profile?"
514
+ say "This helps generate better landing page copy. (5 questions, ~2 min)"
515
+ say
516
+ answer = ask("Continue with purpose profile? (Y/n):")
517
+ answer.to_s.strip.downcase != "n"
518
+ end
519
+
520
+ def collect_purpose_profile
521
+ say
522
+ say "--- Purpose Profile ---"
523
+ say
524
+
525
+ raw_problem = ask("What problem does your product solve?\n>")
526
+ raw_audience = ask("Who is most affected by this problem?\n>")
527
+ raw_belief = ask("Why is this worth solving? What's your core belief?\n>")
528
+ raw_contribution = ask("What's your contribution to the solution?\n>")
529
+ raw_outcome = ask("What does the world look like when you succeed?\n>")
530
+
531
+ {
532
+ raw_problem: raw_problem,
533
+ raw_audience: raw_audience,
534
+ raw_belief: raw_belief,
535
+ raw_contribution: raw_contribution,
536
+ raw_outcome: raw_outcome
537
+ }.reject { |_k, v| v.to_s.strip.empty? }
538
+ end
539
+
540
+ def pick_color_scheme
541
+ say
542
+ say "--- Color Scheme ---"
543
+ selected = color_picker.pick
544
+ say "Selected: #{selected}"
545
+ selected
546
+ end
547
+
548
+ def color_picker
549
+ @color_picker ||= Omaship::ColorPicker.new
550
+ end
551
+
552
+ def poll_until_distilled(ship_id:, token:)
553
+ client = build_api_client(token: token)
554
+ 30.times do
555
+ ship_payload = client.ship(ship_id: ship_id).fetch("ship")
556
+ status = ship_payload["distillation_status"]
557
+
558
+ if status == "complete" || status == "skipped" || status == "none"
559
+ return ship_payload
560
+ end
561
+
562
+ sleep 2
563
+ end
564
+ say "Distillation timed out. You can refine later in the dashboard."
565
+ end
566
+
460
567
  def current_token
461
568
  value = credentials.token
462
569
  if value.to_s.strip.empty?
@@ -560,8 +667,6 @@ module Omaship
560
667
  def permission_denied_message(command:)
561
668
  if command == :new_ship
562
669
  "`omaship new` requires a full-access token. Create a token with Full CLI access and run `omaship login` again."
563
- elsif command == :configure
564
- "`omaship configure` requires a full-access token. Create a token with Full CLI access and run `omaship login` again."
565
670
  elsif command == :deploy
566
671
  "`omaship deploy` requires a full-access token. Create a token with Full CLI access and run `omaship login` again."
567
672
  else
@@ -581,31 +686,10 @@ module Omaship
581
686
  @progress_renderer ||= Omaship::ProgressRenderer.new(out: $stdout)
582
687
  end
583
688
 
584
- def emit_logs(log_entries)
585
- log_entries.each do |entry|
586
- next unless displayable_log_entry?(entry)
587
-
588
- message = entry.fetch("message")
589
- progress_renderer.step(message)
590
- end
591
- end
592
-
593
- def displayable_log_entry?(entry)
594
- entry.fetch("level", "").to_s != "debug"
595
- end
596
-
597
689
  def poll_until_terminal(ship_id:, token:)
598
690
  client = build_api_client(token: token)
599
- last_log_id = 0
600
691
  90.times do
601
692
  ship_payload = client.ship(ship_id: ship_id).fetch("ship")
602
- logs_payload = client.ship_logs(ship_id: ship_id)
603
- logs = logs_payload.fetch("logs").select { |entry| entry.fetch("id") > last_log_id }
604
- if logs.any?
605
- last_log_id = logs.last.fetch("id")
606
- end
607
-
608
- emit_logs(logs)
609
693
  status = ship_payload.fetch("status")
610
694
  if %w[live error].include?(status)
611
695
  return ship_payload
@@ -616,27 +700,6 @@ module Omaship
616
700
  raise Thor::Error, "Provisioning timed out."
617
701
  end
618
702
 
619
- def poll_until_configuration_complete(ship_id:, token:)
620
- client = build_api_client(token: token)
621
- 60.times do
622
- ship_payload = client.ship(ship_id: ship_id).fetch("ship")
623
- pending = ship_payload.fetch("package_update_pending")
624
- if pending
625
- progress_renderer.step("Applying configuration changes.")
626
- sleep 2
627
- else
628
- error_message = ship_payload["error_message"].to_s.strip
629
- if error_message.empty?
630
- return
631
- end
632
-
633
- raise Thor::Error, error_message
634
- end
635
- end
636
-
637
- raise Thor::Error, "Configuration timed out."
638
- end
639
-
640
703
  def poll_until_deploy_finished(ship_id:, token:)
641
704
  client = build_api_client(token: token)
642
705
  last_status = nil
@@ -686,8 +749,15 @@ module Omaship
686
749
  end
687
750
  end
688
751
 
752
+ def open_browser(url)
753
+ opener = RUBY_PLATFORM.include?("darwin") ? "open" : "xdg-open"
754
+ system(opener, url)
755
+ rescue Errno::ENOENT
756
+ say "Could not open browser. Visit: #{url}"
757
+ end
758
+
689
759
  def resolved_host
690
- options[:host] || credentials.host || ENV["OMASHIP_HOST"] || "http://localhost:3000"
760
+ options[:host] || credentials.host || ENV["OMASHIP_HOST"] || "https://omaship.com"
691
761
  end
692
762
 
693
763
  def existing_credentials_status_message
@@ -0,0 +1,129 @@
1
+ require "io/console"
2
+
3
+ module Omaship
4
+ class ColorPicker
5
+ SCHEMES = [
6
+ { key: "flexoki-light", bg: "#FFFCF0", primary: "#205EA6", accent: "#AD8301", text: "#100F0F" },
7
+ { key: "flexoki-dark", bg: "#100F0F", primary: "#4385BE", accent: "#D0A215", text: "#FFFCF0" },
8
+ { key: "catppuccin-latte", bg: "#EFF1F5", primary: "#8839EF", accent: "#179299", text: "#4C4F69" },
9
+ { key: "catppuccin-mocha", bg: "#1E1E2E", primary: "#CBA6F7", accent: "#94E2D5", text: "#CDD6F4" },
10
+ { key: "rosepine-dawn", bg: "#FAF4ED", primary: "#907AA9", accent: "#D7827E", text: "#575279" },
11
+ { key: "rosepine-moon", bg: "#232136", primary: "#C4A7E7", accent: "#EA9A97", text: "#E0DEF4" },
12
+ { key: "nord-snow", bg: "#ECEFF4", primary: "#5E81AC", accent: "#88C0D0", text: "#2E3440" },
13
+ { key: "nord-frost", bg: "#2E3440", primary: "#88C0D0", accent: "#5E81AC", text: "#ECEFF4" },
14
+ { key: "mono-light", bg: "#FFFFFF", primary: "#18181B", accent: "#71717A", text: "#18181B" },
15
+ { key: "mono-dark", bg: "#18181B", primary: "#FAFAFA", accent: "#A1A1AA", text: "#FAFAFA" },
16
+ { key: "solarized-light", bg: "#FDF6E3", primary: "#268BD2", accent: "#2AA198", text: "#657B83" },
17
+ { key: "solarized-dark", bg: "#002B36", primary: "#268BD2", accent: "#2AA198", text: "#839496" }
18
+ ].freeze
19
+
20
+ DEFAULT_INDEX = 9
21
+
22
+ def initialize(out: $stdout, input: $stdin)
23
+ @out = out
24
+ @input = input
25
+ @index = DEFAULT_INDEX
26
+ end
27
+
28
+ def pick
29
+ render
30
+ loop do
31
+ key = read_key
32
+ case key
33
+ when :left, "h"
34
+ @index = (@index - 1) % SCHEMES.size
35
+ render
36
+ when :right, "l"
37
+ @index = (@index + 1) % SCHEMES.size
38
+ render
39
+ when :enter, "q"
40
+ @out.print "\e[?25h"
41
+ @out.puts
42
+ return SCHEMES[@index][:key]
43
+ when :ctrl_c
44
+ @out.print "\e[?25h"
45
+ @out.puts
46
+ return SCHEMES[DEFAULT_INDEX][:key]
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def render
54
+ scheme = SCHEMES[@index]
55
+ @out.print "\e[?25l"
56
+ @out.print "\r\e[K"
57
+ lines = build_preview(scheme)
58
+ move_up = @rendered_lines || 0
59
+ @out.print "\e[#{move_up}A" if move_up > 0
60
+ lines.each do |line|
61
+ @out.print "\r\e[K#{line}\n"
62
+ end
63
+ @rendered_lines = lines.size
64
+ end
65
+
66
+ def build_preview(scheme)
67
+ name = scheme[:key]
68
+ bg_block = color_block(scheme[:bg])
69
+ primary_block = color_block(scheme[:primary])
70
+ accent_block = color_block(scheme[:accent])
71
+ text_block = color_block(scheme[:text])
72
+
73
+ nav = SCHEMES.map.with_index do |s, i|
74
+ if i == @index
75
+ "\e[1m[#{s[:key]}]\e[0m"
76
+ else
77
+ " #{s[:key]} "
78
+ end
79
+ end
80
+
81
+ [
82
+ "",
83
+ " #{left_arrow} #{name} #{right_arrow}",
84
+ "",
85
+ " #{bg_block} bg #{primary_block} primary #{accent_block} accent #{text_block} text",
86
+ "",
87
+ " Use \e[1m<-\e[0m / \e[1m->\e[0m or \e[1mh\e[0m / \e[1ml\e[0m to browse. \e[1mEnter\e[0m to select.",
88
+ ""
89
+ ]
90
+ end
91
+
92
+ def left_arrow
93
+ @index > 0 ? "\e[1m<\e[0m" : " "
94
+ end
95
+
96
+ def right_arrow
97
+ @index < SCHEMES.size - 1 ? "\e[1m>\e[0m" : " "
98
+ end
99
+
100
+ def color_block(hex)
101
+ r, g, b = hex_to_rgb(hex)
102
+ "\e[48;2;#{r};#{g};#{b}m \e[0m"
103
+ end
104
+
105
+ def hex_to_rgb(hex)
106
+ hex = hex.delete("#")
107
+ [ hex[0..1], hex[2..3], hex[4..5] ].map { |c| c.to_i(16) }
108
+ end
109
+
110
+ def read_key
111
+ char = @input.raw { @input.getc }
112
+ case char
113
+ when "\r", "\n"
114
+ :enter
115
+ when "\e"
116
+ seq = @input.raw { @input.read_nonblock(2) rescue "" }
117
+ case seq
118
+ when "[D" then :left
119
+ when "[C" then :right
120
+ else :unknown
121
+ end
122
+ when "\x03"
123
+ :ctrl_c
124
+ else
125
+ char
126
+ end
127
+ end
128
+ end
129
+ end
@@ -1,3 +1,3 @@
1
1
  module Omaship
2
- VERSION = "0.2.2".freeze
2
+ VERSION = "0.4.0".freeze
3
3
  end
data/lib/omaship.rb CHANGED
@@ -6,4 +6,5 @@ require_relative "omaship/api_client"
6
6
  require_relative "omaship/credentials"
7
7
  require_relative "omaship/progress_renderer"
8
8
  require_relative "omaship/ship_detector"
9
+ require_relative "omaship/color_picker"
9
10
  require_relative "omaship/cli"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omaship
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Omaship
@@ -51,8 +51,7 @@ dependencies:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0'
54
- description: CLI for login, ship provisioning, configuration, and deploy operations
55
- against Omaship.
54
+ description: CLI for login, ship provisioning, and deploy operations against Omaship.
56
55
  email:
57
56
  - hello@omaship.com
58
57
  executables:
@@ -66,6 +65,7 @@ files:
66
65
  - lib/omaship.rb
67
66
  - lib/omaship/api_client.rb
68
67
  - lib/omaship/cli.rb
68
+ - lib/omaship/color_picker.rb
69
69
  - lib/omaship/credentials.rb
70
70
  - lib/omaship/progress_renderer.rb
71
71
  - lib/omaship/ship_detector.rb