@nospt/plugin-dev-ai-hub 1.2.0 → 1.2.1
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 +0 -7
- package/config.schema.json +0 -5
- package/dist/components/AiResourcesPage/ResourceCard.esm.js +6 -1
- package/dist/components/AiResourcesPage/ResourceCard.esm.js.map +1 -1
- package/dist/components/AiResourcesPage/ResourceCard.module.css.esm.js +2 -2
- package/dist/components/RecommendedBadge/RecommendedBadgeIcon.esm.js +4 -10
- package/dist/components/RecommendedBadge/RecommendedBadgeIcon.esm.js.map +1 -1
- package/package.json +2 -2
- package/dist/components/RecommendedBadge/recommended-badge.svg +0 -9
package/README.md
CHANGED
|
@@ -110,13 +110,6 @@ devAiHub:
|
|
|
110
110
|
# an overlay, so this never reflows the page; it is capped at the viewport
|
|
111
111
|
# width and floored at a sane minimum. Defaults to 615 when unset.
|
|
112
112
|
detailPanelWidth: 615
|
|
113
|
-
# Optional. Image for the "Recommended" mark — the badge on resource cards,
|
|
114
|
-
# the browse page's Recommended section heading, and the Admin Panel
|
|
115
|
-
# curation list. Any browser-loadable source — an absolute URL, an
|
|
116
|
-
# app-served static path, or a data URI. Defaults to the bundled
|
|
117
|
-
# medal-and-ribbon mark when unset. The heading's text stays amber to
|
|
118
|
-
# match that default, so pick an image that sits well beside it.
|
|
119
|
-
recommendedBadgeIcon: https://example.com/recommended.svg
|
|
120
113
|
# Optional. Default sort applied to the browse page on load, before any
|
|
121
114
|
# explicit user selection: `usage`, `newest`, or `name`. Users can still
|
|
122
115
|
# change it from the page's sort dropdown. Defaults to `usage` when unset.
|
package/config.schema.json
CHANGED
|
@@ -13,11 +13,6 @@
|
|
|
13
13
|
"description": "Width of the resource detail drawer, in pixels. The drawer is an overlay, so this never reflows the page; it is capped at the viewport width and floored at a sane minimum. Defaults to 615 when unset.",
|
|
14
14
|
"visibility": "frontend"
|
|
15
15
|
},
|
|
16
|
-
"recommendedBadgeIcon": {
|
|
17
|
-
"type": "string",
|
|
18
|
-
"description": "Image used for the \"Recommended\" mark — the badge on resource cards, the browse page's Recommended section heading, and the Admin Panel curation list. Any browser-loadable source works — an absolute URL, an app-served static path, or a data URI. Defaults to the bundled medal-and-ribbon mark when unset.",
|
|
19
|
-
"visibility": "frontend"
|
|
20
|
-
},
|
|
21
16
|
"defaultSort": {
|
|
22
17
|
"type": "string",
|
|
23
18
|
"description": "Default sort applied to the browse page on load, before any explicit user selection (issue #102): `usage` (installs plus a weighted view count — the \"most popular\" composite), `newest` (when the plugin first saw the resource, ADR-0021), or `name`. Users can still change the sort from the page's sort dropdown; this only controls the initial order. An unrecognised value — including the removed `installs`, `downloads`, `views` and `recommended` modes — is rejected with a logged error and the page falls back to `usage`. Defaults to `usage` when unset.",
|
|
@@ -3,6 +3,7 @@ import { Text } from '@backstage/ui';
|
|
|
3
3
|
import { RiEyeLine, RiDownloadLine } from '@remixicon/react';
|
|
4
4
|
import { useTelemetryCounts } from '../../hooks/useTelemetryCounts.esm.js';
|
|
5
5
|
import { ToolIcon } from '../ToolIcon/ToolIcon.esm.js';
|
|
6
|
+
import { RecommendedBadgeIcon } from '../RecommendedBadge/RecommendedBadgeIcon.esm.js';
|
|
6
7
|
import { countLabel } from './countLabel.esm.js';
|
|
7
8
|
import { formatOwnerLabel } from './ownerLabel.esm.js';
|
|
8
9
|
import { getTypeMeta } from './typeMeta.esm.js';
|
|
@@ -84,12 +85,16 @@ function ResourceCard({
|
|
|
84
85
|
}
|
|
85
86
|
)
|
|
86
87
|
] }),
|
|
87
|
-
(resource.version || ownerLabel || !!counts?.install || !!counts?.view) && /* @__PURE__ */ jsxs("div", { className: styles.footer, children: [
|
|
88
|
+
(resource.recommended || resource.version || ownerLabel || !!counts?.install || !!counts?.view) && /* @__PURE__ */ jsxs("div", { className: styles.footer, children: [
|
|
88
89
|
/* @__PURE__ */ jsx(Text, { variant: "body-x-small", color: "secondary", children: [
|
|
89
90
|
resource.version && `v${resource.version.replace(/^v/, "")}`,
|
|
90
91
|
ownerLabel
|
|
91
92
|
].filter(Boolean).join(" \xB7 ") }),
|
|
92
93
|
/* @__PURE__ */ jsxs("span", { className: styles.countsRow, children: [
|
|
94
|
+
resource.recommended && /* @__PURE__ */ jsxs("span", { className: styles.recommendedBadge, children: [
|
|
95
|
+
/* @__PURE__ */ jsx(RecommendedBadgeIcon, { size: 12 }),
|
|
96
|
+
"Recommended"
|
|
97
|
+
] }),
|
|
93
98
|
!!counts?.view && /* @__PURE__ */ jsxs(
|
|
94
99
|
"span",
|
|
95
100
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResourceCard.esm.js","sources":["../../../src/components/AiResourcesPage/ResourceCard.tsx"],"sourcesContent":["import { Text } from '@backstage/ui';\nimport { RiDownloadLine, RiEyeLine } from '@remixicon/react';\nimport type {\n ResourceSummary,\n TelemetryCounts,\n} from '@nospt/plugin-dev-ai-hub-common';\nimport { useTelemetryCounts } from '../../hooks/useTelemetryCounts';\nimport { ToolIcon } from '../ToolIcon';\nimport { countLabel } from './countLabel';\nimport { formatOwnerLabel } from './ownerLabel';\nimport { getTypeMeta } from './typeMeta';\nimport { useFrameworkLabel } from '../../hooks/useFrameworkLabel';\nimport styles from './ResourceCard.module.css';\n\ninterface ResourceCardProps {\n resource: ResourceSummary;\n onView: (entityRef: string) => void;\n /** Extra class (e.g. the initial-load entrance animation) merged onto the\n * card's own root — kept off a wrapper div so this stays the grid's direct\n * child and CSS Grid's row-height stretch (the footer's margin-top: auto\n * relies on it for equal card heights) keeps working. */\n className?: string;\n style?: React.CSSProperties;\n /**\n * This resource's slice of the page-level telemetry summary (issue #102 —\n * fetched once to drive sorting), seeding `useTelemetryCounts` so the card\n * does not also fire its own initial request. Omit to fall back to the\n * card fetching for itself.\n */\n seedCounts?: TelemetryCounts;\n}\n\n/**\n * The per-type card: type identity (colour/icon/label from the registry),\n * title, framework badges, tags, and a footer of version · owner plus the\n * view and install counts.\n *\n * Deliberately no description — clamped to two lines at card width it was\n * unreadable, and the drawer shows it in full the moment a card is opened.\n * Deliberately no \"View source\" link either: it competed with the card's own\n * click target and lives in the drawer, which is where someone deciding\n * whether to install is already looking.\n */\nconst MAX_VISIBLE_TAGS = 3;\nconst POPULAR_THRESHOLD = 5;\n\nexport function ResourceCard({\n resource,\n onView,\n className,\n style,\n seedCounts,\n}: ResourceCardProps) {\n const meta = getTypeMeta(resource.type);\n const frameworkLabel = useFrameworkLabel();\n const counts = useTelemetryCounts(resource.entityRef, seedCounts);\n\n const visibleTags = resource.tags.slice(0, MAX_VISIBLE_TAGS);\n const hiddenTagCount = resource.tags.length - visibleTags.length;\n const ownerLabel = formatOwnerLabel(resource.owner);\n\n return (\n <div\n className={className ? `${styles.card} ${className}` : styles.card}\n style={\n {\n '--card-accent': meta.color,\n '--card-accent-bg': meta.colorBg,\n ...style,\n } as React.CSSProperties\n }\n onClick={() => onView(resource.entityRef)}\n onKeyDown={e => {\n if (e.key === 'Enter') onView(resource.entityRef);\n }}\n role=\"button\"\n tabIndex={0}\n aria-label={`View ${resource.title ?? resource.name}`}\n >\n <div className={styles.header}>\n <div className={styles.iconBox}>\n <meta.Icon size={18} />\n </div>\n <div className={styles.headerText}>\n {/* Type reads as an eyebrow above the title. Below it, the reserved\n second title line would leave a gap between a one-line title and\n its own label; above it, that space falls before the description\n where nothing looks detached. */}\n <Text variant=\"body-x-small\" className={styles.typeLabel}>\n {meta.label}\n </Text>\n <Text\n variant=\"title-x-small\"\n weight=\"bold\"\n as=\"h3\"\n className={styles.title}\n >\n {resource.title ?? resource.name}\n </Text>\n </div>\n </div>\n\n <div className={styles.body}>\n {resource.frameworks.length > 0 && (\n <div className={styles.badges}>\n {/* Icon only — the tool's mark is recognisable enough on a card,\n and dropping the label keeps the row narrow. The name stays\n reachable: ToolIcon carries it as an aria-label, and `title`\n surfaces it on hover. Every framework is shown: icons are\n narrow enough that the full set fits, so there is no overflow\n pill to hide the tail behind. */}\n {resource.frameworks.map(f => (\n <span\n key={f}\n className={styles.frameworkBadge}\n title={frameworkLabel(f)}\n >\n <ToolIcon tool={f} size={16} />\n </span>\n ))}\n </div>\n )}\n\n {resource.tags.length > 0 && (\n <div className={styles.tags}>\n {visibleTags.map(t => (\n <span key={t} className={styles.tag}>\n #{t}\n </span>\n ))}\n {hiddenTagCount > 0 && (\n <span\n className={styles.tag}\n title={resource.tags.slice(MAX_VISIBLE_TAGS).join(', ')}\n >\n +{hiddenTagCount}\n </span>\n )}\n </div>\n )}\n\n {(resource.version ||\n ownerLabel ||\n !!counts?.install ||\n !!counts?.view) && (\n <div className={styles.footer}>\n <Text variant=\"body-x-small\" color=\"secondary\">\n {[\n resource.version && `v${resource.version.replace(/^v/, '')}`,\n ownerLabel,\n ]\n .filter(Boolean)\n .join(' · ')}\n </Text>\n {/* Views and installs are the social proof someone browsing\n actually scans for, so they are chips at full foreground rather\n than grey footnotes beside the version. */}\n <span className={styles.countsRow}>\n {!!counts?.view && (\n <span\n className={styles.count}\n title={countLabel(counts.view, 'view')}\n >\n <RiEyeLine size={14} />\n {counts.view}\n </span>\n )}\n {!!counts?.install && (\n <span\n className={\n counts.install >= POPULAR_THRESHOLD\n ? styles.countPopular\n : styles.count\n }\n title={countLabel(counts.install, 'install')}\n >\n {counts.install >= POPULAR_THRESHOLD ? (\n '\\u{1F525}'\n ) : (\n <RiDownloadLine size={14} />\n )}\n {counts.install}\n </span>\n )}\n </span>\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ResourceCard.esm.js","sources":["../../../src/components/AiResourcesPage/ResourceCard.tsx"],"sourcesContent":["import { Text } from '@backstage/ui';\nimport { RiDownloadLine, RiEyeLine } from '@remixicon/react';\nimport type {\n ResourceSummary,\n TelemetryCounts,\n} from '@nospt/plugin-dev-ai-hub-common';\nimport { useTelemetryCounts } from '../../hooks/useTelemetryCounts';\nimport { ToolIcon } from '../ToolIcon';\nimport { RecommendedBadgeIcon } from '../RecommendedBadge';\nimport { countLabel } from './countLabel';\nimport { formatOwnerLabel } from './ownerLabel';\nimport { getTypeMeta } from './typeMeta';\nimport { useFrameworkLabel } from '../../hooks/useFrameworkLabel';\nimport styles from './ResourceCard.module.css';\n\ninterface ResourceCardProps {\n resource: ResourceSummary;\n onView: (entityRef: string) => void;\n /** Extra class (e.g. the initial-load entrance animation) merged onto the\n * card's own root — kept off a wrapper div so this stays the grid's direct\n * child and CSS Grid's row-height stretch (the footer's margin-top: auto\n * relies on it for equal card heights) keeps working. */\n className?: string;\n style?: React.CSSProperties;\n /**\n * This resource's slice of the page-level telemetry summary (issue #102 —\n * fetched once to drive sorting), seeding `useTelemetryCounts` so the card\n * does not also fire its own initial request. Omit to fall back to the\n * card fetching for itself.\n */\n seedCounts?: TelemetryCounts;\n}\n\n/**\n * The per-type card: type identity (colour/icon/label from the registry),\n * title, framework badges, tags, and a footer of version · owner plus the\n * view and install counts.\n *\n * Deliberately no description — clamped to two lines at card width it was\n * unreadable, and the drawer shows it in full the moment a card is opened.\n * Deliberately no \"View source\" link either: it competed with the card's own\n * click target and lives in the drawer, which is where someone deciding\n * whether to install is already looking.\n */\nconst MAX_VISIBLE_TAGS = 3;\nconst POPULAR_THRESHOLD = 5;\n\nexport function ResourceCard({\n resource,\n onView,\n className,\n style,\n seedCounts,\n}: ResourceCardProps) {\n const meta = getTypeMeta(resource.type);\n const frameworkLabel = useFrameworkLabel();\n const counts = useTelemetryCounts(resource.entityRef, seedCounts);\n\n const visibleTags = resource.tags.slice(0, MAX_VISIBLE_TAGS);\n const hiddenTagCount = resource.tags.length - visibleTags.length;\n const ownerLabel = formatOwnerLabel(resource.owner);\n\n return (\n <div\n className={className ? `${styles.card} ${className}` : styles.card}\n style={\n {\n '--card-accent': meta.color,\n '--card-accent-bg': meta.colorBg,\n ...style,\n } as React.CSSProperties\n }\n onClick={() => onView(resource.entityRef)}\n onKeyDown={e => {\n if (e.key === 'Enter') onView(resource.entityRef);\n }}\n role=\"button\"\n tabIndex={0}\n aria-label={`View ${resource.title ?? resource.name}`}\n >\n <div className={styles.header}>\n <div className={styles.iconBox}>\n <meta.Icon size={18} />\n </div>\n <div className={styles.headerText}>\n {/* Type reads as an eyebrow above the title. Below it, the reserved\n second title line would leave a gap between a one-line title and\n its own label; above it, that space falls before the description\n where nothing looks detached. */}\n <Text variant=\"body-x-small\" className={styles.typeLabel}>\n {meta.label}\n </Text>\n <Text\n variant=\"title-x-small\"\n weight=\"bold\"\n as=\"h3\"\n className={styles.title}\n >\n {resource.title ?? resource.name}\n </Text>\n </div>\n </div>\n\n <div className={styles.body}>\n {resource.frameworks.length > 0 && (\n <div className={styles.badges}>\n {/* Icon only — the tool's mark is recognisable enough on a card,\n and dropping the label keeps the row narrow. The name stays\n reachable: ToolIcon carries it as an aria-label, and `title`\n surfaces it on hover. Every framework is shown: icons are\n narrow enough that the full set fits, so there is no overflow\n pill to hide the tail behind. */}\n {resource.frameworks.map(f => (\n <span\n key={f}\n className={styles.frameworkBadge}\n title={frameworkLabel(f)}\n >\n <ToolIcon tool={f} size={16} />\n </span>\n ))}\n </div>\n )}\n\n {resource.tags.length > 0 && (\n <div className={styles.tags}>\n {visibleTags.map(t => (\n <span key={t} className={styles.tag}>\n #{t}\n </span>\n ))}\n {hiddenTagCount > 0 && (\n <span\n className={styles.tag}\n title={resource.tags.slice(MAX_VISIBLE_TAGS).join(', ')}\n >\n +{hiddenTagCount}\n </span>\n )}\n </div>\n )}\n\n {(resource.recommended ||\n resource.version ||\n ownerLabel ||\n !!counts?.install ||\n !!counts?.view) && (\n <div className={styles.footer}>\n <Text variant=\"body-x-small\" color=\"secondary\">\n {[\n resource.version && `v${resource.version.replace(/^v/, '')}`,\n ownerLabel,\n ]\n .filter(Boolean)\n .join(' · ')}\n </Text>\n {/* Views and installs are the social proof someone browsing\n actually scans for, so they are chips at full foreground rather\n than grey footnotes beside the version. */}\n <span className={styles.countsRow}>\n {/* A curator's opinion (#68, ADR-0017), not something the\n resource's author declared — kept visually apart from the\n type identity so it never reads as catalog metadata. In the\n footer rather than the header, where it took its width from\n the title and truncated it. */}\n {resource.recommended && (\n <span className={styles.recommendedBadge}>\n <RecommendedBadgeIcon size={12} />\n Recommended\n </span>\n )}\n {!!counts?.view && (\n <span\n className={styles.count}\n title={countLabel(counts.view, 'view')}\n >\n <RiEyeLine size={14} />\n {counts.view}\n </span>\n )}\n {!!counts?.install && (\n <span\n className={\n counts.install >= POPULAR_THRESHOLD\n ? styles.countPopular\n : styles.count\n }\n title={countLabel(counts.install, 'install')}\n >\n {counts.install >= POPULAR_THRESHOLD ? (\n '\\u{1F525}'\n ) : (\n <RiDownloadLine size={14} />\n )}\n {counts.install}\n </span>\n )}\n </span>\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import styleInject from '../../node_modules_dist/style-inject/dist/style-inject.es.esm.js';
|
|
2
2
|
|
|
3
|
-
var css_248z = "@layer components {\n /*\n * One continuous surface, generous radius, hairline edges — the macOS card\n * idiom. What made the previous card read as clunky was structural rather\n * than stylistic: a two-tone header band with its own background and bottom\n * rule, a second rule above the footer, and 4px corners. Three hard lines\n * cutting a small card into three boxes.\n *\n * Separation is now done with space and type weight instead of lines and\n * fills, which is the same trade the rest of the plugin makes.\n */\n .
|
|
4
|
-
var styles = {"card":"
|
|
3
|
+
var css_248z = "@layer components {\n /*\n * One continuous surface, generous radius, hairline edges — the macOS card\n * idiom. What made the previous card read as clunky was structural rather\n * than stylistic: a two-tone header band with its own background and bottom\n * rule, a second rule above the footer, and 4px corners. Three hard lines\n * cutting a small card into three boxes.\n *\n * Separation is now done with space and type weight instead of lines and\n * fills, which is the same trade the rest of the plugin makes.\n */\n .ResourceCard_card__c6cb3d0bd1 {\n background: var(--bui-bg-neutral-1);\n border: 1px solid var(--bui-border-1);\n /* The type's colour, kept as the card's identity at a glance in the grid.\n 2px rather than 3: on a 16px radius a thick stripe fights the curve. */\n border-left: 2px solid var(--card-accent);\n border-radius: var(--bui-radius-5);\n overflow: hidden;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n gap: var(--bui-space-2);\n padding: var(--bui-space-4);\n /* Room for the tallest realistic card (2-line title, badges, tags,\n footer) so the grid reads as a grid. The footer's margin-top: auto\n absorbs the slack on lighter cards. */\n min-height: 176px;\n transition: box-shadow 160ms ease,\n transform 160ms cubic-bezier(0.16, 1, 0.3, 1), border-color 160ms ease;\n }\n\n .ResourceCard_card__c6cb3d0bd1:hover {\n border-color: color-mix(\n in srgb,\n var(--card-accent) 40%,\n var(--bui-border-1)\n );\n border-left-color: var(--card-accent);\n /* Two-part shadow: a soft ambient one and a tighter contact one. A single\n large blur reads as a glow rather than as elevation. */\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06),\n 0 12px 28px -12px color-mix(in srgb, var(--card-accent) 40%, transparent);\n transform: translateY(-2px);\n }\n\n .ResourceCard_card__c6cb3d0bd1:focus-visible {\n outline: 2px solid var(--bui-ring, var(--card-accent));\n outline-offset: 2px;\n }\n\n /* The header is a fixed-height zone with its contents centred, rather than\n a top-aligned block with a reserved second title line. Same outcome for\n alignment across the grid — everything below starts at one height — but a\n one-line title now sits centred in that zone instead of leaving an\n orphaned gap beneath it, which is what made short-titled cards read as\n detached from their own content. */\n .ResourceCard_header__c6cb3d0bd1 {\n display: flex;\n align-items: center;\n gap: var(--bui-space-3);\n flex-shrink: 0;\n min-height: 3.5rem;\n }\n\n .ResourceCard_iconBox__c6cb3d0bd1 {\n background: var(--card-accent-bg);\n color: var(--card-accent);\n border-radius: var(--bui-radius-4);\n padding: var(--bui-space-2);\n display: flex;\n flex-shrink: 0;\n }\n\n .ResourceCard_headerText__c6cb3d0bd1 {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n justify-content: center;\n }\n\n .ResourceCard_title__c6cb3d0bd1 {\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n /* Tighter leading than body copy: a two-line title should read as one\n object rather than as two stacked lines. */\n line-height: 1.25;\n }\n\n .ResourceCard_typeLabel__c6cb3d0bd1 {\n display: block;\n color: var(--bui-fg-secondary);\n font-weight: 600;\n font-size: 0.6875rem;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n margin-bottom: 2px;\n }\n\n /* A curator's opinion (#68, ADR-0017), not the type's own colour — amber\n rather than the card's accent so it never reads as part of the type\n identity, and a pill so it sits in the footer's chip row beside the\n counts. */\n .ResourceCard_recommendedBadge__c6cb3d0bd1 {\n display: inline-flex;\n align-items: center;\n gap: var(--bui-space-1);\n flex-shrink: 0;\n background: var(--bui-bg-warning, #fff4e5);\n color: var(--bui-fg-warning, #92400e);\n border-radius: var(--bui-radius-full, 999px);\n padding: 2px var(--bui-space-2);\n font-size: 0.6875rem;\n font-weight: 600;\n line-height: 1.4;\n white-space: nowrap;\n }\n\n .ResourceCard_body__c6cb3d0bd1 {\n display: flex;\n flex-direction: column;\n gap: var(--bui-space-3);\n flex: 1;\n }\n\n .ResourceCard_badges__c6cb3d0bd1 {\n display: flex;\n flex-wrap: wrap;\n gap: var(--bui-space-1);\n }\n\n /* Holds a single tool icon, so the padding is square and the badge reads as\n a circle rather than a pill. */\n .ResourceCard_frameworkBadge__c6cb3d0bd1 {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: var(--bui-bg-neutral-2);\n border-radius: var(--bui-radius-full, 999px);\n padding: var(--bui-space-1);\n font-size: 0.8125rem;\n line-height: 1;\n color: var(--bui-fg-primary);\n }\n\n .ResourceCard_tags__c6cb3d0bd1 {\n display: flex;\n flex-wrap: wrap;\n gap: var(--bui-space-1);\n }\n\n /* Quiet chips rather than bare \"#text\": a chip reads as a discrete value,\n which is what a tag is, and the surface keeps them from competing with the\n description. Not interactive, so they stay low-contrast. */\n .ResourceCard_tag__c6cb3d0bd1 {\n font-size: 0.6875rem;\n line-height: 1.6;\n padding: 0 var(--bui-space-2);\n border-radius: var(--bui-radius-full, 999px);\n background: var(--bui-bg-neutral-2);\n color: var(--bui-fg-secondary);\n white-space: nowrap;\n }\n\n /* No rule above the footer — the gap does the separating, and the meta is\n already de-emphasised enough to read as a different tier. */\n .ResourceCard_footer__c6cb3d0bd1 {\n margin-top: auto;\n padding-top: var(--bui-space-1);\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--bui-space-2);\n font-size: 0.6875rem;\n }\n\n .ResourceCard_countsRow__c6cb3d0bd1 {\n display: flex;\n align-items: center;\n gap: var(--bui-space-2);\n flex-shrink: 0;\n }\n\n /* Chips, not footnotes: views and installs are the signal a browser scans\n for, so they carry a surface and the full foreground colour while the\n version/owner meta stays secondary beside them. */\n .ResourceCard_count__c6cb3d0bd1 {\n display: inline-flex;\n align-items: center;\n gap: var(--bui-space-1);\n padding: 2px var(--bui-space-2);\n border-radius: var(--bui-radius-full, 999px);\n background: var(--bui-bg-neutral-2);\n font-size: 0.75rem;\n font-weight: 600;\n color: var(--bui-fg-primary);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n\n /* The glyph takes the card's accent while the number stays foreground: a\n fully monochrome chip reads as detached on a card that is otherwise\n colour-coded, and colouring the number too would cost its legibility. */\n .ResourceCard_count__c6cb3d0bd1 svg {\n color: var(--card-accent);\n flex-shrink: 0;\n }\n\n /* Past the popularity threshold the install chip takes the type's own\n colour — the one place on the card where the accent marks a value rather\n than an identity. */\n .ResourceCard_countPopular__c6cb3d0bd1 {\n background: var(--card-accent-bg);\n color: var(--card-accent);\n }\n}\n";
|
|
4
|
+
var styles = {"card":"ResourceCard_card__c6cb3d0bd1","header":"ResourceCard_header__c6cb3d0bd1","iconBox":"ResourceCard_iconBox__c6cb3d0bd1","headerText":"ResourceCard_headerText__c6cb3d0bd1","title":"ResourceCard_title__c6cb3d0bd1","typeLabel":"ResourceCard_typeLabel__c6cb3d0bd1","recommendedBadge":"ResourceCard_recommendedBadge__c6cb3d0bd1","body":"ResourceCard_body__c6cb3d0bd1","badges":"ResourceCard_badges__c6cb3d0bd1","frameworkBadge":"ResourceCard_frameworkBadge__c6cb3d0bd1","tags":"ResourceCard_tags__c6cb3d0bd1","tag":"ResourceCard_tag__c6cb3d0bd1","footer":"ResourceCard_footer__c6cb3d0bd1","countsRow":"ResourceCard_countsRow__c6cb3d0bd1","count":"ResourceCard_count__c6cb3d0bd1","countPopular":"ResourceCard_countPopular__c6cb3d0bd1 ResourceCard_count__c6cb3d0bd1"};
|
|
5
5
|
styleInject(css_248z);
|
|
6
6
|
|
|
7
7
|
export { styles as default };
|
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { useApi, configApiRef } from '@backstage/core-plugin-api';
|
|
3
|
-
import defaultRecommendedBadgeUrl from './recommended-badge.svg';
|
|
4
2
|
|
|
5
3
|
function RecommendedBadgeIcon({
|
|
6
4
|
size = 14,
|
|
7
5
|
className
|
|
8
6
|
}) {
|
|
9
|
-
const config = useApi(configApiRef);
|
|
10
|
-
const src = config.getOptionalString("devAiHub.ui.recommendedBadgeIcon") ?? defaultRecommendedBadgeUrl;
|
|
11
7
|
return /* @__PURE__ */ jsx(
|
|
12
|
-
"
|
|
8
|
+
"span",
|
|
13
9
|
{
|
|
14
|
-
src,
|
|
15
|
-
width: size,
|
|
16
|
-
height: size,
|
|
17
10
|
className,
|
|
18
|
-
|
|
19
|
-
"aria-hidden": "true"
|
|
11
|
+
style: { fontSize: size, lineHeight: 1 },
|
|
12
|
+
"aria-hidden": "true",
|
|
13
|
+
children: "\u2B50"
|
|
20
14
|
}
|
|
21
15
|
);
|
|
22
16
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RecommendedBadgeIcon.esm.js","sources":["../../../src/components/RecommendedBadge/RecommendedBadgeIcon.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"RecommendedBadgeIcon.esm.js","sources":["../../../src/components/RecommendedBadge/RecommendedBadgeIcon.tsx"],"sourcesContent":["export interface RecommendedBadgeIconProps {\n size?: number;\n className?: string;\n}\n\n/**\n * The \"Recommended\" mark (#68, ADR-0017): a star emoji.\n *\n * Decorative — the text beside it (\"Recommended\") carries the meaning, so it\n * is hidden from assistive tech. `size` is the font size in pixels.\n */\nexport function RecommendedBadgeIcon({\n size = 14,\n className,\n}: RecommendedBadgeIconProps) {\n return (\n <span\n className={className}\n style={{ fontSize: size, lineHeight: 1 }}\n aria-hidden=\"true\"\n >\n ⭐\n </span>\n );\n}\n"],"names":[],"mappings":";;AAWO,SAAS,oBAAA,CAAqB;AAAA,EACnC,IAAA,GAAO,EAAA;AAAA,EACP;AACF,CAAA,EAA8B;AAC5B,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,SAAA;AAAA,MACA,KAAA,EAAO,EAAE,QAAA,EAAU,IAAA,EAAM,YAAY,CAAA,EAAE;AAAA,MACvC,aAAA,EAAY,MAAA;AAAA,MACb,QAAA,EAAA;AAAA;AAAA,GAED;AAEJ;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nospt/plugin-dev-ai-hub",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Frontend plugin for Dev AI Hub",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "./dist/index.esm.js",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"@backstage/plugin-catalog-react": "^3.2.2",
|
|
67
67
|
"@backstage/plugin-permission-react": "^0.5.4",
|
|
68
68
|
"@backstage/ui": "^0.18.0",
|
|
69
|
-
"@nospt/plugin-dev-ai-hub-common": "^1.2.
|
|
69
|
+
"@nospt/plugin-dev-ai-hub-common": "^1.2.1",
|
|
70
70
|
"@remixicon/react": "^4.9.0",
|
|
71
71
|
"react-markdown": "^9.0.0",
|
|
72
72
|
"react-syntax-highlighter": "^16.1.1",
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" role="img" aria-label="Recommended">
|
|
2
|
-
<!-- Ribbon tails, behind the medal -->
|
|
3
|
-
<path d="M8.6 13.5H11V21L9.8 18.5L8.6 21V13.5Z" fill="#92400E" fill-opacity="0.55" />
|
|
4
|
-
<path d="M13 13.5H15.4V21L14.2 18.5L13 21V13.5Z" fill="#92400E" fill-opacity="0.55" />
|
|
5
|
-
<!-- Medal — a 12-point rosette -->
|
|
6
|
-
<polygon points="12.00,1.80 13.45,3.59 15.60,2.76 15.96,5.04 18.24,5.40 17.41,7.55 19.20,9.00 17.41,10.45 18.24,12.60 15.96,12.96 15.60,15.24 13.45,14.41 12.00,16.20 10.55,14.41 8.40,15.24 8.04,12.96 5.76,12.60 6.59,10.45 4.80,9.00 6.59,7.55 5.76,5.40 8.04,5.04 8.40,2.76 10.55,3.59" fill="#92400E" />
|
|
7
|
-
<!-- Inner star -->
|
|
8
|
-
<polygon points="12.00,5.60 12.82,7.87 15.23,7.95 13.33,9.43 14.00,11.75 12.00,10.40 10.00,11.75 10.67,9.43 8.77,7.95 11.18,7.87" fill="#FFFFFF" />
|
|
9
|
-
</svg>
|