lutaml-jsonschema 0.1.11 → 0.1.12

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: c119b203d422df51dd4ef79c0e28ae59d1a3e8a01536c74baef3931f35481428
4
- data.tar.gz: fd6e00bacd9f5d5ea9bab4afdff42adb786e1b0d7293f571c71e1e1dacb1d8f6
3
+ metadata.gz: 0ad24a6671a3bcf4e811c7588dbd4ca4fff96d589d625a7e6dd2507a74a115d4
4
+ data.tar.gz: 947415a532ef91f5287564a63a422f9c5021e01b5ec43a174d8e68b53e81dcca
5
5
  SHA512:
6
- metadata.gz: 56edc028665efb3442c0ffe58eb7fe62576c02686cabf184fc2337749c6d460e9a7f817aef2e7737a8cc6b1a2dc1bd61ee0765ae5df558df1fde73e9ac0cc0a3
7
- data.tar.gz: fed677f3dfb49ad12f056a494e73c2dceee12929f7f15c1753a6aac9d03387bba4ab1aa68c33bdb2ee152af46dd4a0a76898d3466575ad88cf0fdf944af766d1
6
+ metadata.gz: d76f7513a5de8fe76529844d0a110f1901f3f9fc4968213f3ae872505370aec7ffd71ab6d349745a9af383b03d6d32ce19f972eccf585c6efd91281cc3d1f6e6
7
+ data.tar.gz: 72a9b35c92992f4ac0fa82f63b477b362bb358b752d1deb773c3613a29e07ea0b4cf144dbaf11fd43b1c1b5eec3b5719ad72e2d3064bea17e112f1dafe55fc0a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.12] - 2026-05-10
4
+
5
+ ### Redoc-style UX improvements (round 6)
6
+
7
+ - Property title display in type row when it differs from name (Redoc TypeTitle pattern)
8
+ - Copy fallback using `execCommand('copy')` for HTTP contexts where clipboard API unavailable
9
+ - Dark panel full-height background via BackgroundStub `::before` pattern
10
+ - Search result description snippets in search modal
11
+ - Tooltip with directional CSS arrow on copy buttons
12
+ - DetailPanel properties table: shows title, format with angle brackets, default value, enum count, inline examples
13
+
3
14
  ## [0.1.11] - 2026-05-09
4
15
 
5
16
  ### Redoc-style UX improvements (round 5)
@@ -221,18 +221,28 @@
221
221
  <tr v-for="prop in properties" :key="prop.name">
222
222
  <td>
223
223
  <span class="font-mono">{{ prop.name }}</span>
224
+ <span v-if="prop.title && prop.title !== prop.name" class="prop-title">({{ prop.title }})</span>
224
225
  <span v-if="prop.deprecated" class="badge badge-deprecated-sm">deprecated</span>
225
226
  </td>
226
227
  <td>
227
228
  <span class="prop-type">{{ prop.type || 'any' }}</span>
228
- <span v-if="prop.format" class="prop-format">{{ prop.format }}</span>
229
+ <span v-if="prop.format" class="prop-format">&lt;{{ prop.format }}&gt;</span>
229
230
  <span v-if="prop.itemsType" class="prop-format">[{{ prop.itemsType }}]</span>
231
+ <span v-if="prop.default != null" class="prop-default">default: {{ prop.default }}</span>
232
+ <span v-if="prop.enum?.length" class="prop-enum">{{ prop.enum.length }} values</span>
230
233
  </td>
231
234
  <td>
232
235
  <span v-if="prop.required" class="badge badge-required-sm">yes</span>
233
236
  <span v-else class="text-muted">no</span>
234
237
  </td>
235
- <td class="text-secondary">{{ prop.description || '—' }}</td>
238
+ <td>
239
+ <span class="text-secondary">{{ prop.description || '—' }}</span>
240
+ <div v-if="prop.examples?.length" class="prop-examples">
241
+ <span class="prop-examples-label">Examples:</span>
242
+ <span v-for="(ex, i) in prop.examples.slice(0, 3)" :key="i" class="prop-example-chip">{{ typeof ex === 'object' ? JSON.stringify(ex) : ex }}</span>
243
+ <span v-if="prop.examples.length > 3" class="text-muted">+{{ prop.examples.length - 3 }} more</span>
244
+ </div>
245
+ </td>
236
246
  </tr>
237
247
  </tbody>
238
248
  </table>
@@ -564,8 +574,57 @@ const definitionItem = computed(() => {
564
574
 
565
575
  .prop-format {
566
576
  font-size: var(--text-xs);
567
- color: var(--text-muted);
577
+ color: var(--color-accent);
568
578
  margin-left: var(--space-1);
579
+ font-family: var(--font-mono);
580
+ }
581
+
582
+ .prop-title {
583
+ display: block;
584
+ font-size: var(--text-xs);
585
+ color: var(--text-secondary);
586
+ font-style: italic;
587
+ }
588
+
589
+ .prop-default {
590
+ display: block;
591
+ font-size: 10px;
592
+ color: var(--text-muted);
593
+ font-family: var(--font-mono);
594
+ }
595
+
596
+ .prop-enum {
597
+ display: block;
598
+ font-size: 10px;
599
+ color: var(--color-teal);
600
+ background: var(--color-teal-alpha);
601
+ padding: 1px 4px;
602
+ border-radius: 2px;
603
+ display: inline-block;
604
+ margin-top: 2px;
605
+ }
606
+
607
+ .prop-examples {
608
+ margin-top: var(--space-1);
609
+ display: flex;
610
+ flex-wrap: wrap;
611
+ gap: 4px;
612
+ align-items: center;
613
+ }
614
+
615
+ .prop-examples-label {
616
+ font-size: 10px;
617
+ color: var(--text-muted);
618
+ font-weight: 500;
619
+ }
620
+
621
+ .prop-example-chip {
622
+ font-size: 10px;
623
+ font-family: var(--font-mono);
624
+ background: var(--bg-secondary);
625
+ padding: 1px 4px;
626
+ border-radius: 2px;
627
+ color: var(--text-secondary);
569
628
  }
570
629
 
571
630
  .badge-type {
@@ -16,6 +16,7 @@
16
16
  <span class="font-mono">{{ field.prop.name }}</span>
17
17
  </button>
18
18
  <span class="field-type-badge" :class="typeBadgeClass(field.prop)">{{ displayType(field.prop, field.resolvedDef?.title || field.resolvedDef?.name) }}</span>
19
+ <span v-if="field.prop.title && field.prop.title !== field.prop.name" class="field-title-badge">{{ field.prop.title }}</span>
19
20
  <span v-if="field.prop.format" class="field-format-badge">&lt;{{ field.prop.format }}&gt;</span>
20
21
  <span v-if="field.prop.contentMediaType" class="field-format-badge">content-type: {{ field.prop.contentMediaType }}</span>
21
22
  <span v-if="field.prop.contentEncoding" class="field-format-badge">encoding: {{ field.prop.contentEncoding }}</span>
@@ -246,6 +247,7 @@ import {
246
247
  import { renderInlineMarkdown } from '../composables/useMarkdownLite'
247
248
  import { jsonToCollapsibleHtml } from '../composables/useJsonViewer'
248
249
  import type { BuilderField } from '../composables/useBuilderField'
250
+ import { copyToClipboard } from '../composables/useClipboard'
249
251
 
250
252
  const schemaStore = useSchemaStore()
251
253
  const uiStore = useUiStore()
@@ -437,11 +439,11 @@ function togglePattern(pattern: string) {
437
439
  }
438
440
 
439
441
  async function copyJson() {
440
- try {
441
- await navigator.clipboard.writeText(outputJson.value)
442
+ const ok = await copyToClipboard(outputJson.value)
443
+ if (ok) {
442
444
  copied.value = true
443
445
  setTimeout(() => { copied.value = false }, 2000)
444
- } catch { /* noop */ }
446
+ }
445
447
  }
446
448
  </script>
447
449
 
@@ -571,6 +573,13 @@ async function copyJson() {
571
573
  font-weight: 500;
572
574
  }
573
575
 
576
+ .field-title-badge {
577
+ font-size: 10px;
578
+ font-style: italic;
579
+ color: var(--text-secondary);
580
+ flex-shrink: 0;
581
+ }
582
+
574
583
  .deprecated-badge {
575
584
  font-size: 10px;
576
585
  font-weight: 600;
@@ -1001,6 +1010,16 @@ async function copyJson() {
1001
1010
  .builder-preview {
1002
1011
  position: sticky;
1003
1012
  top: var(--space-4);
1013
+ z-index: 0;
1014
+ }
1015
+
1016
+ .builder-preview::before {
1017
+ content: '';
1018
+ position: absolute;
1019
+ inset: 0;
1020
+ background: var(--panel-dark-bg);
1021
+ border-radius: var(--radius-md);
1022
+ z-index: -1;
1004
1023
  }
1005
1024
 
1006
1025
  .preview-inner {
@@ -1148,7 +1167,7 @@ async function copyJson() {
1148
1167
 
1149
1168
  .copy-tooltip {
1150
1169
  position: absolute;
1151
- bottom: calc(100% + 4px);
1170
+ bottom: calc(100% + 8px);
1152
1171
  left: 50%;
1153
1172
  transform: translateX(-50%);
1154
1173
  background: var(--bg-elevated);
@@ -1163,6 +1182,16 @@ async function copyJson() {
1163
1182
  animation: tooltipFade var(--transition-slow);
1164
1183
  }
1165
1184
 
1185
+ .copy-tooltip::after {
1186
+ content: '';
1187
+ position: absolute;
1188
+ top: 100%;
1189
+ left: 50%;
1190
+ margin-left: -4px;
1191
+ border: 4px solid transparent;
1192
+ border-top-color: var(--bg-elevated);
1193
+ }
1194
+
1166
1195
  @keyframes tooltipFade {
1167
1196
  from { opacity: 0; transform: translateX(-50%) translateY(2px); }
1168
1197
  to { opacity: 1; transform: translateX(-50%) translateY(0); }
@@ -52,6 +52,7 @@
52
52
  <div class="result-content">
53
53
  <span class="result-name" v-html="highlightMatch(result.name)"></span>
54
54
  <span class="result-schema">{{ result.schemaName }}</span>
55
+ <span v-if="result.description" class="result-desc">{{ result.description }}</span>
55
56
  </div>
56
57
  <span :class="['badge', `badge-${result.type}`]">{{ result.type }}</span>
57
58
  </div>
@@ -235,6 +236,15 @@ function highlightMatch(text: string): string {
235
236
  color: var(--text-muted);
236
237
  }
237
238
 
239
+ .result-desc {
240
+ font-size: var(--text-xs);
241
+ color: var(--text-muted);
242
+ display: -webkit-box;
243
+ -webkit-line-clamp: 1;
244
+ -webkit-box-orient: vertical;
245
+ overflow: hidden;
246
+ }
247
+
238
248
  .badge-schema {
239
249
  background: var(--badge-schema-bg);
240
250
  color: var(--badge-schema);
@@ -0,0 +1,21 @@
1
+ function fallbackCopy(text: string): boolean {
2
+ const ta = document.createElement('textarea')
3
+ ta.value = text
4
+ ta.style.cssText = 'position:fixed;top:0;left:0;width:2em;height:2em;padding:0;border:none;outline:none;box-shadow:none;background:transparent'
5
+ document.body.appendChild(ta)
6
+ ta.select()
7
+ let ok = false
8
+ try { ok = document.execCommand('copy') } catch { /* noop */ }
9
+ document.body.removeChild(ta)
10
+ return ok
11
+ }
12
+
13
+ export async function copyToClipboard(text: string): Promise<boolean> {
14
+ if (navigator.clipboard?.writeText) {
15
+ try {
16
+ await navigator.clipboard.writeText(text)
17
+ return true
18
+ } catch { /* fall through */ }
19
+ }
20
+ return fallbackCopy(text)
21
+ }
@@ -221,6 +221,7 @@ import { useSchemaStore } from '../stores/schemaStore'
221
221
  import SchemaBuilder from '../components/SchemaBuilder.vue'
222
222
  import { downloadFile } from '../composables/useDownload'
223
223
  import { renderInlineMarkdown } from '../composables/useMarkdownLite'
224
+ import { copyToClipboard } from '../composables/useClipboard'
224
225
  import type { SpaSchema } from '../types'
225
226
 
226
227
  const schemaStore = useSchemaStore()
@@ -264,9 +265,11 @@ const sourceLineCount = computed(() => {
264
265
  function copySource() {
265
266
  const src = schemaStore.selectedSchema?.sourceJson
266
267
  if (!src) return
267
- navigator.clipboard.writeText(src).then(() => {
268
- sourceCopied.value = true
269
- setTimeout(() => { sourceCopied.value = false }, 2000)
268
+ copyToClipboard(src).then(ok => {
269
+ if (ok) {
270
+ sourceCopied.value = true
271
+ setTimeout(() => { sourceCopied.value = false }, 2000)
272
+ }
270
273
  })
271
274
  }
272
275
 
@@ -612,7 +615,7 @@ watch(() => schemaStore.selectedItemKey, (key) => {
612
615
 
613
616
  .copy-tooltip {
614
617
  position: absolute;
615
- bottom: calc(100% + 4px);
618
+ bottom: calc(100% + 8px);
616
619
  left: 50%;
617
620
  transform: translateX(-50%);
618
621
  background: var(--bg-elevated);
@@ -627,6 +630,16 @@ watch(() => schemaStore.selectedItemKey, (key) => {
627
630
  animation: tooltipFade var(--transition-slow);
628
631
  }
629
632
 
633
+ .copy-tooltip::after {
634
+ content: '';
635
+ position: absolute;
636
+ top: 100%;
637
+ left: 50%;
638
+ margin-left: -4px;
639
+ border: 4px solid transparent;
640
+ border-top-color: var(--bg-elevated);
641
+ }
642
+
630
643
  @keyframes tooltipFade {
631
644
  from { opacity: 0; transform: translateX(-50%) translateY(2px); }
632
645
  to { opacity: 1; transform: translateX(-50%) translateY(0); }
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Jsonschema
5
- VERSION = "0.1.11"
5
+ VERSION = "0.1.12"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lutaml-jsonschema
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.11
4
+ version: 0.1.12
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-05-09 00:00:00.000000000 Z
11
+ date: 2026-05-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -106,6 +106,7 @@ files:
106
106
  - frontend/src/components/SchemaBuilder.vue
107
107
  - frontend/src/components/SearchModal.vue
108
108
  - frontend/src/composables/useBuilderField.ts
109
+ - frontend/src/composables/useClipboard.ts
109
110
  - frontend/src/composables/useDefinitionResolver.ts
110
111
  - frontend/src/composables/useDownload.ts
111
112
  - frontend/src/composables/useJsonViewer.ts