sevgi 0.95.0 → 0.98.2

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.
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sevgi
4
+ class Executor
5
+ # Describes the outcome of one executor invocation.
6
+ #
7
+ # A successful result has a value and no error. A captured script, file, or
8
+ # library failure has an {Executor::Error} and may retain a value produced
9
+ # before the failure. The source stack is an immutable snapshot in load
10
+ # order; it is never shared with the executor's internal mutable state.
11
+ #
12
+ # @example Inspect successful execution
13
+ # result = Sevgi.execute("6 * 7")
14
+ # result.success? #=> true
15
+ # result.value #=> 42
16
+ #
17
+ # @example Inspect a captured failure
18
+ # result = Sevgi.execute("missing", file: "drawing.sevgi")
19
+ # result.error? #=> true
20
+ # result.error.cause #=> #<NameError ...>
21
+ # result.stack #=> ["drawing.sevgi"]
22
+ #
23
+ # @see Sevgi.execute
24
+ # @see Sevgi.execute_file
25
+ # @see https://sevgi.roktas.dev/execution/ Execution guide
26
+ Result = Data.define(:value, :error, :stack) do
27
+ # @!attribute [r] value
28
+ # @return [Object, nil] last value produced, or nil when no value was produced
29
+ # @!attribute [r] error
30
+ # @return [Sevgi::Executor::Error, nil] captured failure, or nil after successful execution
31
+ # @!attribute [r] stack
32
+ # @return [Array<String>] frozen owned source-path snapshot in load order
33
+
34
+ # Creates an execution result.
35
+ # @param value [Object, nil] last value produced before execution finished
36
+ # @param error [Sevgi::Executor::Error, nil] captured execution failure
37
+ # @param stack [Array<String>] source files visited in load order
38
+ # @return [void]
39
+ def initialize(value:, error:, stack:)
40
+ super(value:, error:, stack: Array(stack).map { it.dup.freeze }.freeze)
41
+ end
42
+
43
+ private_class_method :[]
44
+
45
+ # Reports whether execution completed without a captured error.
46
+ # @return [Boolean] true when execution succeeded
47
+ def success? = error.nil?
48
+
49
+ # Reports whether execution captured an error.
50
+ # @return [Boolean] true when execution failed
51
+ def error? = !success?
52
+ end
53
+ end
54
+ end
@@ -13,7 +13,7 @@ module Sevgi
13
13
  # @!attribute [r] recent
14
14
  # @return [Object, nil] last expression result from the executed source
15
15
  # @!attribute [r] error
16
- # @return [Sevgi::Executor::Error, nil] captured execution error
16
+ # @return [Exception, nil] captured execution error
17
17
  attr_reader :scope, :recent, :error
18
18
 
19
19
  # Creates a script execution scope.
@@ -35,7 +35,7 @@ module Sevgi
35
35
 
36
36
  # Executes one source object in this scope.
37
37
  # @param source [Sevgi::Executor::Source] source to evaluate
38
- # @param receiver [Object, nil] receiver used while booting the DSL
38
+ # @param receiver [Object, nil] explicit boot receiver, or nil to use the isolated scope module
39
39
  # @yield optional boot block that installs DSL methods before evaluation
40
40
  # @yieldreturn [void]
41
41
  # @return [Sevgi::Executor::Scope] self, with recent or error populated
@@ -54,7 +54,7 @@ module Sevgi
54
54
  # @api private
55
55
  def capture(source, error)
56
56
  push(source)
57
- @error = Executor::Error.new(error, self)
57
+ @error = error
58
58
  self
59
59
  end
60
60
 
@@ -77,6 +77,16 @@ module Sevgi
77
77
  # @note The stack is owned by this scope and is not shared with concurrent executions.
78
78
  def stack = @stack.keys
79
79
 
80
+ # Builds the immutable public result for this scope.
81
+ # @return [Sevgi::Executor::Result] execution result snapshot
82
+ # @api private
83
+ def result
84
+ sources = stack.freeze
85
+ error = Executor::Error.new(@error, sources) if @error
86
+
87
+ Result.new(value: recent, error:, stack: sources)
88
+ end
89
+
80
90
  # Returns the most recently pushed source.
81
91
  # @return [Sevgi::Executor::Source, nil] most recent source object
82
92
  # @api private
@@ -87,7 +97,8 @@ module Sevgi
87
97
  def boot(receiver, &boot)
88
98
  return unless boot
89
99
 
90
- (receiver ||= scope).public_send(receiver.is_a?(::Module) ? :module_exec : :instance_exec, &boot)
100
+ receiver = scope if receiver.nil?
101
+ receiver.public_send(receiver.is_a?(::Module) ? :module_exec : :instance_exec, &boot)
91
102
  end
92
103
 
93
104
  def evaluate(source)
@@ -123,7 +134,7 @@ module Sevgi
123
134
  @recent = run(source, receiver, &boot)
124
135
  # rubocop:disable Lint/RescueException
125
136
  rescue Exception => e
126
- @error = Executor::Error.new(e, self)
137
+ @error = e
127
138
  throw(:result, self)
128
139
  # rubocop:enable Lint/RescueException
129
140
  ensure
@@ -4,38 +4,47 @@ module Sevgi
4
4
  class Executor
5
5
  # Describes Ruby source evaluated by the Sevgi executor.
6
6
  # @api private
7
- Source = Data.define(:string, :file, :line) do
8
- # @overload call(string:, file: nil, line: nil)
7
+ Source = Data.define(:string, :file, :line, :origin) do
8
+ # @overload call(string:, file: nil, line: nil, origin: nil)
9
9
  # Builds a source object.
10
10
  # @param string [String] Ruby source string
11
11
  # @param file [String, nil] source file name for diagnostics
12
12
  # @param line [Integer, nil] starting source line for diagnostics
13
+ # @param origin [String, nil] physical source path used for load-cycle identity
13
14
  # @return [Sevgi::Executor::Source] source object
14
15
  def self.call(...) = new(...)
15
16
 
16
17
  # Builds a source object from a file.
17
18
  # @param file [String] source file to read
19
+ # @param as [String, nil] logical source name used for evaluation and diagnostics
18
20
  # @return [Sevgi::Executor::Source] source object with file contents
19
21
  # @raise [Errno::ENOENT] when the file cannot be read
20
- def self.load(file) = new(string: ::File.read(file), file: file, line: 1)
22
+ def self.load(file, as: nil) = new(string: ::File.read(file), file: as || file, line: 1, origin: file)
21
23
 
22
24
  # Creates a source object.
23
25
  # @param string [String] Ruby source string
24
26
  # @param file [String, nil] source file name for diagnostics
25
27
  # @param line [Integer, nil] starting source line for diagnostics
28
+ # @param origin [String, nil] physical source path used for load-cycle identity
26
29
  # @return [void]
27
- def initialize(string:, file: nil, line: nil) = super(string:, file: file || "sevgi", line: line || 1)
30
+ def initialize(string:, file: nil, line: nil, origin: nil)
31
+ super(string:, file: file || "sevgi", line: line || 1, origin:)
32
+ end
33
+
34
+ private_class_method :[]
28
35
 
29
36
  # Returns the stack key used for this source.
30
37
  # @return [String] source file name
31
38
  def key = file
32
39
 
33
40
  # Returns the canonical identity used for active-load cycle detection.
34
- # @return [String] canonical source identity
41
+ # @return [Integer, String] object identity for inline source, or canonical physical identity for loaded source
35
42
  def identity
36
- ::File.realpath(file)
43
+ return object_id unless origin
44
+
45
+ ::File.realpath(origin)
37
46
  rescue ::SystemCallError
38
- ::File.expand_path(file)
47
+ ::File.expand_path(origin)
39
48
  end
40
49
  end
41
50
  end
@@ -1,13 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "singleton"
3
+ require "sevgi/function"
4
4
 
5
5
  require_relative "executor/error"
6
+ require_relative "executor/result"
6
7
  require_relative "executor/scope"
7
8
  require_relative "executor/source"
8
9
 
9
10
  module Sevgi
10
- # Executes Sevgi script source inside an isolated module scope.
11
+ # Internal Sevgi script runtime and namespace for public execution result types.
11
12
  #
12
13
  # The executor is used by script mode and by the `Load` DSL word to preserve a
13
14
  # useful load stack while keeping DSL methods out of the caller's global object
@@ -16,160 +17,189 @@ module Sevgi
16
17
  # state. The process SIGINT handler is shared by Ruby, so executor runs guard it
17
18
  # with a reference-counted critical section and restore the previous handler
18
19
  # after the last active execution finishes.
20
+ #
21
+ # Consumers execute the full DSL through {Sevgi.execute} or {Sevgi.execute_file}, then inspect {Executor::Result},
22
+ # {Executor::Error}, and {Executor::CycleError}. The custom receiver and boot lifecycle is internal plumbing for the
23
+ # top-level API and Rake integration.
24
+ #
25
+ # @see https://sevgi.roktas.dev/execution/ Execution guide
19
26
  class Executor
20
- include Singleton
21
-
27
+ private_class_method :new
22
28
  private_constant :Scope
23
29
 
24
30
  # Thread-current key used for the fiber-local executor scope stack.
25
31
  # @api private
26
32
  SCOPE_KEY = :sevgi_executor_scopes
27
- private_constant :SCOPE_KEY, :Source
33
+ SOURCE_LINE_MAX = (2 ** 31) - 1
34
+ private_constant :SCOPE_KEY, :SOURCE_LINE_MAX, :Source
35
+
36
+ # Owns mutable execution and process-signal state outside the public executor surface.
37
+ # @api private
38
+ class State
39
+ def initialize
40
+ @signal_count = 0
41
+ @signal_mutex = Mutex.new
42
+ @signal_previous = nil
43
+ end
44
+
45
+ def create(scope = nil) = Scope.new(scope).tap { scopes << it }
46
+ def current = scopes.last
47
+
48
+ def restore
49
+ @signal_mutex.synchronize do
50
+ next if @signal_count.zero?
51
+
52
+ @signal_count -= 1
53
+ next unless @signal_count.zero?
54
+
55
+ Signal.trap("INT", @signal_previous)
56
+ @signal_previous = nil
57
+ end
58
+ end
59
+
60
+ def shutdown(scope = nil)
61
+ return scopes.pop unless scope
62
+ return scopes.pop if scopes.last.equal?(scope)
63
+
64
+ scopes.delete(scope)
65
+ end
66
+
67
+ def trap
68
+ @signal_mutex.synchronize do
69
+ @signal_previous = Signal.trap("INT") { Kernel.abort("") } if @signal_count.zero?
70
+ @signal_count += 1
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def scopes = Thread.current[SCOPE_KEY] ||= []
77
+ end
78
+
79
+ STATE = State.new
80
+ private_constant :STATE, :State
28
81
 
29
82
  # Loads a script file inside the current executor scope.
30
83
  # @param file [String] path to a Sevgi script file
31
- # @return [Sevgi::Executor::Scope] current execution scope
84
+ # @return [Sevgi::Executor::Scope] current internal execution scope
32
85
  # @raise [Sevgi::PanicError] when there is no active executor scope
33
86
  # @note Uses the active executor scope from the current fiber.
34
87
  # @api private
35
88
  def self.load(file, ...)
36
- PanicError.("box stack empty; create a box first") unless instance.current
89
+ PanicError.("box stack empty; create a box first") unless STATE.current
37
90
 
38
- instance.current.load(file, ...)
91
+ STATE.current.load(file, ...)
39
92
  end
40
93
 
94
+ private_class_method :load
95
+
41
96
  # Executes Ruby source inside a managed Sevgi script scope.
42
97
  # @param string [String] source to evaluate
43
98
  # @param file [String, nil] source file name used for errors and backtraces
44
99
  # @param line [Integer, nil] starting source line used for errors and backtraces
45
100
  # @param require [String, nil] optional Ruby library to require before execution
46
- # @param receiver [Object, nil] receiver used while booting the DSL
101
+ # @param receiver [Object, nil] receiver used verbatim while booting the DSL; nil selects the isolated execution
102
+ # module, while false and other executable objects remain explicit receivers
47
103
  # @yield optional boot block that installs DSL methods before evaluation
48
104
  # @yieldreturn [void]
49
- # @return [Sevgi::Executor::Scope, nil] execution scope, or nil for empty source
50
- # @note Required-library load failures are captured as {Sevgi::Executor::Error} on the returned scope.
105
+ # @return [Sevgi::Executor::Result] immutable execution result
106
+ # @raise [Sevgi::ArgumentError] when source, file, line, required library, or receiver is invalid
107
+ # @note Script and required-library failures are captured in {Sevgi::Executor::Result#error}; inspect
108
+ # {Sevgi::Executor::Error#cause} for the original exception.
109
+ # @note Empty source without `require:` is a strict no-op: no scope is created, the receiver and boot block are
110
+ # unused, and the result stack is empty. Supplying `require:` uses the normal boot and evaluation lifecycle.
51
111
  # @note Reentrant and concurrent calls keep independent scope stacks per fiber. The temporary SIGINT handler remains
52
112
  # process-global while any execution is active.
113
+ # @api private
53
114
  def self.execute(string, file: nil, line: nil, require: nil, receiver: nil, &block)
115
+ validate_source!(string, file, line)
116
+ validate_context!(require, receiver)
117
+
54
118
  execute_source(Source.new(string:, file:, line:), require:, receiver:, &block)
55
119
  end
56
120
 
57
121
  # Executes a file inside a managed Sevgi script scope.
58
122
  # @param file [String] source file to read and execute
123
+ # @param as [String, nil] logical source name used for evaluation and diagnostics
59
124
  # @param require [String, nil] optional Ruby library to require before execution
60
- # @param receiver [Object, nil] receiver used while booting the DSL
125
+ # @param receiver [Object, nil] receiver used verbatim while booting the DSL; nil selects the isolated execution
126
+ # module, while false and other executable objects remain explicit receivers
61
127
  # @yield optional boot block that installs DSL methods before evaluation
62
128
  # @yieldreturn [void]
63
- # @return [Sevgi::Executor::Scope, nil] execution scope, or nil for an empty file
64
- # @note File-read and required-library load failures are captured as {Sevgi::Executor::Error} on the returned scope.
129
+ # @return [Sevgi::Executor::Result] immutable execution result
130
+ # @raise [Sevgi::ArgumentError] when file, logical source name, required library, or receiver is invalid
131
+ # @note File-read, script, and required-library failures are captured in {Sevgi::Executor::Result#error}; inspect
132
+ # {Sevgi::Executor::Result#stack} for nested loads.
133
+ # @note An empty file without `require:` is a strict no-op: no scope is created, the receiver and boot block are
134
+ # unused, and the result stack is empty. Supplying `require:` uses the normal boot and evaluation lifecycle.
65
135
  # @note Reentrant and concurrent calls keep independent scope stacks per fiber. The temporary SIGINT handler remains
66
136
  # process-global while any execution is active.
67
- def self.execute_file(file, require: nil, receiver: nil, &block)
137
+ # @api private
138
+ def self.execute_file(file, as: nil, require: nil, receiver: nil, &block)
139
+ ArgumentError.("Executor file must be a String") unless file.is_a?(::String)
140
+ ArgumentError.("Executor logical file must be a String or nil") unless as.nil? || as.is_a?(::String)
141
+ validate_context!(require, receiver)
142
+
68
143
  source = nil
69
144
  begin
70
- source = Source.load(file)
145
+ source = Source.load(file, as:)
71
146
  rescue ::SystemCallError => e
72
- return capture_error(Source.new(string: "", file:, line: 1), e)
147
+ return capture_error(Source.new(string: "", file: as || file, line: 1, origin: file), e)
73
148
  end
74
149
 
75
150
  execute_source(source, require:, receiver:, &block)
76
151
  end
77
152
 
78
- # Removes the current executor scope.
79
- # @return [Sevgi::Executor::Scope, nil] removed scope
80
- # @api private
81
- def self.shutdown
82
- instance.shutdown
83
- end
84
-
85
- def initialize
86
- @signal_count = 0
87
- @signal_mutex = Mutex.new
88
- @signal_previous = nil
89
- end
153
+ private_class_method :execute, :execute_file
90
154
 
91
- # Creates and pushes a new executor scope.
92
- # @param scope [Module, nil] existing module scope to reuse
93
- # @return [Sevgi::Executor::Scope] created scope
94
- # @api private
95
- def create(scope = nil) = Scope.new(scope).tap { scopes << it }
96
-
97
- # Returns the active executor scope.
98
- # @return [Sevgi::Executor::Scope, nil] active scope, if any
99
- # @api private
100
- def current = scopes.last
155
+ class << self
156
+ private
101
157
 
102
- # Restores the process SIGINT handler when the last active execution ends.
103
- # @return [void]
104
- # @api private
105
- def restore
106
- @signal_mutex.synchronize do
107
- next if @signal_count.zero?
108
-
109
- @signal_count -= 1
110
- next unless @signal_count.zero?
111
-
112
- Signal.trap("INT", @signal_previous)
113
- @signal_previous = nil
158
+ def capture_error(source, error)
159
+ acquired = STATE.trap
160
+ scope = STATE.create
161
+ scope.capture(source, error).result
162
+ ensure
163
+ STATE.restore if acquired
164
+ STATE.shutdown(scope) if scope
114
165
  end
115
- end
116
166
 
117
- # Removes the active executor scope.
118
- # @param scope [Sevgi::Executor::Scope, nil] exact scope to remove, or nil to pop the current scope
119
- # @return [Sevgi::Executor::Scope, nil] removed scope
120
- # @api private
121
- def shutdown(scope = nil)
122
- return scopes.pop unless scope
123
- return scopes.pop if scopes.last.equal?(scope)
167
+ def execute_source(source, require:, receiver:, &block)
168
+ acquired = false
169
+ return Result.new(value: nil, error: nil, stack: []) if source.string.empty? && require.nil?
124
170
 
125
- scopes.delete(scope)
126
- end
127
-
128
- # Installs the process SIGINT handler while one or more executions are active.
129
- # @return [void]
130
- # @api private
131
- def trap
132
- @signal_mutex.synchronize do
133
- @signal_previous = Signal.trap("INT") { Kernel.abort("") } if @signal_count.zero?
171
+ acquired = STATE.trap
172
+ scope = STATE.create
173
+ catch(:result) { run_source(scope, source, require, receiver, &block) }
174
+ scope.result
134
175
 
135
- @signal_count += 1
176
+ ensure
177
+ STATE.restore if acquired
178
+ STATE.shutdown(scope) if scope
136
179
  end
137
- end
138
180
 
139
- def self.capture_error(source, error)
140
- acquired = instance.trap
141
- scope = instance.create
142
- scope.capture(source, error)
143
- ensure
144
- instance.restore if acquired
145
- instance.shutdown(scope) if scope
146
- end
181
+ def run_source(scope, source, library, receiver, &block)
182
+ ::Kernel.require(library) if library
183
+ scope.call(source, receiver, &block)
184
+ rescue ::LoadError => e
185
+ scope.capture(source, e)
186
+ end
147
187
 
148
- def self.execute_source(source, require:, receiver:, &block)
149
- acquired = false
150
- return if source.string.empty? && require.nil?
188
+ def validate_context!(library, receiver)
189
+ ArgumentError.("Executor library must be a String or nil") unless library.nil? || library.is_a?(::String)
151
190
 
152
- acquired = instance.trap
153
- scope = instance.create
154
- catch(:result) { run_source(scope, source, require, receiver, &block) }
191
+ valid = (receiver in ::Object) && receiver.respond_to?(:public_send) && receiver.respond_to?(:instance_exec)
192
+ ArgumentError.("Executor receiver must be an executable Object or nil") unless valid
193
+ end
155
194
 
156
- ensure
157
- instance.restore if acquired
158
- instance.shutdown(scope) if scope
159
- end
195
+ def validate_source!(string, file, line)
196
+ ArgumentError.("Executor source must be a String") unless string.is_a?(::String)
197
+ ArgumentError.("Executor file must be a String or nil") unless file.nil? || file.is_a?(::String)
160
198
 
161
- def self.run_source(scope, source, library, receiver, &block)
162
- ::Kernel.require(library) if library
163
- scope.call(source, receiver, &block)
164
- rescue ::LoadError => e
165
- scope.capture(source, e)
199
+ valid = line.nil? || (line.is_a?(::Integer) && line.between?(1, SOURCE_LINE_MAX))
200
+ ArgumentError.("Executor line must be between 1 and #{SOURCE_LINE_MAX}") unless valid
201
+ end
166
202
  end
167
-
168
- private_class_method :capture_error, :execute_source, :run_source
169
-
170
- private
171
-
172
- def scopes = Thread.current[SCOPE_KEY] ||= []
173
203
  end
174
204
 
175
205
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubygems"
4
+ require "sevgi"
5
+
6
+ module Sevgi
7
+ # Locates the agent skill packaged for the installed Sevgi version.
8
+ # @api private
9
+ module Skill
10
+ extend self
11
+
12
+ Error = Class.new(::Sevgi::Error)
13
+
14
+ def path
15
+ spec = ::Gem::Specification.find_by_name("sevgi-appendix", "= #{::Sevgi::VERSION}")
16
+ packaged = packaged_path(spec)
17
+ # Package managers may replace a versioned gem path with their stable prefix.
18
+ path = ::File.expand_path(ENV.fetch("SEVGI_SKILL", packaged))
19
+
20
+ Error.("Sevgi skill is unavailable at #{path}.") unless ::File.file?(::File.join(path, "SKILL.md"))
21
+
22
+ path
23
+ rescue ::Gem::MissingSpecError
24
+ Error.("sevgi-appendix #{::Sevgi::VERSION} is not installed.")
25
+ end
26
+
27
+ private
28
+
29
+ def packaged_path(spec)
30
+ relative = spec.metadata["sevgi_skill_path"]
31
+ root = ::File.expand_path(spec.full_gem_path)
32
+ path = ::File.expand_path(relative, root) if relative.is_a?(String) && !relative.empty?
33
+ inside = path&.start_with?("#{root}#{::File::SEPARATOR}")
34
+
35
+ Error.("sevgi-appendix #{::Sevgi::VERSION} does not declare a valid skill path.") unless inside
36
+
37
+ path
38
+ end
39
+ end
40
+
41
+ private_constant :Skill
42
+ end
data/lib/sevgi/svg.rb ADDED
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sevgi
4
+ # Public SVG facade installed by `require "sevgi"`.
5
+ #
6
+ # Capitalized methods are SVG DSL operations: {SVG.Canvas} creates a canvas,
7
+ # {SVG.Document} defines a document profile, and {SVG.Paper} registers a
8
+ # paper size. Double-colon names are Ruby constants and types, such as
9
+ # {SVG::Canvas} and {SVG::Document}. The global `SVG(...)` method builds a
10
+ # drawing; there is intentionally no stuttering `SVG.SVG(...)` form.
11
+ #
12
+ # Lowercase constructors remain on {Sevgi::Graphics} for focused component
13
+ # use. Script execution belongs to {Sevgi.execute} and is not part of this
14
+ # SVG-domain facade.
15
+ #
16
+ # @example Compose a drawing through the facade
17
+ # SVG.Paper 85, 55, :card
18
+ # canvas = SVG.Canvas :card, margins: 4
19
+ # profile = SVG.Document attributes: {viewBox: "0 0 85 55"}
20
+ #
21
+ # drawing = SVG profile, canvas do
22
+ # rect width: 85, height: 55, rx: 3
23
+ # end
24
+ #
25
+ # drawing.Render
26
+ # @example Distinguish an operation from its result type
27
+ # canvas = SVG.Canvas width: 24, height: 24, unit: :px
28
+ # canvas.is_a?(SVG::Canvas) #=> true
29
+ # @see https://sevgi.roktas.dev/getting-started/ Getting started
30
+ # @see https://sevgi.roktas.dev/library-mode/ Library mode guide
31
+ module SVG
32
+ # SVG attribute collection and normalization API.
33
+ Attributes = Graphics::Attributes
34
+ # Canvas value type returned by {SVG.Canvas}.
35
+ Canvas = Graphics::Canvas
36
+ # Explicit SVG content serialization types.
37
+ Content = Graphics::Content
38
+ # Document profiles and document classes.
39
+ Document = Graphics::Document
40
+ # Base SVG/XML element type.
41
+ Element = Graphics::Element
42
+ # Duplicate-id and structural lint failure.
43
+ LintError = Graphics::LintError
44
+ # CSS-like canvas margin value.
45
+ Margin = Graphics::Margin
46
+ # Graphics mixtures available for document composition.
47
+ Mixtures = Graphics::Mixtures
48
+ # Contract for one callable drawing module.
49
+ Module = Graphics::Module
50
+ # Recursive contract for namespaces of callable drawing modules.
51
+ Modules = Graphics::Modules
52
+ # Immutable paper-size value and profile registry.
53
+ Paper = Graphics::Paper
54
+ # Current Sevgi toolkit version.
55
+ VERSION = Sevgi::VERSION
56
+
57
+ # Builds a canvas from a paper profile or explicit dimensions.
58
+ # @return [Sevgi::Graphics::Canvas] canvas value
59
+ # @see Sevgi::Toplevel#Canvas
60
+ def self.Canvas(...) = Sevgi.Canvas(...)
61
+
62
+ # Converts inline SVG/XML into an immutable Derender node.
63
+ # @return [Sevgi::Derender::Node] selected node
64
+ # @see Sevgi::Toplevel#Decompile
65
+ def self.Decompile(...) = Sevgi.Decompile(...)
66
+
67
+ # Converts an SVG/XML file into an immutable Derender node.
68
+ # @return [Sevgi::Derender::Node] selected node
69
+ # @see Sevgi::Toplevel#DecompileFile
70
+ def self.DecompileFile(...) = Sevgi.DecompileFile(...)
71
+
72
+ # Converts inline SVG/XML into formatted Sevgi DSL source.
73
+ # @return [String] formatted Sevgi DSL source
74
+ # @see Sevgi::Toplevel#Derender
75
+ def self.Derender(...) = Sevgi.Derender(...)
76
+
77
+ # Converts an SVG/XML file into formatted Sevgi DSL source.
78
+ # @return [String] formatted Sevgi DSL source
79
+ # @see Sevgi::Toplevel#DerenderFile
80
+ def self.DerenderFile(...) = Sevgi.DerenderFile(...)
81
+
82
+ # Defines, validates, or looks up a document profile.
83
+ # @return [Class] document class
84
+ # @see Sevgi::Toplevel#Document
85
+ def self.Document(...) = Sevgi.Document(...)
86
+
87
+ # Defines or replaces a document profile.
88
+ # @return [Class] document class
89
+ # @see Sevgi::Toplevel#Document!
90
+ def self.Document!(...) = Sevgi.Document!(...)
91
+
92
+ # Includes an inline SVG/XML node under a graphics element.
93
+ # @return [Sevgi::Graphics::Element, nil] included element, or nil when no output is produced
94
+ # @see Sevgi::Toplevel#Evaluate
95
+ def self.Evaluate(...) = Sevgi.Evaluate(...)
96
+
97
+ # Includes only an inline SVG/XML node's children under a graphics element.
98
+ # @return [Array<Sevgi::Graphics::Element>] immutable included-child snapshot
99
+ # @see Sevgi::Toplevel#EvaluateChildren
100
+ def self.EvaluateChildren(...) = Sevgi.EvaluateChildren(...)
101
+
102
+ # Includes only an SVG/XML file node's children under a graphics element.
103
+ # @return [Array<Sevgi::Graphics::Element>] immutable included-child snapshot
104
+ # @see Sevgi::Toplevel#EvaluateChildrenFile
105
+ def self.EvaluateChildrenFile(...) = Sevgi.EvaluateChildrenFile(...)
106
+
107
+ # Includes an SVG/XML file node under a graphics element.
108
+ # @return [Sevgi::Graphics::Element, nil] included element, or nil when no output is produced
109
+ # @see Sevgi::Toplevel#EvaluateFile
110
+ def self.EvaluateFile(...) = Sevgi.EvaluateFile(...)
111
+
112
+ # Fits a drawable grid to a graphics canvas.
113
+ # @return [Sevgi::Sundries::Grid] fitted grid
114
+ # @see Sevgi::Toplevel#Grid
115
+ def self.Grid(...) = Sevgi.Grid(...)
116
+
117
+ # Loads nested `.sevgi` files through the active executor scope.
118
+ # @return [Array<String>] requested file names
119
+ # @see Sevgi::Toplevel#Load
120
+ def self.Load(...) = Sevgi.Load(...)
121
+
122
+ # Extends a document profile with a named or anonymous graphics mixture.
123
+ # @return [Module, nil] anonymous mixture when supplied, otherwise nil
124
+ # @see Sevgi::Toplevel#Mixin
125
+ def self.Mixin(...) = Sevgi.Mixin(...)
126
+
127
+ # Defines or validates a named paper profile.
128
+ # @return [Symbol, String] original paper profile name
129
+ # @see Sevgi::Toplevel#Paper
130
+ def self.Paper(...) = Sevgi.Paper(...)
131
+
132
+ # Defines or overwrites a named paper profile.
133
+ # @return [Symbol, String] original paper profile name
134
+ # @see Sevgi::Toplevel#Paper!
135
+ def self.Paper!(...) = Sevgi.Paper!(...)
136
+ end
137
+ end