diff --git a/src/components/FileExplorer.tsx b/src/components/FileExplorer.tsx
index 17d926b44..13d36fc85 100644
--- a/src/components/FileExplorer.tsx
+++ b/src/components/FileExplorer.tsx
@@ -7,6 +7,7 @@ import jsonIconUrl from '~/images/file-icons/json.svg?url'
import svelteIconUrl from '~/images/file-icons/svelte.svg?url'
import vueIconUrl from '~/images/file-icons/vue.svg?url'
import markoIconUrl from '~/images/file-icons/marko.svg?url'
+import emberIconUrl from '~/images/ember-logo.svg?url'
import textIconUrl from '~/images/file-icons/txt.svg?url'
import type { GitHubFileNode } from '~/utils/documents.server'
import { twMerge } from 'tailwind-merge'
@@ -33,6 +34,9 @@ const getFileIconPath = (filename: string) => {
return vueIconUrl
case 'marko':
return markoIconUrl
+ case 'gjs':
+ case 'gts':
+ return emberIconUrl
default:
return textIconUrl
}
diff --git a/src/components/markdown/codeBlock.shared.tsx b/src/components/markdown/codeBlock.shared.tsx
index 2befa457e..ffbbdcc30 100644
--- a/src/components/markdown/codeBlock.shared.tsx
+++ b/src/components/markdown/codeBlock.shared.tsx
@@ -83,6 +83,8 @@ export function getCodeBlockLanguageFromFilePath(filePath: string) {
if (['cts', 'mts'].includes(ext)) return 'ts'
if (['cjs', 'mjs'].includes(ext)) return 'js'
+ if (ext === 'gts') return 'ts'
+ if (ext === 'gjs') return 'js'
if (['prettierrc', 'babelrc', 'webmanifest'].includes(ext)) return 'json'
if (['env', 'example'].includes(ext)) return 'sh'
diff --git a/src/images/ember-logo.svg b/src/images/ember-logo.svg
new file mode 100644
index 000000000..e0a795691
--- /dev/null
+++ b/src/images/ember-logo.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/libraries/frameworks.tsx b/src/libraries/frameworks.tsx
index 683a864e6..c4214b9a8 100644
--- a/src/libraries/frameworks.tsx
+++ b/src/libraries/frameworks.tsx
@@ -1,5 +1,6 @@
import alpineLogo from '../images/alpine-logo.svg'
import angularLogo from '../images/angular-logo.svg'
+import emberLogo from '../images/ember-logo.svg'
import jsLogo from '../images/js-logo.svg'
import litLogo from '../images/lit-logo.svg'
import markoLogo from '../images/marko-logo.svg'
@@ -40,6 +41,13 @@ export const frameworkOptions = [
color: 'bg-red-500',
fontColor: 'text-fuchsia-500',
},
+ {
+ label: 'Ember',
+ value: 'ember',
+ logo: emberLogo,
+ color: 'bg-orange-600',
+ fontColor: 'text-orange-600',
+ },
{
label: 'Solid',
value: 'solid',
diff --git a/src/libraries/libraries.ts b/src/libraries/libraries.ts
index 4e2f763e3..f15ea4216 100644
--- a/src/libraries/libraries.ts
+++ b/src/libraries/libraries.ts
@@ -314,7 +314,7 @@ export const table: LibrarySlim = {
to: '/table',
tagline: 'Headless UI for building powerful tables & datagrids',
description:
- 'Supercharge your tables or build a datagrid from scratch for TS/JS, React, Vue, Solid, Svelte, Qwik, Angular, and Lit while retaining 100% control over markup and styles.',
+ 'Supercharge your tables or build a datagrid from scratch in any framework while retaining 100% control over markup and styles.',
bgStyle: 'bg-blue-500',
borderStyle: 'border-blue-500/50',
textStyle: 'text-blue-500',
@@ -326,6 +326,7 @@ export const table: LibrarySlim = {
repo: 'tanstack/table',
frameworks: [
'angular',
+ 'ember',
'react',
'preact',
'solid',
diff --git a/src/libraries/types.ts b/src/libraries/types.ts
index 8f452652c..5cec2be84 100644
--- a/src/libraries/types.ts
+++ b/src/libraries/types.ts
@@ -6,6 +6,7 @@ export type { LibraryId } from './ids'
export type Framework =
| 'angular'
| 'alpine'
+ | 'ember'
| 'lit'
| 'marko'
| 'preact'
diff --git a/src/routes/-libraries-utils.ts b/src/routes/-libraries-utils.ts
index 1cb17260e..ef6848c89 100644
--- a/src/routes/-libraries-utils.ts
+++ b/src/routes/-libraries-utils.ts
@@ -41,3 +41,17 @@ export function getFrameworkLibraryCounts(allLibraries: Array) {
return counts
}
+
+export function orderFrameworksForBrowse<
+ TFramework extends { value: Framework },
+>(
+ frameworks: ReadonlyArray,
+ counts: Partial>,
+) {
+ return [...frameworks].sort((a, b) => {
+ if (a.value === 'vanilla') return 1
+ if (b.value === 'vanilla') return -1
+
+ return (counts[b.value] ?? 0) - (counts[a.value] ?? 0)
+ })
+}
diff --git a/src/routes/libraries.tsx b/src/routes/libraries.tsx
index 13c606ab5..802098262 100644
--- a/src/routes/libraries.tsx
+++ b/src/routes/libraries.tsx
@@ -7,6 +7,7 @@ import LibraryCard from '~/components/LibraryCard'
import {
getFrameworkLibraryCounts,
getVisibleLibraries,
+ orderFrameworksForBrowse,
orderLibrariesForBrowse,
} from './-libraries-utils'
@@ -30,8 +31,11 @@ function LibrariesPage() {
const allLibraries = getVisibleLibraries()
const ordered = orderLibrariesForBrowse(allLibraries)
const frameworkCounts = getFrameworkLibraryCounts(allLibraries)
- const frameworksWithLibraries = frameworkOptions.filter(
- (framework) => (frameworkCounts[framework.value] ?? 0) > 0,
+ const frameworksWithLibraries = orderFrameworksForBrowse(
+ frameworkOptions.filter(
+ (framework) => (frameworkCounts[framework.value] ?? 0) > 0,
+ ),
+ frameworkCounts,
)
const deprecatedLibraries = [reactChartsProject]
diff --git a/src/routes/libraries_.$framework.tsx b/src/routes/libraries_.$framework.tsx
index 17bf9d67e..917f61166 100644
--- a/src/routes/libraries_.$framework.tsx
+++ b/src/routes/libraries_.$framework.tsx
@@ -6,6 +6,7 @@ import LibraryCard from '~/components/LibraryCard'
import {
getFrameworkLibraryCounts,
getVisibleLibraries,
+ orderFrameworksForBrowse,
orderLibrariesForBrowse,
} from './-libraries-utils'
@@ -45,8 +46,11 @@ function LibrariesFrameworkPage() {
const allLibraries = getVisibleLibraries()
const frameworkCounts = getFrameworkLibraryCounts(allLibraries)
- const frameworksWithLibraries = frameworkOptions.filter(
- (option) => (frameworkCounts[option.value] ?? 0) > 0,
+ const frameworksWithLibraries = orderFrameworksForBrowse(
+ frameworkOptions.filter(
+ (option) => (frameworkCounts[option.value] ?? 0) > 0,
+ ),
+ frameworkCounts,
)
const filteredLibraries = orderLibrariesForBrowse(
allLibraries.filter((library) =>
diff --git a/src/utils/llms.ts b/src/utils/llms.ts
index 987f9cc0a..066389521 100644
--- a/src/utils/llms.ts
+++ b/src/utils/llms.ts
@@ -41,6 +41,7 @@ const librarySections = [
const frameworkLabels: Record = {
angular: 'Angular',
alpine: 'Alpine',
+ ember: 'Ember',
lit: 'Lit',
marko: 'Marko',
preact: 'Preact',
diff --git a/src/utils/npm-packages.ts b/src/utils/npm-packages.ts
index f37c4e023..546d329a7 100644
--- a/src/utils/npm-packages.ts
+++ b/src/utils/npm-packages.ts
@@ -26,10 +26,11 @@ export const frameworkMeta: Record =
solid: { name: 'Solid', color: '#2C4F7C' },
vue: { name: 'Vue', color: '#42B883' },
svelte: { name: 'Svelte', color: '#FF3E00' },
- marko: { name: 'Marko', color: '#44bfef' },
angular: { name: 'Angular', color: '#DD0031' },
- alpine: { name: 'Alpine', color: '#77C1D2' },
lit: { name: 'Lit', color: '#325CFF' },
+ alpine: { name: 'Alpine', color: '#77C1D2' },
+ marko: { name: 'Marko', color: '#44bfef' },
+ ember: { name: 'Ember', color: '#E04E39' },
qwik: { name: 'Qwik', color: '#18B6F6' },
vanilla: { name: 'Vanilla', color: '#F7DF1E' },
}
diff --git a/tests/libraries-utils.test.ts b/tests/libraries-utils.test.ts
new file mode 100644
index 000000000..129c09266
--- /dev/null
+++ b/tests/libraries-utils.test.ts
@@ -0,0 +1,33 @@
+import assert from 'node:assert/strict'
+import { orderFrameworksForBrowse } from '../src/routes/-libraries-utils'
+import type { Framework } from '../src/libraries/types'
+
+const frameworks: Array<{ value: Framework }> = [
+ { value: 'react' },
+ { value: 'vue' },
+ { value: 'solid' },
+ { value: 'vanilla' },
+ { value: 'ember' },
+]
+
+const ordered = orderFrameworksForBrowse(frameworks, {
+ react: 8,
+ vue: 5,
+ solid: 5,
+ vanilla: 10,
+ ember: 1,
+})
+
+assert.deepEqual(
+ ordered.map((framework) => framework.value),
+ ['react', 'vue', 'solid', 'ember', 'vanilla'],
+ 'frameworks sort by library count, preserve ties, and keep Vanilla last',
+)
+
+assert.deepEqual(
+ frameworks.map((framework) => framework.value),
+ ['react', 'vue', 'solid', 'vanilla', 'ember'],
+ 'framework sorting does not mutate the shared framework options',
+)
+
+console.log('libraries utils tests passed')