lutaml 0.10.5 → 0.10.7
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 +4 -4
- data/.gitignore +8 -0
- data/.rubocop_todo.yml +35 -61
- data/exe/lutaml +1 -0
- data/lib/lutaml/cli/interactive_shell/command_base.rb +4 -0
- data/lib/lutaml/cli/interactive_shell.rb +3 -3
- data/lib/lutaml/cli/uml/spa_command.rb +4 -0
- data/lib/lutaml/qea/factory/association_builder.rb +1 -0
- data/lib/lutaml/qea/factory/class_transformer.rb +4 -2
- data/lib/lutaml/qea/factory/generalization_builder.rb +2 -2
- data/lib/lutaml/uml/inheritance_walker.rb +1 -0
- data/lib/lutaml/uml/model_helpers.rb +1 -1
- data/lib/lutaml/uml_repository/exporters/markdown/class_page_builder.rb +3 -1
- data/lib/lutaml/uml_repository/exporters/markdown_exporter.rb +2 -1
- data/lib/lutaml/uml_repository/repository.rb +1 -0
- data/lib/lutaml/uml_repository/static_site/data_transformer.rb +22 -6
- data/lib/lutaml/uml_repository/static_site/generator.rb +32 -9
- data/lib/lutaml/uml_repository/static_site/serializers/class_serializer.rb +7 -2
- data/lib/lutaml/uml_repository/static_site/serializers/diagram_serializer.rb +22 -1
- data/lib/lutaml/uml_repository/static_site/serializers/inheritance_resolver.rb +12 -4
- data/lib/lutaml/uml_repository/static_site/serializers/package_serializer.rb +10 -3
- data/lib/lutaml/uml_repository/static_site/serializers/package_tree_builder.rb +6 -2
- data/lib/lutaml/version.rb +1 -1
- data/templates/static_site/assets/scripts/core/state.js +28 -1
- data/templates/static_site/assets/styles/06-diagrams.css +48 -0
- data/templates/static_site/components/content.liquid +73 -3
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 84ecefe8a15ae033ee203318b3db70ac20ea3f727d4a94cb98ffdd6444f0111f
|
|
4
|
+
data.tar.gz: afe30e1675098ee1248114e4999117b0b8b159c902ae304e5d4b8212ac801bfa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4ac75c14a3991c8b7885f4fc62fbe29640be5fe7b4d2cc9a5abfc01c585e038dd50a495a69cdab4483fb6840671b1a1ab35a66263e570c1da7cc005ce9fda626
|
|
7
|
+
data.tar.gz: 92f724a84ddfc10d49800a2f118bdc31d6d80054d9238a2feacc0a1f9e74312ea1fc96ac95b58893f5ec24d88f1fbe09f2b2eff64be6b45f879208babfa162b8
|
data/.gitignore
CHANGED
|
@@ -8,9 +8,17 @@ Gemfile.lock
|
|
|
8
8
|
# rspec failure tracking
|
|
9
9
|
.rspec_status
|
|
10
10
|
/spec/reports/
|
|
11
|
+
/spec/tmp/
|
|
11
12
|
/tmp/
|
|
12
13
|
/.yardoc
|
|
13
14
|
/_yardoc/
|
|
14
15
|
|
|
15
16
|
_site/
|
|
16
17
|
old-docs
|
|
18
|
+
|
|
19
|
+
# Editor / OS
|
|
20
|
+
.DS_Store
|
|
21
|
+
|
|
22
|
+
# Transient planning files
|
|
23
|
+
TODO.*/
|
|
24
|
+
.rubocop_todo.yml.tmp
|
data/.rubocop_todo.yml
CHANGED
|
@@ -1,51 +1,43 @@
|
|
|
1
1
|
# This configuration was generated by
|
|
2
2
|
# `rubocop --auto-gen-config`
|
|
3
|
-
# on 2026-
|
|
3
|
+
# on 2026-05-07 04:34:28 UTC using RuboCop version 1.86.1.
|
|
4
4
|
# The point is for the user to remove these configuration records
|
|
5
5
|
# one by one as the offenses are removed from the code base.
|
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
|
8
8
|
|
|
9
|
-
# Offense count:
|
|
10
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
11
|
-
Layout/EmptyLineAfterGuardClause:
|
|
12
|
-
Exclude:
|
|
13
|
-
- 'lib/lutaml/qea/factory/association_builder.rb'
|
|
14
|
-
- 'lib/lutaml/uml/inheritance_walker.rb'
|
|
15
|
-
- 'lib/lutaml/uml_repository/static_site/data_transformer.rb'
|
|
16
|
-
- 'lib/lutaml/uml_repository/static_site/serializers/class_serializer.rb'
|
|
17
|
-
- 'lib/lutaml/uml_repository/static_site/serializers/diagram_serializer.rb'
|
|
18
|
-
- 'lib/lutaml/uml_repository/static_site/serializers/inheritance_resolver.rb'
|
|
19
|
-
- 'lib/lutaml/uml_repository/static_site/serializers/package_serializer.rb'
|
|
20
|
-
|
|
21
|
-
# Offense count: 4
|
|
9
|
+
# Offense count: 7
|
|
22
10
|
# This cop supports safe autocorrection (--autocorrect).
|
|
23
|
-
# Configuration parameters:
|
|
24
|
-
|
|
11
|
+
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
|
12
|
+
# SupportedStyles: with_first_argument, with_fixed_indentation
|
|
13
|
+
Layout/ArgumentAlignment:
|
|
25
14
|
Exclude:
|
|
26
|
-
- 'lib/lutaml/
|
|
15
|
+
- 'lib/lutaml/uml_repository/static_site/generator.rb'
|
|
16
|
+
- 'spec/lutaml/uml_repository/static_site/serializers/diagram_serializer_spec.rb'
|
|
27
17
|
|
|
28
|
-
# Offense count:
|
|
18
|
+
# Offense count: 6
|
|
29
19
|
# This cop supports safe autocorrection (--autocorrect).
|
|
30
|
-
|
|
20
|
+
# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
|
|
21
|
+
# SupportedHashRocketStyles: key, separator, table
|
|
22
|
+
# SupportedColonStyles: key, separator, table
|
|
23
|
+
# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
|
|
24
|
+
Layout/HashAlignment:
|
|
31
25
|
Exclude:
|
|
32
|
-
- '
|
|
26
|
+
- 'spec/lutaml/uml_repository/static_site/serializers/diagram_serializer_spec.rb'
|
|
33
27
|
|
|
34
|
-
# Offense count:
|
|
28
|
+
# Offense count: 175
|
|
35
29
|
# This cop supports safe autocorrection (--autocorrect).
|
|
36
30
|
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
|
|
37
31
|
# URISchemes: http, https
|
|
38
32
|
Layout/LineLength:
|
|
39
33
|
Enabled: false
|
|
40
34
|
|
|
41
|
-
# Offense count:
|
|
35
|
+
# Offense count: 3
|
|
42
36
|
# This cop supports safe autocorrection (--autocorrect).
|
|
43
|
-
# Configuration parameters:
|
|
44
|
-
|
|
45
|
-
# SupportedStylesForEmptyBraces: space, no_space
|
|
46
|
-
Layout/SpaceInsideHashLiteralBraces:
|
|
37
|
+
# Configuration parameters: AllowInHeredoc.
|
|
38
|
+
Layout/TrailingWhitespace:
|
|
47
39
|
Exclude:
|
|
48
|
-
- '
|
|
40
|
+
- 'spec/lutaml/uml_repository/static_site/serializers/diagram_serializer_spec.rb'
|
|
49
41
|
|
|
50
42
|
# Offense count: 1
|
|
51
43
|
Lint/BinaryOperatorWithIdenticalOperands:
|
|
@@ -98,13 +90,6 @@ Lint/MissingSuper:
|
|
|
98
90
|
Exclude:
|
|
99
91
|
- 'lib/lutaml/uml_repository/lazy_repository.rb'
|
|
100
92
|
|
|
101
|
-
# Offense count: 2
|
|
102
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
103
|
-
Lint/RedundantRequireStatement:
|
|
104
|
-
Exclude:
|
|
105
|
-
- 'lib/lutaml/qea/factory/generalization_builder.rb'
|
|
106
|
-
- 'lib/lutaml/uml_repository/static_site/serializers/inheritance_resolver.rb'
|
|
107
|
-
|
|
108
93
|
# Offense count: 1
|
|
109
94
|
# This cop supports safe autocorrection (--autocorrect).
|
|
110
95
|
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions.
|
|
@@ -118,7 +103,7 @@ Lint/UselessConstantScoping:
|
|
|
118
103
|
Exclude:
|
|
119
104
|
- 'lib/lutaml/cli/interactive_shell.rb'
|
|
120
105
|
|
|
121
|
-
# Offense count:
|
|
106
|
+
# Offense count: 51
|
|
122
107
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
|
|
123
108
|
Metrics/AbcSize:
|
|
124
109
|
Enabled: false
|
|
@@ -134,7 +119,7 @@ Metrics/BlockLength:
|
|
|
134
119
|
Metrics/CyclomaticComplexity:
|
|
135
120
|
Enabled: false
|
|
136
121
|
|
|
137
|
-
# Offense count:
|
|
122
|
+
# Offense count: 67
|
|
138
123
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
139
124
|
Metrics/MethodLength:
|
|
140
125
|
Max: 62
|
|
@@ -209,7 +194,7 @@ RSpec/ContextWording:
|
|
|
209
194
|
RSpec/DescribeClass:
|
|
210
195
|
Enabled: false
|
|
211
196
|
|
|
212
|
-
# Offense count:
|
|
197
|
+
# Offense count: 654
|
|
213
198
|
# Configuration parameters: CountAsOne.
|
|
214
199
|
RSpec/ExampleLength:
|
|
215
200
|
Max: 30
|
|
@@ -263,7 +248,7 @@ RSpec/MessageSpies:
|
|
|
263
248
|
- 'spec/lutaml/uml_repository/presenters/diagram_presenter_spec.rb'
|
|
264
249
|
- 'spec/lutaml/uml_repository/repository_spec.rb'
|
|
265
250
|
|
|
266
|
-
# Offense count:
|
|
251
|
+
# Offense count: 38
|
|
267
252
|
RSpec/MultipleExpectations:
|
|
268
253
|
Max: 6
|
|
269
254
|
|
|
@@ -285,6 +270,12 @@ RSpec/NoExpectationExample:
|
|
|
285
270
|
- 'spec/lutaml/qea/verification/comprehensive_equivalence_spec.rb'
|
|
286
271
|
- 'spec/lutaml/uml_repository/static_site/generator_spec.rb'
|
|
287
272
|
|
|
273
|
+
# Offense count: 6
|
|
274
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
275
|
+
RSpec/ReceiveMessages:
|
|
276
|
+
Exclude:
|
|
277
|
+
- 'spec/lutaml/uml_repository/static_site/serializers/diagram_serializer_spec.rb'
|
|
278
|
+
|
|
288
279
|
# Offense count: 12
|
|
289
280
|
RSpec/RepeatedExample:
|
|
290
281
|
Exclude:
|
|
@@ -309,6 +300,12 @@ RSpec/SpecFilePathFormat:
|
|
|
309
300
|
- 'spec/lutaml/parsers/serialize_xmi_to_liquid_spec.rb'
|
|
310
301
|
- 'spec/lutaml/uml_repository/web_ui/app_spec.rb'
|
|
311
302
|
|
|
303
|
+
# Offense count: 10
|
|
304
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
305
|
+
RSpec/VerifiedDoubleReference:
|
|
306
|
+
Exclude:
|
|
307
|
+
- 'spec/lutaml/uml_repository/static_site/serializers/diagram_serializer_spec.rb'
|
|
308
|
+
|
|
312
309
|
# Offense count: 3
|
|
313
310
|
Security/Eval:
|
|
314
311
|
Exclude:
|
|
@@ -336,13 +333,6 @@ Style/EvalWithLocation:
|
|
|
336
333
|
Exclude:
|
|
337
334
|
- 'spec/lutaml/cli/uml/search_command_spec.rb'
|
|
338
335
|
|
|
339
|
-
# Offense count: 1
|
|
340
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
341
|
-
# Configuration parameters: AllowMethodComparison, ComparisonsThreshold.
|
|
342
|
-
Style/MultipleComparison:
|
|
343
|
-
Exclude:
|
|
344
|
-
- 'lib/lutaml/cli/interactive_shell.rb'
|
|
345
|
-
|
|
346
336
|
# Offense count: 1
|
|
347
337
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
348
338
|
Style/PartitionInsteadOfDoubleSelect:
|
|
@@ -355,19 +345,3 @@ Style/SafeNavigationChainLength:
|
|
|
355
345
|
Exclude:
|
|
356
346
|
- 'lib/lutaml/qea/validation/attribute_validator.rb'
|
|
357
347
|
- 'lib/lutaml/qea/validation/operation_validator.rb'
|
|
358
|
-
|
|
359
|
-
# Offense count: 1
|
|
360
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
361
|
-
# Configuration parameters: EnforcedStyleForMultiline.
|
|
362
|
-
# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma
|
|
363
|
-
Style/TrailingCommaInArguments:
|
|
364
|
-
Exclude:
|
|
365
|
-
- 'lib/lutaml/qea/factory/class_transformer.rb'
|
|
366
|
-
|
|
367
|
-
# Offense count: 2
|
|
368
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
369
|
-
# Configuration parameters: EnforcedStyleForMultiline.
|
|
370
|
-
# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma
|
|
371
|
-
Style/TrailingCommaInHashLiteral:
|
|
372
|
-
Exclude:
|
|
373
|
-
- 'lib/lutaml/cli/interactive_shell.rb'
|
data/exe/lutaml
CHANGED
|
@@ -12,15 +12,19 @@ module Lutaml
|
|
|
12
12
|
|
|
13
13
|
def repository = shell.repository
|
|
14
14
|
def current_path = shell.current_path
|
|
15
|
+
|
|
15
16
|
def current_path=(path)
|
|
16
17
|
shell.instance_variable_set(:@current_path, path)
|
|
17
18
|
end
|
|
19
|
+
|
|
18
20
|
def config = shell.config
|
|
19
21
|
def bookmarks = shell.bookmarks
|
|
20
22
|
def last_results = shell.last_results
|
|
23
|
+
|
|
21
24
|
def last_results=(results)
|
|
22
25
|
shell.instance_variable_set(:@last_results, results)
|
|
23
26
|
end
|
|
27
|
+
|
|
24
28
|
def path_history = shell.path_history
|
|
25
29
|
end
|
|
26
30
|
end
|
|
@@ -101,7 +101,7 @@ module Lutaml
|
|
|
101
101
|
"history" => :@help,
|
|
102
102
|
"clear" => :@help, "cls" => :@help,
|
|
103
103
|
"config" => :@help,
|
|
104
|
-
"stats" => :@help
|
|
104
|
+
"stats" => :@help
|
|
105
105
|
}.freeze
|
|
106
106
|
|
|
107
107
|
METHOD_MAP = {
|
|
@@ -119,7 +119,7 @@ module Lutaml
|
|
|
119
119
|
"history" => :cmd_history,
|
|
120
120
|
"clear" => :cmd_clear, "cls" => :cmd_clear,
|
|
121
121
|
"config" => :cmd_config,
|
|
122
|
-
"stats" => :cmd_stats
|
|
122
|
+
"stats" => :cmd_stats
|
|
123
123
|
}.freeze
|
|
124
124
|
|
|
125
125
|
def execute_command(input)
|
|
@@ -127,7 +127,7 @@ module Lutaml
|
|
|
127
127
|
command = parts[0].downcase
|
|
128
128
|
args = parts[1..]
|
|
129
129
|
|
|
130
|
-
if
|
|
130
|
+
if ["exit", "quit", "q"].include?(command)
|
|
131
131
|
@running = false
|
|
132
132
|
return
|
|
133
133
|
end
|
|
@@ -47,6 +47,9 @@ module Lutaml
|
|
|
47
47
|
desc: "Minify HTML/CSS/JS output"
|
|
48
48
|
thor_class.option :theme, type: :string, default: "light",
|
|
49
49
|
desc: "Default theme: 'light' or 'dark'"
|
|
50
|
+
thor_class.option :render_diagrams, type: :boolean, default: false,
|
|
51
|
+
desc: "Render diagram SVGs " \
|
|
52
|
+
"(may increase output size)"
|
|
50
53
|
end
|
|
51
54
|
|
|
52
55
|
def run(input_path) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
@@ -169,6 +172,7 @@ module Lutaml
|
|
|
169
172
|
title: options[:title] || "UML Model Browser",
|
|
170
173
|
minify: options[:minify] || false,
|
|
171
174
|
theme: options[:theme] || "light",
|
|
175
|
+
render_diagrams: options[:render_diagrams] || false,
|
|
172
176
|
}
|
|
173
177
|
|
|
174
178
|
Lutaml::UmlRepository::StaticSite.generate(repository,
|
|
@@ -60,7 +60,7 @@ module Lutaml
|
|
|
60
60
|
.load_association_generalizations(ea_object.ea_object_id)
|
|
61
61
|
|
|
62
62
|
klass.associations = assoc_builder.load_class_associations(
|
|
63
|
-
ea_object.ea_object_id, ea_object.ea_guid
|
|
63
|
+
ea_object.ea_object_id, ea_object.ea_guid
|
|
64
64
|
)
|
|
65
65
|
end
|
|
66
66
|
end
|
|
@@ -116,7 +116,9 @@ module Lutaml
|
|
|
116
116
|
return [] if ea_guid.nil?
|
|
117
117
|
return [] unless database.tagged_values
|
|
118
118
|
|
|
119
|
-
ea_tags = database.tagged_values.select
|
|
119
|
+
ea_tags = database.tagged_values.select do |tag|
|
|
120
|
+
tag.element_id == ea_guid
|
|
121
|
+
end
|
|
120
122
|
TaggedValueTransformer.new(database).transform_collection(ea_tags)
|
|
121
123
|
end
|
|
122
124
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "set"
|
|
4
3
|
require_relative "base_transformer"
|
|
5
4
|
require_relative "attribute_transformer"
|
|
6
5
|
require_relative "generalization_transformer"
|
|
@@ -31,7 +30,8 @@ module Lutaml
|
|
|
31
30
|
generalization = if ea_connector.nil?
|
|
32
31
|
gen_transformer.transform(nil, current_obj)
|
|
33
32
|
else
|
|
34
|
-
gen_transformer.transform(ea_connector,
|
|
33
|
+
gen_transformer.transform(ea_connector,
|
|
34
|
+
current_obj)
|
|
35
35
|
end
|
|
36
36
|
return nil unless generalization
|
|
37
37
|
|
|
@@ -49,7 +49,9 @@ module Lutaml
|
|
|
49
49
|
stereotypes_array = normalize_stereotypes(klass.stereotype)
|
|
50
50
|
return "" if stereotypes_array.empty?
|
|
51
51
|
|
|
52
|
-
"**Stereotypes**: #{stereotypes_array.map
|
|
52
|
+
"**Stereotypes**: #{stereotypes_array.map do |s|
|
|
53
|
+
"`#{s}`"
|
|
54
|
+
end.join(', ')}\n\n"
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
def build_definition_section(klass)
|
|
@@ -41,7 +41,8 @@ module Lutaml
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def generate_index_page
|
|
44
|
-
content = Markdown::IndexPageBuilder.new(repository, options,
|
|
44
|
+
content = Markdown::IndexPageBuilder.new(repository, options,
|
|
45
|
+
link_resolver).build
|
|
45
46
|
File.write(File.join(output_dir, "index.md"), content)
|
|
46
47
|
end
|
|
47
48
|
|
|
@@ -32,13 +32,24 @@ module Lutaml
|
|
|
32
32
|
def transform
|
|
33
33
|
{
|
|
34
34
|
metadata: Serializers::MetadataBuilder.new(repository).build,
|
|
35
|
-
packageTree: Serializers::PackageTreeBuilder.new(repository,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
packageTree: Serializers::PackageTreeBuilder.new(repository,
|
|
36
|
+
id_generator).build,
|
|
37
|
+
packages: Serializers::PackageSerializer.new(repository,
|
|
38
|
+
id_generator, options).build_map,
|
|
39
|
+
classes: Serializers::ClassSerializer.new(repository, id_generator,
|
|
40
|
+
options, inheritance_resolver).build_map,
|
|
41
|
+
attributes: Serializers::AttributeSerializer.new(repository,
|
|
42
|
+
id_generator, options).build_map,
|
|
39
43
|
associations: build_associations_map,
|
|
40
|
-
operations: Serializers::OperationSerializer.new(repository,
|
|
41
|
-
|
|
44
|
+
operations: Serializers::OperationSerializer.new(repository,
|
|
45
|
+
id_generator).build_map,
|
|
46
|
+
diagrams: (if options[:include_diagrams]
|
|
47
|
+
Serializers::DiagramSerializer.new(
|
|
48
|
+
repository, id_generator, options
|
|
49
|
+
).build_map
|
|
50
|
+
else
|
|
51
|
+
{}
|
|
52
|
+
end),
|
|
42
53
|
}
|
|
43
54
|
end
|
|
44
55
|
|
|
@@ -69,12 +80,14 @@ module Lutaml
|
|
|
69
80
|
|
|
70
81
|
klass.association_generalization.each do |assoc_gen|
|
|
71
82
|
next unless assoc_gen.respond_to?(:parent_object_id)
|
|
83
|
+
|
|
72
84
|
parent_object_id = assoc_gen.parent_object_id
|
|
73
85
|
next unless parent_object_id
|
|
74
86
|
|
|
75
87
|
parent_class = class_lookup.by_object_id(parent_object_id)
|
|
76
88
|
if parent_class&.xmi_id
|
|
77
89
|
next if parent_class.xmi_id == klass.xmi_id
|
|
90
|
+
|
|
78
91
|
unless map[klass.xmi_id].include?(parent_class.xmi_id)
|
|
79
92
|
map[klass.xmi_id] << parent_class.xmi_id
|
|
80
93
|
end
|
|
@@ -91,6 +104,7 @@ module Lutaml
|
|
|
91
104
|
|
|
92
105
|
def find_class_by_xmi_id(xmi_id)
|
|
93
106
|
return nil unless xmi_id
|
|
107
|
+
|
|
94
108
|
class_lookup.by_xmi_id(xmi_id)
|
|
95
109
|
rescue StandardError
|
|
96
110
|
nil
|
|
@@ -98,6 +112,7 @@ module Lutaml
|
|
|
98
112
|
|
|
99
113
|
def find_class_by_object_id(object_id)
|
|
100
114
|
return nil unless object_id
|
|
115
|
+
|
|
101
116
|
class_lookup.by_object_id(object_id)
|
|
102
117
|
rescue StandardError
|
|
103
118
|
nil
|
|
@@ -114,6 +129,7 @@ module Lutaml
|
|
|
114
129
|
|
|
115
130
|
def format_definition(definition)
|
|
116
131
|
return nil if definition.nil? || definition.empty?
|
|
132
|
+
|
|
117
133
|
formatted = definition.strip
|
|
118
134
|
if @options[:max_definition_length] &&
|
|
119
135
|
formatted.length > @options[:max_definition_length]
|
|
@@ -11,6 +11,27 @@ require_relative "id_generator"
|
|
|
11
11
|
module Lutaml
|
|
12
12
|
module UmlRepository
|
|
13
13
|
module StaticSite
|
|
14
|
+
# Resolves Liquid {% include %} paths to template files on disk.
|
|
15
|
+
# Unlike LocalFileSystem (which adds a "_" prefix), this resolves
|
|
16
|
+
# paths directly: "components/header" → "<root>/components/header.liquid"
|
|
17
|
+
class TemplateFileSystem
|
|
18
|
+
attr_reader :root
|
|
19
|
+
|
|
20
|
+
def initialize(root)
|
|
21
|
+
@root = root
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def read_template_file(template_path)
|
|
25
|
+
full = File.expand_path("#{template_path}.liquid", @root)
|
|
26
|
+
unless full.start_with?(@root)
|
|
27
|
+
raise Liquid::FileSystemError,
|
|
28
|
+
"Illegal template path: #{template_path}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
File.read(full)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
14
35
|
# Main static site generator for LutaML UML Browser.
|
|
15
36
|
#
|
|
16
37
|
# Follows Dependency Inversion Principle by injecting dependencies
|
|
@@ -143,7 +164,8 @@ module Lutaml
|
|
|
143
164
|
include_diagrams: config_opts["include_diagrams"] != false,
|
|
144
165
|
format_definitions: config_opts["format_definitions"] != false,
|
|
145
166
|
max_definition_length: config_opts["max_definition_length"],
|
|
146
|
-
}.merge(@options.slice(:include_diagrams, :format_definitions
|
|
167
|
+
}.merge(@options.slice(:include_diagrams, :format_definitions,
|
|
168
|
+
:render_diagrams))
|
|
147
169
|
end
|
|
148
170
|
|
|
149
171
|
def search_options
|
|
@@ -163,12 +185,9 @@ module Lutaml
|
|
|
163
185
|
end
|
|
164
186
|
|
|
165
187
|
def setup_liquid
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
# Changed from :strict to handle missing includes
|
|
170
|
-
environment.error_mode = :lax
|
|
171
|
-
@liquid_environment = environment
|
|
188
|
+
@liquid_environment = Liquid::Environment.new
|
|
189
|
+
@liquid_environment.file_system = TemplateFileSystem.new(@options[:template_path])
|
|
190
|
+
@liquid_environment.error_mode = :lax
|
|
172
191
|
end
|
|
173
192
|
|
|
174
193
|
# Generate single-file SPA
|
|
@@ -226,7 +245,7 @@ module Lutaml
|
|
|
226
245
|
|
|
227
246
|
def render_components # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
228
247
|
# Set up Liquid file system for recursive includes
|
|
229
|
-
temp_file_system =
|
|
248
|
+
temp_file_system = TemplateFileSystem.new(@options[:template_path])
|
|
230
249
|
|
|
231
250
|
component_names = ["header", "sidebar", "content", "package_details",
|
|
232
251
|
"class_details"]
|
|
@@ -274,7 +293,10 @@ module Lutaml
|
|
|
274
293
|
# Render and write index.html
|
|
275
294
|
template_content = File.read(File.join(@options[:template_path],
|
|
276
295
|
"multi_file.liquid"))
|
|
277
|
-
|
|
296
|
+
file_system = TemplateFileSystem.new(@options[:template_path])
|
|
297
|
+
template = Liquid::Template.parse(template_content,
|
|
298
|
+
error_mode: :lax)
|
|
299
|
+
template.registers[:file_system] = file_system
|
|
278
300
|
html = template.render(context)
|
|
279
301
|
html = minify_html(html) if @options[:minify]
|
|
280
302
|
File.write(File.join(output_dir, "index.html"), html)
|
|
@@ -337,6 +359,7 @@ module Lutaml
|
|
|
337
359
|
"03-layout.css",
|
|
338
360
|
"04-components.css",
|
|
339
361
|
"05-utilities.css",
|
|
362
|
+
"06-diagrams.css",
|
|
340
363
|
]
|
|
341
364
|
|
|
342
365
|
css_parts = css_files.map do |file|
|
|
@@ -9,7 +9,8 @@ module Lutaml
|
|
|
9
9
|
class ClassSerializer
|
|
10
10
|
include Lutaml::Uml::ModelHelpers
|
|
11
11
|
|
|
12
|
-
def initialize(repository, id_generator, options,
|
|
12
|
+
def initialize(repository, id_generator, options,
|
|
13
|
+
inheritance_resolver)
|
|
13
14
|
@repository = repository
|
|
14
15
|
@id_generator = id_generator
|
|
15
16
|
@options = options
|
|
@@ -81,6 +82,7 @@ module Lutaml
|
|
|
81
82
|
|
|
82
83
|
def serialize_class_operations(klass)
|
|
83
84
|
return [] unless klass.respond_to?(:operations) && klass.operations
|
|
85
|
+
|
|
84
86
|
klass.operations.map { |op| @id_generator.operation_id(op, klass) }
|
|
85
87
|
end
|
|
86
88
|
|
|
@@ -88,7 +90,8 @@ module Lutaml
|
|
|
88
90
|
return [] unless klass.is_a?(Lutaml::Uml::Enum) && klass.owned_literal
|
|
89
91
|
|
|
90
92
|
klass.owned_literal.map do |literal|
|
|
91
|
-
{ name: literal.name,
|
|
93
|
+
{ name: literal.name,
|
|
94
|
+
definition: format_definition(literal.definition) }
|
|
92
95
|
end
|
|
93
96
|
rescue StandardError
|
|
94
97
|
[]
|
|
@@ -97,11 +100,13 @@ module Lutaml
|
|
|
97
100
|
def package_id_for_class(klass)
|
|
98
101
|
ns = klass.respond_to?(:namespace) ? klass.namespace : nil
|
|
99
102
|
return nil unless ns.is_a?(Lutaml::Uml::Package)
|
|
103
|
+
|
|
100
104
|
@id_generator.package_id(ns)
|
|
101
105
|
end
|
|
102
106
|
|
|
103
107
|
def format_definition(definition)
|
|
104
108
|
return nil if definition.nil? || definition.empty?
|
|
109
|
+
|
|
105
110
|
formatted = definition.strip
|
|
106
111
|
if @options[:max_definition_length] &&
|
|
107
112
|
formatted.length > @options[:max_definition_length]
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../../presenters/diagram_presenter"
|
|
4
|
+
|
|
3
5
|
module Lutaml
|
|
4
6
|
module UmlRepository
|
|
5
7
|
module StaticSite
|
|
@@ -25,13 +27,31 @@ module Lutaml
|
|
|
25
27
|
private
|
|
26
28
|
|
|
27
29
|
def serialize(diagram, id)
|
|
28
|
-
{
|
|
30
|
+
data = {
|
|
29
31
|
id: id,
|
|
30
32
|
xmiId: diagram.xmi_id,
|
|
31
33
|
name: diagram.name,
|
|
32
34
|
type: diagram.diagram_type,
|
|
33
35
|
package: find_diagram_package(diagram),
|
|
36
|
+
objectCount: (diagram.diagram_objects || []).size,
|
|
37
|
+
linkCount: (diagram.diagram_links || []).size,
|
|
34
38
|
}
|
|
39
|
+
|
|
40
|
+
if @options[:render_diagrams]
|
|
41
|
+
svg = render_svg(diagram)
|
|
42
|
+
data[:svg] = svg if svg
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
data
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def render_svg(diagram)
|
|
49
|
+
return nil unless diagram.diagram_objects&.any?
|
|
50
|
+
|
|
51
|
+
presenter = Presenters::DiagramPresenter.new(diagram, @repository)
|
|
52
|
+
presenter.svg_output
|
|
53
|
+
rescue StandardError
|
|
54
|
+
nil
|
|
35
55
|
end
|
|
36
56
|
|
|
37
57
|
def find_diagram_package(diagram)
|
|
@@ -48,6 +68,7 @@ module Lutaml
|
|
|
48
68
|
|
|
49
69
|
def package_diagrams(package)
|
|
50
70
|
return [] unless @options[:include_diagrams]
|
|
71
|
+
|
|
51
72
|
package.diagrams || []
|
|
52
73
|
rescue StandardError
|
|
53
74
|
[]
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "set"
|
|
4
3
|
require_relative "../../../uml/model_helpers"
|
|
5
4
|
require_relative "../../class_lookup_index"
|
|
6
5
|
|
|
@@ -24,6 +23,7 @@ module Lutaml
|
|
|
24
23
|
if parent_xmi_ids && !parent_xmi_ids.empty?
|
|
25
24
|
parents = parent_xmi_ids.filter_map do |parent_xmi_id|
|
|
26
25
|
next if parent_xmi_id == klass.xmi_id
|
|
26
|
+
|
|
27
27
|
parent = class_lookup.by_xmi_id(parent_xmi_id)
|
|
28
28
|
parent ? @id_generator.class_id(parent) : nil
|
|
29
29
|
end
|
|
@@ -32,6 +32,7 @@ module Lutaml
|
|
|
32
32
|
|
|
33
33
|
parent = @repository.supertype_of(klass)
|
|
34
34
|
return [] if parent && parent.xmi_id == klass.xmi_id
|
|
35
|
+
|
|
35
36
|
parent ? [@id_generator.class_id(parent)] : []
|
|
36
37
|
rescue StandardError => e
|
|
37
38
|
warn "Error finding generalizations for #{klass.name}: #{e.message}"
|
|
@@ -65,7 +66,9 @@ module Lutaml
|
|
|
65
66
|
visited.add(parent_class.xmi_id)
|
|
66
67
|
|
|
67
68
|
if parent_class.attributes
|
|
68
|
-
sorted_attrs = parent_class.attributes.sort_by
|
|
69
|
+
sorted_attrs = parent_class.attributes.sort_by do |a|
|
|
70
|
+
a.name || ""
|
|
71
|
+
end
|
|
69
72
|
sorted_attrs.each do |attr|
|
|
70
73
|
attr_id = @id_generator.attribute_id(attr, parent_class)
|
|
71
74
|
inherited << {
|
|
@@ -164,7 +167,8 @@ module Lutaml
|
|
|
164
167
|
ownedProps: serialize_general_attrs(gen, :owned_props),
|
|
165
168
|
assocProps: serialize_general_attrs(gen, :assoc_props),
|
|
166
169
|
inheritedProps: serialize_general_attrs(gen, :inherited_props),
|
|
167
|
-
inheritedAssocProps: serialize_general_attrs(gen,
|
|
170
|
+
inheritedAssocProps: serialize_general_attrs(gen,
|
|
171
|
+
:inherited_assoc_props),
|
|
168
172
|
}
|
|
169
173
|
rescue StandardError => e
|
|
170
174
|
warn "Error serializing generalization: #{e.message}"
|
|
@@ -219,7 +223,10 @@ module Lutaml
|
|
|
219
223
|
|
|
220
224
|
def serialize_general_attrs(gen, method)
|
|
221
225
|
return [] unless gen.respond_to?(method)
|
|
222
|
-
|
|
226
|
+
|
|
227
|
+
(gen.send(method) || []).map do |attr|
|
|
228
|
+
serialize_general_attribute(attr)
|
|
229
|
+
end
|
|
223
230
|
end
|
|
224
231
|
|
|
225
232
|
def serialize_cardinality(cardinality)
|
|
@@ -233,6 +240,7 @@ module Lutaml
|
|
|
233
240
|
|
|
234
241
|
def format_definition(definition)
|
|
235
242
|
return nil if definition.nil? || definition.empty?
|
|
243
|
+
|
|
236
244
|
formatted = definition.strip
|
|
237
245
|
if @options[:max_definition_length] &&
|
|
238
246
|
formatted.length > @options[:max_definition_length]
|
|
@@ -38,9 +38,15 @@ module Lutaml
|
|
|
38
38
|
stereotypes: normalize_stereotypes(
|
|
39
39
|
package.respond_to?(:stereotype) ? package.stereotype : nil,
|
|
40
40
|
),
|
|
41
|
-
classes: (package.classes || []).map
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
classes: (package.classes || []).map do |c|
|
|
42
|
+
@id_generator.class_id(c)
|
|
43
|
+
end,
|
|
44
|
+
subPackages: (package.packages || []).map do |p|
|
|
45
|
+
@id_generator.package_id(p)
|
|
46
|
+
end,
|
|
47
|
+
diagrams: package_diagrams(package).map do |d|
|
|
48
|
+
@id_generator.diagram_id(d)
|
|
49
|
+
end,
|
|
44
50
|
parent: if package.respond_to?(:namespace) &&
|
|
45
51
|
package.namespace.is_a?(Lutaml::Uml::Package)
|
|
46
52
|
@id_generator.package_id(package.namespace)
|
|
@@ -59,6 +65,7 @@ module Lutaml
|
|
|
59
65
|
|
|
60
66
|
def package_diagrams(package)
|
|
61
67
|
return [] unless @options[:include_diagrams]
|
|
68
|
+
|
|
62
69
|
package.diagrams || []
|
|
63
70
|
rescue StandardError => e
|
|
64
71
|
warn "Error getting diagrams for #{package.name}: #{e.message}"
|
|
@@ -44,14 +44,18 @@ module Lutaml
|
|
|
44
44
|
def build_tree_node(package) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
45
45
|
pkg_id = @id_generator.package_id(package)
|
|
46
46
|
|
|
47
|
-
sorted_children = (package.packages || []).sort_by
|
|
47
|
+
sorted_children = (package.packages || []).sort_by do |p|
|
|
48
|
+
p.name || ""
|
|
49
|
+
end
|
|
48
50
|
sorted_classes = (package.classes || [])
|
|
49
51
|
.reject { |c| c.name.nil? || c.name.empty? }
|
|
50
52
|
.sort_by(&:name)
|
|
51
53
|
|
|
52
54
|
child_nodes = sorted_children.map { |child| build_tree_node(child) }
|
|
53
55
|
|
|
54
|
-
total_class_count = sorted_classes.size + child_nodes.sum
|
|
56
|
+
total_class_count = sorted_classes.size + child_nodes.sum do |c|
|
|
57
|
+
c[:classCount] || 0
|
|
58
|
+
end
|
|
55
59
|
|
|
56
60
|
{
|
|
57
61
|
id: pkg_id,
|
data/lib/lutaml/version.rb
CHANGED
|
@@ -10,9 +10,10 @@ document.addEventListener('alpine:init', () => {
|
|
|
10
10
|
config: window.APP_CONFIG || {},
|
|
11
11
|
|
|
12
12
|
// Current State
|
|
13
|
-
currentView: 'welcome', // 'welcome' | 'package' | 'class' | 'search'
|
|
13
|
+
currentView: 'welcome', // 'welcome' | 'package' | 'class' | 'search' | 'diagram'
|
|
14
14
|
currentPackage: null,
|
|
15
15
|
currentClass: null,
|
|
16
|
+
currentDiagram: null,
|
|
16
17
|
searchQuery: '',
|
|
17
18
|
searchResults: [],
|
|
18
19
|
|
|
@@ -86,6 +87,7 @@ document.addEventListener('alpine:init', () => {
|
|
|
86
87
|
this.currentView = 'welcome';
|
|
87
88
|
this.currentPackage = null;
|
|
88
89
|
this.currentClass = null;
|
|
90
|
+
this.currentDiagram = null;
|
|
89
91
|
this.breadcrumbs = [];
|
|
90
92
|
this.pushState();
|
|
91
93
|
},
|
|
@@ -118,6 +120,21 @@ document.addEventListener('alpine:init', () => {
|
|
|
118
120
|
this.pushState();
|
|
119
121
|
},
|
|
120
122
|
|
|
123
|
+
selectDiagram(diagramId) {
|
|
124
|
+
if (!this.data || !this.data.diagrams[diagramId]) {
|
|
125
|
+
console.warn('Diagram not found:', diagramId);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const diag = this.data.diagrams[diagramId];
|
|
130
|
+
|
|
131
|
+
this.currentDiagram = diagramId;
|
|
132
|
+
this.currentPackage = diag.package;
|
|
133
|
+
this.currentView = 'diagram';
|
|
134
|
+
this.updateBreadcrumbs();
|
|
135
|
+
this.pushState();
|
|
136
|
+
},
|
|
137
|
+
|
|
121
138
|
performSearch(query) {
|
|
122
139
|
if (!query || query.length < 2) {
|
|
123
140
|
this.searchResults = [];
|
|
@@ -196,6 +213,8 @@ document.addEventListener('alpine:init', () => {
|
|
|
196
213
|
this.breadcrumbs = this.buildPackageBreadcrumbs(this.currentPackage);
|
|
197
214
|
} else if (this.currentView === 'class' && this.currentClass) {
|
|
198
215
|
this.breadcrumbs = this.buildClassBreadcrumbs(this.currentClass);
|
|
216
|
+
} else if (this.currentView === 'diagram' && this.currentDiagram) {
|
|
217
|
+
this.breadcrumbs = [{ name: `Diagram: ${this.data.diagrams[this.currentDiagram]?.name || ''}`, type: 'diagram' }];
|
|
199
218
|
} else if (this.currentView === 'search') {
|
|
200
219
|
this.breadcrumbs = [{ name: `Search: ${this.searchQuery}`, type: 'search' }];
|
|
201
220
|
}
|
|
@@ -275,6 +294,8 @@ document.addEventListener('alpine:init', () => {
|
|
|
275
294
|
this.selectPackage(decodeURIComponent(parts[1]));
|
|
276
295
|
} else if (parts[0] === 'class' && parts[1]) {
|
|
277
296
|
this.selectClass(decodeURIComponent(parts[1]));
|
|
297
|
+
} else if (parts[0] === 'diagram' && parts[1]) {
|
|
298
|
+
this.selectDiagram(decodeURIComponent(parts[1]));
|
|
278
299
|
} else if (parts[0] === 'search') {
|
|
279
300
|
const params = new URLSearchParams(queryString);
|
|
280
301
|
const query = params.get('q');
|
|
@@ -294,6 +315,8 @@ document.addEventListener('alpine:init', () => {
|
|
|
294
315
|
hash = `#/package/${encodeURIComponent(this.currentPackage)}`;
|
|
295
316
|
} else if (this.currentView === 'class' && this.currentClass) {
|
|
296
317
|
hash = `#/class/${encodeURIComponent(this.currentClass)}`;
|
|
318
|
+
} else if (this.currentView === 'diagram' && this.currentDiagram) {
|
|
319
|
+
hash = `#/diagram/${encodeURIComponent(this.currentDiagram)}`;
|
|
297
320
|
} else if (this.currentView === 'search' && this.searchQuery) {
|
|
298
321
|
hash = `#/search?q=${encodeURIComponent(this.searchQuery)}`;
|
|
299
322
|
}
|
|
@@ -370,6 +393,7 @@ function app() {
|
|
|
370
393
|
get currentView() { return Alpine.store('app').currentView; },
|
|
371
394
|
get currentPackage() { return Alpine.store('app').currentPackage; },
|
|
372
395
|
get currentClass() { return Alpine.store('app').currentClass; },
|
|
396
|
+
get currentDiagram() { return Alpine.store('app').currentDiagram; },
|
|
373
397
|
get sidebarVisible() {
|
|
374
398
|
return Alpine.store('app').sidebarVisible;
|
|
375
399
|
},
|
|
@@ -392,6 +416,9 @@ function app() {
|
|
|
392
416
|
selectClass(id) {
|
|
393
417
|
Alpine.store('app').selectClass(id);
|
|
394
418
|
},
|
|
419
|
+
selectDiagram(id) {
|
|
420
|
+
Alpine.store('app').selectDiagram(id);
|
|
421
|
+
},
|
|
395
422
|
toggleTheme() {
|
|
396
423
|
Alpine.store('app').toggleTheme();
|
|
397
424
|
},
|
|
@@ -274,4 +274,52 @@
|
|
|
274
274
|
.diagram-wrapper svg {
|
|
275
275
|
filter: brightness(0.9);
|
|
276
276
|
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/* Inline Diagram Viewer (content pane) */
|
|
280
|
+
.diagram-toolbar {
|
|
281
|
+
display: flex;
|
|
282
|
+
gap: 0.5rem;
|
|
283
|
+
padding: 0.75rem 0;
|
|
284
|
+
margin-bottom: 1rem;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.diagram-toolbar .btn-sm {
|
|
288
|
+
padding: 0.375rem 0.75rem;
|
|
289
|
+
font-size: 0.8rem;
|
|
290
|
+
background: var(--bg-secondary, #f8f9fa);
|
|
291
|
+
border: 1px solid var(--color-border, #dcdfe6);
|
|
292
|
+
border-radius: 4px;
|
|
293
|
+
cursor: pointer;
|
|
294
|
+
transition: all 0.15s ease;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.diagram-toolbar .btn-sm:hover {
|
|
298
|
+
background: var(--color-primary, #3498db);
|
|
299
|
+
color: white;
|
|
300
|
+
border-color: var(--color-primary, #3498db);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.diagram-svg-container {
|
|
304
|
+
background: white;
|
|
305
|
+
border: 1px solid var(--color-border, #dcdfe6);
|
|
306
|
+
border-radius: 8px;
|
|
307
|
+
padding: 1rem;
|
|
308
|
+
overflow: hidden;
|
|
309
|
+
transition: transform 0.1s ease-out;
|
|
310
|
+
min-height: 400px;
|
|
311
|
+
display: flex;
|
|
312
|
+
align-items: center;
|
|
313
|
+
justify-content: center;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.diagram-svg-container svg {
|
|
317
|
+
max-width: 100%;
|
|
318
|
+
display: block;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.diagram-item .diagram-objects {
|
|
322
|
+
font-size: 0.8rem;
|
|
323
|
+
color: var(--color-text-secondary, #7f8c8d);
|
|
324
|
+
margin-left: auto;
|
|
277
325
|
}
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
<h3>Diagrams (<span x-text="pkg.diagrams.length"></span>)</h3>
|
|
74
74
|
<ul class="diagram-list">
|
|
75
75
|
<template x-for="diagramId in pkg.diagrams" :key="diagramId">
|
|
76
|
-
<li class="diagram-item">
|
|
76
|
+
<li class="diagram-item clickable-row" @click="$store.app.selectDiagram(diagramId)">
|
|
77
77
|
<svg class="item-icon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
78
78
|
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
|
79
79
|
<line x1="3" y1="9" x2="21" y2="9"></line>
|
|
@@ -81,6 +81,7 @@
|
|
|
81
81
|
</svg>
|
|
82
82
|
<span class="diagram-name" x-text="$store.app.data.diagrams[diagramId] && $store.app.data.diagrams[diagramId].name"></span>
|
|
83
83
|
<span class="diagram-type" x-text="$store.app.data.diagrams[diagramId] && `(${$store.app.data.diagrams[diagramId].type})`"></span>
|
|
84
|
+
<span class="diagram-objects" x-show="$store.app.data.diagrams[diagramId] && $store.app.data.diagrams[diagramId].objectCount" x-text="`${$store.app.data.diagrams[diagramId].objectCount} objects`"></span>
|
|
84
85
|
</li>
|
|
85
86
|
</template>
|
|
86
87
|
</ul>
|
|
@@ -503,6 +504,75 @@
|
|
|
503
504
|
</template>
|
|
504
505
|
</div>
|
|
505
506
|
</div>
|
|
507
|
+
|
|
508
|
+
<!-- Diagram View -->
|
|
509
|
+
<div x-show="currentView === 'diagram'" x-transition class="view-diagram">
|
|
510
|
+
<template x-if="currentDiagram && data.diagrams[currentDiagram]">
|
|
511
|
+
<div class="diagram-details" x-data="{
|
|
512
|
+
get diag() { return data.diagrams[currentDiagram]; },
|
|
513
|
+
get pkg() { return diag.package ? data.packages[diag.package] : null; },
|
|
514
|
+
diagramScale: 1.0,
|
|
515
|
+
diagramPanX: 0,
|
|
516
|
+
diagramPanY: 0,
|
|
517
|
+
isPanning: false,
|
|
518
|
+
panStartX: 0,
|
|
519
|
+
panStartY: 0,
|
|
520
|
+
zoom(factor) { this.diagramScale = Math.max(0.1, Math.min(5.0, this.diagramScale * factor)); },
|
|
521
|
+
resetView() { this.diagramScale = 1.0; this.diagramPanX = 0; this.diagramPanY = 0; },
|
|
522
|
+
startPan(e) { this.isPanning = true; this.panStartX = e.clientX - this.diagramPanX; this.panStartY = e.clientY - this.diagramPanY; },
|
|
523
|
+
doPan(e) { if (!this.isPanning) return; this.diagramPanX = e.clientX - this.panStartX; this.diagramPanY = e.clientY - this.panStartY; },
|
|
524
|
+
endPan() { this.isPanning = false; },
|
|
525
|
+
downloadSvg() {
|
|
526
|
+
const svgEl = document.querySelector('.diagram-svg-container svg');
|
|
527
|
+
if (!svgEl) return;
|
|
528
|
+
const data = new XMLSerializer().serializeToString(svgEl);
|
|
529
|
+
const blob = new Blob([data], { type: 'image/svg+xml' });
|
|
530
|
+
const url = URL.createObjectURL(blob);
|
|
531
|
+
const a = document.createElement('a');
|
|
532
|
+
a.href = url; a.download = (this.diag.name || 'diagram').replace(/[^a-zA-Z0-9_-]/g, '_') + '.svg';
|
|
533
|
+
a.click(); URL.revokeObjectURL(url);
|
|
534
|
+
}
|
|
535
|
+
}">
|
|
536
|
+
<div class="entity-header">
|
|
537
|
+
<div class="entity-title">
|
|
538
|
+
<h1 class="entity-name" x-text="diag.name"></h1>
|
|
539
|
+
<p class="entity-subtitle">
|
|
540
|
+
<span x-text="diag.type"></span>
|
|
541
|
+
<span x-show="diag.objectCount"> - <span x-text="diag.objectCount"></span> elements, <span x-text="diag.linkCount"></span> connectors</span>
|
|
542
|
+
</p>
|
|
543
|
+
</div>
|
|
544
|
+
<div style="display: flex; gap: 0.5rem; align-items: center;">
|
|
545
|
+
<button class="link-button" x-show="diag.package" @click="selectPackage(diag.package)">
|
|
546
|
+
← Back to package
|
|
547
|
+
</button>
|
|
548
|
+
<span class="entity-type-badge">Diagram</span>
|
|
549
|
+
</div>
|
|
550
|
+
</div>
|
|
551
|
+
|
|
552
|
+
<div class="diagram-toolbar" x-show="diag.svg">
|
|
553
|
+
<button class="btn btn-sm" @click="zoom(1.2)">Zoom In</button>
|
|
554
|
+
<button class="btn btn-sm" @click="zoom(0.8)">Zoom Out</button>
|
|
555
|
+
<button class="btn btn-sm" @click="resetView()">Reset</button>
|
|
556
|
+
<button class="btn btn-sm" @click="downloadSvg()">Download SVG</button>
|
|
557
|
+
</div>
|
|
558
|
+
|
|
559
|
+
<div class="diagram-svg-container"
|
|
560
|
+
x-show="diag.svg"
|
|
561
|
+
x-ref="diagramContainer"
|
|
562
|
+
@wheel.prevent="zoom($event.deltaY > 0 ? 0.9 : 1.1)"
|
|
563
|
+
@mousedown="startPan($event)"
|
|
564
|
+
@mousemove="doPan($event)"
|
|
565
|
+
@mouseup="endPan()"
|
|
566
|
+
@mouseleave="endPan()"
|
|
567
|
+
:style="`transform: translate(${diagramPanX}px, ${diagramPanY}px) scale(${diagramScale}); transform-origin: center; cursor: ${isPanning ? 'grabbing' : 'grab'};`">
|
|
568
|
+
<div x-html="diag.svg"></div>
|
|
569
|
+
</div>
|
|
570
|
+
|
|
571
|
+
<div x-show="!diag.svg" class="empty-state">
|
|
572
|
+
<p class="empty-state-message">SVG rendering not available for this diagram. Re-generate with <code>--render-diagrams</code> to include SVG.</p>
|
|
573
|
+
</div>
|
|
574
|
+
</div>
|
|
575
|
+
</template>
|
|
506
576
|
</div>
|
|
507
|
-
|
|
508
|
-
</
|
|
577
|
+
|
|
578
|
+
</div>
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lutaml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.10.
|
|
4
|
+
version: 0.10.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-05-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: expressir
|