lutaml 0.10.7 → 0.10.8

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.
@@ -1,110 +1,97 @@
1
- <div class="content-pane">
1
+ <div class="content-area">
2
2
  <!-- Welcome Screen -->
3
3
  <div x-show="currentView === 'welcome'" x-transition class="view-welcome">
4
- <div class="welcome-content">
5
- <h1>Welcome to {{ config.title }}</h1>
6
- <p class="welcome-description">Browse UML packages, classes, and associations with powerful search capabilities.</p>
7
-
8
- <div class="stats-grid" x-show="data && data.metadata">
9
- <div class="stat-card">
10
- <div class="stat-value" x-text="data.metadata.statistics.packages"></div>
11
- <div class="stat-label">Packages</div>
12
- </div>
13
- <div class="stat-card">
14
- <div class="stat-value" x-text="data.metadata.statistics.classes"></div>
15
- <div class="stat-label">Classes</div>
16
- </div>
17
- <div class="stat-card">
18
- <div class="stat-value" x-text="data.metadata.statistics.attributes"></div>
19
- <div class="stat-label">Attributes</div>
20
- </div>
21
- <div class="stat-card">
22
- <div class="stat-value" x-text="data.metadata.statistics.associations"></div>
23
- <div class="stat-label">Associations</div>
24
- </div>
4
+ <span class="welcome-logo">{{ lutamlIcon }}</span>
5
+ <h1 class="welcome-title" x-text="JSON.parse(config).title || 'UML Model Browser'"></h1>
6
+ <p class="welcome-subtitle">Browse packages, classes, associations, and diagrams.</p>
7
+
8
+ <div class="welcome-stats" x-show="data && data.metadata">
9
+ <div class="welcome-stat">
10
+ <div class="welcome-stat-value" x-text="data.metadata.statistics.packages"></div>
11
+ <div class="welcome-stat-label">Packages</div>
25
12
  </div>
26
-
27
- <div class="welcome-actions">
28
- <p>Select a package from the sidebar or use the search to get started.</p>
29
- <kbd class="keyboard-hint">Press <kbd>/</kbd> or <kbd>Ctrl+K</kbd> to search</kbd>
13
+ <div class="welcome-stat">
14
+ <div class="welcome-stat-value" x-text="data.metadata.statistics.classes"></div>
15
+ <div class="welcome-stat-label">Classes</div>
16
+ </div>
17
+ <div class="welcome-stat">
18
+ <div class="welcome-stat-value" x-text="data.metadata.statistics.attributes"></div>
19
+ <div class="welcome-stat-label">Attributes</div>
30
20
  </div>
21
+ <div class="welcome-stat">
22
+ <div class="welcome-stat-value" x-text="data.metadata.statistics.associations"></div>
23
+ <div class="welcome-stat-label">Associations</div>
24
+ </div>
25
+ </div>
26
+
27
+ <div class="welcome-actions">
28
+ Select a package from the sidebar or press <kbd>/</kbd> to search.
31
29
  </div>
32
30
  </div>
33
31
 
34
- <!-- Package Details INLINE WITH FIXES -->
32
+ <!-- Package Details -->
35
33
  <div x-show="currentView === 'package'" x-transition class="view-package">
36
34
  <template x-if="currentPackage && data.packages[currentPackage]">
37
35
  <div class="package-details" x-data="{ get pkg() { return $store.app.data.packages[$store.app.currentPackage]; } }">
38
36
  <div class="entity-header">
39
- <h1 class="entity-name" x-text="pkg.name"></h1>
40
- <span class="entity-type-badge">Package</span>
37
+ <div class="entity-title">
38
+ <h1 class="entity-name" x-text="pkg.name"></h1>
39
+ <p class="entity-subtitle" x-show="pkg.path"><code x-text="pkg.path"></code></p>
40
+ </div>
41
+ <span class="entity-badge badge-package">Package</span>
41
42
  </div>
42
43
 
43
- <dl class="entity-metadata">
44
- <dt>Path</dt>
45
- <dd><code x-text="pkg.path"></code></dd>
46
-
47
- <template x-if="pkg.stereotypes && pkg.stereotypes.length > 0">
48
- <div>
49
- <dt>Stereotypes</dt>
50
- <dd>
51
- <template x-for="stereotype in pkg.stereotypes" :key="stereotype">
52
- <span class="stereotype-badge" x-text="stereotype"></span>
53
- </template>
54
- </dd>
55
- </div>
56
- </template>
57
- </dl>
44
+ <div class="entity-metadata" x-show="pkg.stereotypes && pkg.stereotypes.length > 0">
45
+ <div class="metadata-item">
46
+ <span class="metadata-label">Stereotypes</span>
47
+ <span class="metadata-value">
48
+ <template x-for="s in pkg.stereotypes" :key="s">
49
+ <span class="stereotype-tag" x-text="s"></span>
50
+ </template>
51
+ </span>
52
+ </div>
53
+ </div>
58
54
 
59
55
  <div class="entity-definition" x-show="pkg.definition">
60
- <h3>Description</h3>
61
56
  <div class="definition-content" x-html="pkg.definition"></div>
62
57
  </div>
63
58
 
64
59
  <!-- Empty State -->
65
60
  <template x-if="(!pkg.subPackages || pkg.subPackages.length === 0) && (!pkg.classes || pkg.classes.length === 0) && (!pkg.diagrams || pkg.diagrams.length === 0)">
66
61
  <div class="empty-state">
67
- <p class="empty-state-message">This package is empty.</p>
62
+ <p>This package is empty.</p>
68
63
  </div>
69
64
  </template>
70
65
 
71
- <!-- Diagrams FIRST and PROMINENT -->
66
+ <!-- Diagrams -->
72
67
  <div class="section" x-show="pkg.diagrams && pkg.diagrams.length > 0">
73
- <h3>Diagrams (<span x-text="pkg.diagrams.length"></span>)</h3>
74
- <ul class="diagram-list">
68
+ <h3 class="section-title">Diagrams <span class="section-count" x-text="pkg.diagrams.length"></span></h3>
69
+ <div class="item-list">
75
70
  <template x-for="diagramId in pkg.diagrams" :key="diagramId">
76
- <li class="diagram-item clickable-row" @click="$store.app.selectDiagram(diagramId)">
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
- <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
79
- <line x1="3" y1="9" x2="21" y2="9"></line>
80
- <line x1="9" y1="21" x2="9" y2="9"></line>
71
+ <div class="list-item" @click="$store.app.selectDiagram(diagramId)">
72
+ <svg class="list-item-icon" width="16" height="16" viewBox="0 0 16 16" fill="none">
73
+ <rect x="2" y="2" width="12" height="12" rx="1.5" stroke="currentColor" stroke-width="1.3"/>
74
+ <path d="M2 6h12M6 6v8" stroke="currentColor" stroke-width="1.3"/>
81
75
  </svg>
82
- <span class="diagram-name" x-text="$store.app.data.diagrams[diagramId] && $store.app.data.diagrams[diagramId].name"></span>
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>
85
- </li>
76
+ <span class="list-item-name" x-text="$store.app.data.diagrams[diagramId] && $store.app.data.diagrams[diagramId].name"></span>
77
+ <span class="list-item-meta" x-show="$store.app.data.diagrams[diagramId] && $store.app.data.diagrams[diagramId].objectCount" x-text="`${$store.app.data.diagrams[diagramId].objectCount} objects`"></span>
78
+ <span class="badge badge-diagram" x-show="$store.app.data.diagrams[diagramId]" x-text="$store.app.data.diagrams[diagramId] && $store.app.data.diagrams[diagramId].type"></span>
79
+ </div>
86
80
  </template>
87
- </ul>
88
- </div>
89
-
90
- <!-- Info message -->
91
- <template x-if="(!pkg.classes || pkg.classes.length === 0) && pkg.diagrams && pkg.diagrams.length > 0">
92
- <div class="info-message">
93
- <p>This package contains <strong x-text="pkg.diagrams.length"></strong> diagram(s) but no classes.</p>
94
81
  </div>
95
- </template>
82
+ </div>
96
83
 
97
84
  <!-- Sub-packages -->
98
85
  <div class="section" x-show="pkg.subPackages && pkg.subPackages.length > 0">
99
- <h3>Sub-packages</h3>
100
- <div class="package-list">
86
+ <h3 class="section-title">Sub-packages <span class="section-count" x-text="pkg.subPackages.length"></span></h3>
87
+ <div class="item-list">
101
88
  <template x-for="subPkgId in pkg.subPackages" :key="subPkgId">
102
- <div class="package-item" @click="$store.app.selectPackage(subPkgId)">
103
- <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">
104
- <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path>
89
+ <div class="list-item" @click="$store.app.selectPackage(subPkgId)">
90
+ <svg class="list-item-icon" width="16" height="16" viewBox="0 0 16 16" fill="none">
91
+ <path d="M2 5a2 2 0 012-2h3l1.5 2H12a2 2 0 012 2v4a2 2 0 01-2 2H4a2 2 0 01-2-2V5z" stroke="currentColor" stroke-width="1.2"/>
105
92
  </svg>
106
- <span class="item-name" x-text="data.packages[subPkgId].name"></span>
107
- <span class="item-count" x-text="`${(data.packages[subPkgId].classes || []).length} classes`"></span>
93
+ <span class="list-item-name" x-text="data.packages[subPkgId].name"></span>
94
+ <span class="list-item-meta" x-text="`${(data.packages[subPkgId].classes || []).length} classes`"></span>
108
95
  </div>
109
96
  </template>
110
97
  </div>
@@ -112,41 +99,50 @@
112
99
 
113
100
  <!-- Classes -->
114
101
  <div class="section" x-show="pkg.classes && pkg.classes.length > 0">
115
- <h3>Classes</h3>
116
- <table class="data-table classes-table">
117
- <thead>
118
- <tr>
119
- <th>Name</th>
120
- <th>Type</th>
121
- <th>Stereotypes</th>
122
- <th>Attributes</th>
123
- </tr>
124
- </thead>
125
- <tbody>
126
- <template x-for="classId in pkg.classes" :key="classId">
127
- <tr @click="$store.app.selectClass(classId)" class="clickable-row">
128
- <td><button class="link-button" x-text="data.classes[classId].name"></button></td>
129
- <td><span class="type-badge" x-text="data.classes[classId].type"></span></td>
130
- <td>
131
- <template x-if="data.classes[classId].stereotypes && data.classes[classId].stereotypes.length > 0">
132
- <div class="stereotypes-cell">
133
- <template x-for="stereotype in data.classes[classId].stereotypes" :key="stereotype">
134
- <span class="stereotype-tag" x-text="stereotype"></span>
135
- </template>
136
- </div>
137
- </template>
138
- </td>
139
- <td><span class="count-value" x-text="(data.classes[classId].attributes || []).length"></span></td>
102
+ <h3 class="section-title">Classes <span class="section-count" x-text="pkg.classes.length"></span></h3>
103
+ <div class="table-wrapper">
104
+ <table class="data-table">
105
+ <thead>
106
+ <tr>
107
+ <th>Name</th>
108
+ <th>Type</th>
109
+ <th>Stereotypes</th>
110
+ <th>Attrs</th>
140
111
  </tr>
141
- </template>
142
- </tbody>
143
- </table>
112
+ </thead>
113
+ <tbody>
114
+ <template x-for="classId in pkg.classes" :key="classId">
115
+ <tr @click="$store.app.selectClass(classId)" class="clickable-row">
116
+ <td><button class="link-button" x-text="data.classes[classId].name"></button></td>
117
+ <td><span class="badge" :class="'badge-' + (data.classes[classId].type === 'Enumeration' ? 'enum' : data.classes[classId].type === 'DataType' ? 'datatype' : 'class')" x-text="data.classes[classId].type"></span></td>
118
+ <td>
119
+ <template x-if="data.classes[classId].stereotypes && data.classes[classId].stereotypes.length > 0">
120
+ <span>
121
+ <template x-for="st in data.classes[classId].stereotypes" :key="st">
122
+ <span class="stereotype-tag" x-text="st"></span>
123
+ </template>
124
+ </span>
125
+ </template>
126
+ </td>
127
+ <td style="color: var(--text-muted); font-variant-numeric: tabular-nums;" x-text="(data.classes[classId].attributes || []).length"></td>
128
+ </tr>
129
+ </template>
130
+ </tbody>
131
+ </table>
132
+ </div>
144
133
  </div>
134
+
135
+ <!-- Info message -->
136
+ <template x-if="(!pkg.classes || pkg.classes.length === 0) && pkg.diagrams && pkg.diagrams.length > 0">
137
+ <div class="info-message">
138
+ This package contains <strong x-text="pkg.diagrams.length"></strong> diagram(s) but no classes.
139
+ </div>
140
+ </template>
145
141
  </div>
146
142
  </template>
147
143
  </div>
148
144
 
149
- <!-- Class Details INLINE WITH FIXES -->
145
+ <!-- Class Details -->
150
146
  <div x-show="currentView === 'class'" x-transition class="view-class">
151
147
  <template x-if="currentClass && data.classes[currentClass]">
152
148
  <div class="class-details" x-data="{
@@ -155,14 +151,10 @@
155
151
  findClassByName(name) { return window.UMLUtils.findClassByName(data, name); },
156
152
  getAssociationEnds(assoc, cls) {
157
153
  if (!assoc || !cls) return { local: {}, remote: {}, direction: '↔' };
158
- if (assoc.source && assoc.source.class === cls.xmiId) {
159
- return { local: assoc.source, remote: assoc.target, direction: '' };
160
- } else if (assoc.target && assoc.target.class === cls.xmiId) {
161
- return { local: assoc.target, remote: assoc.source, direction: '←' };
162
- }
154
+ if (assoc.source && assoc.source.class === cls.xmiId) return { local: assoc.source, remote: assoc.target, direction: '→' };
155
+ if (assoc.target && assoc.target.class === cls.xmiId) return { local: assoc.target, remote: assoc.source, direction: '' };
163
156
  return { local: assoc.source || {}, remote: assoc.target || {}, direction: '↔' };
164
- }
165
- ,
157
+ },
166
158
  groupByParent(items) {
167
159
  if (!items || items.length === 0) return [];
168
160
  const groups = {};
@@ -178,164 +170,131 @@
178
170
  <h1 class="entity-name" x-text="cls.qualifiedName"></h1>
179
171
  <p class="entity-subtitle" x-show="cls.name !== cls.qualifiedName" x-text="`(${cls.name})`"></p>
180
172
  </div>
181
- <span class="entity-type-badge" x-text="cls.type"></span>
182
- <span class="abstract-badge" x-show="cls.isAbstract">Abstract</span>
173
+ <span class="entity-badge" :class="'badge-' + (cls.type === 'Enumeration' ? 'enum' : cls.type === 'DataType' ? 'datatype' : cls.type === 'Interface' ? 'interface' : 'class')" x-text="cls.type"></span>
174
+ <span class="entity-badge badge-abstract" x-show="cls.isAbstract">Abstract</span>
183
175
  </div>
184
176
 
185
- <dl class="entity-metadata">
186
- <dt>Qualified Name</dt>
187
- <dd><code x-text="cls.qualifiedName"></code></dd>
188
-
189
- <dt>Package</dt>
190
- <dd>
191
- <button class="link-button" @click="selectPackage(cls.package)" x-text="data.packages[cls.package] && data.packages[cls.package].name"></button>
192
- </dd>
193
-
194
- <template x-if="cls.stereotypes && cls.stereotypes.length > 0">
195
- <div>
196
- <dt>Stereotypes</dt>
197
- <dd>
198
- <template x-for="stereotype in cls.stereotypes" :key="stereotype">
199
- <span class="stereotype-badge" x-text="stereotype"></span>
200
- </template>
201
- </dd>
202
- </div>
203
- </template>
204
- </dl>
177
+ <div class="entity-metadata">
178
+ <div class="metadata-item">
179
+ <span class="metadata-label">Package</span>
180
+ <span class="metadata-value"><button class="link-button" @click="selectPackage(cls.package)" x-text="data.packages[cls.package] && data.packages[cls.package].name"></button></span>
181
+ </div>
182
+ <div class="metadata-item" x-show="cls.stereotypes && cls.stereotypes.length > 0">
183
+ <span class="metadata-label">Stereotypes</span>
184
+ <span class="metadata-value">
185
+ <template x-for="st in cls.stereotypes" :key="st">
186
+ <span class="stereotype-tag" x-text="st"></span>
187
+ </template>
188
+ </span>
189
+ </div>
190
+ </div>
205
191
 
206
192
  <div class="entity-definition" x-show="cls.definition">
207
- <h3>Description</h3>
208
193
  <div class="definition-content" x-html="cls.definition"></div>
209
194
  </div>
210
195
 
211
- <!-- Inheritance (Generalization) -->
196
+ <!-- Inheritance -->
212
197
  <div class="section" x-show="(cls.generalizations && cls.generalizations.length > 0) || (cls.specializations && cls.specializations.length > 0)">
213
- <h3>Inheritance</h3>
198
+ <h3 class="section-title">Inheritance</h3>
214
199
 
215
- <div x-show="cls.generalizations && cls.generalizations.length > 0">
216
- <h4>Extends (Superclasses)</h4>
217
- <div class="inheritance-list">
200
+ <div x-show="cls.generalizations && cls.generalizations.length > 0" style="margin-bottom: var(--space-4);">
201
+ <div class="section-title" style="font-size: var(--text-xs);">Extends</div>
202
+ <div class="item-list">
218
203
  <template x-for="parentId in cls.generalizations" :key="parentId">
219
- <div class="inheritance-item">
220
- <button @click="selectClass(parentId)" class="link-button">
221
- <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">
222
- <polyline points="18 15 12 9 6 15"></polyline>
223
- </svg>
224
- <span x-text="data.classes[parentId] && data.classes[parentId].name"></span>
225
- </button>
226
- <div class="inheritance-meta" x-show="data.classes[parentId]">
227
- <span class="inheritance-path" x-text="data.classes[parentId] && data.classes[parentId].qualifiedName"></span>
228
- </div>
204
+ <div class="list-item" @click="selectClass(parentId)">
205
+ <svg class="list-item-icon" width="14" height="14" viewBox="0 0 14 14" fill="none">
206
+ <polyline points="4 10 7 4 10 10" stroke="currentColor" stroke-width="1.3" fill="none"/>
207
+ </svg>
208
+ <span class="list-item-name" x-text="data.classes[parentId] && data.classes[parentId].name"></span>
209
+ <span class="list-item-meta" x-text="data.classes[parentId] && data.classes[parentId].qualifiedName"></span>
229
210
  </div>
230
211
  </template>
231
212
  </div>
232
213
  </div>
233
214
 
234
215
  <div x-show="cls.specializations && cls.specializations.length > 0">
235
- <h4>Extended by (Subclasses)</h4>
236
- <div class="inheritance-list">
216
+ <div class="section-title" style="font-size: var(--text-xs);">Extended by</div>
217
+ <div class="item-list">
237
218
  <template x-for="childId in cls.specializations" :key="childId">
238
- <button @click="selectClass(childId)" class="inheritance-item link-button">
239
- <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">
240
- <polyline points="6 9 12 15 18 9"></polyline>
219
+ <div class="list-item" @click="selectClass(childId)">
220
+ <svg class="list-item-icon" width="14" height="14" viewBox="0 0 14 14" fill="none">
221
+ <polyline points="4 4 7 10 10 4" stroke="currentColor" stroke-width="1.3" fill="none"/>
241
222
  </svg>
242
- <span x-text="data.classes[childId] && data.classes[childId].name"></span>
243
- </button>
223
+ <span class="list-item-name" x-text="data.classes[childId] && data.classes[childId].name"></span>
224
+ </div>
244
225
  </template>
245
226
  </div>
246
227
  </div>
247
228
  </div>
248
229
 
249
- <!-- Attributes with TYPE CLICKABILITY -->
230
+ <!-- Attributes -->
250
231
  <div class="section" x-show="cls.attributes && cls.attributes.length > 0">
251
- <h3>Attributes</h3>
252
- <table class="data-table attributes-table">
253
- <thead>
254
- <tr>
255
- <th>Name</th>
256
- <th>Type</th>
257
- <th>Visibility</th>
258
- <th>Cardinality</th>
259
- <th>Modifiers</th>
260
- </tr>
261
- </thead>
262
- <tbody>
263
- <template x-for="attrId in cls.attributes" :key="attrId">
264
- <tr x-data="{ attr: data.attributes[attrId] }">
265
- <td>
266
- <code x-text="attr.name"></code>
267
- <div class="cell-description" x-show="attr.definition" x-text="attr.definition"></div>
268
- </td>
269
- <td>
270
- <template x-if="isUmlBasicType(attr.type)">
271
- <code class="type-name uml-basic-type" x-text="attr.type" title="UML basic type"></code>
272
- </template>
273
- <template x-if="!isUmlBasicType(attr.type) && findClassByName(attr.type)">
274
- <button class="type-link" @click="selectClass(findClassByName(attr.type))" x-text="attr.type" title="Click to navigate"></button>
275
- </template>
276
- <template x-if="!isUmlBasicType(attr.type) && !findClassByName(attr.type)">
277
- <span class="type-unresolved" :title="'Unresolved type: ' + attr.type">
278
- <span x-text="'⚠️ ' + attr.type"></span>
279
- </span>
280
- </template>
281
- </td>
282
- <td><span class="visibility-badge" :data-visibility="attr.visibility" x-text="attr.visibility"></span></td>
283
- <td><code x-show="attr.cardinality" x-text="`${attr.cardinality.min}..${attr.cardinality.max}`"></code></td>
284
- <td>
285
- <span class="modifier-badge" x-show="attr.isStatic">static</span>
286
- <span class="modifier-badge" x-show="attr.isReadOnly">readOnly</span>
287
- </td>
288
- </tr>
289
- </template>
290
- </tbody>
291
- </table>
232
+ <h3 class="section-title">Attributes <span class="section-count" x-text="cls.attributes.length"></span></h3>
233
+ <div class="table-wrapper">
234
+ <table class="data-table">
235
+ <thead>
236
+ <tr><th>Name</th><th>Type</th><th>Visibility</th><th>Cardinality</th><th>Modifiers</th></tr>
237
+ </thead>
238
+ <tbody>
239
+ <template x-for="attrId in cls.attributes" :key="attrId">
240
+ <tr x-data="{ attr: data.attributes[attrId] }">
241
+ <td>
242
+ <code x-text="attr.name"></code>
243
+ <div class="cell-description" x-show="attr.definition" x-text="attr.definition"></div>
244
+ </td>
245
+ <td>
246
+ <template x-if="isUmlBasicType(attr.type)">
247
+ <code class="uml-basic-type" x-text="attr.type"></code>
248
+ </template>
249
+ <template x-if="!isUmlBasicType(attr.type) && findClassByName(attr.type)">
250
+ <button class="type-link" @click="selectClass(findClassByName(attr.type))" x-text="attr.type"></button>
251
+ </template>
252
+ <template x-if="!isUmlBasicType(attr.type) && !findClassByName(attr.type)">
253
+ <span class="type-unresolved" x-text="attr.type"></span>
254
+ </template>
255
+ </td>
256
+ <td><span class="visibility-badge" :data-visibility="attr.visibility" x-text="attr.visibility"></span></td>
257
+ <td><code x-show="attr.cardinality" x-text="`${attr.cardinality.min}..${attr.cardinality.max}`"></code></td>
258
+ <td>
259
+ <span class="modifier-badge" x-show="attr.isStatic">static</span>
260
+ <span class="modifier-badge" x-show="attr.isReadOnly">readOnly</span>
261
+ </td>
262
+ </tr>
263
+ </template>
264
+ </tbody>
265
+ </table>
266
+ </div>
292
267
  </div>
293
268
 
294
- <!-- Inherited Attributes GROUPED BY PARENT -->
269
+ <!-- Inherited Attributes -->
295
270
  <div class="section" x-show="cls.inheritedAttributes && cls.inheritedAttributes.length > 0">
296
- <h3>Inherited Attributes (<span x-text="cls.inheritedAttributes.length"></span>)</h3>
297
-
271
+ <h3 class="section-title">Inherited Attributes <span class="section-count" x-text="cls.inheritedAttributes.length"></span></h3>
298
272
  <template x-for="(parentGroup, idx) in groupByParent(cls.inheritedAttributes)" :key="idx">
299
- <div class="inherited-group">
300
- <h4 class="inherited-group-header">
273
+ <div class="inheritance-group">
274
+ <div class="inheritance-header">
301
275
  <span>From:</span>
302
276
  <button class="link-button" @click="selectClass(parentGroup[0].inheritedFrom)" x-text="parentGroup[0].inheritedFromName"></button>
303
- </h4>
277
+ </div>
304
278
  <div class="table-wrapper">
305
- <table class="data-table inherited-attributes-table">
306
- <thead>
307
- <tr>
308
- <th>Name</th>
309
- <th>Type</th>
310
- <th>Visibility</th>
311
- <th>Cardinality</th>
312
- <th>Modifiers</th>
313
- </tr>
314
- </thead>
279
+ <table class="data-table inherited-table">
280
+ <thead><tr><th>Name</th><th>Type</th><th>Visibility</th><th>Cardinality</th></tr></thead>
315
281
  <tbody>
316
282
  <template x-for="item in parentGroup" :key="item.attributeId">
317
283
  <tr x-data="{ attr: item.attribute }">
318
- <td>
319
- <code x-text="attr.name"></code>
320
- <div class="cell-description" x-show="attr.definition" x-text="attr.definition"></div>
321
- </td>
284
+ <td><code x-text="attr.name"></code></td>
322
285
  <td>
323
286
  <template x-if="isUmlBasicType(attr.type)">
324
- <code class="type-name uml-basic-type" x-text="attr.type"></code>
287
+ <code class="uml-basic-type" x-text="attr.type"></code>
325
288
  </template>
326
289
  <template x-if="!isUmlBasicType(attr.type) && findClassByName(attr.type)">
327
290
  <button class="type-link" @click="selectClass(findClassByName(attr.type))" x-text="attr.type"></button>
328
291
  </template>
329
292
  <template x-if="!isUmlBasicType(attr.type) && !findClassByName(attr.type)">
330
- <span class="type-unresolved"><span x-text="'⚠️ ' + attr.type"></span></span>
293
+ <span class="type-unresolved" x-text="attr.type"></span>
331
294
  </template>
332
295
  </td>
333
296
  <td><span class="visibility-badge" :data-visibility="attr.visibility" x-text="attr.visibility"></span></td>
334
297
  <td><code x-show="attr.cardinality" x-text="`${attr.cardinality.min}..${attr.cardinality.max}`"></code></td>
335
- <td>
336
- <span class="modifier-badge" x-show="attr.isStatic">static</span>
337
- <span class="modifier-badge" x-show="attr.isReadOnly">readOnly</span>
338
- </td>
339
298
  </tr>
340
299
  </template>
341
300
  </tbody>
@@ -345,44 +304,28 @@
345
304
  </template>
346
305
  </div>
347
306
 
348
- <!-- Associations WITH DESCRIPTIONS -->
307
+ <!-- Associations -->
349
308
  <div class="section" x-show="cls.associations && cls.associations.length > 0">
350
- <h3>Associations (<span x-text="cls.associations.length"></span>)</h3>
309
+ <h3 class="section-title">Associations <span class="section-count" x-text="cls.associations.length"></span></h3>
351
310
  <div class="table-wrapper">
352
- <table class="data-table associations-table">
353
- <thead>
354
- <tr>
355
- <th>Name (Local Role)</th>
356
- <th>Target</th>
357
- <th>Dir</th>
358
- <th>Cardinality</th>
359
- <th>Target Role</th>
360
- <th>Target Card</th>
361
- <th>Type</th>
362
- </tr>
363
- </thead>
311
+ <table class="data-table">
312
+ <thead><tr><th>Role</th><th>Target</th><th>Dir</th><th>Cardinality</th><th>Target Role</th><th>Target Card</th><th>Type</th></tr></thead>
364
313
  <tbody>
365
314
  <template x-for="assocId in cls.associations" :key="assocId">
366
315
  <tr x-data="{
367
316
  assoc: data.associations[assocId],
368
317
  get ends() {
369
318
  if (!this.assoc || !cls) return { local: {}, remote: {}, direction: '↔' };
370
- if (this.assoc.source && this.assoc.source.class === cls.xmiId) {
371
- return { local: this.assoc.source, remote: this.assoc.target, direction: '' };
372
- } else if (this.assoc.target && this.assoc.target.class === cls.xmiId) {
373
- return { local: this.assoc.target, remote: this.assoc.source, direction: '←' };
374
- }
319
+ if (this.assoc.source && this.assoc.source.class === cls.xmiId) return { local: this.assoc.source, remote: this.assoc.target, direction: '→' };
320
+ if (this.assoc.target && this.assoc.target.class === cls.xmiId) return { local: this.assoc.target, remote: this.assoc.source, direction: '' };
375
321
  return { local: this.assoc.source || {}, remote: this.assoc.target || {}, direction: '↔' };
376
322
  }
377
323
  }">
378
324
  <td>
379
325
  <code x-text="ends.local ? (ends.local.role || '(unnamed)') : ''"></code>
380
- <div class="cell-description" x-show="assoc && assoc.name && assoc.name !== (ends.local && ends.local.role)" x-text="assoc ? `Association: ${assoc.name}` : ''"></div>
381
326
  <div class="cell-description" x-show="assoc && assoc.definition" x-text="assoc ? assoc.definition : ''"></div>
382
327
  </td>
383
- <td>
384
- <button class="link-button" @click="ends.remote && ends.remote.class && selectClass(ends.remote.class)" x-text="ends.remote ? ends.remote.className : ''"></button>
385
- </td>
328
+ <td><button class="link-button" @click="ends.remote && ends.remote.class && selectClass(ends.remote.class)" x-text="ends.remote ? ends.remote.className : ''"></button></td>
386
329
  <td><span x-text="ends.direction"></span></td>
387
330
  <td><code x-show="ends.local && ends.local.cardinality" x-text="ends.local && ends.local.cardinality ? `${ends.local.cardinality.min}..${ends.local.cardinality.max}` : ''"></code></td>
388
331
  <td><code x-text="ends.remote ? (ends.remote.role || '(unnamed)') : ''"></code></td>
@@ -390,9 +333,7 @@
390
333
  <td>
391
334
  <span x-show="ends.local && ends.local.aggregation === 'composite'" title="Composition">◆</span>
392
335
  <span x-show="ends.local && ends.local.aggregation === 'shared'" title="Aggregation">◇</span>
393
- <span x-show="ends.remote && ends.remote.aggregation === 'composite'" title="Composition">◆</span>
394
- <span x-show="ends.remote && ends.remote.aggregation === 'shared'" title="Aggregation">◇</span>
395
- <span x-show="!(ends.local && ends.local.aggregation) && !(ends.remote && ends.remote.aggregation)">Assoc</span>
336
+ <span x-show="!(ends.local && ends.local.aggregation) && !(ends.remote && ends.remote.aggregation)">—</span>
396
337
  </td>
397
338
  </tr>
398
339
  </template>
@@ -401,62 +342,34 @@
401
342
  </div>
402
343
  </div>
403
344
 
404
- <!-- Inherited Associations WITH DESCRIPTIONS -->
345
+ <!-- Inherited Associations -->
405
346
  <div class="section" x-show="cls.inheritedAssociations && cls.inheritedAssociations.length > 0">
406
- <h3>Inherited Associations (<span x-text="cls.inheritedAssociations.length"></span>)</h3>
407
-
347
+ <h3 class="section-title">Inherited Associations <span class="section-count" x-text="cls.inheritedAssociations.length"></span></h3>
408
348
  <template x-for="(parentGroup, idx) in groupByParent(cls.inheritedAssociations)" :key="idx">
409
- <div class="inherited-group">
410
- <h4 class="inherited-group-header">
349
+ <div class="inheritance-group">
350
+ <div class="inheritance-header">
411
351
  <span>From:</span>
412
352
  <button class="link-button" @click="selectClass(parentGroup[0].inheritedFrom)" x-text="parentGroup[0].inheritedFromName"></button>
413
- </h4>
353
+ </div>
414
354
  <div class="table-wrapper">
415
- <table class="data-table inherited-associations-table">
416
- <thead>
417
- <tr>
418
- <th>Name (Local Role)</th>
419
- <th>Target</th>
420
- <th>Dir</th>
421
- <th>Cardinality</th>
422
- <th>Target Role</th>
423
- <th>Target Card</th>
424
- <th>Type</th>
425
- </tr>
426
- </thead>
355
+ <table class="data-table inherited-table">
356
+ <thead><tr><th>Role</th><th>Target</th><th>Dir</th><th>Cardinality</th><th>Target Role</th></tr></thead>
427
357
  <tbody>
428
358
  <template x-for="item in parentGroup" :key="item.associationId">
429
359
  <tr x-data="{
430
360
  assoc: data.associations[item.associationId],
431
361
  get ends() {
432
362
  if (!this.assoc || !cls) return { local: {}, remote: {}, direction: '↔' };
433
- if (this.assoc.source && this.assoc.source.class === cls.xmiId) {
434
- return { local: this.assoc.source, remote: this.assoc.target, direction: '' };
435
- } else if (this.assoc.target && this.assoc.target.class === cls.xmiId) {
436
- return { local: this.assoc.target, remote: this.assoc.source, direction: '←' };
437
- }
363
+ if (this.assoc.source && this.assoc.source.class === cls.xmiId) return { local: this.assoc.source, remote: this.assoc.target, direction: '→' };
364
+ if (this.assoc.target && this.assoc.target.class === cls.xmiId) return { local: this.assoc.target, remote: this.assoc.source, direction: '' };
438
365
  return { local: this.assoc.source || {}, remote: this.assoc.target || {}, direction: '↔' };
439
366
  }
440
367
  }">
441
- <td>
442
- <code x-text="ends.local ? (ends.local.role || '(unnamed)') : ''"></code>
443
- <div class="cell-description" x-show="assoc && assoc.name" x-text="assoc ? `Association: ${assoc.name}` : ''"></div>
444
- <div class="cell-description" x-show="assoc && assoc.definition" x-text="assoc ? assoc.definition : ''"></div>
445
- </td>
446
- <td>
447
- <button class="link-button" @click="ends.remote && ends.remote.class && selectClass(ends.remote.class)" x-text="ends.remote ? ends.remote.className : ''"></button>
448
- </td>
368
+ <td><code x-text="ends.local ? (ends.local.role || '(unnamed)') : ''"></code></td>
369
+ <td><button class="link-button" @click="ends.remote && ends.remote.class && selectClass(ends.remote.class)" x-text="ends.remote ? ends.remote.className : ''"></button></td>
449
370
  <td><span x-text="ends.direction"></span></td>
450
371
  <td><code x-show="ends.local && ends.local.cardinality" x-text="ends.local && ends.local.cardinality ? `${ends.local.cardinality.min}..${ends.local.cardinality.max}` : ''"></code></td>
451
372
  <td><code x-text="ends.remote ? (ends.remote.role || '(unnamed)') : ''"></code></td>
452
- <td><code x-show="ends.remote && ends.remote.cardinality" x-text="ends.remote && ends.remote.cardinality ? `${ends.remote.cardinality.min}..${ends.remote.cardinality.max}` : ''"></code></td>
453
- <td>
454
- <span x-show="ends.local && ends.local.aggregation === 'composite'">◆</span>
455
- <span x-show="ends.local && ends.local.aggregation === 'shared'">◇</span>
456
- <span x-show="ends.remote && ends.remote.aggregation === 'composite'">◆</span>
457
- <span x-show="ends.remote && ends.remote.aggregation === 'shared'">◇</span>
458
- <span x-show="!(ends.local && ends.local.aggregation) && !(ends.remote && ends.remote.aggregation)">Assoc</span>
459
- </td>
460
373
  </tr>
461
374
  </template>
462
375
  </tbody>
@@ -472,31 +385,24 @@
472
385
  <!-- Search Results -->
473
386
  <div x-show="currentView === 'search'" x-transition class="view-search">
474
387
  <div x-data="searchResultsView">
475
- <h2>Search Results</h2>
476
- <p class="search-query">Showing results for "<span x-text="searchQuery"></span>"</p>
388
+ <h2 style="margin-bottom: var(--space-2);">Search Results</h2>
389
+ <p style="color: var(--text-muted); margin-bottom: var(--space-6); font-size: var(--text-sm);">
390
+ Showing results for "<strong x-text="searchQuery"></strong>"
391
+ </p>
477
392
 
478
- <div x-show="searchResults.length === 0" class="no-results">
393
+ <div x-show="searchResults.length === 0" class="empty-state">
479
394
  <p>No results found.</p>
480
395
  </div>
481
396
 
482
- <div x-show="searchResults.length > 0" class="results-container">
397
+ <div x-show="searchResults.length > 0">
483
398
  <template x-for="group in groupedResults" :key="group.type">
484
399
  <div class="result-group">
485
- <h3 x-text="`${group.type}s (${group.items.length})`"></h3>
486
-
487
- <div class="result-list">
400
+ <h3 class="result-group-title" x-text="`${group.type}s (${group.items.length})`"></h3>
401
+ <div class="item-list">
488
402
  <template x-for="result in group.items" :key="result.id">
489
- <div class="result-item" @click="openResult(result)">
490
- <div class="result-header">
491
- <span class="result-name" x-html="highlightMatches(result.name, searchQuery)"></span>
492
- <span class="result-score" :title="`Relevance: ${result.score.toFixed(2)}`">
493
- <div class="score-dot" :class="getScoreClass(result.score)"></div>
494
- </span>
495
- </div>
496
- <div class="result-details">
497
- <span class="result-type" x-text="result.entityType"></span>
498
- <span class="result-path" x-text="result.qualifiedName || result.package"></span>
499
- </div>
403
+ <div class="list-item" @click="openResult(result)">
404
+ <span class="list-item-name" x-html="highlightMatches(result.name, searchQuery)"></span>
405
+ <span class="list-item-meta" x-text="result.entityType"></span>
500
406
  </div>
501
407
  </template>
502
408
  </div>
@@ -504,6 +410,7 @@
504
410
  </template>
505
411
  </div>
506
412
  </div>
413
+ </div>
507
414
 
508
415
  <!-- Diagram View -->
509
416
  <div x-show="currentView === 'diagram'" x-transition class="view-diagram">
@@ -525,8 +432,8 @@
525
432
  downloadSvg() {
526
433
  const svgEl = document.querySelector('.diagram-svg-container svg');
527
434
  if (!svgEl) return;
528
- const data = new XMLSerializer().serializeToString(svgEl);
529
- const blob = new Blob([data], { type: 'image/svg+xml' });
435
+ const d = new XMLSerializer().serializeToString(svgEl);
436
+ const blob = new Blob([d], { type: 'image/svg+xml' });
530
437
  const url = URL.createObjectURL(blob);
531
438
  const a = document.createElement('a');
532
439
  a.href = url; a.download = (this.diag.name || 'diagram').replace(/[^a-zA-Z0-9_-]/g, '_') + '.svg';
@@ -536,29 +443,26 @@
536
443
  <div class="entity-header">
537
444
  <div class="entity-title">
538
445
  <h1 class="entity-name" x-text="diag.name"></h1>
539
- <p class="entity-subtitle">
446
+ <p class="diagram-meta">
540
447
  <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>
448
+ <span x-show="diag.objectCount"> · <span x-text="diag.objectCount"></span> elements, <span x-text="diag.linkCount"></span> connectors</span>
542
449
  </p>
543
450
  </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
- &larr; Back to package
547
- </button>
548
- <span class="entity-type-badge">Diagram</span>
451
+ <div style="display: flex; gap: var(--space-2); align-items: center;">
452
+ <button class="link-button" x-show="diag.package" @click="selectPackage(diag.package)">&larr; Package</button>
453
+ <span class="entity-badge badge-diagram">Diagram</span>
549
454
  </div>
550
455
  </div>
551
456
 
552
457
  <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>
458
+ <button class="btn-sm" @click="zoom(1.2)">Zoom In</button>
459
+ <button class="btn-sm" @click="zoom(0.8)">Zoom Out</button>
460
+ <button class="btn-sm" @click="resetView()">Reset</button>
461
+ <button class="btn-sm" @click="downloadSvg()">Download SVG</button>
557
462
  </div>
558
463
 
559
464
  <div class="diagram-svg-container"
560
465
  x-show="diag.svg"
561
- x-ref="diagramContainer"
562
466
  @wheel.prevent="zoom($event.deltaY > 0 ? 0.9 : 1.1)"
563
467
  @mousedown="startPan($event)"
564
468
  @mousemove="doPan($event)"
@@ -569,10 +473,9 @@
569
473
  </div>
570
474
 
571
475
  <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>
476
+ <p>SVG rendering not available. Re-generate with <code>--render-diagrams</code> to include SVG.</p>
573
477
  </div>
574
478
  </div>
575
479
  </template>
576
480
  </div>
577
-
578
- </div>
481
+ </div>