@nuxtseo/design-system 0.0.0-stage → 0.1.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 +101 -2
- package/app/app.config.ts +596 -0
- package/app/component-attributes.d.ts +8 -0
- package/app/components/README.md +38 -0
- package/app/components/brand/UiBrandMark.vue +63 -0
- package/app/components/brand/UiModuleIcon.vue +45 -0
- package/app/components/brand/UiProLettering.vue +26 -0
- package/app/components/brand/UiRangeTerrain.vue +311 -0
- package/app/components/brand/range-glyphs.ts +18 -0
- package/app/components/container/BrowserTab.vue +145 -0
- package/app/components/container/IntegrationRow.vue +120 -0
- package/app/components/container/KnowDoRails.vue +40 -0
- package/app/components/container/RailPanel.vue +93 -0
- package/app/components/container/UiAlert.vue +120 -0
- package/app/components/container/UiAppShell.vue +165 -0
- package/app/components/container/UiCard.vue +204 -0
- package/app/components/container/UiDisclosure.vue +43 -0
- package/app/components/container/UiEmptyState.vue +102 -0
- package/app/components/container/UiErrorState.vue +61 -0
- package/app/components/container/UiLoadingState.vue +20 -0
- package/app/components/container/UiPageHeader.vue +95 -0
- package/app/components/container/UiPopover.vue +102 -0
- package/app/components/container/UiSampleDataOverlay.vue +47 -0
- package/app/components/container/UiSectionHeader.vue +83 -0
- package/app/components/container/UiSummaryStrip.vue +40 -0
- package/app/components/container/UiToast.vue +230 -0
- package/app/components/container/UiToaster.vue +271 -0
- package/app/components/container/UiWidgetState.vue +143 -0
- package/app/components/container/ui-nav-drawer.ts +48 -0
- package/app/components/data/ActionRail.vue +152 -0
- package/app/components/data/FacetStrip.vue +93 -0
- package/app/components/data/UiBarCell.vue +37 -0
- package/app/components/data/UiBarChart.vue +281 -0
- package/app/components/data/UiChartAnnotationControls.vue +74 -0
- package/app/components/data/UiChartAnnotations.vue +163 -0
- package/app/components/data/UiChartBrushOverlay.vue +96 -0
- package/app/components/data/UiChartDataReveal.vue +81 -0
- package/app/components/data/UiChartFrame.vue +123 -0
- package/app/components/data/UiChartTooltip.vue +81 -0
- package/app/components/data/UiCoverageNote.vue +90 -0
- package/app/components/data/UiCwvSparkline.vue +188 -0
- package/app/components/data/UiDataList.vue +276 -0
- package/app/components/data/UiDataTableSection.vue +232 -0
- package/app/components/data/UiDistributionBars.vue +164 -0
- package/app/components/data/UiDonutChart.vue +132 -0
- package/app/components/data/UiDoorwayColumn.vue +108 -0
- package/app/components/data/UiDoorwayGrid.vue +58 -0
- package/app/components/data/UiDoorwayRow.vue +83 -0
- package/app/components/data/UiEntitySummary.vue +261 -0
- package/app/components/data/UiFactsGrid.vue +107 -0
- package/app/components/data/UiImpactMeter.vue +33 -0
- package/app/components/data/UiIssueRow.vue +149 -0
- package/app/components/data/UiLineChart.vue +431 -0
- package/app/components/data/UiMetricBarPopover.vue +121 -0
- package/app/components/data/UiMetricLabel.vue +30 -0
- package/app/components/data/UiMetricStat.vue +73 -0
- package/app/components/data/UiMetricToggle.vue +156 -0
- package/app/components/data/UiMetricsRow.vue +64 -0
- package/app/components/data/UiPositionMetric.vue +53 -0
- package/app/components/data/UiRelatedFixes.vue +113 -0
- package/app/components/data/UiRichText.vue +325 -0
- package/app/components/data/UiScatterPlot.vue +144 -0
- package/app/components/data/UiSeverityMarker.vue +30 -0
- package/app/components/data/UiSparkColumns.vue +160 -0
- package/app/components/data/UiSparkline.vue +681 -0
- package/app/components/data/UiStat.vue +445 -0
- package/app/components/data/UiStats.vue +76 -0
- package/app/components/data/UiTable.vue +517 -0
- package/app/components/data/UiTableExport.vue +40 -0
- package/app/components/data/UiTableFrame.vue +149 -0
- package/app/components/data/UiTablePagination.vue +37 -0
- package/app/components/data/UiTableShell.vue +48 -0
- package/app/components/data/UiTableTd.vue +39 -0
- package/app/components/data/UiTableTh.vue +61 -0
- package/app/components/data/UiTopEntityStackChart.vue +393 -0
- package/app/components/data/cells/UiTableDash.vue +3 -0
- package/app/components/data/cells/UiTableHeaderCell.vue +72 -0
- package/app/components/data/cells/UiTableMetricCell.vue +72 -0
- package/app/components/data/cells/UiTablePathCell.vue +76 -0
- package/app/components/data/cells/UiTableScoreTile.vue +49 -0
- package/app/components/data/cells/UiTableTrendCell.vue +41 -0
- package/app/components/element/UiAtmosphere.vue +64 -0
- package/app/components/element/UiAuthHeading.vue +24 -0
- package/app/components/element/UiAuthProviders.vue +46 -0
- package/app/components/element/UiBrandIcon.vue +39 -0
- package/app/components/element/UiButton.vue +549 -0
- package/app/components/element/UiCelebration.vue +65 -0
- package/app/components/element/UiChannelChips.vue +62 -0
- package/app/components/element/UiChatQuickLink.vue +319 -0
- package/app/components/element/UiChip.vue +101 -0
- package/app/components/element/UiConfirmDialog.vue +84 -0
- package/app/components/element/UiCopyButton.vue +90 -0
- package/app/components/element/UiDitherAvatar.vue +177 -0
- package/app/components/element/UiDotLabel.vue +19 -0
- package/app/components/element/UiEjectMenu.vue +181 -0
- package/app/components/element/UiEjectOverlay.vue +29 -0
- package/app/components/element/UiFavicon.vue +235 -0
- package/app/components/element/UiFaviconStack.vue +84 -0
- package/app/components/element/UiFilterMenu.vue +324 -0
- package/app/components/element/UiFocusedSurface.vue +129 -0
- package/app/components/element/UiHealthDot.vue +32 -0
- package/app/components/element/UiHelpLabel.vue +28 -0
- package/app/components/element/UiIcon.vue +22 -0
- package/app/components/element/UiInput.vue +35 -0
- package/app/components/element/UiLogoPlausible.vue +12 -0
- package/app/components/element/UiLogoPro.vue +25 -0
- package/app/components/element/UiLogoUmami.vue +12 -0
- package/app/components/element/UiNavIcon.vue +107 -0
- package/app/components/element/UiNavList.vue +156 -0
- package/app/components/element/UiOnboardingAnswers.vue +125 -0
- package/app/components/element/UiOverflowCount.vue +31 -0
- package/app/components/element/UiProBadge.vue +19 -0
- package/app/components/element/UiProgressCircle.vue +79 -0
- package/app/components/element/UiProgressPercent.vue +35 -0
- package/app/components/element/UiRelativeTime.vue +58 -0
- package/app/components/element/UiScrollFade.vue +16 -0
- package/app/components/element/UiSelect.vue +55 -0
- package/app/components/element/UiSeverityDot.vue +27 -0
- package/app/components/element/UiSiteFavicon.vue +24 -0
- package/app/components/element/UiSkeleton.vue +148 -0
- package/app/components/element/UiSlotText.vue +65 -0
- package/app/components/element/UiSourceLogos.vue +113 -0
- package/app/components/element/UiSourceRail.vue +71 -0
- package/app/components/element/UiStaticTerminal.vue +107 -0
- package/app/components/element/UiStatusBadge.vue +62 -0
- package/app/components/element/UiSyncDot.vue +46 -0
- package/app/components/element/UiTabs.vue +245 -0
- package/app/components/element/UiTogglePill.vue +86 -0
- package/app/components/element/UiTooltip.vue +390 -0
- package/app/components/element/UiTooltipGrid.vue +457 -0
- package/app/components/element/UiTrend.vue +184 -0
- package/app/components/element/UiTrialBenefits.vue +71 -0
- package/app/components/element/UiUrlLabel.vue +75 -0
- package/app/components/element/UiValenceDot.vue +37 -0
- package/app/components/element/UiWizardModal.vue +284 -0
- package/app/components/element/UiWizardNav.vue +59 -0
- package/app/components/element/UiWizardProgress.vue +78 -0
- package/app/components/element/UiWizardShell.vue +43 -0
- package/app/components/element/eject-menu.ts +359 -0
- package/app/components/element/eject-tracker.ts +34 -0
- package/app/components/element/onboarding-answers.ts +68 -0
- package/app/components/element/onboarding-intents.ts +19 -0
- package/app/composables/dataVizColors.ts +350 -0
- package/app/composables/formatting.ts +252 -0
- package/app/composables/semanticColors.ts +80 -0
- package/app/composables/useActivityOverlay.ts +55 -0
- package/app/composables/useAppShellNav.ts +21 -0
- package/app/composables/useChartBrush.ts +221 -0
- package/app/composables/useChartHover.ts +153 -0
- package/app/composables/useChartTickPlan.ts +186 -0
- package/app/composables/useCloseOnNavigation.ts +7 -0
- package/app/composables/useConfirm.ts +38 -0
- package/app/composables/useEdgeFade.ts +37 -0
- package/app/composables/useEject.ts +69 -0
- package/app/composables/useEntranceMotion.ts +22 -0
- package/app/composables/useFaviconBacking.ts +164 -0
- package/app/composables/useGridCols.ts +39 -0
- package/app/composables/useIssueOverlay.ts +43 -0
- package/app/composables/useOnboardingService.ts +106 -0
- package/app/composables/useOpenedOnce.ts +19 -0
- package/app/composables/useScrollSpy.ts +178 -0
- package/app/composables/useSquircleFallback.ts +21 -0
- package/app/composables/useWizardRoute.ts +123 -0
- package/app/plugins/favicon.ts +29 -0
- package/app/plugins/lazy-motion.ts +22 -0
- package/app/plugins/selection-highlight.client.ts +176 -0
- package/app/shared/cli.ts +10 -0
- package/app/shared/error-state.ts +150 -0
- package/app/shared/icons.ts +1 -0
- package/app/shared/motion.ts +1 -0
- package/app/shared/nav.ts +38 -0
- package/app/shared/navigation.ts +5 -0
- package/app/shared/static-terminal.ts +14 -0
- package/app/shared/table-export.ts +36 -0
- package/app/shared/table.ts +105 -0
- package/app/shared/ui-icons.ts +1 -0
- package/app/shared/view-state.ts +85 -0
- package/app/shared/widget-failure.ts +126 -0
- package/app/utils/chartAnnotations.ts +326 -0
- package/app/utils/countries.ts +337 -0
- package/app/utils/export-download.ts +100 -0
- package/app/utils/favicon-fallback.ts +12 -0
- package/app/utils/icon-color.ts +105 -0
- package/app/utils/number.ts +74 -0
- package/app/utils/render-link.ts +14 -0
- package/app/utils/slot-text.ts +18 -0
- package/app/utils/squircle.ts +154 -0
- package/app/utils/topEntityStack.ts +197 -0
- package/app/utils/vue.ts +57 -0
- package/build/design-system.ts +47 -0
- package/build/icon-server-bundle.ts +276 -0
- package/build/runtime/icon-server-bundle.ts +29 -0
- package/css/global.css +1892 -0
- package/nuxt.config.ts +132 -0
- package/package.json +86 -3
- package/public/brand-icon-dark.png +0 -0
- package/public/brand-icon-light.png +0 -0
- package/public/favicon-dark.png +0 -0
- package/public/favicon-light.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/fonts/README.md +31 -0
- package/public/fonts/fira-code-OFL.txt +93 -0
- package/public/fonts/fira-code-var-latin.woff2 +0 -0
- package/public/fonts/hubot-sans-OFL.txt +93 -0
- package/public/fonts/hubot-sans-var-latin.woff2 +0 -0
- package/public/icon-dark.png +0 -0
- package/public/icon.png +0 -0
- package/public/touch-icon-dark.png +0 -0
- package/public/touch-icon.png +0 -0
- package/server/api/favicon.get.ts +122 -0
- package/server/utils/favicon-source.ts +51 -0
- package/shared/contracts/feedback.ts +42 -0
- package/shared/contracts/index.ts +1 -0
- package/shared/delivery-channels.ts +53 -0
- package/shared/icons/index.ts +143 -0
- package/shared/icons/registry.ts +347 -0
- package/shared/icons/ui-slots.ts +58 -0
- package/shared/markdown.ts +44 -0
- package/shared/motion.ts +120 -0
- package/shared/rangi.ts +60 -0
- package/shared/rich-text.ts +34 -0
- package/shared/ui-icons.ts +6 -0
- package/shared/utils/urls.ts +64 -0
- package/types/index.ts +1 -0
- package/types/ui.ts +339 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Both feedback endpoints are built into the PRO Worker
|
|
5
|
+
* (`layers/saas/server/api/feedback{,-thumbs}.post.ts`), because the row lands
|
|
6
|
+
* in the pro D1 `feedback` table, which is also the triage ledger. The site
|
|
7
|
+
* Worker has no handler and no binding to that database.
|
|
8
|
+
*
|
|
9
|
+
* The site's docs rail and free tools still POST these paths from the browser.
|
|
10
|
+
* That works in production only because `proRoutes()` in
|
|
11
|
+
* `deploy/cloudflare-env.ts` binds `nuxtseo.com/api/feedback*` to the pro
|
|
12
|
+
* Worker, so Cloudflare hands the request over at the edge.
|
|
13
|
+
*
|
|
14
|
+
* The paths are constants so that coupling is checkable: the site client, the
|
|
15
|
+
* dev proxy in `apps/site/nuxt.config.ts`, and the route-coverage guard in
|
|
16
|
+
* `tests/pro-worker-route-coverage.feature.test.ts` all read these. If you
|
|
17
|
+
* rename an endpoint, the guard fails instead of production 404ing silently.
|
|
18
|
+
*/
|
|
19
|
+
export const FEEDBACK_THUMBS_PATH = '/api/feedback-thumbs'
|
|
20
|
+
export const FEEDBACK_COMMENT_PATH = '/api/feedback'
|
|
21
|
+
|
|
22
|
+
export interface ThumbsFeedbackResponse {
|
|
23
|
+
thumbs: 'up' | 'down'
|
|
24
|
+
stats: {
|
|
25
|
+
up: number
|
|
26
|
+
down: number
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const ThumbsFeedbackSchema = z.object({
|
|
31
|
+
thumbs: z.enum(['up', 'down']),
|
|
32
|
+
toolId: z.string().optional(),
|
|
33
|
+
context: z.record(z.string(), z.unknown()).optional(),
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
export const CommentFeedbackSchema = z.object({
|
|
37
|
+
comment: z.string().min(3, 'Must be at least 3 characters'),
|
|
38
|
+
toolId: z.string().optional(),
|
|
39
|
+
context: z.record(z.string(), z.unknown()).optional(),
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
export type CommentFeedbackSchemaOutput = z.output<typeof CommentFeedbackSchema>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './feedback'
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The delivery-channel vocabulary: one place that names each channel and picks
|
|
3
|
+
* its glyph. Reports, alerts and notification settings all render the same four
|
|
4
|
+
* channels, and before this each surface re-derived its own label/icon pair, so
|
|
5
|
+
* "Email + Discord" strings and icon choices drifted per screen.
|
|
6
|
+
*
|
|
7
|
+
* Icons are semantic roles from the icon registry and stay MONOCHROME — the
|
|
8
|
+
* channels are told apart by glyph shape, never by colour (DESIGN.md).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const DELIVERY_CHANNEL_KEYS = ['email', 'discord', 'slack', 'webhook'] as const
|
|
12
|
+
|
|
13
|
+
export type DeliveryChannelKey = typeof DELIVERY_CHANNEL_KEYS[number]
|
|
14
|
+
|
|
15
|
+
export interface DeliveryChannelMeta {
|
|
16
|
+
key: DeliveryChannelKey
|
|
17
|
+
label: string
|
|
18
|
+
/** Semantic icon role (see `shared/icons/registry.ts`). */
|
|
19
|
+
icon: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const DELIVERY_CHANNELS: Record<DeliveryChannelKey, DeliveryChannelMeta> = {
|
|
23
|
+
email: { key: 'email', label: 'Email', icon: 'mail' },
|
|
24
|
+
discord: { key: 'discord', label: 'Discord', icon: 'discord' },
|
|
25
|
+
slack: { key: 'slack', label: 'Slack', icon: 'slack' },
|
|
26
|
+
webhook: { key: 'webhook', label: 'Webhook', icon: 'webhook' },
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function deliveryChannelMeta(key: string): DeliveryChannelMeta | null {
|
|
30
|
+
return DELIVERY_CHANNELS[key as DeliveryChannelKey] ?? null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function deliveryChannelLabel(key: string): string {
|
|
34
|
+
return deliveryChannelMeta(key)?.label ?? key
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Enabled channels in canonical order, from any record shaped like the stored
|
|
39
|
+
* `channels` column (`{ email: true, discord: false, … }`).
|
|
40
|
+
*/
|
|
41
|
+
export function enabledDeliveryChannels(
|
|
42
|
+
channels: Partial<Record<DeliveryChannelKey, unknown>> | null | undefined,
|
|
43
|
+
): DeliveryChannelKey[] {
|
|
44
|
+
if (!channels)
|
|
45
|
+
return []
|
|
46
|
+
return DELIVERY_CHANNEL_KEYS.filter(key => !!channels[key])
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** "Email & Discord" / "No delivery channel" — the prose form of the same set. */
|
|
50
|
+
export function deliveryChannelsSummary(keys: readonly string[], empty = 'No delivery channel'): string {
|
|
51
|
+
const labels = keys.map(deliveryChannelLabel)
|
|
52
|
+
return labels.length ? labels.join(' & ') : empty
|
|
53
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public icon API for the design system.
|
|
3
|
+
*
|
|
4
|
+
* - `DEFAULT_ICON_SET` — the set an app renders unless its Nuxt config picks
|
|
5
|
+
* another with `designSystem.iconSet`. The choice is read once at build time
|
|
6
|
+
* (`build/design-system.ts`), so UiIcon, UiButton, and Nuxt UI internals all
|
|
7
|
+
* follow it. Preview sets first in the brand-kit `/icons` playground.
|
|
8
|
+
* - `resolveUiIcon(role, set)` — role → iconify id for `set`, with fallback. Runtime
|
|
9
|
+
* code renders roles through `UiIcon` instead, which follows the app's choice.
|
|
10
|
+
* - Raw `i-*` ids pass straight through, so adoption can be incremental.
|
|
11
|
+
*
|
|
12
|
+
* Source scanning is disabled in the layer config. `iconBundleList()` is the
|
|
13
|
+
* explicit active-set client bundle, so candidate-set literals stay build-only.
|
|
14
|
+
*/
|
|
15
|
+
import type { IconRole, IconSet, RoleMap } from './registry'
|
|
16
|
+
import { ICON_ALIASES, ICON_ROLES, ICON_SETS } from './registry'
|
|
17
|
+
|
|
18
|
+
export type { IconRole, IconSet, RoleMap } from './registry'
|
|
19
|
+
export { ICON_ALIASES, ICON_ROLES, ICON_SETS } from './registry'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The icon set an app renders when its Nuxt config sets no `designSystem.iconSet`.
|
|
23
|
+
* Nuxt SEO renders hugeicons. Compare sets first in brand-kit `/icons`.
|
|
24
|
+
*/
|
|
25
|
+
export const DEFAULT_ICON_SET: IconSet = 'hugeicons'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Parse an untrusted icon set name, such as a Nuxt config value, into an
|
|
29
|
+
* `IconSet`. `undefined` selects `DEFAULT_ICON_SET`.
|
|
30
|
+
*/
|
|
31
|
+
export function parseIconSet(value: unknown): IconSet {
|
|
32
|
+
if (value === undefined)
|
|
33
|
+
return DEFAULT_ICON_SET
|
|
34
|
+
if (typeof value === 'string' && (ICON_SETS as string[]).includes(value))
|
|
35
|
+
return value as IconSet
|
|
36
|
+
throw new TypeError(`designSystem.iconSet must be one of ${ICON_SETS.join(', ')}. Received ${JSON.stringify(value)}.`)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** A curated semantic role or registered synonym. Prefer this for static registries. */
|
|
40
|
+
export type IconAlias = keyof typeof ICON_ALIASES
|
|
41
|
+
export type SemanticUiIcon = IconRole | IconAlias
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Component-facing icon input. Runtime provider/flag data can be dynamic, while
|
|
45
|
+
* literal role names still retain autocomplete through `SemanticUiIcon`.
|
|
46
|
+
*/
|
|
47
|
+
export type RawUiIcon = `i-${string}`
|
|
48
|
+
export type UiIcon = SemanticUiIcon | RawUiIcon | (string & {})
|
|
49
|
+
|
|
50
|
+
function idFor(role: RoleMap, set: IconSet): string | undefined {
|
|
51
|
+
if (role.fixed)
|
|
52
|
+
return role.fixed
|
|
53
|
+
// active set first, then fall back through the remaining sets in priority order
|
|
54
|
+
const order = [set, ...ICON_SETS.filter(s => s !== set)]
|
|
55
|
+
for (const s of order) {
|
|
56
|
+
const bare = role[s]
|
|
57
|
+
if (bare)
|
|
58
|
+
return `i-${s}-${bare}`
|
|
59
|
+
}
|
|
60
|
+
return undefined
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Resolve a semantic role to its iconify id for `set`; pass raw `i-*` strings through. */
|
|
64
|
+
export function resolveUiIcon(icon: UiIcon | undefined, set: IconSet = DEFAULT_ICON_SET): string | undefined {
|
|
65
|
+
if (!icon)
|
|
66
|
+
return undefined
|
|
67
|
+
if (icon.startsWith('i-'))
|
|
68
|
+
return icon
|
|
69
|
+
// resolve aliases (synonyms) to their canonical role first
|
|
70
|
+
const name = (ICON_ALIASES as Record<string, IconRole>)[icon] ?? icon
|
|
71
|
+
const role = (ICON_ROLES as Record<string, RoleMap>)[name]
|
|
72
|
+
if (!role)
|
|
73
|
+
return icon // unknown name: pass through unchanged
|
|
74
|
+
return idFor(role, set)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Map of every role → resolved id for `set`. Used to generate app.config ui.icons. */
|
|
78
|
+
export function iconMapFor(set: IconSet = DEFAULT_ICON_SET): Record<IconRole, string> {
|
|
79
|
+
const out = {} as Record<IconRole, string>
|
|
80
|
+
for (const role of Object.keys(ICON_ROLES) as IconRole[])
|
|
81
|
+
out[role] = idFor(ICON_ROLES[role] as RoleMap, set)!
|
|
82
|
+
return out
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Collections that contain a hyphen, so `i-<collection>-<name>` can't be split naively.
|
|
86
|
+
const HYPHEN_COLLECTIONS = ['simple-icons', 'svg-spinners', 'circle-flags', 'vscode-icons']
|
|
87
|
+
|
|
88
|
+
// Brand marks used by UiSourceLogos. Keep these local in every app that extends
|
|
89
|
+
// the design system, so Source identity never depends on Iconify at runtime.
|
|
90
|
+
const SOURCE_LOGO_ICONS = [
|
|
91
|
+
'logos:google-search-console',
|
|
92
|
+
'logos:bing',
|
|
93
|
+
'logos:nuxt-icon',
|
|
94
|
+
'logos:chrome',
|
|
95
|
+
'logos:lighthouse',
|
|
96
|
+
'logos:cloudflare-icon',
|
|
97
|
+
'logos:vercel-icon',
|
|
98
|
+
'logos:netlify-icon',
|
|
99
|
+
'logos:google-analytics',
|
|
100
|
+
'logos:posthog-icon',
|
|
101
|
+
'logos:reddit-icon',
|
|
102
|
+
'logos:x',
|
|
103
|
+
'logos:ycombinator',
|
|
104
|
+
'logos:bluesky',
|
|
105
|
+
'logos:youtube-icon',
|
|
106
|
+
'logos:github-icon',
|
|
107
|
+
'simple-icons:plausibleanalytics',
|
|
108
|
+
'simple-icons:umami',
|
|
109
|
+
] as const
|
|
110
|
+
|
|
111
|
+
/** `i-lucide-arrow-right` → `lucide:arrow-right` (handles multi-hyphen collections). */
|
|
112
|
+
function toPrefixName(id: string): string {
|
|
113
|
+
const body = id.replace(/^i-/, '')
|
|
114
|
+
const coll = HYPHEN_COLLECTIONS.find(c => body.startsWith(`${c}-`))
|
|
115
|
+
return coll ? `${coll}:${body.slice(coll.length + 1)}` : body.replace('-', ':')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The exact `prefix:name` ids for every role under `set` — feed to @nuxt/icon's
|
|
120
|
+
* `clientBundle.icons` so the build ships only the active set's resolved icons
|
|
121
|
+
* (plus the fixed brand/loading marks), never all four candidate sets.
|
|
122
|
+
*/
|
|
123
|
+
export function iconBundleList(set: IconSet = DEFAULT_ICON_SET): string[] {
|
|
124
|
+
return [...new Set([
|
|
125
|
+
...Object.values(iconMapFor(set)).map(toPrefixName),
|
|
126
|
+
...SOURCE_LOGO_ICONS,
|
|
127
|
+
])]
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Role + synonym-alias name → `prefix:name` id, for @nuxt/icon's `aliases` config.
|
|
132
|
+
* MUST be `prefix:name` (not `i-...`): @nuxt/icon uses an alias's value as the resolved
|
|
133
|
+
* name verbatim and re-prefixes `i-`, so an `i-` value becomes a broken `i-i-...`.
|
|
134
|
+
*/
|
|
135
|
+
export function iconAliasMap(set: IconSet = DEFAULT_ICON_SET): Record<string, string> {
|
|
136
|
+
const resolved = iconMapFor(set)
|
|
137
|
+
const out: Record<string, string> = {}
|
|
138
|
+
for (const [role, id] of Object.entries(resolved))
|
|
139
|
+
out[role] = toPrefixName(id)
|
|
140
|
+
for (const [alias, role] of Object.entries(ICON_ALIASES))
|
|
141
|
+
out[alias] = toPrefixName(resolved[role as IconRole])
|
|
142
|
+
return out
|
|
143
|
+
}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Central, set-swappable icon registry.
|
|
3
|
+
*
|
|
4
|
+
* Every icon in the product is referenced by a *semantic role* (`next`, `delete`,
|
|
5
|
+
* `trending-up`) — never a raw iconify id. Each role maps to a real icon in every
|
|
6
|
+
* candidate set, so the entire app can be flipped between sets by changing
|
|
7
|
+
* `designSystem.iconSet` in the Nuxt config. Compare sets visually first in the
|
|
8
|
+
* brand-kit `/icons` playground.
|
|
9
|
+
*
|
|
10
|
+
* Values are the *bare* icon name (no `i-<set>-` prefix); the resolver adds the
|
|
11
|
+
* prefix for the active set. A role may instead pin a `fixed` full id (used for
|
|
12
|
+
* brand marks that only live in one set, e.g. `i-simple-icons-github`).
|
|
13
|
+
*
|
|
14
|
+
* Naming conventions per set: lucide/carbon use bare names; solar uses the
|
|
15
|
+
* `-linear` stroke style; hugeicons uses the `-01` variant.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export type IconSet = 'lucide' | 'carbon' | 'solar' | 'hugeicons'
|
|
19
|
+
|
|
20
|
+
/** The 4 candidate sets, in fallback priority order when a role lacks the active set. */
|
|
21
|
+
export const ICON_SETS: IconSet[] = ['lucide', 'carbon', 'solar', 'hugeicons']
|
|
22
|
+
|
|
23
|
+
export type RoleMap = Partial<Record<IconSet, string>> & { fixed?: string }
|
|
24
|
+
|
|
25
|
+
export const ICON_ROLES = {
|
|
26
|
+
// ── navigation ─────────────────────────────────────────────
|
|
27
|
+
'next': { lucide: 'arrow-right', carbon: 'arrow-right', solar: 'arrow-right-linear', hugeicons: 'arrow-right-01' },
|
|
28
|
+
'back': { lucide: 'arrow-left', carbon: 'arrow-left', solar: 'arrow-left-linear', hugeicons: 'arrow-left-01' },
|
|
29
|
+
'up': { lucide: 'arrow-up', carbon: 'arrow-up', solar: 'arrow-up-linear', hugeicons: 'arrow-up-01' },
|
|
30
|
+
'down': { lucide: 'arrow-down', carbon: 'arrow-down', solar: 'arrow-down-linear', hugeicons: 'arrow-down-01' },
|
|
31
|
+
'external': { lucide: 'external-link', carbon: 'launch', solar: 'arrow-right-up-linear', hugeicons: 'link-square-02' },
|
|
32
|
+
'expand': { lucide: 'chevron-down', carbon: 'chevron-down', solar: 'alt-arrow-down-linear', hugeicons: 'arrow-down-01' },
|
|
33
|
+
'collapse': { lucide: 'chevron-up', carbon: 'chevron-up', solar: 'alt-arrow-up-linear', hugeicons: 'arrow-up-01' },
|
|
34
|
+
'chevron-down': { lucide: 'chevron-down', carbon: 'chevron-down', solar: 'alt-arrow-down-linear', hugeicons: 'arrow-down-01' },
|
|
35
|
+
'chevron-up': { lucide: 'chevron-up', carbon: 'chevron-up', solar: 'alt-arrow-up-linear', hugeicons: 'arrow-up-01' },
|
|
36
|
+
'chevron-right': { lucide: 'chevron-right', carbon: 'chevron-right', solar: 'alt-arrow-right-linear', hugeicons: 'arrow-right-01' },
|
|
37
|
+
'chevron-left': { lucide: 'chevron-left', carbon: 'chevron-left', solar: 'alt-arrow-left-linear', hugeicons: 'arrow-left-01' },
|
|
38
|
+
'chevrons-up': { lucide: 'chevrons-up', carbon: 'chevron-up', solar: 'double-alt-arrow-up-linear', hugeicons: 'arrow-up-double' },
|
|
39
|
+
'page-first': { lucide: 'chevrons-left', carbon: 'page-first', solar: 'double-alt-arrow-left-linear', hugeicons: 'arrow-left-double' },
|
|
40
|
+
'page-last': { lucide: 'chevrons-right', carbon: 'page-last', solar: 'double-alt-arrow-right-linear', hugeicons: 'arrow-right-double' },
|
|
41
|
+
'menu': { lucide: 'menu', carbon: 'menu', solar: 'hamburger-menu-linear', hugeicons: 'menu-01' },
|
|
42
|
+
'hash': { lucide: 'hash', carbon: 'hashtag', solar: 'hashtag-linear', hugeicons: 'hashtag' },
|
|
43
|
+
'home': { lucide: 'house', carbon: 'home', solar: 'home-linear', hugeicons: 'home-01' },
|
|
44
|
+
'corner-down': { lucide: 'corner-down-right', carbon: 'arrow-down-right', solar: 'arrow-right-down-linear', hugeicons: 'arrow-turn-down' },
|
|
45
|
+
|
|
46
|
+
// ── crud / actions ─────────────────────────────────────────
|
|
47
|
+
'add': { lucide: 'plus', carbon: 'add', solar: 'add-circle-linear', hugeicons: 'add-01' },
|
|
48
|
+
'close': { lucide: 'x', carbon: 'close', solar: 'close-circle-linear', hugeicons: 'cancel-01' },
|
|
49
|
+
'delete': { lucide: 'trash-2', carbon: 'trash-can', solar: 'trash-bin-trash-linear', hugeicons: 'delete-02' },
|
|
50
|
+
'edit': { lucide: 'pencil', carbon: 'edit', solar: 'pen-linear', hugeicons: 'pencil-edit-02' },
|
|
51
|
+
'copy': { lucide: 'copy', carbon: 'copy', solar: 'copy-linear', hugeicons: 'copy-01' },
|
|
52
|
+
'check': { lucide: 'check', carbon: 'checkmark', solar: 'check-read-linear', hugeicons: 'tick-02' },
|
|
53
|
+
'refresh': { lucide: 'rotate-cw', carbon: 'renew', solar: 'refresh-linear', hugeicons: 'reload' },
|
|
54
|
+
'search': { lucide: 'search', carbon: 'search', solar: 'magnifer-linear', hugeicons: 'search-01' },
|
|
55
|
+
'download': { lucide: 'download', carbon: 'download', solar: 'download-linear', hugeicons: 'download-01' },
|
|
56
|
+
'upload': { lucide: 'upload', carbon: 'upload', solar: 'upload-linear', hugeicons: 'upload-01' },
|
|
57
|
+
'paperclip': { lucide: 'paperclip', carbon: 'attachment', solar: 'paperclip-linear', hugeicons: 'attachment' },
|
|
58
|
+
'send': { lucide: 'send', carbon: 'send', solar: 'plain-linear', hugeicons: 'sent-02' },
|
|
59
|
+
'save': { lucide: 'save', carbon: 'save', solar: 'diskette-linear', hugeicons: 'floppy-disk' },
|
|
60
|
+
'filter': { lucide: 'list-filter', carbon: 'filter', solar: 'filter-linear', hugeicons: 'filter' },
|
|
61
|
+
'filter-remove': { lucide: 'filter-x', carbon: 'filter-remove', solar: 'filter-broken', hugeicons: 'filter-remove' },
|
|
62
|
+
'settings': { lucide: 'settings', carbon: 'settings', solar: 'settings-linear', hugeicons: 'settings-01' },
|
|
63
|
+
'sliders': { lucide: 'sliders-horizontal', carbon: 'settings-adjust', solar: 'tuning-2-linear', hugeicons: 'tools' },
|
|
64
|
+
'more': { lucide: 'ellipsis-vertical', carbon: 'overflow-menu-vertical', solar: 'menu-dots-linear', hugeicons: 'more-vertical' },
|
|
65
|
+
'more-horizontal': { lucide: 'ellipsis', carbon: 'overflow-menu-horizontal', solar: 'menu-dots-bold', hugeicons: 'more-horizontal' },
|
|
66
|
+
'minus': { lucide: 'minus', carbon: 'subtract', solar: 'minus-circle-linear', hugeicons: 'remove-01' },
|
|
67
|
+
'share': { lucide: 'share-2', carbon: 'share', solar: 'share-linear', hugeicons: 'share-08' },
|
|
68
|
+
'undo': { lucide: 'undo-2', carbon: 'undo', solar: 'undo-left-linear', hugeicons: 'arrow-turn-backward' },
|
|
69
|
+
'move': { lucide: 'move', carbon: 'move', hugeicons: 'move' },
|
|
70
|
+
'grip': { lucide: 'grip-vertical', carbon: 'draggable', solar: 'menu-dots-linear', hugeicons: 'drag-drop-vertical' },
|
|
71
|
+
'stop': { lucide: 'square', carbon: 'stop', solar: 'stop-linear', hugeicons: 'stop' },
|
|
72
|
+
'play': { lucide: 'play', carbon: 'play', solar: 'play-linear', hugeicons: 'play' },
|
|
73
|
+
'pause': { lucide: 'pause', carbon: 'pause', solar: 'pause-linear', hugeicons: 'pause' },
|
|
74
|
+
'scan': { lucide: 'scan', carbon: 'scan', solar: 'scanner-linear', hugeicons: 'scan-image' },
|
|
75
|
+
'gamepad': { lucide: 'gamepad-2', carbon: 'game-console', solar: 'gamepad-linear', hugeicons: 'game-controller-03' },
|
|
76
|
+
|
|
77
|
+
// ── status / meta ──────────────────────────────────────────
|
|
78
|
+
'info': { lucide: 'info', carbon: 'information', solar: 'info-circle-linear', hugeicons: 'information-circle' },
|
|
79
|
+
'note': { lucide: 'info', carbon: 'information', solar: 'info-square-linear', hugeicons: 'information-square' },
|
|
80
|
+
'tip': { lucide: 'lightbulb', carbon: 'idea', solar: 'lightbulb-linear', hugeicons: 'idea-01' },
|
|
81
|
+
'warning': { lucide: 'triangle-alert', carbon: 'warning', solar: 'danger-triangle-linear', hugeicons: 'alert-02' },
|
|
82
|
+
'caution': { lucide: 'circle-alert', carbon: 'warning-alt', solar: 'danger-circle-linear', hugeicons: 'alert-circle' },
|
|
83
|
+
'success': { lucide: 'circle-check', carbon: 'checkmark-filled', solar: 'check-circle-linear', hugeicons: 'checkmark-circle-02' },
|
|
84
|
+
'error': { lucide: 'circle-x', carbon: 'close-filled', solar: 'close-circle-linear', hugeicons: 'cancel-circle' },
|
|
85
|
+
'help': { lucide: 'circle-help', carbon: 'help', solar: 'question-circle-linear', hugeicons: 'help-circle' },
|
|
86
|
+
'lock': { lucide: 'lock', carbon: 'locked', solar: 'lock-keyhole-linear', hugeicons: 'square-lock-02' },
|
|
87
|
+
'view': { lucide: 'eye', carbon: 'view', solar: 'eye-linear', hugeicons: 'view' },
|
|
88
|
+
'hide': { lucide: 'eye-off', carbon: 'view-off', solar: 'eye-closed-linear', hugeicons: 'view-off' },
|
|
89
|
+
'ai': { lucide: 'sparkles', carbon: 'magic-wand', solar: 'magic-stick-3-linear', hugeicons: 'ai-magic' },
|
|
90
|
+
'bot': { lucide: 'bot', carbon: 'bot', solar: 'chat-round-line-linear', hugeicons: 'ai-chat-02' },
|
|
91
|
+
'star': { lucide: 'star', carbon: 'star', solar: 'star-linear', hugeicons: 'star' },
|
|
92
|
+
'heart': { lucide: 'heart', carbon: 'favorite', solar: 'heart-linear', hugeicons: 'favourite' },
|
|
93
|
+
'bell': { lucide: 'bell', carbon: 'notification', solar: 'bell-linear', hugeicons: 'notification-01' },
|
|
94
|
+
'shield': { lucide: 'shield', carbon: 'security', solar: 'shield-linear', hugeicons: 'shield-01' },
|
|
95
|
+
'shield-check': { lucide: 'shield-check', carbon: 'security', solar: 'shield-check-linear', hugeicons: 'shield-01' },
|
|
96
|
+
'zap': { lucide: 'zap', carbon: 'lightning', solar: 'bolt-linear', hugeicons: 'flash' },
|
|
97
|
+
'flag': { lucide: 'flag', carbon: 'flag', solar: 'flag-linear', hugeicons: 'flag-02' },
|
|
98
|
+
'tag': { lucide: 'tag', carbon: 'tag', solar: 'tag-linear', hugeicons: 'tag-01' },
|
|
99
|
+
'pin': { lucide: 'pin', carbon: 'pin', solar: 'pin-linear', hugeicons: 'pin' },
|
|
100
|
+
'bookmark': { lucide: 'bookmark', carbon: 'bookmark', solar: 'bookmark-linear', hugeicons: 'bookmark-01' },
|
|
101
|
+
'gift': { lucide: 'gift', carbon: 'gift', solar: 'gift-linear', hugeicons: 'gift' },
|
|
102
|
+
'trophy': { lucide: 'trophy', carbon: 'trophy', solar: 'cup-star-linear', hugeicons: 'champion' },
|
|
103
|
+
'target': { lucide: 'target', carbon: 'concept', solar: 'target-linear', hugeicons: 'target-02' },
|
|
104
|
+
// Rivalry / "you vs them" — competitor surfaces. lucide+hugeicons render swords;
|
|
105
|
+
// carbon/solar have no sword glyph, so they fall back to the rivalry-as-aim stand-in.
|
|
106
|
+
'versus': { lucide: 'swords', carbon: 'concept', solar: 'target-linear', hugeicons: 'sword-03' },
|
|
107
|
+
'bug': { lucide: 'bug', carbon: 'debug', solar: 'bug-linear', hugeicons: 'bug-01' },
|
|
108
|
+
'circle-dash': { lucide: 'circle-dashed', carbon: 'circle-dash', solar: 'record-circle-linear', hugeicons: 'circle' },
|
|
109
|
+
'loading': { fixed: 'i-svg-spinners-90-ring-with-bg' },
|
|
110
|
+
|
|
111
|
+
// ── theme ──────────────────────────────────────────────────
|
|
112
|
+
'light': { lucide: 'sun', carbon: 'sun', solar: 'sun-linear', hugeicons: 'sun-02' },
|
|
113
|
+
'dark': { lucide: 'moon', carbon: 'moon', solar: 'moon-linear', hugeicons: 'moon-02' },
|
|
114
|
+
'system': { lucide: 'monitor', carbon: 'screen', solar: 'monitor-linear', hugeicons: 'computer' },
|
|
115
|
+
|
|
116
|
+
// ── objects / domain ───────────────────────────────────────
|
|
117
|
+
'rocket': { lucide: 'rocket', carbon: 'rocket', solar: 'rocket-linear', hugeicons: 'rocket-01' },
|
|
118
|
+
'link': { lucide: 'link', carbon: 'link', solar: 'link-linear', hugeicons: 'link-02' },
|
|
119
|
+
'unlink': { lucide: 'unlink', carbon: 'unlink', solar: 'link-broken-linear', hugeicons: 'unlink-03' },
|
|
120
|
+
'file': { lucide: 'file-text', carbon: 'document', solar: 'document-text-linear', hugeicons: 'file-01' },
|
|
121
|
+
'file-search': { lucide: 'file-search', carbon: 'document-view', hugeicons: 'file-search' },
|
|
122
|
+
'file-check': { lucide: 'file-check', carbon: 'document-tasks', hugeicons: 'file-validation' },
|
|
123
|
+
'folder': { lucide: 'folder', carbon: 'folder', solar: 'folder-linear', hugeicons: 'folder-01' },
|
|
124
|
+
'calendar': { lucide: 'calendar', carbon: 'calendar', solar: 'calendar-linear', hugeicons: 'calendar-03' },
|
|
125
|
+
'clock': { lucide: 'clock', carbon: 'time', solar: 'clock-circle-linear', hugeicons: 'clock-01' },
|
|
126
|
+
'timer': { lucide: 'timer', carbon: 'timer', solar: 'stopwatch-linear', hugeicons: 'timer-02' },
|
|
127
|
+
'mail': { lucide: 'mail', carbon: 'email', solar: 'letter-linear', hugeicons: 'mail-01' },
|
|
128
|
+
'globe': { lucide: 'globe', carbon: 'wikis', solar: 'global-linear', hugeicons: 'global' },
|
|
129
|
+
'plug': { lucide: 'plug', carbon: 'plug', solar: 'plug-circle-linear', hugeicons: 'plug-01' },
|
|
130
|
+
'database': { lucide: 'database', carbon: 'data-base', solar: 'database-linear', hugeicons: 'database' },
|
|
131
|
+
'server': { lucide: 'server', carbon: 'bare-metal-server', solar: 'server-linear', hugeicons: 'database-02' },
|
|
132
|
+
'cloud': { lucide: 'cloud', carbon: 'cloud', solar: 'cloud-linear', hugeicons: 'cloud' },
|
|
133
|
+
'key': { lucide: 'key', carbon: 'password', solar: 'key-linear', hugeicons: 'key-01' },
|
|
134
|
+
'user': { lucide: 'user', carbon: 'user', solar: 'user-linear', hugeicons: 'user' },
|
|
135
|
+
'users': { lucide: 'users', carbon: 'group', solar: 'users-group-rounded-linear', hugeicons: 'user-group' },
|
|
136
|
+
'user-plus': { lucide: 'user-plus', carbon: 'user-follow', solar: 'user-plus-linear', hugeicons: 'user-add-01' },
|
|
137
|
+
'credit-card': { lucide: 'credit-card', carbon: 'purchase', solar: 'card-linear', hugeicons: 'credit-card' },
|
|
138
|
+
'dollar': { lucide: 'dollar-sign', carbon: 'currency-dollar', solar: 'dollar-linear', hugeicons: 'dollar-01' },
|
|
139
|
+
'book': { lucide: 'book-open', carbon: 'book', solar: 'book-linear', hugeicons: 'book-02' },
|
|
140
|
+
'graduation': { lucide: 'graduation-cap', carbon: 'education', solar: 'square-academic-cap-linear', hugeicons: 'mortarboard-02' },
|
|
141
|
+
'image': { lucide: 'image', carbon: 'image', solar: 'gallery-linear', hugeicons: 'image-01' },
|
|
142
|
+
'code': { lucide: 'code', carbon: 'code', solar: 'code-linear', hugeicons: 'source-code' },
|
|
143
|
+
'terminal': { lucide: 'terminal', carbon: 'terminal', solar: 'programming-linear', hugeicons: 'computer-terminal-01' },
|
|
144
|
+
'command': { lucide: 'command', carbon: 'apps', solar: 'command-linear', hugeicons: 'command' },
|
|
145
|
+
'layout': { lucide: 'layout-dashboard', carbon: 'dashboard', solar: 'widget-linear', hugeicons: 'dashboard-square-01' },
|
|
146
|
+
'layers': { lucide: 'layers', carbon: 'layers', solar: 'layers-linear', hugeicons: 'layers-01' },
|
|
147
|
+
'list': { lucide: 'list', carbon: 'list', solar: 'list-linear', hugeicons: 'task-01' },
|
|
148
|
+
'table': { lucide: 'table', carbon: 'table', solar: 'widget-linear', hugeicons: 'grid-table' },
|
|
149
|
+
'grid': { lucide: 'grid-3x3', carbon: 'grid', solar: 'widget-2-linear', hugeicons: 'grid-view' },
|
|
150
|
+
'map': { lucide: 'map-pin', carbon: 'location', solar: 'map-point-linear', hugeicons: 'location-01' },
|
|
151
|
+
'wifi': { lucide: 'wifi', carbon: 'wifi', solar: 'wi-fi-router-linear', hugeicons: 'wifi-01' },
|
|
152
|
+
'monitor': { lucide: 'monitor', carbon: 'screen', solar: 'monitor-linear', hugeicons: 'computer' },
|
|
153
|
+
'mouse-pointer': { lucide: 'mouse-pointer-click', carbon: 'cursor-1', solar: 'cursor-linear', hugeicons: 'cursor-01' },
|
|
154
|
+
'gauge': { lucide: 'gauge', carbon: 'meter', solar: 'spedometer-max-linear', hugeicons: 'dashboard-speed-01' },
|
|
155
|
+
'flask': { lucide: 'flask-conical', carbon: 'chemistry', solar: 'test-tube-linear', hugeicons: 'test-tube-01' },
|
|
156
|
+
'wand': { lucide: 'wand-sparkles', carbon: 'magic-wand-filled', solar: 'magic-stick-3-linear', hugeicons: 'magic-wand-01' },
|
|
157
|
+
'git': { lucide: 'git-branch', carbon: 'branch', solar: 'branching-paths-up-linear', hugeicons: 'git-branch' },
|
|
158
|
+
'version': { lucide: 'git-branch', carbon: 'version', solar: 'branching-paths-up-linear', hugeicons: 'git-branch' },
|
|
159
|
+
'message': { lucide: 'message-square', carbon: 'chat', solar: 'chat-round-linear', hugeicons: 'message-01' },
|
|
160
|
+
'phone': { lucide: 'phone', carbon: 'phone', solar: 'phone-linear', hugeicons: 'call' },
|
|
161
|
+
|
|
162
|
+
// ── charts / data ──────────────────────────────────────────
|
|
163
|
+
'chart': { lucide: 'chart-area', carbon: 'chart-area', solar: 'chart-square-linear', hugeicons: 'analytics-01' },
|
|
164
|
+
'chart-line': { lucide: 'chart-line', carbon: 'chart-line', solar: 'graph-linear', hugeicons: 'analytics-up' },
|
|
165
|
+
'chart-bar': { lucide: 'chart-column', carbon: 'chart-bar', solar: 'chart-2-linear', hugeicons: 'bar-chart' },
|
|
166
|
+
'chart-pie': { lucide: 'chart-pie', carbon: 'chart-pie', solar: 'pie-chart-2-linear', hugeicons: 'pie-chart' },
|
|
167
|
+
'activity': { lucide: 'activity', carbon: 'activity', solar: 'pulse-linear', hugeicons: 'activity-01' },
|
|
168
|
+
'analytics': { lucide: 'chart-no-axes-combined', carbon: 'analytics', solar: 'chart-linear', hugeicons: 'analytics-01' },
|
|
169
|
+
'trending-up': { lucide: 'trending-up', carbon: 'growth', solar: 'graph-up-linear', hugeicons: 'analytics-up' },
|
|
170
|
+
'trending-down': { lucide: 'trending-down', carbon: 'misuse', solar: 'graph-down-linear', hugeicons: 'analytics-down' },
|
|
171
|
+
'percent': { lucide: 'percent', carbon: 'percentage', solar: 'sale-linear', hugeicons: 'percent' },
|
|
172
|
+
|
|
173
|
+
'sort': { lucide: 'chevrons-up-down', carbon: 'arrows-vertical', solar: 'sort-vertical-linear', hugeicons: 'arrow-up-down' },
|
|
174
|
+
'arrow-up-right': { lucide: 'arrow-up-right', carbon: 'arrow-up-right', solar: 'arrow-right-up-linear', hugeicons: 'arrow-up-right-01' },
|
|
175
|
+
'arrow-down-right': { lucide: 'arrow-down-right', carbon: 'arrow-down-right', solar: 'arrow-right-down-linear', hugeicons: 'arrow-down-right-01' },
|
|
176
|
+
'compare': { lucide: 'git-compare', carbon: 'compare', solar: 'transfer-horizontal-linear', hugeicons: 'arrow-data-transfer-horizontal' },
|
|
177
|
+
'thumbs-up': { lucide: 'thumbs-up', carbon: 'thumbs-up', solar: 'like-linear', hugeicons: 'thumbs-up' },
|
|
178
|
+
'thumbs-down': { lucide: 'thumbs-down', carbon: 'thumbs-down', solar: 'dislike-linear', hugeicons: 'thumbs-down' },
|
|
179
|
+
|
|
180
|
+
// ── more objects / domain ──────────────────────────────────
|
|
181
|
+
'smartphone': { lucide: 'smartphone', carbon: 'mobile', solar: 'smartphone-linear', hugeicons: 'smart-phone-01' },
|
|
182
|
+
'unplug': { lucide: 'unplug', carbon: 'connection-signal-off', solar: 'plug-circle-broken', hugeicons: 'plug-socket' },
|
|
183
|
+
'radar': { lucide: 'radar', carbon: 'radar', solar: 'radar-2-linear', hugeicons: 'radar-01' },
|
|
184
|
+
'wrench': { lucide: 'wrench', carbon: 'tool-kit', hugeicons: 'wrench-01' },
|
|
185
|
+
'history': { lucide: 'history', carbon: 'recently-viewed', solar: 'history-linear', hugeicons: 'time-quarter-pass' },
|
|
186
|
+
'cpu': { lucide: 'cpu', carbon: 'chip', solar: 'cpu-linear', hugeicons: 'cpu' },
|
|
187
|
+
'puzzle': { lucide: 'puzzle', carbon: 'plug-filled', solar: 'pip-linear', hugeicons: 'puzzle' },
|
|
188
|
+
'inbox': { lucide: 'inbox', carbon: 'email-new', solar: 'inbox-linear', hugeicons: 'inbox' },
|
|
189
|
+
'shopping-cart': { lucide: 'shopping-cart', carbon: 'shopping-cart', solar: 'cart-large-2-linear', hugeicons: 'shopping-cart-01' },
|
|
190
|
+
'hourglass': { lucide: 'hourglass', carbon: 'hourglass', solar: 'hourglass-linear' },
|
|
191
|
+
'cable': { lucide: 'cable', carbon: 'connect', solar: 'usb-linear', hugeicons: 'cable' },
|
|
192
|
+
'crown': { lucide: 'crown', solar: 'crown-linear', hugeicons: 'crown' },
|
|
193
|
+
'cloud-off': { lucide: 'cloud-off', carbon: 'cloud-offline', solar: 'cloud-cross-linear' },
|
|
194
|
+
'braces': { lucide: 'braces', carbon: 'code', solar: 'code-square-linear', hugeicons: 'source-code-square' },
|
|
195
|
+
'accessibility': { lucide: 'accessibility', carbon: 'accessibility', solar: 'accessibility-linear' },
|
|
196
|
+
'video': { lucide: 'video', carbon: 'video', solar: 'videocamera-linear', hugeicons: 'video-01' },
|
|
197
|
+
'log-out': { lucide: 'log-out', carbon: 'logout', solar: 'logout-2-linear', hugeicons: 'logout-01' },
|
|
198
|
+
'log-in': { lucide: 'log-in', carbon: 'login', solar: 'login-2-linear', hugeicons: 'login-01' },
|
|
199
|
+
'list-ordered': { lucide: 'list-ordered', carbon: 'list-numbered', solar: 'list-arrow-down-linear', hugeicons: 'left-to-right-list-number' },
|
|
200
|
+
'list-checks': { lucide: 'list-checks', carbon: 'task', solar: 'checklist-minimalistic-linear', hugeicons: 'task-done-01' },
|
|
201
|
+
'folder-plus': { lucide: 'folder-plus', carbon: 'folder-add', solar: 'folder-with-files-linear', hugeicons: 'folder-add' },
|
|
202
|
+
'briefcase': { lucide: 'briefcase', carbon: 'portfolio', solar: 'case-linear', hugeicons: 'job-search' },
|
|
203
|
+
'archive': { lucide: 'archive', carbon: 'archive', solar: 'archive-linear', hugeicons: 'archive-02' },
|
|
204
|
+
'building': { lucide: 'building-2', carbon: 'building', solar: 'buildings-2-linear', hugeicons: 'building-03' },
|
|
205
|
+
'newspaper': { lucide: 'newspaper', carbon: 'document', solar: 'document-text-linear', hugeicons: 'news' },
|
|
206
|
+
'megaphone': { lucide: 'megaphone', carbon: 'bullhorn', hugeicons: 'megaphone-01' },
|
|
207
|
+
'life-buoy': { lucide: 'life-buoy', carbon: 'help', hugeicons: 'help-circle' },
|
|
208
|
+
'compass': { lucide: 'compass', carbon: 'compass', solar: 'compass-linear', hugeicons: 'navigation-03' },
|
|
209
|
+
'sprout': { lucide: 'sprout', carbon: 'tree', solar: 'leaf-linear', hugeicons: 'plant-02' },
|
|
210
|
+
'receipt': { lucide: 'receipt', carbon: 'receipt', solar: 'bill-list-linear', hugeicons: 'invoice-01' },
|
|
211
|
+
'scale': { lucide: 'scale', carbon: 'scales', solar: 'scale-linear', hugeicons: 'justice-scale-01' },
|
|
212
|
+
'webhook': { lucide: 'webhook', carbon: 'webhook', solar: 'link-circle-linear', hugeicons: 'api' },
|
|
213
|
+
'badge': { lucide: 'badge-check', carbon: 'badge', solar: 'medal-ribbon-linear', hugeicons: 'checkmark-badge-01' },
|
|
214
|
+
'at-sign': { lucide: 'at-sign', carbon: 'at', solar: 'mention-circle-linear', hugeicons: 'at' },
|
|
215
|
+
'clipboard': { lucide: 'clipboard', carbon: 'document-tasks', solar: 'clipboard-linear', hugeicons: 'clipboard' },
|
|
216
|
+
'search-x': { lucide: 'search-x', carbon: 'search-locate-mirror', solar: 'minimalistic-magnifer-zoom-out-linear', hugeicons: 'search-remove' },
|
|
217
|
+
'file-x': { lucide: 'file-x', carbon: 'document-unknown', solar: 'file-corrupted-linear', hugeicons: 'file-not-found' },
|
|
218
|
+
'calendar-clock': { lucide: 'calendar-clock', carbon: 'calendar-heat-map', solar: 'calendar-date-linear', hugeicons: 'calendar-02' },
|
|
219
|
+
'video-off': { lucide: 'video-off', carbon: 'video-off', solar: 'videocamera-broken', hugeicons: 'video-off' },
|
|
220
|
+
'wifi-off': { lucide: 'wifi-off', carbon: 'wifi-off', solar: 'wi-fi-router-minimalistic-linear', hugeicons: 'wifi-disconnected-01' },
|
|
221
|
+
|
|
222
|
+
// ── brand ─ stroke-weight (hugeicons) so they sit with the line icons rather
|
|
223
|
+
// than reading as heavy solid silhouettes. Pinned (fixed) for a consistent brand
|
|
224
|
+
// family across every active set.
|
|
225
|
+
// Google defaults to a monochrome G in muted product contexts. `google-color`
|
|
226
|
+
// is the official multicolor mark for source identity where color is the cue.
|
|
227
|
+
'google': { fixed: 'i-simple-icons-google' },
|
|
228
|
+
'google-color': { fixed: 'i-logos-google-icon' },
|
|
229
|
+
'nuxt': { fixed: 'i-simple-icons-nuxtdotjs' },
|
|
230
|
+
'github': { fixed: 'i-hugeicons-github' },
|
|
231
|
+
'discord': { fixed: 'i-hugeicons-discord' },
|
|
232
|
+
'linkedin': { fixed: 'i-hugeicons-linkedin-01' },
|
|
233
|
+
'x-twitter': { fixed: 'i-hugeicons-new-twitter' },
|
|
234
|
+
'facebook': { fixed: 'i-hugeicons-facebook-01' },
|
|
235
|
+
'slack': { fixed: 'i-hugeicons-slack' },
|
|
236
|
+
} as const satisfies Record<string, RoleMap>
|
|
237
|
+
|
|
238
|
+
export type IconRole = keyof typeof ICON_ROLES
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Alternate names that resolve to a canonical role — synonyms and common
|
|
242
|
+
* iconify-id variants encountered during migration (e.g. `alert-triangle`,
|
|
243
|
+
* `loader-2`, `check-circle` all collapse to one role). Keeps the role list
|
|
244
|
+
* small while letting the codemod map many raw ids onto few roles.
|
|
245
|
+
*/
|
|
246
|
+
export const ICON_ALIASES = {
|
|
247
|
+
// status synonyms
|
|
248
|
+
'alert-triangle': 'warning',
|
|
249
|
+
'octagon-alert': 'warning',
|
|
250
|
+
'warning-filled': 'warning',
|
|
251
|
+
'file-warning': 'warning',
|
|
252
|
+
'alert-circle': 'caution',
|
|
253
|
+
'circle-alert': 'caution',
|
|
254
|
+
'check-circle': 'success',
|
|
255
|
+
'check-circle-2': 'success',
|
|
256
|
+
'badge-check': 'success',
|
|
257
|
+
'task-complete': 'success',
|
|
258
|
+
'checkmark-outline': 'success',
|
|
259
|
+
'checkmark-filled': 'success',
|
|
260
|
+
'x-circle': 'error',
|
|
261
|
+
'close-filled': 'error',
|
|
262
|
+
'close-outline': 'error',
|
|
263
|
+
'help-circle': 'help',
|
|
264
|
+
'file-question': 'help',
|
|
265
|
+
'message-circle-question': 'help',
|
|
266
|
+
'shield-alert': 'shield',
|
|
267
|
+
'shield-check': 'shield-check',
|
|
268
|
+
// loaders / refresh
|
|
269
|
+
'loader': 'loading',
|
|
270
|
+
'loader-2': 'loading',
|
|
271
|
+
'loader-circle': 'loading',
|
|
272
|
+
'spinner': 'loading',
|
|
273
|
+
'renew': 'refresh',
|
|
274
|
+
'reset': 'refresh',
|
|
275
|
+
'rotate-cw': 'refresh',
|
|
276
|
+
'restart': 'refresh',
|
|
277
|
+
// objects
|
|
278
|
+
'arrow-right': 'next',
|
|
279
|
+
'arrow-left': 'back',
|
|
280
|
+
'external-link': 'external',
|
|
281
|
+
'link-2': 'link',
|
|
282
|
+
'bot-message-square': 'bot',
|
|
283
|
+
'robot': 'bot',
|
|
284
|
+
'message-circle': 'message',
|
|
285
|
+
'chat': 'message',
|
|
286
|
+
'tags': 'tag',
|
|
287
|
+
'tag-group': 'tag',
|
|
288
|
+
'globe-2': 'globe',
|
|
289
|
+
'earth': 'globe',
|
|
290
|
+
'wikis': 'globe',
|
|
291
|
+
'cog': 'settings',
|
|
292
|
+
'cog-6-tooth': 'settings',
|
|
293
|
+
'magnifying-glass': 'search',
|
|
294
|
+
'pen': 'edit',
|
|
295
|
+
'pen-line': 'edit',
|
|
296
|
+
'pencil': 'edit',
|
|
297
|
+
'trash': 'delete',
|
|
298
|
+
'trash-2': 'delete',
|
|
299
|
+
'document-export': 'download',
|
|
300
|
+
'file-text': 'file',
|
|
301
|
+
'code-bracket': 'code',
|
|
302
|
+
'code-reference': 'code',
|
|
303
|
+
'xml': 'code',
|
|
304
|
+
// charts
|
|
305
|
+
'line-chart': 'chart-line',
|
|
306
|
+
'bar-chart-3': 'chart-bar',
|
|
307
|
+
'pie-chart': 'chart-pie',
|
|
308
|
+
'chart-no-axes-column': 'chart-bar',
|
|
309
|
+
'trending-up-carbon': 'trending-up',
|
|
310
|
+
// misc
|
|
311
|
+
'triangle-alert': 'warning',
|
|
312
|
+
'clipboard-check': 'list-checks',
|
|
313
|
+
'clipboard-document-check': 'list-checks',
|
|
314
|
+
'beaker': 'flask',
|
|
315
|
+
'swords': 'versus',
|
|
316
|
+
'crossed-swords': 'versus',
|
|
317
|
+
'tool-box': 'wrench',
|
|
318
|
+
'tools': 'wrench',
|
|
319
|
+
'tool': 'wrench',
|
|
320
|
+
// bare-name variants that collide with a role's canonical id
|
|
321
|
+
'map': 'map',
|
|
322
|
+
'file': 'file',
|
|
323
|
+
'filter': 'filter',
|
|
324
|
+
'circle-dot': 'circle-dash',
|
|
325
|
+
// more synonyms surfaced in migration
|
|
326
|
+
'key-square': 'key',
|
|
327
|
+
'key-round': 'key',
|
|
328
|
+
'shield-off': 'shield',
|
|
329
|
+
'pause-circle': 'pause',
|
|
330
|
+
'bell-ring': 'bell',
|
|
331
|
+
'search-check': 'search',
|
|
332
|
+
'rocket-launch': 'rocket',
|
|
333
|
+
'sparkle': 'ai',
|
|
334
|
+
'sparkle-fill': 'ai',
|
|
335
|
+
'ai-label': 'ai',
|
|
336
|
+
'code-duotone': 'code',
|
|
337
|
+
'books': 'book',
|
|
338
|
+
'template': 'layout',
|
|
339
|
+
'blocks': 'grid',
|
|
340
|
+
// brand logo aliases (carbon `logo-*`, ph duotone, etc.)
|
|
341
|
+
'logo-x': 'x-twitter',
|
|
342
|
+
'logo-github': 'github',
|
|
343
|
+
'logo-discord': 'discord',
|
|
344
|
+
'logo-linkedin': 'linkedin',
|
|
345
|
+
'logo-facebook': 'facebook',
|
|
346
|
+
'logo-slack': 'slack',
|
|
347
|
+
} as const satisfies Record<string, IconRole>
|