@docsector/docsector-reader 3.4.0 → 3.6.0

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.
package/README.md CHANGED
@@ -73,7 +73,9 @@ Transform Markdown content into beautiful, navigable documentation sites — wit
73
73
  - 🧬 **Scaffolded Homepage Override Wiring** — New consumer projects automatically wire `virtual:docsector-homepage-override` into i18n message building
74
74
  - 📖 **Expandable Markdown Sections** — Use `<d-expandable title="...">...</d-expandable>` to collapse secondary content while keeping rich Markdown support inside the body
75
75
  - 📎 **File Attachment Blocks** — Use `<d-file src="/files/...">...</d-file>` in Markdown to render downloadable file cards with automatic local size detection and support for external URLs
76
+ - 🌐 **Embedded URL Blocks** — Use `<d-embedded-url url="https://...">...</d-embedded-url>` to render curated embeds for YouTube, Vimeo, Spotify, and CodePen with a safe link-card fallback for unsupported URLs
76
77
  - 🧭 **Quick Links Custom Element** — Use `<d-quick-links>` and `<d-quick-link>` in Markdown to render rich home navigation cards
78
+ - 🗂️ **Cards Custom Element** — Use `<d-block-cards>` and `<d-block-card>` in Markdown to render linked content cards with optional cover images
77
79
  - 🗂️ **API Catalog Well-Known** — Auto-generates `/.well-known/api-catalog` as Linkset JSON for machine-readable API discovery
78
80
  - 🗃️ **Multi-Version History** — Archive older major versions under `src/pages/.old/<version>/` and expose them at prefixed routes (e.g. `/v0.x/guide/...`) while keeping the current docs at unprefixed routes
79
81
  - 🏷️ **Version Selector Badges** — Every version in the sidebar selector displays a color-coded badge: green for released, orange for draft, red for deprecated; fully customizable via `badge: { label, color, textColor }`
@@ -1036,6 +1038,21 @@ Notes:
1036
1038
  - The block body is rendered as an inline Markdown caption.
1037
1039
  - External URLs also work, so the same syntax can later point to R2 or another CDN without changing the page structure.
1038
1040
 
1041
+ ### Embedded URL Blocks
1042
+
1043
+ ```html
1044
+ <d-embedded-url url="https://www.youtube.com/watch?v=M7lc1UVf-VE" title="YouTube player demo">
1045
+ Optional caption rendered as inline Markdown.
1046
+ </d-embedded-url>
1047
+ ```
1048
+
1049
+ Notes:
1050
+
1051
+ - Supported providers currently include YouTube, Vimeo, Spotify, and CodePen.
1052
+ - The block preserves the original query string, so provider options such as `autoplay=1&loop=1` keep working when supported by the destination service.
1053
+ - Unsupported or private URLs fall back to a safe external-link card instead of attempting a generic iframe.
1054
+ - Raw HTML remains the escape hatch when you need a provider outside the curated list or full manual iframe control.
1055
+
1039
1056
  ---
1040
1057
 
1041
1058
 
package/bin/docsector.js CHANGED
@@ -23,7 +23,7 @@ const packageRoot = resolve(__dirname, '..')
23
23
  const args = process.argv.slice(2)
24
24
  const command = args[0]
25
25
 
26
- const VERSION = '3.4.0'
26
+ const VERSION = '3.6.0'
27
27
 
28
28
  const HELP = `
29
29
  Docsector Reader v${VERSION}
package/jsconfig.json CHANGED
@@ -13,5 +13,5 @@
13
13
  "stores/*": ["src/stores/*"]
14
14
  }
15
15
  },
16
- "exclude": ["node_modules", "dist"]
16
+ "exclude": ["node_modules", "dist", "tmp"]
17
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docsector/docsector-reader",
3
- "version": "3.4.0",
3
+ "version": "3.6.0",
4
4
  "description": "A documentation rendering engine built with Vue 3, Quasar v2 and Vite. Transform Markdown into beautiful, navigable documentation sites.",
5
5
  "productName": "Docsector Reader",
6
6
  "author": "Rodrigo de Araujo Vieira",
@@ -0,0 +1,60 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720" role="img" aria-labelledby="title desc">
2
+ <title id="title">Getting started cover</title>
3
+ <desc id="desc">Abstract cover with warm gradients and onboarding steps.</desc>
4
+ <defs>
5
+ <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
6
+ <stop offset="0%" stop-color="#12141c"/>
7
+ <stop offset="48%" stop-color="#1b2230"/>
8
+ <stop offset="100%" stop-color="#27384d"/>
9
+ </linearGradient>
10
+ <linearGradient id="panel" x1="0" y1="0" x2="1" y2="1">
11
+ <stop offset="0%" stop-color="#f1d8a1" stop-opacity="0.95"/>
12
+ <stop offset="100%" stop-color="#b78f41" stop-opacity="0.85"/>
13
+ </linearGradient>
14
+ <linearGradient id="accent" x1="0" y1="0" x2="1" y2="0">
15
+ <stop offset="0%" stop-color="#69d8c3"/>
16
+ <stop offset="100%" stop-color="#8ab6ff"/>
17
+ </linearGradient>
18
+ <filter id="blur" x="-20%" y="-20%" width="140%" height="140%">
19
+ <feGaussianBlur stdDeviation="24"/>
20
+ </filter>
21
+ </defs>
22
+
23
+ <rect width="1280" height="720" fill="url(#bg)"/>
24
+ <circle cx="1120" cy="132" r="136" fill="#69d8c3" opacity="0.16" filter="url(#blur)"/>
25
+ <circle cx="180" cy="640" r="170" fill="#8ab6ff" opacity="0.18" filter="url(#blur)"/>
26
+
27
+ <rect x="90" y="96" width="470" height="528" rx="34" fill="#0f141c" opacity="0.92" stroke="#3a4c63"/>
28
+ <rect x="126" y="140" width="398" height="18" rx="9" fill="url(#accent)" opacity="0.9"/>
29
+ <rect x="126" y="188" width="182" height="28" rx="14" fill="#f5efe1" opacity="0.92"/>
30
+ <rect x="126" y="242" width="300" height="16" rx="8" fill="#8da6c7" opacity="0.7"/>
31
+ <rect x="126" y="272" width="260" height="16" rx="8" fill="#8da6c7" opacity="0.55"/>
32
+
33
+ <g transform="translate(126 344)">
34
+ <rect width="318" height="68" rx="18" fill="#17212d" stroke="#32465d"/>
35
+ <circle cx="36" cy="34" r="15" fill="#69d8c3"/>
36
+ <path d="M29 34l5 5 10-12" fill="none" stroke="#0d1722" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
37
+ <rect x="72" y="22" width="110" height="12" rx="6" fill="#f1f5f9"/>
38
+ <rect x="72" y="42" width="160" height="10" rx="5" fill="#8da6c7" opacity="0.75"/>
39
+ </g>
40
+
41
+ <g transform="translate(126 430)">
42
+ <rect width="356" height="68" rx="18" fill="#17212d" stroke="#32465d"/>
43
+ <circle cx="36" cy="34" r="15" fill="#69d8c3" opacity="0.82"/>
44
+ <path d="M29 34l5 5 10-12" fill="none" stroke="#0d1722" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
45
+ <rect x="72" y="22" width="132" height="12" rx="6" fill="#f1f5f9"/>
46
+ <rect x="72" y="42" width="200" height="10" rx="5" fill="#8da6c7" opacity="0.75"/>
47
+ </g>
48
+
49
+ <g transform="translate(640 88)">
50
+ <rect width="548" height="548" rx="44" fill="url(#panel)"/>
51
+ <rect x="78" y="84" width="392" height="244" rx="26" fill="#0f141c" opacity="0.92"/>
52
+ <path d="M128 282c42-72 86-108 132-108 35 0 69 15 103 44 31 27 58 40 80 40 39 0 73-21 104-64l29 20c-42 63-87 94-136 94-33 0-67-13-102-39-32-24-60-36-84-36-34 0-67 28-98 84z" fill="#69d8c3"/>
53
+ <circle cx="272" cy="206" r="46" fill="#8ab6ff" opacity="0.92"/>
54
+ <rect x="78" y="372" width="188" height="22" rx="11" fill="#0f141c" opacity="0.88"/>
55
+ <rect x="78" y="414" width="300" height="16" rx="8" fill="#243447" opacity="0.6"/>
56
+ <rect x="78" y="444" width="262" height="16" rx="8" fill="#243447" opacity="0.44"/>
57
+ <rect x="78" y="484" width="164" height="56" rx="18" fill="#0f141c" opacity="0.88"/>
58
+ <rect x="102" y="506" width="114" height="12" rx="6" fill="#f5efe1" opacity="0.88"/>
59
+ </g>
60
+ </svg>
@@ -0,0 +1,71 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720" role="img" aria-labelledby="title desc">
2
+ <title id="title">Quick links cover</title>
3
+ <desc id="desc">Abstract navigation cover with cards and directional accents.</desc>
4
+ <defs>
5
+ <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
6
+ <stop offset="0%" stop-color="#10261f"/>
7
+ <stop offset="45%" stop-color="#16372d"/>
8
+ <stop offset="100%" stop-color="#1e4d3e"/>
9
+ </linearGradient>
10
+ <linearGradient id="card" x1="0" y1="0" x2="1" y2="1">
11
+ <stop offset="0%" stop-color="#f3f0e7"/>
12
+ <stop offset="100%" stop-color="#d5ddd1"/>
13
+ </linearGradient>
14
+ <linearGradient id="accent" x1="0" y1="0" x2="1" y2="0">
15
+ <stop offset="0%" stop-color="#69d8c3"/>
16
+ <stop offset="100%" stop-color="#d3b874"/>
17
+ </linearGradient>
18
+ <filter id="blur" x="-20%" y="-20%" width="140%" height="140%">
19
+ <feGaussianBlur stdDeviation="26"/>
20
+ </filter>
21
+ </defs>
22
+
23
+ <rect width="1280" height="720" fill="url(#bg)"/>
24
+ <circle cx="154" cy="138" r="126" fill="#69d8c3" opacity="0.18" filter="url(#blur)"/>
25
+ <circle cx="1090" cy="598" r="170" fill="#d3b874" opacity="0.16" filter="url(#blur)"/>
26
+
27
+ <g transform="translate(98 112)">
28
+ <rect width="452" height="480" rx="42" fill="#0f1714" opacity="0.94" stroke="#385247"/>
29
+ <rect x="44" y="48" width="162" height="18" rx="9" fill="url(#accent)"/>
30
+ <rect x="44" y="100" width="278" height="18" rx="9" fill="#f3f0e7" opacity="0.94"/>
31
+ <rect x="44" y="136" width="212" height="14" rx="7" fill="#9cb5ac" opacity="0.58"/>
32
+
33
+ <g transform="translate(44 204)">
34
+ <rect width="364" height="86" rx="24" fill="#17231e" stroke="#33493f"/>
35
+ <rect x="28" y="24" width="116" height="12" rx="6" fill="#f3f0e7"/>
36
+ <rect x="28" y="46" width="176" height="10" rx="5" fill="#8aa196" opacity="0.82"/>
37
+ <circle cx="320" cy="43" r="20" fill="#22362d"/>
38
+ <path d="M311 43h18M321 34l9 9-9 9" fill="none" stroke="#69d8c3" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
39
+ </g>
40
+
41
+ <g transform="translate(44 310)">
42
+ <rect width="330" height="86" rx="24" fill="#17231e" stroke="#33493f"/>
43
+ <rect x="28" y="24" width="96" height="12" rx="6" fill="#f3f0e7"/>
44
+ <rect x="28" y="46" width="156" height="10" rx="5" fill="#8aa196" opacity="0.82"/>
45
+ <circle cx="286" cy="43" r="20" fill="#22362d"/>
46
+ <path d="M277 43h18M287 34l9 9-9 9" fill="none" stroke="#69d8c3" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
47
+ </g>
48
+ </g>
49
+
50
+ <g transform="translate(646 100)">
51
+ <rect width="536" height="520" rx="44" fill="url(#card)" opacity="0.98"/>
52
+ <rect x="58" y="56" width="420" height="248" rx="28" fill="#0f1714" opacity="0.94"/>
53
+ <g transform="translate(110 114)">
54
+ <rect width="116" height="116" rx="28" fill="#69d8c3"/>
55
+ <path d="M40 58h36M58 40l18 18-18 18" fill="none" stroke="#0f1714" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/>
56
+ </g>
57
+ <g transform="translate(250 114)">
58
+ <rect width="116" height="116" rx="28" fill="#d3b874"/>
59
+ <path d="M40 58h36M58 40l18 18-18 18" fill="none" stroke="#0f1714" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/>
60
+ </g>
61
+ <g transform="translate(180 272)">
62
+ <rect width="116" height="116" rx="28" fill="#8ab6ff"/>
63
+ <path d="M40 58h36M58 40l18 18-18 18" fill="none" stroke="#0f1714" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/>
64
+ </g>
65
+ <rect x="58" y="360" width="204" height="18" rx="9" fill="#10261f" opacity="0.92"/>
66
+ <rect x="58" y="396" width="294" height="14" rx="7" fill="#425b52" opacity="0.6"/>
67
+ <rect x="58" y="424" width="248" height="14" rx="7" fill="#425b52" opacity="0.42"/>
68
+ <rect x="58" y="470" width="172" height="58" rx="18" fill="#10261f" opacity="0.92"/>
69
+ <rect x="84" y="493" width="120" height="12" rx="6" fill="#f3f0e7" opacity="0.92"/>
70
+ </g>
71
+ </svg>
@@ -0,0 +1,223 @@
1
+ <script setup>
2
+ const BASE_URL = import.meta.env.BASE_URL || '/'
3
+
4
+ defineOptions({
5
+ name: 'DBlockCard'
6
+ })
7
+
8
+ const props = defineProps({
9
+ items: {
10
+ type: Array,
11
+ default: () => []
12
+ },
13
+ title: {
14
+ type: String,
15
+ default: ''
16
+ }
17
+ })
18
+
19
+ const isExternal = (item) => {
20
+ const href = item?.href || ''
21
+ return /^https?:\/\//i.test(href)
22
+ }
23
+
24
+ const itemTag = (item) => {
25
+ if (item?.to) return 'router-link'
26
+ return 'a'
27
+ }
28
+
29
+ const itemProps = (item) => {
30
+ if (item?.to) {
31
+ return {
32
+ to: item.to
33
+ }
34
+ }
35
+
36
+ const href = item?.href || ''
37
+ return {
38
+ href,
39
+ target: isExternal(item) ? '_blank' : undefined,
40
+ rel: isExternal(item) ? 'noopener noreferrer' : undefined
41
+ }
42
+ }
43
+
44
+ const resolveAssetUrl = (raw = '') => {
45
+ const value = String(raw || '').trim()
46
+
47
+ if (!value) {
48
+ return ''
49
+ }
50
+
51
+ if (/^(?:[a-z]+:)?\/\//i.test(value) || /^(?:data:|blob:)/i.test(value)) {
52
+ return value
53
+ }
54
+
55
+ const trimmedBase = String(BASE_URL).replace(/\/$/, '')
56
+
57
+ if (value.startsWith('/')) {
58
+ return `${trimmedBase}${value}` || value
59
+ }
60
+
61
+ const normalized = value.replace(/^\.\//, '')
62
+ return `${trimmedBase}/${normalized}`.replace(/\/+/g, '/')
63
+ }
64
+ </script>
65
+
66
+ <template>
67
+ <div class="d-cards">
68
+ <h3 v-if="title" class="d-cards__title">{{ title }}</h3>
69
+
70
+ <div class="d-cards__grid">
71
+ <component
72
+ :is="itemTag(item)"
73
+ v-for="(item, index) in props.items"
74
+ :key="`${item.title}-${index}`"
75
+ class="d-cards__link"
76
+ v-bind="itemProps(item)"
77
+ >
78
+ <q-card flat bordered class="d-cards__card">
79
+ <q-img
80
+ v-if="item.image"
81
+ :src="resolveAssetUrl(item.image)"
82
+ :alt="item.title"
83
+ ratio="1.7778"
84
+ class="d-cards__image"
85
+ />
86
+
87
+ <q-card-section v-else-if="item.icon" class="d-cards__icon-section">
88
+ <div class="d-cards__icon-surface">
89
+ <q-icon :name="item.icon" size="56px" color="primary" />
90
+ </div>
91
+ </q-card-section>
92
+
93
+ <q-card-section class="d-cards__body">
94
+ <div class="d-cards__heading-row">
95
+ <div class="d-cards__heading-group">
96
+ <q-icon
97
+ v-if="item.image && item.icon"
98
+ :name="item.icon"
99
+ size="18px"
100
+ color="primary"
101
+ />
102
+ <div class="d-cards__label">{{ item.title }}</div>
103
+ </div>
104
+
105
+ <q-icon :name="isExternal(item) ? 'arrow_outward' : 'arrow_forward'" size="18px" />
106
+ </div>
107
+
108
+ <div class="d-cards__description">{{ item.description }}</div>
109
+ </q-card-section>
110
+ </q-card>
111
+ </component>
112
+ </div>
113
+ </div>
114
+ </template>
115
+
116
+ <style lang="sass">
117
+ body.body--light
118
+ --d-cards-card-bg: #fffdf8
119
+ --d-cards-card-border: rgba(123, 94, 45, 0.16)
120
+ --d-cards-card-shadow: rgba(94, 73, 37, 0.08)
121
+ --d-cards-card-shadow-hover: rgba(94, 73, 37, 0.16)
122
+ --d-cards-description: #4d5563
123
+ --d-cards-icon-surface: linear-gradient(180deg, rgba(210, 190, 145, 0.22), rgba(210, 190, 145, 0.08))
124
+
125
+ body.body--dark
126
+ --d-cards-card-bg: rgba(255, 248, 235, 0.035)
127
+ --d-cards-card-border: rgba(255, 235, 194, 0.12)
128
+ --d-cards-card-shadow: rgba(0, 0, 0, 0.25)
129
+ --d-cards-card-shadow-hover: rgba(0, 0, 0, 0.38)
130
+ --d-cards-description: rgba(255, 255, 255, 0.82)
131
+ --d-cards-icon-surface: linear-gradient(180deg, rgba(193, 166, 103, 0.18), rgba(193, 166, 103, 0.05))
132
+
133
+ .d-cards
134
+ margin: 0 auto
135
+
136
+ .d-cards__title
137
+ text-align: center
138
+ margin: 0 0 16px
139
+
140
+ .d-cards__grid
141
+ display: grid
142
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr))
143
+ gap: 16px
144
+
145
+ .d-cards__link
146
+ display: block
147
+ color: inherit
148
+ text-decoration: none
149
+ border-bottom: 0 !important
150
+ background: transparent !important
151
+
152
+ .d-cards__link:hover,
153
+ .d-cards__link:focus-visible,
154
+ .d-cards__link:active,
155
+ .d-cards__link:visited
156
+ color: inherit !important
157
+ text-decoration: none
158
+ border-bottom: 0 !important
159
+ background: transparent !important
160
+
161
+ .d-cards__card
162
+ height: 100%
163
+ overflow: hidden
164
+ border-radius: 18px
165
+ background: var(--d-cards-card-bg)
166
+ border-color: var(--d-cards-card-border)
167
+ box-shadow: 0 12px 28px var(--d-cards-card-shadow)
168
+ transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease
169
+
170
+ .d-cards__link:hover .d-cards__card,
171
+ .d-cards__link:focus-visible .d-cards__card
172
+ transform: translateY(-2px)
173
+ box-shadow: 0 18px 36px var(--d-cards-card-shadow-hover)
174
+
175
+ .d-cards__link:focus-visible
176
+ outline: none
177
+
178
+ .d-cards__image
179
+ background: rgba(0, 0, 0, 0.05)
180
+
181
+ .d-cards__icon-section
182
+ display: flex
183
+ padding-bottom: 0
184
+
185
+ .d-cards__icon-surface
186
+ display: flex
187
+ align-items: center
188
+ justify-content: center
189
+ width: 100%
190
+ min-height: 156px
191
+ border-radius: 14px
192
+ background: var(--d-cards-icon-surface)
193
+
194
+ .d-cards__body
195
+ display: grid
196
+ gap: 10px
197
+
198
+ .d-cards__heading-row
199
+ display: flex
200
+ align-items: flex-start
201
+ justify-content: space-between
202
+ gap: 12px
203
+
204
+ .d-cards__heading-group
205
+ display: flex
206
+ align-items: center
207
+ gap: 8px
208
+ min-width: 0
209
+
210
+ .d-cards__label
211
+ font-size: 1rem
212
+ font-weight: 700
213
+ line-height: 1.35
214
+
215
+ .d-cards__description
216
+ color: var(--d-cards-description)
217
+ font-size: 0.97rem
218
+ line-height: 1.55
219
+
220
+ @media (max-width: 599px)
221
+ .d-cards__grid
222
+ grid-template-columns: 1fr
223
+ </style>