1import { Dt as watch, Et as useTemplateRef, Ht as normalizeClass, J as vModelSelect, Lt as ref, Q as Fragment, Vt as unref, Wt as toDisplayString, Y as vModelText, bt as renderList, et as computed, ht as onMounted, it as createElementBlock, jt as withDirectives, lt as defineComponent, mt as onBeforeUnmount, n as _plugin_vue_export_helper_default, ot as createTextVNode, pt as nextTick, rt as createCommentVNode, st as createVNode, tt as createBaseVNode, vt as openBlock } from "./chunks/framework.BcTgqkOM.js"; 2import { t as sites } from "./chunks/showcase-sites.cc4oV5RS.js"; 3//#region data/utils.js 4/** 5* Generate a safe filename slug from a URL 6* @param {string} url - The URL to convert 7* @returns {string} - The slugified filename 8*/ 9function getSafeSlug(url) { 10 const { hostname, pathname } = new URL(url); 11 return `${hostname}${pathname}`.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""); 12} 13//#endregion 14//#region .vitepress/theme/components/ShowcaseList.vue?vue&type=script&setup=true&lang.ts 15var _hoisted_1$1 = { class: "showcase-container" }; 16var _hoisted_2 = { class: "filters-wrapper" }; 17var _hoisted_3 = { class: "filters" }; 18var _hoisted_4 = { class: "filter-group" }; 19var _hoisted_5 = { for: "category-filter" }; 20var _hoisted_6 = { value: "all" }; 21var _hoisted_7 = ["value"]; 22var _hoisted_8 = { class: "filter-group" }; 23var _hoisted_9 = { for: "country-filter" }; 24var _hoisted_10 = { value: "all" }; 25var _hoisted_11 = ["value"]; 26var _hoisted_12 = { class: "filter-group" }; 27var _hoisted_13 = { for: "feature-filter" }; 28var _hoisted_14 = { value: "all" }; 29var _hoisted_15 = ["value"]; 30var _hoisted_16 = { class: "filter-group" }; 31var _hoisted_17 = { for: "framework-filter" }; 32var _hoisted_18 = { value: "all" }; 33var _hoisted_19 = ["value"]; 34var _hoisted_20 = { class: "filter-group" }; 35var _hoisted_21 = { for: "cms-filter" }; 36var _hoisted_22 = { value: "all" }; 37var _hoisted_23 = ["value"]; 38var _hoisted_24 = { class: "filter-group" }; 39var _hoisted_25 = { for: "search-filter" }; 40var _hoisted_26 = ["placeholder"]; 41var _hoisted_27 = { class: "showcase-list" }; 42var _hoisted_28 = { 43 key: 0, 44 class: "no-results" 45}; 46var _hoisted_29 = { 47 key: 0, 48 class: "featured-badge" 49}; 50var _hoisted_30 = ["href"]; 51var _hoisted_31 = ["src", "alt"]; 52var _hoisted_32 = ["href"]; 53var _hoisted_33 = { key: 0 }; 54var _hoisted_34 = { key: 1 }; 55var _hoisted_35 = { key: 0 }; 56var _hoisted_36 = ["href"]; 57var PAGE_SIZE = 48; 58//#endregion 59//#region .vitepress/theme/components/ShowcaseList.vue 60var ShowcaseList_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({ 61 __name: "ShowcaseList", 62 props: { 63 sites: {}, 64 labels: {} 65 }, 66 setup(__props) { 67 const props = __props; 68 /** 69 * Generic sorting utility that sorts items by their display labels. 70 * @param items - Array of items to sort 71 * @param labelMap - Map of item keys to their display labels 72 * @returns Sorted array 73 */ 74 const sortByLabel = (items, labelMap) => { 75 return items.sort((a, b) => (labelMap[a] || a).localeCompare(labelMap[b] || b)); 76 }; 77 /** 78 * Sets a URL parameter if the value differs from the default. 79 * @param params - URLSearchParams object to update 80 * @param key - Parameter key 81 * @param value - Current value 82 * @param defaultValue - Default/unset value 83 * @param mapValue - Optional function to transform the value before setting 84 */ 85 const setUrlParam = (params, key, value, defaultValue, mapValue) => { 86 if (value !== defaultValue) params.set(key, mapValue ? mapValue(value) : value); 87 }; 88 /** 89 * Number of sites to render at a time. Divisible by 2, 3 and 4 so that full rows are filled 90 * regardless of the number of grid columns. 91 */ 92 const selectedCategory = ref("all"); 93 const selectedFramework = ref("all"); 94 const selectedMigratedFrom = ref("all"); 95 const selectedFeature = ref("all"); 96 const selectedCountry = ref("all"); 97 const searchQuery = ref(""); 98 const updateUrlParams = () => { 99 const params = new URLSearchParams(); 100 if (searchQuery.value) params.set("q", searchQuery.value); 101 setUrlParam(params, "category", selectedCategory.value, "all"); 102 setUrlParam(params, "framework", selectedFramework.value, "all", (v) => v === null ? "none" : v); 103 setUrlParam(params, "migrated-from", selectedMigratedFrom.value, "all"); 104 setUrlParam(params, "feature", selectedFeature.value, "all"); 105 setUrlParam(params, "country", selectedCountry.value, "all", (v) => v === null ? "global" : v); 106 const query = params.toString(); 107 const newUrl = query ? `${window.location.pathname}?${query}` : window.location.pathname; 108 window.history.replaceState({}, "", newUrl); 109 }; 110 onMounted(() => { 111 const params = new URLSearchParams(window.location.search); 112 if (params.has("q")) searchQuery.value = params.get("q") || ""; 113 if (params.has("category")) { 114 const category = params.get("category"); 115 if (category === "all" || Object.keys(props.labels.category).includes(category)) selectedCategory.value = category === "all" ? "all" : category; 116 } 117 if (params.has("framework")) { 118 const framework = params.get("framework"); 119 selectedFramework.value = framework === "none" ? null : framework || "all"; 120 } 121 if (params.has("migrated-from")) { 122 const cms = params.get("migrated-from"); 123 selectedMigratedFrom.value = cms || "all"; 124 } 125 if (params.has("feature")) { 126 const feature = params.get("feature"); 127 selectedFeature.value = feature || "all"; 128 } 129 if (params.has("country")) { 130 const country = params.get("country"); 131 selectedCountry.value = country === "global" ? null : country || "all"; 132 } 133 }); 134 watch([ 135 searchQuery, 136 selectedCategory, 137 selectedFramework, 138 selectedMigratedFrom, 139 selectedFeature, 140 selectedCountry 141 ], () => { 142 updateUrlParams(); 143 }); 144 const frameworks = computed(() => { 145 const uniqueFrameworks = new Set(props.sites.map((site) => site.framework).filter((fw) => !!fw)); 146 const last = ["vanilla", "custom"]; 147 const lastInSet = last.filter((fw) => uniqueFrameworks.has(fw)); 148 last.forEach((fw) => uniqueFrameworks.delete(fw)); 149 return [...sortByLabel(Array.from(uniqueFrameworks), props.labels.framework), ...lastInSet]; 150 }); 151 const migratedFromOptions = computed(() => { 152 const unique = new Set(props.sites.flatMap((site) => site.migrated_from)); 153 return sortByLabel(Array.from(unique), props.labels.cms); 154 }); 155 const features = computed(() => { 156 const uniqueFeatures = /* @__PURE__ */ new Set();
157 props.sites.forEach((site) => { 158 site.features?.forEach((feature) => uniqueFeatures.add(feature)); 159 }); 160 return sortByLabel(Array.from(uniqueFeatures), props.labels.feature); 161 }); 162 const countries = computed(() => { 163 const uniqueCountries = new Set(props.sites.map((site) => site.country).filter((country) => !!country)); 164 return [...Array.from(uniqueCountries).sort((a, b) => getCountryName(a).localeCompare(getCountryName(b))), null]; 165 }); 166 const categoriesSorted = computed(() => { 167 return sortByLabel(Object.keys(props.labels.category), props.labels.category); 168 }); 169 const filteredSites = computed(() => { 170 const rawQuery = searchQuery.value.trim(); 171 const searchRegex = rawQuery ? new RegExp(`\\b${rawQuery.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "i") : null; 172 const filtered = props.sites.filter((site) => { 173 if (searchRegex) { 174 if (!searchRegex.test(site.name) && !searchRegex.test(site.description)) return false; 175 } 176 if (selectedCategory.value !== "all" && !site.categories.includes(selectedCategory.value)) return false; 177 if (selectedFramework.value !== "all" && site.framework !== selectedFramework.value) return false; 178 if (selectedMigratedFrom.value !== "all" && !site.migrated_from.includes(selectedMigratedFrom.value)) return false; 179 if (selectedFeature.value !== "all" && !site.features?.includes(selectedFeature.value)) return false; 180 if (selectedCountry.value !== "all" && site.country !== selectedCountry.value) return false; 181 return true; 182 }); 183 const featured = filtered.filter((site) => site.featured); 184 const rest = filtered.filter((site) => !site.featured).reverse(); 185 return [...featured, ...rest]; 186 }); 187 const visibleCount = ref(PAGE_SIZE); 188 const visibleSites = computed(() => filteredSites.value.slice(0, visibleCount.value)); 189 const hasMore = computed(() => visibleCount.value < filteredSites.value.length);
190 const loadMoreButton = useTemplateRef("load-more"); 191 let observer; 192 const loadMore = () => { 193 visibleCount.value += PAGE_SIZE; 194 }; 195 /** 196 * (Re)start observing the Load More button. Observing an element always triggers the callback 197 * once, so this also loads the next batch when the button is still in view after an update, e.g. 198 * on a tall screen. 199 */ 200 const observeLoadMoreButton = async () => { 201 await nextTick(); 202 observer?.disconnect(); 203 if (loadMoreButton.value) observer?.observe(loadMoreButton.value); 204 }; 205 onMounted(() => { 206 observer = new IntersectionObserver(([entry]) => { 207 if (entry?.isIntersecting) loadMore(); 208 }, { rootMargin: "0px 0px 1000px 0px" }); 209 observeLoadMoreButton(); 210 }); 211 onBeforeUnmount(() => { 212 observer?.disconnect(); 213 }); 214 watch(filteredSites, () => { 215 visibleCount.value = PAGE_SIZE; 216 }); 217 watch(visibleCount, () => { 218 observeLoadMoreButton(); 219 }); 220 /** 221 * Generates a safe image path for a given URL by creating a slug and appending the .webp extension. 222 * @param url - The URL of the site for which to generate the image path. 223 * @returns The generated image path in the format /images/showcase/{slug}.webp 224 */ 225 const getImagePath = (url) => `/images/showcase/${getSafeSlug(url)}.webp`; 226 let countryDisplayNames; 227 /** 228 * Converts a country code (e.g., "US", "FR") to its display name (e.g., "United States", "France"). 229 * If the conversion fails, it returns the original country code. 230 * @param countryCode - The ISO 3166-1 alpha-2 country code to convert. 231 * @returns The display name of the country or the original country code if conversion fails. 232 */ 233 const getCountryName = (countryCode) => { 234 try { 235 countryDisplayNames ??= new Intl.DisplayNames(["en"], { type: "region" }); 236 return countryDisplayNames.of(countryCode) || countryCode; 237 } catch { 238 return countryCode; 239 } 240 }; 241 /** 242 * Resolves keys to their display labels and sorts them alphabetically. 243 * @param keys - Array of category/feature keys 244 * @param labelMap - Map of keys to their display labels 245 * @returns Sorted display labels 246 */ 247 const getSortedLabels = (keys, labelMap) => keys.map((key) => labelMap[key] || key).sort((a, b) => a.localeCompare(b)); 248 return (_ctx, _cache) => { 249 return openBlock(), createElementBlock("div", _hoisted_1$1, [ 250 createBaseVNode("div", _hoisted_2, [createBaseVNode("div", _hoisted_3, [ 251 createBaseVNode("div", _hoisted_4, [ 252 createBaseVNode("label", _hoisted_5, toDisplayString(__props.labels.filters.category.label), 1), 253 withDirectives(createBaseVNode("select", { 254 id: "category-filter", 255 "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => selectedCategory.value = $event) 256 }, [createBaseVNode("option", _hoisted_6, toDisplayString(__props.labels.filters.category.all), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(categoriesSorted.value, (category) => { 257 return openBlock(), createElementBlock("option", { 258 key: category, 259 value: category 260 }, toDisplayString(__props.labels.category[category]), 9, _hoisted_7); 261 }), 128))], 512), [[vModelSelect, selectedCategory.value]]), 262 _cache[6] || (_cache[6] = createBaseVNode("span", { class: "vpi-chevron-down text-icon" }, null, -1)) 263 ]), 264 createBaseVNode("div", _hoisted_8, [ 265 createBaseVNode("label", _hoisted_9, toDisplayString(__props.labels.filters.country.label), 1), 266 withDirectives(createBaseVNode("select", { 267 id: "country-filter", 268 "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => selectedCountry.value = $event) 269 }, [createBaseVNode("option", _hoisted_10, toDisplayString(__props.labels.filters.country.all), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(countries.value, (country) => { 270 return openBlock(), createElementBlock("option", { 271 key: country ?? "global", 272 value: country 273 }, toDisplayString(country ? getCountryName(country) : __props.labels.filters.country.global), 9, _hoisted_11); 274 }), 128))], 512), [[vModelSelect, selectedCountry.value]]), 275 _cache[7] || (_cache[7] = createBaseVNode("span", { class: "vpi-chevron-down text-icon" }, null, -1)) 276 ]), 277 createBaseVNode("div", _hoisted_12, [ 278 createBaseVNode("label", _hoisted_13, toDisplayString(__props.labels.filters.feature.label), 1), 279 withDirectives(createBaseVNode("select", { 280 id: "feature-filter", 281 "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => selectedFeature.value = $event) 282 }, [createBaseVNode("option", _hoisted_14, toDisplayString(__props.labels.filters.feature.all), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(features.value, (feature) => { 283 return openBlock(), createElementBlock("option", { 284 key: feature, 285 value: feature 286 }, toDisplayString(__props.labels.feature[feature] || feature), 9, _hoisted_15); 287 }), 128))], 512), [[vModelSelect, selectedFeature.value]]), 288 _cache[8] || (_cache[8] = createBaseVNode("span", { class: "vpi-chevron-down text-icon" }, null, -1)) 289 ]), 290 createBaseVNode("div", _hoisted_16, [ 291 createBaseVNode("label", _hoisted_17, toDisplayString(__props.labels.filters.framework.label), 1), 292 withDirectives(createBaseVNode("select", { 293 id: "framework-filter", 294 "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => selectedFramework.value = $event) 295 }, [createBaseVNode("option", _hoisted_18, toDisplayString(__props.labels.filters.framework.all), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(frameworks.value, (framework) => { 296 return openBlock(), createElementBlock("option", { 297 key: framework ?? "none", 298 value: framework 299 }, toDisplayString(framework ? __props.labels.framework[framework] : __props.labels.framework.none), 9, _hoisted_19); 300 }), 128))], 512), [[vModelSelect, selectedFramework.value]]), 301 _cache[9] || (_cache[9] = createBaseVNode("span", { class: "vpi-chevron-down text-icon" }, null, -1)) 302 ]), 303 createBaseVNode("div", _hoisted_20, [ 304 createBaseVNode("label", _hoisted_21, toDisplayString(__props.labels.filters.cms.label), 1), 305 withDirectives(createBaseVNode("select", { 306 id: "cms-filter", 307 "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => selectedMigratedFrom.value = $event) 308 }, [createBaseVNode("option", _hoisted_22, toDisplayString(__props.labels.filters.cms.all), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(migratedFromOptions.value, (cms) => { 309 return openBlock(), createElementBlock("option", { 310 key: cms, 311 value: cms 312 }, toDisplayString(__props.labels.cms[cms] || cms), 9, _hoisted_23); 313 }), 128))], 512), [[vModelSelect, selectedMigratedFrom.value]]), 314 _cache[10] || (_cache[10] = createBaseVNode("span", { class: "vpi-chevron-down text-icon" }, null, -1)) 315 ]), 316 createBaseVNode("div", _hoisted_24, [createBaseVNode("label", _hoisted_25, toDisplayString(__props.labels.filters.search.label), 1), withDirectives(createBaseVNode("input", { 317 id: "search-filter", 318 "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => searchQuery.value = $event), 319 type: "search", 320 placeholder: __props.labels.filters.search.placeholder 321 }, null, 8, _hoisted_26), [[vModelText, searchQuery.value]])]) 322 ])]), 323 createBaseVNode("div", _hoisted_27, [filteredSites.value.length === 0 ? (openBlock(), createElementBlock("p", _hoisted_28, toDisplayString(__props.labels.no_results), 1)) : createCommentVNode("", true), (openBlock(true), createElementBlock(Fragment, null, renderList(visibleSites.value, (site) => { 324 return openBlock(), createElementBlock("article", { 325 key: site.url, 326 class: normalizeClass({ featured: site.featured }) 327 }, [ 328 site.featured ? (openBlock(), createElementBlock("span", _hoisted_29, toDisplayString(__props.labels.featured), 1)) : createCommentVNode("", true), 329 createBaseVNode("a", { 330 href: site.url, 331 target: "_blank", 332 rel: "noopener noreferrer nofollow", 333 class: "screenshot-link" 334 }, [createBaseVNode("img", { 335 src: getImagePath(site.url), 336 alt: __props.labels.screenshot_alt.replace("{name}", site.name), 337 loading: "lazy"
338 }, null, 8, _hoisted_31)], 8, _hoisted_30), 339 createBaseVNode("header", null, [createBaseVNode("h3", null, [createBaseVNode("a", { 340 href: site.url, 341 target: "_blank", 342 rel: "noopener noreferrer nofollow" 343 }, toDisplayString(site.name), 9, _hoisted_32)])]), 344 createBaseVNode("p", null, toDisplayString(site.description), 1), 345 createBaseVNode("footer", null, [ 346 createBaseVNode("div", null, [createBaseVNode("strong", null, toDisplayString(__props.labels.filters.category.label_plural) + ":", 1), createTextVNode(" " + toDisplayString(getSortedLabels(site.categories, __props.labels.category).join(", ")), 1)]), 347 createBaseVNode("div", null, [createBaseVNode("strong", null, toDisplayString(__props.labels.filters.country.label) + ":", 1), createTextVNode(" " + toDisplayString(site.country ? getCountryName(site.country) : __props.labels.filters.country.global), 1)]), 348 site.features.length ? (openBlock(), createElementBlock("div", _hoisted_33, [createBaseVNode("strong", null, toDisplayString(__props.labels.filters.feature.label_plural) + ":", 1), createTextVNode(" " + toDisplayString(getSortedLabels(site.features, __props.labels.feature).join(", ")), 1)])) : createCommentVNode("", true), 349 createBaseVNode("div", null, [createBaseVNode("strong", null, toDisplayString(__props.labels.filters.framework.label) + ":", 1), createTextVNode(" " + toDisplayString(site.framework ? __props.labels.framework[site.framework] : __props.labels.framework.none), 1)]), 350 site.migrated_from.length ? (openBlock(), createElementBlock("div", _hoisted_34, [createBaseVNode("strong", null, toDisplayString(__props.labels.filters.cms.label) + ":", 1), createTextVNode(" " + toDisplayString(site.migrated_from.map((c) => __props.labels.cms[c] || c).join(", ")), 1)])) : createCommentVNode("", true), 351 (openBlock(), createElementBlock(Fragment, null, renderList([ 352 "repository", 353 "user_guide", 354 "blog_post", 355 "forum_post", 356 "social_post", 357 "case_study" 358 ], (key) => { 359 return openBlock(), createElementBlock(Fragment, { key }, [site[key] ? (openBlock(), createElementBlock("div", _hoisted_35, [createBaseVNode("a", { 360 href: site[key], 361 target: "_blank", 362 rel: "noopener noreferrer nofollow" 363 }, toDisplayString(__props.labels[key]), 9, _hoisted_36)])) : createCommentVNode("", true)], 64); 364 }), 64)) 365 ]) 366 ], 2); 367 }), 128))]), 368 hasMore.value ? (openBlock(), createElementBlock("button", { 369 key: 0, 370 ref: "load-more", 371 type: "button", 372 class: "load-more", 373 onClick: loadMore 374 }, toDisplayString(__props.labels.load_more), 513)) : createCommentVNode("", true) 375 ]); 376 }; 377 } 378}), [["__scopeId", "data-v-d06e895f"]]); 379//#endregion 380//#region src/en/showcase.md 381var _hoisted_1 = { class: "site-list" }; 382var __pageData = JSON.parse("{\"title\":\"Sveltia CMS Showcase â Git-based headless CMS examples\",\"titleTemplate\":false,\"description\":\"Explore hundreds of real-world websites using Sveltia CMS across industries, built with Astro, Eleventy, Hugo, other frameworks and vanilla JavaScript.\",\"frontmatter\":{\"titleTemplate\":false,\"title\":\"Sveltia CMS Showcase â Git-based headless CMS examples\",\"description\":\"Explore hundreds of real-world websites using Sveltia CMS across industries, built with Astro, Eleventy, Hugo, other frameworks and vanilla JavaScript.\",\"head\":[[\"meta\",{\"property\":\"og:image\",\"content\":\"https://sveltiacms.app/images/highlights/showcase.webp\"}],[\"link\",{\"rel\":\"canonical\",\"href\":\"https://sveltiacms.app/en/showcase\"}]],\"aside\":false,\"labels\":{\"featured\":\"Favorite\",\"repository\":\"Repository\",\"user_guide\":\"User Guide\",\"blog_post\":\"Blog Post\",\"forum_post\":\"Forum Post\",\"social_post\":\"Social Media Post\",\"case_study\":\"Case Study\",\"screenshot_alt\":\"Screenshot of {name}\",\"no_results\":\"No matching sites were found. Try adjusting your filters. Please note that this showcase does not include political or religious sites.\",\"load_more\":\"Show More\",\"category\":{\"art\":\"Art & Culture\",\"community\":\"Community & Organization\",\"documentation\":\"Documentation & Wiki\",\"education\":\"Education & Research\",\"engineering\":\"Engineering & Manufacturing\",\"event\":\"Event & Competition\",\"fashion\":\"Fashion & Beauty\",\"health\":\"Health & Wellness\",\"hospital
382ity\":\"Hospitality & Travel\",\"media\":\"Media & Publishing\",\"personal\":\"Personal Website & Blog\",\"photography\":\"Photography & Videography\",\"portfolio\":\"Portfolio & Creative\",\"public\":\"Government & Public Sector\",\"reference\":\"Reference & Tool\",\"retail\":\"Retail & E-commerce\",\"science\":\"Science & Technology\",\"service\":\"Professional Service\",\"software\":\"Software & SaaS\",\"sport\":\"Sport & Recreation\"},\"framework\":{\"angular\":\"Angular\",\"antora\":\"Antora\",\"astro\":\"Astro\",\"blume\":\"Blume\",\"docusaurus\":\"Docusaurus\",\"eleventy\":\"Eleventy\",\"gatsby\":\"Gatsby\",\"hugo\":\"Hugo\",\"jekyll\":\"Jekyll\",\"mkdocs\":\"MkDocs\",\"react\":\"React\",\"react-router\":\"React Router\",\"middleman\":\"Middleman\",\"next\":\"Next.js\",\"nuxt\":\"Nuxt\",\"pelican\":\"Pelican\",\"solid-start\":\"SolidStart\",\"sphinx\":\"Sphinx\",\"sveltekit\":\"SvelteKit\",\"tanstack\":\"TanStack Start\",\"vitepress\":\"VitePress\",\"vue\":\"Vue\",\"zola\":\"Zola\",\"vanilla\":\"Vanilla JavaScript\",\"custom\":\"Custom Tooling\",\"none\":\"None / Headless\"},\"cms\":{\"ameba-ownd\":\"Ameba Ownd\",\"artstation\":\"ArtStation\",\"basekit\":\"BaseKit\",\"blogger\":\"Blogger\",\"bricksite\":\"Bricksite\",\"canva\":\"Canva\",\"carrd\":\"Carrd\",\"cloudcannon\":\"CloudCannon\",\"cloudrexx\":\"Cloudrexx\",\"datocms\":\"DatoCMS\",\"decap-cm
382s\":\"Decap CMS\",\"drupal\":\"Drupal\",\"duda\":\"Duda\",\"e-monsite\":\"E-monsite\",\"everweb\":\"EverWeb\",\"figma-sites\":\"Figma Sites\",\"framer\":\"Framer\",\"ghost\":\"Ghost\",\"godaddy\":\"GoDaddy Website Builder\",\"google-sites\":\"Google Sites\",\"hostinger\":\"Hostinger Website Builder\",\"infomaniak\":\"Infomaniak Site Creator\",\"jdomni\":\"JD Omni\",\"joomla\":\"Joomla\",\"journo-portfolio\":\"Journo Portfolio\",\"keystatic\":\"Keystatic\",\"mobirise\":\"Mobirise\",\"netlify-cms\":\"Netlify CMS\",\"nicepage\":\"Nicepage\",\"odoo\":\"Odoo\",\"online-gallery\":\"Online Gallery\",\"pages-cms\":\"Pages CMS\",\"podpage\":\"Podpage\",\"readymag\":\"Readymag\",\"shoppagina\":\"Shoppagina\",\"showit\":\"Showit\",\"simdif\":\"SimDif\",\"sitepins\":\"Sitepins\",\"squarespace\":\"Squarespace\",\"static-cms\":\"Static CMS\",\"strato\":\"Strato Sitebuilder\",\"taplink\":\"Taplink\",\"tilda\":\"Tilda\",\"tinacms\":\"TinaCMS\",\"vocus\":\"Vocus\",\"webflow\":\"Webflow\",\"weblium\":\"Weblium\",\"webnode\":\"Webnode\",\"website-world\":\"Website World CMS\",\"weebly\":\"Weebly\",\"wix\":\"Wix\",\"wordpress\":\"WordPress\"},\"feature\":{\"commit-messages\":\"Custom Commit Messages\",\"i18n\":\"Internationalization (i18n)\",\"image-optimization\":\"Image Optimization\",\"external-media-storage\":\"External Media Storage\",\"editorial-workflow\":\"Editorial Workflow\",\"open-authoring\":\"Open Authoring\",\"initialization\":\"Manual Initialization\",\"preview-styles\":\"Custom Preview Styles\",\"preview-templates\":\"Custom Preview Templates\",\"editor-components\":\"Custom Editor Components\",\"field-types\":\"Custom Field Types\",\"file-formats\":\"Custom File Formats\",\"events\":\"Event Hooks\"},\"filters\":{\"search\":{\"label\":\"Search\",\"placeholder\":\"Search by name, description\"},\"category\":{\"label\":\"Category\",\"label_plural\":\"Categories\",\"all\":\"All Categories\"},\"country\":{\"label\":\"Country\",\"all\":\"All Countries\",\"global\":\"Global / Unknown\"},\"feature\":{\"label\":\"Feature\",\"label_plural\":\"Features\",\"all\":\"All Features\"},\"framework\":{\"label\":\"Framework\",\"all\":\"All Frameworks\"},\"cms\":{\"label\":\"Migrated from\",\"all\":\"All Platforms\"}}}},\"headers\":[],\"relativePath\":\"en/showcase.md\",\"filePath\":\"en/showcase.md\"}"); 383var _sfc_main = /*@__PURE__*/ Object.assign({ name: "en/showcase.md" }, { setup(__props) { 384 const siteCount = sites.length; 385 const countryCount = sites.map((site) => site.country).filter((value, index, self) => !!value && self.indexOf(value) === index).length; 386 return (_ctx, _cache) => { 387 return openBlock(), createElementBlock("div", null, [createBaseVNode("div", _hoisted_1, [ 388 _cache[0] || (_cache[0] = createBaseVNode("h1", { 389 id: "showcase", 390 tabindex: "-1" 391 }, [createTextVNode("Showcase "), createBaseVNode("a", { 392 class: "header-anchor", 393 href: "#showcase", 394 "aria-label": "Permalink to âShowcaseâ" 395 }, "â")], -1)), 396 createBaseVNode("p", null, "Sveltia CMS is trusted by thousands of users around the world. Hereâs a diverse collection of " + toDisplayString(unref(siteCount)) + "\xA0handpicked examples from " + toDisplayString(unref(countryCount)) + "\xA0countries, with new sites added daily. Find inspiration for your next project, and see how this modern Git-based headless CMS can be used in various scenarios.", 1), 397 createVNode(ShowcaseList_default, { 398 sites: unref(sites), 399 labels: _ctx.$frontmatter.labels 400 }, null, 8, ["sites", "labels"]) 401 ])]); 402 }; 403} }); 404//#endregion 405export { __pageData, _sfc_main as default };
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.