lutaml-jsonschema 0.1.12 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ad24a6671a3bcf4e811c7588dbd4ca4fff96d589d625a7e6dd2507a74a115d4
4
- data.tar.gz: 947415a532ef91f5287564a63a422f9c5021e01b5ec43a174d8e68b53e81dcca
3
+ metadata.gz: 105f1b74c4e82552c08ad6a406ab2da66979e1bad38b93a0d654a547868275d6
4
+ data.tar.gz: 25b463827ef7593125b58a781f4d3cb60c3a8861b2736b2590879ec957d0b6f4
5
5
  SHA512:
6
- metadata.gz: d76f7513a5de8fe76529844d0a110f1901f3f9fc4968213f3ae872505370aec7ffd71ab6d349745a9af383b03d6d32ce19f972eccf585c6efd91281cc3d1f6e6
7
- data.tar.gz: 72a9b35c92992f4ac0fa82f63b477b362bb358b752d1deb773c3613a29e07ea0b4cf144dbaf11fd43b1c1b5eec3b5719ad72e2d3064bea17e112f1dafe55fc0a
6
+ metadata.gz: 9e9e80db8d96d09d0da8990e71f8b637cb7e11bb7a0debed104d56eb71add45d4f14c3933286f07333f5194ee5addcfcb21dabc3c22a426919a16b8896a0e5a1
7
+ data.tar.gz: 8bdbe14abce145c588ff46c2efec874d9f7a7098e74a615442929821d93f8646f871b43eb3a04da4f4114a1dd6fddbfbd9009ee6000786462afe10891529eeb6
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2026-05-08 02:42:09 UTC using RuboCop version 1.86.1.
3
+ # on 2026-05-10 22:34:24 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
@@ -51,7 +51,7 @@ Lint/IneffectiveAccessModifier:
51
51
  Exclude:
52
52
  - 'lib/lutaml/jsonschema/schema_set.rb'
53
53
 
54
- # Offense count: 15
54
+ # Offense count: 16
55
55
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
56
56
  Metrics/AbcSize:
57
57
  Exclude:
@@ -78,7 +78,7 @@ Metrics/CyclomaticComplexity:
78
78
  - 'lib/lutaml/jsonschema/reference_resolver.rb'
79
79
  - 'lib/lutaml/jsonschema/schema_set.rb'
80
80
 
81
- # Offense count: 16
81
+ # Offense count: 17
82
82
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
83
83
  Metrics/MethodLength:
84
84
  Max: 47
data/CHANGELOG.md CHANGED
@@ -1,5 +1,124 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.15] - 2026-05-10
4
+
5
+ ### Redoc-style UX improvements (round 16)
6
+
7
+ - DetailPanel descriptions rendered with inline markdown (bold, italic, code, links)
8
+ - Source viewer URLs are clickable links (Redoc jsonToHtml pattern)
9
+ - Definition mini-table rows show description tooltip on hover
10
+ - SeeMore component uses scrollHeight-based overflow detection (Redoc SeeMore pattern)
11
+ - Landing page schema grid has search/filter input
12
+ - Property sort toggle in SchemaBuilder (alphabetical vs required-first, Redoc sortPropsAlphabetically pattern)
13
+ - Schema header meta badges with colored count chips (properties, required, definitions)
14
+ - DetailPanel dark mode support (constraint colors, enum chips, example blocks)
15
+
16
+ ### Redoc-style UX improvements (round 17)
17
+
18
+ - Sidebar tree shows properties alongside definitions with type labels
19
+ - Copy link button in schema header for sharing current view via URL
20
+ - selectedPropertyName computed added to schema store
21
+
22
+ ### Redoc-style UX improvements (round 18)
23
+
24
+ - Mini-table required field names shown in bold with red asterisk (Redoc RequiredLabel pattern)
25
+ - Mini-table shows uniqueItems badge for properties with unique constraint
26
+ - Mini-table shows array items type `[type]` for array properties
27
+ - Mini-table shows all properties when <= 8 (smart limit)
28
+
29
+ ### Redoc-style UX improvements (round 19)
30
+
31
+ - Floating back-to-top button appears after scrolling 300px down
32
+ - Back-to-top button responsive on mobile (smaller, closer to edge) (constraint colors, enum chips, example blocks)
33
+
34
+ ## [0.1.14] - 2026-05-10
35
+
36
+ ### Redoc-style UX improvements (round 15)
37
+
38
+ - Markdown renderer converts `\n` to `<br>` for multi-line descriptions
39
+ - Definition mini-table shows `$ref` target name with clickable navigation
40
+ - Definition mini-table shows `const` badge for constant properties
41
+ - Definition mini-table shows deprecated strikethrough on property names
42
+ - Definition mini-table shows `N enum` label instead of just count
43
+ - Definition card header shows `N examples` instead of `N ex`
44
+ - Definition card expanded body shows required fields list and examples section
45
+ - DetailPanel properties tab rows are clickable (navigate to property)
46
+ - DetailPanel properties tab shows `$ref` target with clickable link
47
+ - Improved circular reference label (shows type badge, name, hint text)
48
+ - Source viewer: click on line number to highlight it
49
+ - Landing page card descriptions rendered with markdown
50
+ - Keyboard shortcut: `/` focuses search input from anywhere
51
+ - Search input placeholder shows shortcut hint
52
+ - Search results expand schema tree node automatically
53
+ - Hash navigation expands schema tree node automatically
54
+ - Definition mini-table type badges color-coded by type (string/number/integer/boolean/object/array)
55
+ - Definition mini-table rows show hover effect
56
+
57
+ ## [0.1.13] - 2026-05-10
58
+
59
+ ### Backend: property-level composition resolution
60
+
61
+ - Resolve `allOf` properties by merging sub-schemas into a unified type with merged properties
62
+ - Resolve `anyOf`/`oneOf` properties by showing variant type labels (e.g. `anyOf: object | object`)
63
+ - Resolve `not` properties with negated type label (e.g. `not string`)
64
+ - Extract `$ref` from allOf sub-schemas for frontend definition expansion
65
+ - Extract `resolve_prop_ref` helper to reduce complexity in `build_single_property`
66
+
67
+ ### Redoc-style UX improvements (round 7)
68
+
69
+ - SeeMore description fade uses CSS mask-image instead of background gradient (works on any background)
70
+ - Array type badge shows items range constraint inline (e.g. `array of string [ 1 .. 10 ]`)
71
+ - Schema tree sidebar shows property count badge (P) alongside definition count (D)
72
+ - Definition mini-table shows format (`<email>`) and enum count badges
73
+ - Definition card description truncated to 2 lines when collapsed
74
+ - Landing card stats show min/max properties count
75
+
76
+ ### Redoc-style UX improvements (round 8)
77
+
78
+ - Auto-expand definitions with single property (Redoc expandSingleSchemaField pattern)
79
+ - Clickable $ref navigation — clicking ref chip scrolls to definition
80
+ - Source viewer line numbers highlight on hover
81
+ - Definition mini-table shows composition source (allOf/anyOf/oneOf) badges
82
+ - Scroll centering for definition and property navigation
83
+
84
+ ### Redoc-style UX improvements (round 9)
85
+
86
+ - Definition card header shows required field names when ≤3 required fields
87
+ - JSON preview shows hint when no fields are checked
88
+ - Definition expanded body shows title, type, property count, and composition badges
89
+
90
+ ### Redoc-style UX improvements (round 10)
91
+
92
+ - Clickable $id/$schema URLs in schema header (opens in new tab)
93
+ - Keyboard support for definition card toggle (Enter/Space)
94
+ - Nested builder shows parent property path breadcrumb
95
+
96
+ ### Redoc-style UX improvements (round 11)
97
+
98
+ - Composition type badges with teal color coding (type-composition class)
99
+ - Static label for composition properties instead of editable input
100
+ - `isCompositionType()` helper for frontend composition type detection
101
+ - Skip validation for composition type properties
102
+
103
+ ### Redoc-style UX improvements (round 12)
104
+
105
+ - DetailPanel: clickable $ref link navigates to definition (closes panel, scrolls to def)
106
+ - DetailPanel: readOnly/writeOnly access badges in property overview
107
+ - DetailPanel: enum values shown as individual chips instead of comma-separated list
108
+ - DetailPanel: schema overview shows property count and definition count
109
+
110
+ ### Redoc-style UX improvements (round 13)
111
+
112
+ - Search results show description snippets (truncated to 80 chars)
113
+ - Schema header breadcrumb shows Schema > Definition when a definition is selected
114
+ - Clickable breadcrumb link navigates back to schema overview
115
+
116
+ ### Redoc-style UX improvements (round 14)
117
+
118
+ - Property filter in SchemaBuilder when schema has >8 properties (filters by name, title, description, type)
119
+ - Markdown renderer handles fenced code blocks (` ```code``` `)
120
+ - Global CSS for markdown pre/code blocks in descriptions
121
+
3
122
  ## [0.1.12] - 2026-05-10
4
123
 
5
124
  ### Redoc-style UX improvements (round 6)
data/frontend/src/App.vue CHANGED
@@ -4,17 +4,22 @@
4
4
  <div v-if="!uiStore.sidebarCollapsed" class="sidebar-overlay" @click="uiStore.toggleSidebar"></div>
5
5
  <div class="main-content">
6
6
  <AppHeader />
7
- <div class="content-area">
7
+ <div class="content-area" ref="contentAreaRef" @scroll="handleScroll">
8
8
  <HomeView />
9
9
  </div>
10
10
  </div>
11
11
  <DetailPanel v-if="uiStore.detailPanelOpen" />
12
12
  <SearchModal v-if="uiStore.searchOpen" />
13
+ <button v-if="showBackToTop" class="back-to-top" @click="scrollToTop" title="Back to top">
14
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
15
+ <path d="M8 13V3M4 7l4-4 4 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
16
+ </svg>
17
+ </button>
13
18
  </div>
14
19
  </template>
15
20
 
16
21
  <script setup lang="ts">
17
- import { onMounted } from 'vue'
22
+ import { onMounted, ref } from 'vue'
18
23
  import { useSchemaStore } from './stores/schemaStore'
19
24
  import { useUiStore } from './stores/uiStore'
20
25
  import AppHeader from './components/AppHeader.vue'
@@ -25,6 +30,18 @@ import HomeView from './views/HomeView.vue'
25
30
 
26
31
  const schemaStore = useSchemaStore()
27
32
  const uiStore = useUiStore()
33
+ const contentAreaRef = ref<HTMLElement | null>(null)
34
+ const showBackToTop = ref(false)
35
+
36
+ function handleScroll() {
37
+ const el = contentAreaRef.value
38
+ if (!el) return
39
+ showBackToTop.value = el.scrollTop > 300
40
+ }
41
+
42
+ function scrollToTop() {
43
+ contentAreaRef.value?.scrollTo({ top: 0, behavior: 'smooth' })
44
+ }
28
45
 
29
46
  onMounted(() => {
30
47
  uiStore.initTheme()
@@ -50,6 +67,10 @@ function handleHashNavigation() {
50
67
  const schemaName = decodeURIComponent(parts[0])
51
68
  schemaStore.selectSchema(schemaName)
52
69
 
70
+ if (!uiStore.isSchemaExpanded(schemaName)) {
71
+ uiStore.toggleSchemaExpanded(schemaName)
72
+ }
73
+
53
74
  if (parts.length >= 2) {
54
75
  const target = decodeURIComponent(parts[1])
55
76
  if (target.startsWith('def-')) {
@@ -107,6 +128,31 @@ function isInputFocused(): boolean {
107
128
  display: none;
108
129
  }
109
130
 
131
+ .back-to-top {
132
+ position: fixed;
133
+ bottom: 24px;
134
+ right: 24px;
135
+ width: 40px;
136
+ height: 40px;
137
+ border-radius: 50%;
138
+ background: var(--bg-elevated);
139
+ border: 1px solid var(--border-light);
140
+ box-shadow: var(--shadow-md);
141
+ color: var(--text-secondary);
142
+ cursor: pointer;
143
+ display: flex;
144
+ align-items: center;
145
+ justify-content: center;
146
+ z-index: 50;
147
+ transition: all var(--transition-fast);
148
+ }
149
+
150
+ .back-to-top:hover {
151
+ color: var(--color-primary);
152
+ border-color: var(--color-primary);
153
+ box-shadow: var(--shadow-lg);
154
+ }
155
+
110
156
  @media (max-width: 768px) {
111
157
  .sidebar-overlay {
112
158
  display: block;
@@ -115,5 +161,10 @@ function isInputFocused(): boolean {
115
161
  background: rgba(0, 0, 0, 0.4);
116
162
  z-index: 39;
117
163
  }
164
+
165
+ .back-to-top {
166
+ bottom: 16px;
167
+ right: 16px;
168
+ }
118
169
  }
119
170
  </style>
@@ -43,8 +43,8 @@
43
43
  v-model="searchQuery"
44
44
  type="text"
45
45
  class="search-input"
46
- placeholder="Search schemas, definitions..."
47
- aria-label="Search schemas and definitions"
46
+ placeholder="Filter schemas..."
47
+ aria-label="Filter schemas and definitions"
48
48
  @keydown="handleSearchKey"
49
49
  />
50
50
  <button v-if="searchQuery" class="search-clear" aria-label="Clear search" @click="searchQuery = ''">
@@ -61,9 +61,12 @@
61
61
  :class="{ active: idx === activeResultIdx }"
62
62
  @click="goToSearchResult(result)"
63
63
  >
64
- <span class="badge" :class="resultBadgeClass(result.type)">{{ resultTypeLabel(result.type) }}</span>
65
- <span class="search-result-name">{{ result.title || result.name }}</span>
66
- <span class="search-result-schema text-muted">{{ result.schemaName }}</span>
64
+ <div class="search-result-row">
65
+ <span class="badge" :class="resultBadgeClass(result.type)">{{ resultTypeLabel(result.type) }}</span>
66
+ <span class="search-result-name">{{ result.title || result.name }}</span>
67
+ <span class="search-result-schema text-muted">{{ result.schemaName }}</span>
68
+ </div>
69
+ <div v-if="result.description" class="search-result-desc text-muted">{{ truncateDesc(result.description) }}</div>
67
70
  </button>
68
71
  </div>
69
72
  <div v-else-if="debouncedQuery && !searchResults.length && searchQuery" class="search-no-results text-muted">
@@ -90,13 +93,28 @@
90
93
  </svg>
91
94
  <span class="schema-name">{{ schema.title || schema.name }}</span>
92
95
  <span v-if="schema.$id" class="schema-id-hint" :title="schema.$id">{{ schema.$id }}</span>
96
+ <span class="schema-badge-count">{{ schema.properties.length }}P</span>
93
97
  <span class="schema-badge-count">{{ schema.definitions.length }}D</span>
94
98
  </div>
95
99
 
96
100
  <div v-if="uiStore.isSchemaExpanded(schema.name)" class="schema-children">
101
+ <div
102
+ v-for="prop in schema.properties.slice(0, 10)"
103
+ :key="`p-${prop.name}`"
104
+ class="tree-item property-item"
105
+ :class="{ active: schema.name === schemaStore.selectedSchemaName && prop.name === schemaStore.selectedPropertyName }"
106
+ @click.stop="selectProperty(schema.name, prop.name)"
107
+ >
108
+ <span class="badge badge-property-sm">P</span>
109
+ <span class="tree-item-name">{{ prop.name }}</span>
110
+ <span class="tree-item-type">{{ prop.type || 'any' }}</span>
111
+ </div>
112
+ <div v-if="schema.properties.length > 10" class="tree-more text-muted">
113
+ +{{ schema.properties.length - 10 }} more
114
+ </div>
97
115
  <div
98
116
  v-for="def in schema.definitions"
99
- :key="def.name"
117
+ :key="`d-${def.name}`"
100
118
  class="tree-item definition-item"
101
119
  :class="{ active: schema.name === schemaStore.selectedSchemaName && def.name === schemaStore.selectedDefinitionName }"
102
120
  @click.stop="selectDefinition(schema.name, def.name)"
@@ -232,6 +250,9 @@ function handleSearchKey(event: KeyboardEvent) {
232
250
 
233
251
  function goToSearchResult(result: SpaSearchEntry) {
234
252
  schemaStore.selectSchema(result.schemaName)
253
+ if (!uiStore.isSchemaExpanded(result.schemaName)) {
254
+ uiStore.toggleSchemaExpanded(result.schemaName)
255
+ }
235
256
  if (result.type === 'definition') {
236
257
  schemaStore.selectDefinition(result.name)
237
258
  } else if (result.type === 'property') {
@@ -275,6 +296,17 @@ function selectDefinition(schemaName: string, defName: string) {
275
296
  schemaStore.selectDefinition(defName)
276
297
  uiStore.closeDetailPanel()
277
298
  }
299
+
300
+ function selectProperty(schemaName: string, propName: string) {
301
+ schemaStore.selectSchema(schemaName)
302
+ schemaStore.selectProperty(propName)
303
+ uiStore.openDetailPanel()
304
+ }
305
+
306
+ function truncateDesc(desc: string): string {
307
+ if (desc.length <= 80) return desc
308
+ return desc.slice(0, 77) + '...'
309
+ }
278
310
  </script>
279
311
 
280
312
  <style scoped>
@@ -509,6 +541,19 @@ function selectDefinition(schemaName: string, defName: string) {
509
541
  flex-shrink: 0;
510
542
  }
511
543
 
544
+ .tree-item-type {
545
+ font-size: 9px;
546
+ color: var(--text-muted);
547
+ font-family: var(--font-mono);
548
+ flex-shrink: 0;
549
+ }
550
+
551
+ .tree-more {
552
+ font-size: 10px;
553
+ padding: 2px var(--space-2) 2px 24px;
554
+ font-style: italic;
555
+ }
556
+
512
557
  .badge-definition-sm {
513
558
  background: var(--badge-definition-bg);
514
559
  color: var(--badge-definition);
@@ -603,8 +648,8 @@ function selectDefinition(schemaName: string, defName: string) {
603
648
 
604
649
  .search-result-item {
605
650
  display: flex;
606
- align-items: center;
607
- gap: var(--space-2);
651
+ flex-direction: column;
652
+ gap: 2px;
608
653
  padding: 4px 6px;
609
654
  font-size: var(--text-xs);
610
655
  text-align: left;
@@ -616,6 +661,12 @@ function selectDefinition(schemaName: string, defName: string) {
616
661
  transition: background var(--transition-fast);
617
662
  }
618
663
 
664
+ .search-result-row {
665
+ display: flex;
666
+ align-items: center;
667
+ gap: var(--space-2);
668
+ }
669
+
619
670
  .search-result-item:hover,
620
671
  .search-result-item.active {
621
672
  background: var(--bg-hover);
@@ -639,6 +690,16 @@ function selectDefinition(schemaName: string, defName: string) {
639
690
  .search-result-schema {
640
691
  font-size: 10px;
641
692
  white-space: nowrap;
693
+ flex-shrink: 0;
694
+ }
695
+
696
+ .search-result-desc {
697
+ font-size: 10px;
698
+ line-height: 1.3;
699
+ overflow: hidden;
700
+ text-overflow: ellipsis;
701
+ white-space: nowrap;
702
+ padding-left: 30px;
642
703
  }
643
704
 
644
705
  .stats-section {