sevgi-showcase 0.93.1 → 0.94.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: 50251e3e573b65b4e4fdaf8731f03af5da849ee270a0765df20a3987c0227d9b
4
- data.tar.gz: 68bdb206d1e170ff485784a7ab6caadf1c20f7e2883307cf799b1ea2b205e0c3
3
+ metadata.gz: 36b54e0f7d4b51e0815f49a28194c841e5e56ace94742c2598f95dcf4677e72c
4
+ data.tar.gz: b31295af208cee2aa2aad3061556f9848190c78610733510e73dd557b541995e
5
5
  SHA512:
6
- metadata.gz: bb652c2b35a40a038cb59d5fa19980381c10d497d5c01639d906b0a4704f3ac79ab21db919e117cf82423b3107b21e45055b345da737bcfa8c30cdd19bbb4544
7
- data.tar.gz: 13b69afa718368211e58442d31f495cc73b3792e6d9fd294b4d99c884a01feedf7ad996fc30fbeeec7c9bc3ccc858938f8d3b173df9daf27e23a8189d44fb566
6
+ metadata.gz: 646bd6e535ee9c3d61efbc49ee0e443dec23af2e94cd5b16210bae05f0a70e1a68cbf75272efbcb1d9b2af7ab84d55a2fca470b8bb15f4a806b99822bc30432e
7
+ data.tar.gz: 7ff66e247c4d5f0a98dd9fd69b27ee7e14088f67390482fd596630d1a21067fc9c9a16983af747201f44479bba547bb36f8001af26486adb0d2e1d2be5ed1f08
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ # Changelog
2
+
3
+ Sevgi Showcase follows the root Sevgi release notes:
4
+ https://github.com/roktas/sevgi/blob/main/CHANGELOG.md
data/LICENSE ADDED
@@ -0,0 +1,5 @@
1
+ Sevgi Showcase is distributed under the GNU General Public License v3.0 or later.
2
+
3
+ SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ Full project license: https://github.com/roktas/sevgi/blob/main/LICENSE
data/README.md CHANGED
@@ -1,5 +1,53 @@
1
1
  # Sevgi Showcase
2
2
 
3
- Provides executable examples, rendered outputs, and documentation-site support.
3
+ Executable examples, rendered outputs, and documentation-site support.
4
4
 
5
- See the root [README](../README.md) and the documentation site for usage.
5
+ ## Install
6
+
7
+ ```sh
8
+ gem install sevgi-showcase
9
+ ```
10
+
11
+ ## Require
12
+
13
+ ```ruby
14
+ require "sevgi/showcase"
15
+ ```
16
+
17
+ ## Example
18
+
19
+ ```ruby
20
+ suite = Sevgi::Test::Suite.new("srv")
21
+ suite.valids.map(&:name)
22
+ ```
23
+
24
+ ## Ruby compatibility
25
+
26
+ Requires Ruby 3.4.0 or newer. CI verifies Ruby 3.4 and the current development Ruby from `.ruby-version`.
27
+
28
+ ## Native prerequisites
29
+
30
+ Showcase examples use the top-level Sevgi stack. SVG-only examples need no native export gems.
31
+
32
+ PDF/PNG export workflows match `sevgi-sundries` and require the optional Ruby gems `cairo`, `rsvg2`, and `hexapdf`.
33
+ On Debian/Ubuntu:
34
+
35
+ ```sh
36
+ sudo apt-get update
37
+ sudo apt-get install -y libcairo2-dev libgdk-pixbuf-2.0-dev libgirepository1.0-dev libglib2.0-dev librsvg2-dev pkg-config
38
+ gem install cairo rsvg2 hexapdf
39
+ ```
40
+
41
+ On macOS with Homebrew:
42
+
43
+ ```sh
44
+ brew install cairo gdk-pixbuf gobject-introspection librsvg pkg-config
45
+ gem install cairo rsvg2 hexapdf
46
+ ```
47
+
48
+ ## Links
49
+
50
+ - Documentation: https://sevgi.roktas.dev
51
+ - API documentation: https://www.rubydoc.info/gems/sevgi-showcase
52
+ - Source: https://github.com/roktas/sevgi/tree/main/showcase
53
+ - Changelog: https://github.com/roktas/sevgi/blob/main/CHANGELOG.md
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sevgi
4
+ module Showcase
5
+ # Applies dark-theme color mappings to showcase source files.
6
+ # @api private
7
+ module Dark
8
+ extend self
9
+
10
+ # Applies a color mapping to source text.
11
+ # @param source [String] source text
12
+ # @param mapping [Hash{String => String}] source color to dark color mapping
13
+ # @return [String] transformed source text
14
+ # @raise [Sevgi::ArgumentError] when a mapping key is not found
15
+ def apply(source, mapping)
16
+ applied = Hash.new(0)
17
+ content = replace_quoted(source, mapping, applied)
18
+ content = replace_percent_words(content, mapping, applied)
19
+ missing = mapping.keys.reject { applied[it].positive? }
20
+
21
+ ArgumentError.("Unapplied dark mapping(s): #{missing.join(", ")}") unless missing.empty?
22
+
23
+ content
24
+ end
25
+
26
+ # Applies a color mapping to a source file and preserves its mode.
27
+ # @param source [String] source file path
28
+ # @param target [String] target file path
29
+ # @param mapping [Hash{String => String}] source color to dark color mapping
30
+ # @return [String] target file path
31
+ # @raise [Sevgi::ArgumentError] when a mapping key is not found
32
+ def apply_file(source, target, mapping)
33
+ target.tap do
34
+ File.write(target, apply(File.read(source), mapping))
35
+ File.chmod(File.stat(source).mode & 0o777, target)
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def replace_percent_words(source, mapping, applied)
42
+ source.gsub(/%[wW]\[[^\]]*\]/m) do |literal|
43
+ mapping.reduce(literal) do |text, (key, value)|
44
+ text.gsub(/(^|[\s\[])(#{Regexp.escape(key)})(?=$|[\s\]])/) do
45
+ applied[key] += 1
46
+ "#{Regexp.last_match(1)}#{value}"
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ def replace_quoted(source, mapping, applied)
53
+ source.gsub(/(?<quote>["'])(?<value>.*?)\k<quote>/) do |literal|
54
+ key = Regexp.last_match[:value]
55
+ value = mapping[key]
56
+ next literal unless value
57
+
58
+ applied[key] += 1
59
+ "#{Regexp.last_match[:quote]}#{value}#{Regexp.last_match[:quote]}"
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -45,11 +45,13 @@ module Sevgi
45
45
 
46
46
  # Runs a command and captures stdout, stderr, and exit status.
47
47
  # @param args [Array<String>] command and arguments
48
+ # @yield optional stdin writer evaluated with stdin as receiver
49
+ # @yieldreturn [Object]
48
50
  # @return [Sevgi::Test::Shell::Result]
51
+ # @raise [StandardError] when the input block raises; the child is terminated and reaped before propagation
49
52
  def run(*args, &block)
50
53
  out, err, status = Open3.popen3(*args) do |stdin, stdout, stderr, thread|
51
- inputs(stdin, thread, &block) if block
52
- outputs(stdout, stderr, thread)
54
+ capture(stdin, stdout, stderr, thread, &block)
53
55
  end
54
56
 
55
57
  Result[args, out, err, status.exitstatus]
@@ -57,22 +59,39 @@ module Sevgi
57
59
 
58
60
  private
59
61
 
60
- def inputs(stdin, thread, &block)
61
- stdin.instance_exec(thread, &block)
62
- stdin.close unless stdin.closed?
63
- end
64
-
65
- def outputs(stdout, stderr, thread)
66
- # handle `^C`
62
+ # rubocop:disable Lint/RescueException
63
+ def capture(stdin, stdout, stderr, thread, &block)
67
64
  trap = trap("INT") { handle_sigint(thread.pid) }
65
+ readers = outputs(stdout, stderr)
68
66
 
69
- out = Thread.new { stdout.readlines.map(&:chomp) }
70
- err = Thread.new { stderr.readlines.map(&:chomp) }
71
-
72
- [out.value, err.value, thread.value]
67
+ collect_capture(stdin, thread, readers, &block)
68
+ rescue Exception
69
+ cleanup_failed_capture(stdin, thread, readers)
70
+ raise
73
71
  ensure
72
+ close_input(stdin)
74
73
  trap("INT", trap) if trap
75
74
  end
75
+ # rubocop:enable Lint/RescueException
76
+
77
+ def cleanup_failed_capture(stdin, thread, readers)
78
+ close_input(stdin)
79
+ stop_process(thread)
80
+ Array(readers).each(&:join)
81
+ end
82
+
83
+ def close_input(stdin)
84
+ stdin.close unless stdin.closed?
85
+ rescue IOError
86
+ nil
87
+ end
88
+
89
+ def collect_capture(stdin, thread, readers, &block)
90
+ inputs(stdin, thread, &block)
91
+ close_input(stdin)
92
+
93
+ [readers[0].value, readers[1].value, thread.value]
94
+ end
76
95
 
77
96
  def handle_sigint(pid)
78
97
  message, signal = if @coathooks > 1
@@ -87,6 +106,33 @@ module Sevgi
87
106
  rescue Errno::ESRCH
88
107
  warn("No process to kill.")
89
108
  end
109
+
110
+ def inputs(stdin, thread, &block)
111
+ stdin.instance_exec(thread, &block) if block
112
+ end
113
+
114
+ def kill_process(signal, pid)
115
+ ::Process.kill(signal, pid)
116
+ rescue Errno::ESRCH
117
+ nil
118
+ end
119
+
120
+ def outputs(stdout, stderr)
121
+ [
122
+ Thread.new { stdout.readlines.map(&:chomp) },
123
+ Thread.new { stderr.readlines.map(&:chomp) }
124
+ ]
125
+ end
126
+
127
+ def stop_process(thread)
128
+ return unless thread&.alive?
129
+
130
+ kill_process("TERM", thread.pid)
131
+ return if thread.join(1)
132
+
133
+ kill_process("KILL", thread.pid)
134
+ thread.join
135
+ end
90
136
  end
91
137
 
92
138
  extend self
@@ -4,6 +4,6 @@ module Sevgi
4
4
  # Showcase component namespace.
5
5
  module Showcase
6
6
  # Current showcase component version.
7
- VERSION = "0.93.1"
7
+ VERSION = "0.94.0"
8
8
  end
9
9
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "showcase/dark"
3
4
  require_relative "showcase/minitest"
4
5
 
5
6
  require_relative "showcase/version"
@@ -91,6 +91,6 @@ end
91
91
  board.add_piece 7, col, dark: false
92
92
  end
93
93
 
94
- SVG :minimal, viewBox: board.view_box do
94
+ SVG :minimal, width: board.width, height: board.height, viewBox: board.view_box do
95
95
  board.render_on(self)
96
96
  end.Save
@@ -1,4 +1,4 @@
1
- <svg viewBox="0 0 900 900">
1
+ <svg width="900" height="900" viewBox="0 0 900 900">
2
2
  <style type="text/css">
3
3
  <![CDATA[
4
4
  .board-frame {
data/srv/gear-wheel.sevgi CHANGED
@@ -6,8 +6,13 @@ GEAR_STYLE = {
6
6
  stroke: "none"
7
7
  }.freeze
8
8
 
9
- SVG :minimal, viewBox: "0 0 100 100", style: { background: "lightgreen" } do
10
- mask id: "hole" do
9
+ SVG :minimal, width: 100, height: 100, viewBox: "0 0 100 100" do
10
+ rect x: 0, y: 0, width: 100, height: 100, fill: "lightgreen"
11
+
12
+ mask id: "hole",
13
+ maskUnits: "userSpaceOnUse",
14
+ maskContentUnits: "userSpaceOnUse",
15
+ x: 0, y: 0, width: 100, height: 100 do
11
16
  rect x: 0, y: 0, width: 100, height: 100, fill: "white"
12
17
  circle cx: 50, cy: 50, r: 15, fill: "black"
13
18
  end
data/srv/gear-wheel.svg CHANGED
@@ -1,5 +1,6 @@
1
- <svg viewBox="0 0 100 100" style="background:lightgreen">
2
- <mask id="hole">
1
+ <svg width="100" height="100" viewBox="0 0 100 100">
2
+ <rect x="0" y="0" width="100" height="100" fill="lightgreen"/>
3
+ <mask id="hole" maskUnits="userSpaceOnUse" maskContentUnits="userSpaceOnUse" x="0" y="0" width="100" height="100">
3
4
  <rect x="0" y="0" width="100" height="100" fill="white"/>
4
5
  <circle cx="50" cy="50" r="15" fill="black"/>
5
6
  </mask>
data/srv/grid-cells.sevgi CHANGED
@@ -6,14 +6,18 @@ COLORS = %w[#FF6961 #77DD77 #FFD700].freeze
6
6
  COLS = 8
7
7
  ROWS = 3
8
8
 
9
- SVG :minimal, viewBox: "0 0 #{COLS * CELL_SIZE} #{ROWS * CELL_SIZE}" do
10
- css ".cell" => { stroke: "white", rx: (CELL_SIZE * 0.1).round }
9
+ SVG :minimal,
10
+ width: COLS * CELL_SIZE,
11
+ height: ROWS * CELL_SIZE,
12
+ viewBox: "0 0 #{COLS * CELL_SIZE} #{ROWS * CELL_SIZE}" do
13
+ css ".cell" => { stroke: "white" }
11
14
 
12
15
  COLS.times do |col|
13
16
  ROWS.times do |row|
14
17
  rect class: "cell",
15
18
  x: col * CELL_SIZE, y: row * CELL_SIZE,
16
19
  width: CELL_SIZE, height: CELL_SIZE,
20
+ rx: (CELL_SIZE * 0.1).round,
17
21
  fill: COLORS[(col + row) % COLORS.length]
18
22
  end
19
23
  end
data/srv/grid-cells.svg CHANGED
@@ -1,34 +1,33 @@
1
- <svg viewBox="0 0 160 60">
1
+ <svg width="160" height="60" viewBox="0 0 160 60">
2
2
  <style type="text/css">
3
3
  <![CDATA[
4
4
  .cell {
5
5
  stroke: white;
6
- rx: 2;
7
6
  }
8
7
  ]]>
9
8
  </style>
10
- <rect class="cell" x="0" y="0" width="20" height="20" fill="#FF6961"/>
11
- <rect class="cell" x="0" y="20" width="20" height="20" fill="#77DD77"/>
12
- <rect class="cell" x="0" y="40" width="20" height="20" fill="#FFD700"/>
13
- <rect class="cell" x="20" y="0" width="20" height="20" fill="#77DD77"/>
14
- <rect class="cell" x="20" y="20" width="20" height="20" fill="#FFD700"/>
15
- <rect class="cell" x="20" y="40" width="20" height="20" fill="#FF6961"/>
16
- <rect class="cell" x="40" y="0" width="20" height="20" fill="#FFD700"/>
17
- <rect class="cell" x="40" y="20" width="20" height="20" fill="#FF6961"/>
18
- <rect class="cell" x="40" y="40" width="20" height="20" fill="#77DD77"/>
19
- <rect class="cell" x="60" y="0" width="20" height="20" fill="#FF6961"/>
20
- <rect class="cell" x="60" y="20" width="20" height="20" fill="#77DD77"/>
21
- <rect class="cell" x="60" y="40" width="20" height="20" fill="#FFD700"/>
22
- <rect class="cell" x="80" y="0" width="20" height="20" fill="#77DD77"/>
23
- <rect class="cell" x="80" y="20" width="20" height="20" fill="#FFD700"/>
24
- <rect class="cell" x="80" y="40" width="20" height="20" fill="#FF6961"/>
25
- <rect class="cell" x="100" y="0" width="20" height="20" fill="#FFD700"/>
26
- <rect class="cell" x="100" y="20" width="20" height="20" fill="#FF6961"/>
27
- <rect class="cell" x="100" y="40" width="20" height="20" fill="#77DD77"/>
28
- <rect class="cell" x="120" y="0" width="20" height="20" fill="#FF6961"/>
29
- <rect class="cell" x="120" y="20" width="20" height="20" fill="#77DD77"/>
30
- <rect class="cell" x="120" y="40" width="20" height="20" fill="#FFD700"/>
31
- <rect class="cell" x="140" y="0" width="20" height="20" fill="#77DD77"/>
32
- <rect class="cell" x="140" y="20" width="20" height="20" fill="#FFD700"/>
33
- <rect class="cell" x="140" y="40" width="20" height="20" fill="#FF6961"/>
9
+ <rect class="cell" x="0" y="0" width="20" height="20" rx="2" fill="#FF6961"/>
10
+ <rect class="cell" x="0" y="20" width="20" height="20" rx="2" fill="#77DD77"/>
11
+ <rect class="cell" x="0" y="40" width="20" height="20" rx="2" fill="#FFD700"/>
12
+ <rect class="cell" x="20" y="0" width="20" height="20" rx="2" fill="#77DD77"/>
13
+ <rect class="cell" x="20" y="20" width="20" height="20" rx="2" fill="#FFD700"/>
14
+ <rect class="cell" x="20" y="40" width="20" height="20" rx="2" fill="#FF6961"/>
15
+ <rect class="cell" x="40" y="0" width="20" height="20" rx="2" fill="#FFD700"/>
16
+ <rect class="cell" x="40" y="20" width="20" height="20" rx="2" fill="#FF6961"/>
17
+ <rect class="cell" x="40" y="40" width="20" height="20" rx="2" fill="#77DD77"/>
18
+ <rect class="cell" x="60" y="0" width="20" height="20" rx="2" fill="#FF6961"/>
19
+ <rect class="cell" x="60" y="20" width="20" height="20" rx="2" fill="#77DD77"/>
20
+ <rect class="cell" x="60" y="40" width="20" height="20" rx="2" fill="#FFD700"/>
21
+ <rect class="cell" x="80" y="0" width="20" height="20" rx="2" fill="#77DD77"/>
22
+ <rect class="cell" x="80" y="20" width="20" height="20" rx="2" fill="#FFD700"/>
23
+ <rect class="cell" x="80" y="40" width="20" height="20" rx="2" fill="#FF6961"/>
24
+ <rect class="cell" x="100" y="0" width="20" height="20" rx="2" fill="#FFD700"/>
25
+ <rect class="cell" x="100" y="20" width="20" height="20" rx="2" fill="#FF6961"/>
26
+ <rect class="cell" x="100" y="40" width="20" height="20" rx="2" fill="#77DD77"/>
27
+ <rect class="cell" x="120" y="0" width="20" height="20" rx="2" fill="#FF6961"/>
28
+ <rect class="cell" x="120" y="20" width="20" height="20" rx="2" fill="#77DD77"/>
29
+ <rect class="cell" x="120" y="40" width="20" height="20" rx="2" fill="#FFD700"/>
30
+ <rect class="cell" x="140" y="0" width="20" height="20" rx="2" fill="#77DD77"/>
31
+ <rect class="cell" x="140" y="20" width="20" height="20" rx="2" fill="#FFD700"/>
32
+ <rect class="cell" x="140" y="40" width="20" height="20" rx="2" fill="#FF6961"/>
34
33
  </svg>
data/srv/heart-mask.sevgi CHANGED
@@ -3,8 +3,11 @@
3
3
 
4
4
  HEART_PATH = "M10,35 A20,20,0,0,1,50,35 A20,20,0,0,1,90,35 Q90,65,50,95 Q10,65,10,35 Z"
5
5
 
6
- SVG :minimal, viewBox: "-10 -10 120 120" do
7
- mask id: "heart" do
6
+ SVG :minimal, width: 120, height: 120, viewBox: "-10 -10 120 120" do
7
+ mask id: "heart",
8
+ maskUnits: "userSpaceOnUse",
9
+ maskContentUnits: "userSpaceOnUse",
10
+ x: 0, y: 0, width: 100, height: 100 do
8
11
  rect x: 0, y: 0, width: 100, height: 100, fill: "white"
9
12
  path d: HEART_PATH, fill: "black"
10
13
  end
data/srv/heart-mask.svg CHANGED
@@ -1,5 +1,5 @@
1
- <svg viewBox="-10 -10 120 120">
2
- <mask id="heart">
1
+ <svg width="120" height="120" viewBox="-10 -10 120 120">
2
+ <mask id="heart" maskUnits="userSpaceOnUse" maskContentUnits="userSpaceOnUse" x="0" y="0" width="100" height="100">
3
3
  <rect x="0" y="0" width="100" height="100" fill="white"/>
4
4
  <path d="M10,35 A20,20,0,0,1,50,35 A20,20,0,0,1,90,35 Q90,65,50,95 Q10,65,10,35 Z" fill="black"/>
5
5
  </mask>
data/srv/meter-face.sevgi CHANGED
@@ -6,7 +6,7 @@ LED_GAP = 2
6
6
  LED_WIDTH = 10
7
7
  TOTAL_WIDTH = (LED_COUNT * (LED_WIDTH + LED_GAP)) + 8
8
8
 
9
- SVG :minimal, viewBox: "0 0 #{TOTAL_WIDTH} 30" do
9
+ SVG :minimal, width: TOTAL_WIDTH, height: 30, viewBox: "0 0 #{TOTAL_WIDTH} 30" do
10
10
  rect x: 2, y: 2, width: TOTAL_WIDTH - 4, height: 26,
11
11
  rx: 4, fill: "none", stroke: "#666"
12
12
 
data/srv/meter-face.svg CHANGED
@@ -1,4 +1,4 @@
1
- <svg viewBox="0 0 128 30">
1
+ <svg width="128" height="30" viewBox="0 0 128 30">
2
2
  <rect x="2" y="2" width="124" height="26" rx="4" fill="none" stroke="#666"/>
3
3
  <rect x="5" y="5" width="10" height="20" rx="3" fill="green" fill-opacity="1"/>
4
4
  <rect x="17" y="5" width="10" height="20" rx="3" fill="green" fill-opacity="1"/>
@@ -1,52 +1,55 @@
1
1
  #!/usr/bin/env -S ruby -S sevgi
2
2
  # frozen_string_literal: true
3
3
 
4
- WIDTH = 150 # 15 cm width (ruler's length)
5
- HEIGHT = 20 # 2 cm height
4
+ # 15 cm width (ruler's length)
5
+ WIDTH = 150
6
+ # 2 cm height
7
+ HEIGHT = 20
6
8
 
7
9
  STYLE = {
8
- ".labels": { fill: "black", font: "3pt monospace" },
9
- ".majors": { stroke: "black", "stroke-width": 0.2 },
10
- ".halves": { stroke: "black", "stroke-width": 0.1 },
11
- ".minors": { stroke: "black", "stroke-width": 0.1 },
12
- ".frame": { stroke: "black", "stroke-width": 0.1, fill: "none" }
10
+ ".labels": {fill: "black", font: "3pt monospace"},
11
+ ".majors": {stroke: "black", "stroke-width": 0.2},
12
+ ".halves": {stroke: "black", "stroke-width": 0.1},
13
+ ".minors": {stroke: "black", "stroke-width": 0.1},
14
+ ".frame": {stroke: "black", "stroke-width": 0.1, fill: "none"}
13
15
  }.freeze
14
16
 
15
- SVG width: "210mm", height: "50mm", viewBox: "0 0 210 50" do
16
- css STYLE
17
+ SVG(width: "210mm", height: "50mm", viewBox: "0 0 210 50") do
18
+ css(STYLE)
17
19
 
18
- rect id: "frame", width: WIDTH, height: HEIGHT, class: "frame"
20
+ rect(id: "frame", width: WIDTH, height: HEIGHT, class: "frame")
19
21
 
20
- layer id: "minors" do
22
+ layer(id: "minors") do
21
23
  length = 2
22
24
 
23
25
  (0..WIDTH).step(1) do |x|
24
- VLineBy x:, y: 0, length: length, class: "minors"
25
- VLineBy x:, y: HEIGHT, length: -length, class: "minors"
26
+ VLineBy(x:, y: 0, length: length, class: "minors")
27
+ VLineBy(x:, y: HEIGHT, length: -length, class: "minors")
26
28
  end
27
29
  end
28
30
 
29
- layer id: "halves" do
31
+ layer(id: "halves") do
30
32
  length = 4
31
33
 
32
34
  (0..WIDTH).step(5) do |x|
33
- VLineBy x:, y: 0, length: length, class: "minors"
34
- VLineBy x:, y: HEIGHT, length: -length, class: "minors"
35
+ VLineBy(x:, y: 0, length: length, class: "halves")
36
+ VLineBy(x:, y: HEIGHT, length: -length, class: "halves")
35
37
  end
36
38
  end
37
39
 
38
- layer id: "majors" do
40
+ layer(id: "majors") do
39
41
  length = 6
40
42
 
41
43
  (10..(WIDTH - 10)).step(10) do |x|
42
- VLineBy x:, y: 0, length: length, class: "minors"
43
- VLineBy x:, y: HEIGHT, length: -length, class: "minors"
44
+ VLineBy(x:, y: 0, length: length, class: "majors")
45
+ VLineBy(x:, y: HEIGHT, length: -length, class: "majors")
44
46
  end
45
47
 
46
- layer id: "labels" do
48
+ layer(id: "labels") do
47
49
  (10..(WIDTH - 10)).step(10).each_with_index do |x, i|
48
- text (i + 1).to_s, "text-anchor": "middle", x:, y: length + 5.5, class: "labels"
50
+ text((i + 1).to_s, "text-anchor": "middle", x:, y: length + 5.5, class: "labels")
49
51
  end
50
52
  end
51
53
  end
52
- end.Save
54
+ end
55
+ .Save()
data/srv/ruler-hline.svg CHANGED
@@ -331,98 +331,98 @@
331
331
  <path d="M 150 20 v -2" class="minors"/>
332
332
  </g>
333
333
  <g id="halves">
334
- <path d="M 0 0 v 4" class="minors"/>
335
- <path d="M 0 20 v -4" class="minors"/>
336
- <path d="M 5 0 v 4" class="minors"/>
337
- <path d="M 5 20 v -4" class="minors"/>
338
- <path d="M 10 0 v 4" class="minors"/>
339
- <path d="M 10 20 v -4" class="minors"/>
340
- <path d="M 15 0 v 4" class="minors"/>
341
- <path d="M 15 20 v -4" class="minors"/>
342
- <path d="M 20 0 v 4" class="minors"/>
343
- <path d="M 20 20 v -4" class="minors"/>
344
- <path d="M 25 0 v 4" class="minors"/>
345
- <path d="M 25 20 v -4" class="minors"/>
346
- <path d="M 30 0 v 4" class="minors"/>
347
- <path d="M 30 20 v -4" class="minors"/>
348
- <path d="M 35 0 v 4" class="minors"/>
349
- <path d="M 35 20 v -4" class="minors"/>
350
- <path d="M 40 0 v 4" class="minors"/>
351
- <path d="M 40 20 v -4" class="minors"/>
352
- <path d="M 45 0 v 4" class="minors"/>
353
- <path d="M 45 20 v -4" class="minors"/>
354
- <path d="M 50 0 v 4" class="minors"/>
355
- <path d="M 50 20 v -4" class="minors"/>
356
- <path d="M 55 0 v 4" class="minors"/>
357
- <path d="M 55 20 v -4" class="minors"/>
358
- <path d="M 60 0 v 4" class="minors"/>
359
- <path d="M 60 20 v -4" class="minors"/>
360
- <path d="M 65 0 v 4" class="minors"/>
361
- <path d="M 65 20 v -4" class="minors"/>
362
- <path d="M 70 0 v 4" class="minors"/>
363
- <path d="M 70 20 v -4" class="minors"/>
364
- <path d="M 75 0 v 4" class="minors"/>
365
- <path d="M 75 20 v -4" class="minors"/>
366
- <path d="M 80 0 v 4" class="minors"/>
367
- <path d="M 80 20 v -4" class="minors"/>
368
- <path d="M 85 0 v 4" class="minors"/>
369
- <path d="M 85 20 v -4" class="minors"/>
370
- <path d="M 90 0 v 4" class="minors"/>
371
- <path d="M 90 20 v -4" class="minors"/>
372
- <path d="M 95 0 v 4" class="minors"/>
373
- <path d="M 95 20 v -4" class="minors"/>
374
- <path d="M 100 0 v 4" class="minors"/>
375
- <path d="M 100 20 v -4" class="minors"/>
376
- <path d="M 105 0 v 4" class="minors"/>
377
- <path d="M 105 20 v -4" class="minors"/>
378
- <path d="M 110 0 v 4" class="minors"/>
379
- <path d="M 110 20 v -4" class="minors"/>
380
- <path d="M 115 0 v 4" class="minors"/>
381
- <path d="M 115 20 v -4" class="minors"/>
382
- <path d="M 120 0 v 4" class="minors"/>
383
- <path d="M 120 20 v -4" class="minors"/>
384
- <path d="M 125 0 v 4" class="minors"/>
385
- <path d="M 125 20 v -4" class="minors"/>
386
- <path d="M 130 0 v 4" class="minors"/>
387
- <path d="M 130 20 v -4" class="minors"/>
388
- <path d="M 135 0 v 4" class="minors"/>
389
- <path d="M 135 20 v -4" class="minors"/>
390
- <path d="M 140 0 v 4" class="minors"/>
391
- <path d="M 140 20 v -4" class="minors"/>
392
- <path d="M 145 0 v 4" class="minors"/>
393
- <path d="M 145 20 v -4" class="minors"/>
394
- <path d="M 150 0 v 4" class="minors"/>
395
- <path d="M 150 20 v -4" class="minors"/>
334
+ <path d="M 0 0 v 4" class="halves"/>
335
+ <path d="M 0 20 v -4" class="halves"/>
336
+ <path d="M 5 0 v 4" class="halves"/>
337
+ <path d="M 5 20 v -4" class="halves"/>
338
+ <path d="M 10 0 v 4" class="halves"/>
339
+ <path d="M 10 20 v -4" class="halves"/>
340
+ <path d="M 15 0 v 4" class="halves"/>
341
+ <path d="M 15 20 v -4" class="halves"/>
342
+ <path d="M 20 0 v 4" class="halves"/>
343
+ <path d="M 20 20 v -4" class="halves"/>
344
+ <path d="M 25 0 v 4" class="halves"/>
345
+ <path d="M 25 20 v -4" class="halves"/>
346
+ <path d="M 30 0 v 4" class="halves"/>
347
+ <path d="M 30 20 v -4" class="halves"/>
348
+ <path d="M 35 0 v 4" class="halves"/>
349
+ <path d="M 35 20 v -4" class="halves"/>
350
+ <path d="M 40 0 v 4" class="halves"/>
351
+ <path d="M 40 20 v -4" class="halves"/>
352
+ <path d="M 45 0 v 4" class="halves"/>
353
+ <path d="M 45 20 v -4" class="halves"/>
354
+ <path d="M 50 0 v 4" class="halves"/>
355
+ <path d="M 50 20 v -4" class="halves"/>
356
+ <path d="M 55 0 v 4" class="halves"/>
357
+ <path d="M 55 20 v -4" class="halves"/>
358
+ <path d="M 60 0 v 4" class="halves"/>
359
+ <path d="M 60 20 v -4" class="halves"/>
360
+ <path d="M 65 0 v 4" class="halves"/>
361
+ <path d="M 65 20 v -4" class="halves"/>
362
+ <path d="M 70 0 v 4" class="halves"/>
363
+ <path d="M 70 20 v -4" class="halves"/>
364
+ <path d="M 75 0 v 4" class="halves"/>
365
+ <path d="M 75 20 v -4" class="halves"/>
366
+ <path d="M 80 0 v 4" class="halves"/>
367
+ <path d="M 80 20 v -4" class="halves"/>
368
+ <path d="M 85 0 v 4" class="halves"/>
369
+ <path d="M 85 20 v -4" class="halves"/>
370
+ <path d="M 90 0 v 4" class="halves"/>
371
+ <path d="M 90 20 v -4" class="halves"/>
372
+ <path d="M 95 0 v 4" class="halves"/>
373
+ <path d="M 95 20 v -4" class="halves"/>
374
+ <path d="M 100 0 v 4" class="halves"/>
375
+ <path d="M 100 20 v -4" class="halves"/>
376
+ <path d="M 105 0 v 4" class="halves"/>
377
+ <path d="M 105 20 v -4" class="halves"/>
378
+ <path d="M 110 0 v 4" class="halves"/>
379
+ <path d="M 110 20 v -4" class="halves"/>
380
+ <path d="M 115 0 v 4" class="halves"/>
381
+ <path d="M 115 20 v -4" class="halves"/>
382
+ <path d="M 120 0 v 4" class="halves"/>
383
+ <path d="M 120 20 v -4" class="halves"/>
384
+ <path d="M 125 0 v 4" class="halves"/>
385
+ <path d="M 125 20 v -4" class="halves"/>
386
+ <path d="M 130 0 v 4" class="halves"/>
387
+ <path d="M 130 20 v -4" class="halves"/>
388
+ <path d="M 135 0 v 4" class="halves"/>
389
+ <path d="M 135 20 v -4" class="halves"/>
390
+ <path d="M 140 0 v 4" class="halves"/>
391
+ <path d="M 140 20 v -4" class="halves"/>
392
+ <path d="M 145 0 v 4" class="halves"/>
393
+ <path d="M 145 20 v -4" class="halves"/>
394
+ <path d="M 150 0 v 4" class="halves"/>
395
+ <path d="M 150 20 v -4" class="halves"/>
396
396
  </g>
397
397
  <g id="majors">
398
- <path d="M 10 0 v 6" class="minors"/>
399
- <path d="M 10 20 v -6" class="minors"/>
400
- <path d="M 20 0 v 6" class="minors"/>
401
- <path d="M 20 20 v -6" class="minors"/>
402
- <path d="M 30 0 v 6" class="minors"/>
403
- <path d="M 30 20 v -6" class="minors"/>
404
- <path d="M 40 0 v 6" class="minors"/>
405
- <path d="M 40 20 v -6" class="minors"/>
406
- <path d="M 50 0 v 6" class="minors"/>
407
- <path d="M 50 20 v -6" class="minors"/>
408
- <path d="M 60 0 v 6" class="minors"/>
409
- <path d="M 60 20 v -6" class="minors"/>
410
- <path d="M 70 0 v 6" class="minors"/>
411
- <path d="M 70 20 v -6" class="minors"/>
412
- <path d="M 80 0 v 6" class="minors"/>
413
- <path d="M 80 20 v -6" class="minors"/>
414
- <path d="M 90 0 v 6" class="minors"/>
415
- <path d="M 90 20 v -6" class="minors"/>
416
- <path d="M 100 0 v 6" class="minors"/>
417
- <path d="M 100 20 v -6" class="minors"/>
418
- <path d="M 110 0 v 6" class="minors"/>
419
- <path d="M 110 20 v -6" class="minors"/>
420
- <path d="M 120 0 v 6" class="minors"/>
421
- <path d="M 120 20 v -6" class="minors"/>
422
- <path d="M 130 0 v 6" class="minors"/>
423
- <path d="M 130 20 v -6" class="minors"/>
424
- <path d="M 140 0 v 6" class="minors"/>
425
- <path d="M 140 20 v -6" class="minors"/>
398
+ <path d="M 10 0 v 6" class="majors"/>
399
+ <path d="M 10 20 v -6" class="majors"/>
400
+ <path d="M 20 0 v 6" class="majors"/>
401
+ <path d="M 20 20 v -6" class="majors"/>
402
+ <path d="M 30 0 v 6" class="majors"/>
403
+ <path d="M 30 20 v -6" class="majors"/>
404
+ <path d="M 40 0 v 6" class="majors"/>
405
+ <path d="M 40 20 v -6" class="majors"/>
406
+ <path d="M 50 0 v 6" class="majors"/>
407
+ <path d="M 50 20 v -6" class="majors"/>
408
+ <path d="M 60 0 v 6" class="majors"/>
409
+ <path d="M 60 20 v -6" class="majors"/>
410
+ <path d="M 70 0 v 6" class="majors"/>
411
+ <path d="M 70 20 v -6" class="majors"/>
412
+ <path d="M 80 0 v 6" class="majors"/>
413
+ <path d="M 80 20 v -6" class="majors"/>
414
+ <path d="M 90 0 v 6" class="majors"/>
415
+ <path d="M 90 20 v -6" class="majors"/>
416
+ <path d="M 100 0 v 6" class="majors"/>
417
+ <path d="M 100 20 v -6" class="majors"/>
418
+ <path d="M 110 0 v 6" class="majors"/>
419
+ <path d="M 110 20 v -6" class="majors"/>
420
+ <path d="M 120 0 v 6" class="majors"/>
421
+ <path d="M 120 20 v -6" class="majors"/>
422
+ <path d="M 130 0 v 6" class="majors"/>
423
+ <path d="M 130 20 v -6" class="majors"/>
424
+ <path d="M 140 0 v 6" class="majors"/>
425
+ <path d="M 140 20 v -6" class="majors"/>
426
426
  <g id="labels">
427
427
  <text text-anchor="middle" x="10" y="11.5" class="labels">1</text>
428
428
  <text text-anchor="middle" x="20" y="11.5" class="labels">2</text>
data/srv/ruler-hline.yml CHANGED
@@ -1,3 +1,2 @@
1
1
  dark:
2
2
  black: white
3
- yellow: purple
data/srv/ruler-line.yml CHANGED
@@ -1,3 +1,2 @@
1
1
  dark:
2
2
  black: white
3
- yellow: purple
data/srv/snow-flake.yml CHANGED
@@ -1,3 +1 @@
1
- dark:
2
- black: white
3
- yellow: purple
1
+ dark: {}
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sevgi-showcase
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.93.1
4
+ version: 0.94.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Recai Oktaş
@@ -15,22 +15,25 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.93.1
18
+ version: 0.94.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 0.93.1
25
+ version: 0.94.0
26
26
  description: Provides documents and examples to demonstrate the Sevgi toolkit.
27
27
  email: roktas@gmail.com
28
28
  executables: []
29
29
  extensions: []
30
30
  extra_rdoc_files: []
31
31
  files:
32
+ - CHANGELOG.md
33
+ - LICENSE
32
34
  - README.md
33
35
  - lib/sevgi/showcase.rb
36
+ - lib/sevgi/showcase/dark.rb
34
37
  - lib/sevgi/showcase/minitest.rb
35
38
  - lib/sevgi/showcase/minitest/script.rb
36
39
  - lib/sevgi/showcase/minitest/shell.rb
@@ -82,7 +85,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
82
85
  requirements:
83
86
  - - ">="
84
87
  - !ruby/object:Gem::Version
85
- version: 3.4.0.pre.preview1
88
+ version: 3.4.0
86
89
  required_rubygems_version: !ruby/object:Gem::Requirement
87
90
  requirements:
88
91
  - - ">="