Compare commits

..
56 Commits
Author SHA1 Message Date
Petter Lauvrak 1675213eb6 feat(icons): added bell-check icon (#4152)
* Added icons/bell-check.svg

* Added icons/bell-check.json
2026-04-24 15:50:13 +02:00
Spleefies ac25ea327d feat(icons): added layers-minus icon (#4005)
* Added icons/layers-minus.svg

* Added icons/layers-minus.json

* use alternative design

* Update layers-minus.svg
2026-04-24 15:50:03 +02:00
Jamie LawandKarsa b9e922b100 Updated icons/rotate-3d.svg (#4299)
Co-authored-by: Karsa <contact@karsa.org>
2026-04-24 15:42:47 +02:00
Swastik SharmaandKarsa 6e42a8792a feat(icon): add heart-x icon (#4264)
* feat(icon): add heart-x icon

* Update icons/heart-x.json

Co-authored-by: Karsa <contact@karsa.org>

* Update icons/heart-x.json

* Update heart-x.json

---------

Co-authored-by: Karsa <contact@karsa.org>
2026-04-24 15:42:41 +02:00
SpleefiesandKarsa d7f8d17986 feat(docs): blur background of framework-select (#4238)
* blur background of framework-select

* Update docs/.vitepress/theme/components/guide/FrameworkSelect.vue

Co-authored-by: Karsa <contact@karsa.org>

* remove duplicate label css

* Update docs/.vitepress/theme/components/guide/FrameworkSelect.vue

---------

Co-authored-by: Karsa <contact@karsa.org>
2026-04-24 14:41:42 +02:00
Karsa c81e6846c5 test(build-font): added comprehensive unit tests on build-font tool (#4315) 2026-04-24 14:12:43 +02:00
Swastik SharmaandEric Fennis 804d25586b feat(icons): add power and quick tags to zap and zap-off (#4268)
* feat(icons): add power and quick tags to zap and zap-off

* chore(icons): add contributor name

* Update icons/zap.json

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>

* Update icons/zap-off.json

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>

* Update icons/zap.json

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>

* Update icons/zap-off.json

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-04-24 14:10:20 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 39e3964a9b chore(deps-dev): bump astro from 6.0.8 to 6.1.6 (#4310)
Bumps [astro](https://github.com/withastro/astro/tree/HEAD/packages/astro) from 6.0.8 to 6.1.6.
- [Release notes](https://github.com/withastro/astro/releases)
- [Changelog](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md)
- [Commits](https://github.com/withastro/astro/commits/astro@6.1.6/packages/astro)

---
updated-dependencies:
- dependency-name: astro
  dependency-version: 6.1.6
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-24 13:31:53 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 2f8257d8b4 chore(deps): bump svgo from 3.3.2 to 3.3.3 (#4119)
Bumps [svgo](https://github.com/svg/svgo) from 3.3.2 to 3.3.3.
- [Release notes](https://github.com/svg/svgo/releases)
- [Commits](https://github.com/svg/svgo/compare/v3.3.2...v3.3.3)

---
updated-dependencies:
- dependency-name: svgo
  dependency-version: 3.3.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-24 11:24:47 +02:00
blt-randEric Fennis fe0bd9b887 fix(@lucide/svelte): proper doc comments for svelte components (#4267)
* fix(@lucide/svelte): proper doc comments for svelte components

* Update license text

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-04-24 10:42:50 +02:00
Jessica Lu d0229f4cfc docs: add missing period to TypeScript Support guide (#4309)
Add period to the end of sentence in the description of https://lucide.dev/guide/react/advanced/typescript.
2026-04-24 10:07:51 +02:00
Jakob Guddas cc1caae101 feat(icons): added timeline icon (#4270)
* Added icons/timeline.svg

* Added icons/timeline.json

* Updated icons/timeline.svg

* Updated icons/timeline.json
2026-04-23 17:26:46 +02:00
KarsaandEric Fennis d632a8e272 fix(build-font): add collision detection to font codepoints & make allocation explicitly sequential (#4300)
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-04-23 17:24:21 +02:00
Karsa 653e44b832 feat(packages): use .mjs for ESM bundles (#4285) 2026-04-23 17:00:02 +02:00
Jakob GuddasandKarsa f0eaba827e fix(gh-icon): update colors for ColoredPath component (#4233)
* Update colors for ColoredPath component

* fix(site): fix linting issues SvgPreview

---------

Co-authored-by: Karsa <contact@karsa.org>
2026-04-20 11:23:35 +02:00
Swastik SharmaandKarsa dc803026e0 fix(packages/angular): allow string inputs for size (#4253)
* fix: Type change for size: string | number across the LucideConfig

* fix(packages/angular): fix typing issues in @lucide/angular, should update all usages and declarations of size

* fix: revert changes made to lucide-react context.ts

---------

Co-authored-by: Karsa <contact@karsa.org>
2026-04-20 07:41:32 +02:00
Zeenat Lawal 3c62e4bfef feat(icons): added bookmark-off icon (#4283)
* feat: add icons/bookmark-off.svg

* feat: add icons/bookmark-off.json

* feat: update icons/bookmark-off.json contributors and tags
2026-04-09 08:46:23 +02:00
Karsa 6d6da7ca3a feat(docs): use initOnMounted: true for useSessionStorage in CarbonAdOverlay (#4275)
* feat(docs): use computed for `hide-ad`

* Update CarbonAdOverlay.vue

* Update CarbonAdOverlay.vue

* feat(site): add initOnMounted: true to useSessionStorage in CarbonAdOverlay.vue
2026-04-08 10:01:52 +02:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Karsa
2453b6467d chore(deps): bump vite from 5.4.21 to 6.4.2 (#4286)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.21 to 6.4.2.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.4.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.4.2/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.4.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Karsa <contact@karsa.org>
2026-04-08 09:55:28 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> a88e7070cc chore(deps): bump lodash-es from 4.17.23 to 4.18.1 (#4251)
Bumps [lodash-es](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1)

---
updated-dependencies:
- dependency-name: lodash-es
  dependency-version: 4.18.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-08 09:44:26 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> a7611e2957 chore(deps-dev): bump vite from 7.3.1 to 7.3.2 (#4276)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.3.1 to 7.3.2.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.3.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.3.2/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.3.2
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-08 09:29:30 +02:00
Karsa 7c2ff2e5d6 fix(icons): remove ui/ux tag from heart-minus, add delete instead (#4266) 2026-04-04 17:25:09 +02:00
blt-r 51e37e2e2c fix aria-hidden="true" in @lucide/svelte (#4234) 2026-04-03 14:43:35 +02:00
Karsa 5faceb5495 fix(docs): improve mobile layout of v1 banner (#4254)
* fix(docs): improve mobile layout of v1 banner

* fix(docs): make top banner sticky instead of fixed, this way there's no need for hack-ish explicit heights

* fix(docs): add --vp-layout-top-height media query declaration for when navbar becomes sticky
2026-04-03 10:16:39 +02:00
Karsa 3814a64cd5 fix(docs): fix typo in combining icons docs 2026-04-01 13:22:05 +02:00
Jakob GuddasandKarsa 43c76b1f08 fix(icons): renamed text-select to square-dashed-text (#3943)
* fix(icons): renamed `text-select` to `square-dashed-text`

* Update square-dashed-text.json

* Update square-dashed-text.json

---------

Co-authored-by: Karsa <contact@karsa.org>
2026-03-30 09:15:51 +02:00
Karsa 2401a39571 fix(icons): fixes gap issues in radio-off.svg (#4227) 2026-03-28 09:38:32 +01:00
Karsa 7623e23f78 feat(docs): add Zephyr Cloud to Hero Backers tier & rework updateSponsors script to also handle, well, sponsors (#4226) 2026-03-28 07:48:26 +01:00
Ankit Sharma 663a9d7fc3 Fix link to icon design principles in statement (#4225)
Updated the link to the icon design guide in the brand logos statement. Previous like was giving 404 error.
2026-03-28 07:48:12 +01:00
Toma d53f615cf3 fix(lucide-static): add viewBox attribute to sprite symbol elements (#4223)
Sprite symbols were missing the viewBox attribute, causing icons to be clipped instead of scaling proportionally when used at smaller sizes.

Fixes #2768
2026-03-28 07:47:55 +01:00
Ankit Sharma d8700b257a docs: fix broken links in documentation (#4224)
docs: fix broken links in documentation (pull request template and brand logos statement) from "lucide.dev/guide/design/" to "lucide.dev/contribute/"
2026-03-28 07:47:36 +01:00
Eric Fennis 30bb61917b ci(@lucide/angular): Fix publishing problem (#4213)
* ci(@lucide/angular): Fix publishing problem

* Format files
2026-03-26 14:14:22 +01:00
Rajnish Mehta 1e6b96db3d chore(icons): Username change knarlix to RajnishKMehta (#4208)
* Update shield-cog-corner.json

* Update shield-cog.json
2026-03-26 07:32:49 +01:00
Ryan Hutchison 5c52182778 Add packageDirname for @lucide/angular (#4211) 2026-03-26 07:31:46 +01:00
Tony SullivanandKarsa 1fde1f1c1a feat(icons): added map-pin-search icon (#4125)
* Added icons/map-pin-search.svg

* Added icons/map-pin-search.json

---------

Co-authored-by: Karsa <contact@karsa.org>
2026-03-25 18:46:24 +01:00
Eric Fennis dada0a8297 fix(lucide-react): Fix dynamic imports (#4210)
* fix(lucide-react): Fix dynamic imports

* Adjust imports

* Format code

* Ignore eslint error
2026-03-25 18:44:25 +01:00
Karsa fa09963bf8 fix(pipeline): revert custom angular publish step. not even this solves it. 2026-03-24 17:58:37 +01:00
Sondre Kongsgård cc53931091 feat(icons): added radio-off icon (#4138)
* Added icons/radio-off.svg

* Added icons/radio-off.json
2026-03-24 17:52:39 +01:00
Karsa 86b3ddfdca Merge remote-tracking branch 'origin/main' 2026-03-24 17:51:45 +01:00
Karsa f1360e1d9b feat(pipeline): fix working directory (angular, not @lucide/angular) 2026-03-24 17:50:32 +01:00
Jakob Guddas e2df99b883 feat(icons): added beef-off icon (#3816)
* Added icons/beef-off.svg

* Added icons/beef-off.json
2026-03-24 17:47:31 +01:00
Karsa aed7aeaa72 feat(pipeline): move angular publish step from lucide-static ⇒ release 2026-03-24 17:46:54 +01:00
Youri NouchetandKarsa 11ff82b056 feat(icons): added sport-shoe icon (#3953)
* Added icons/sport-shoe.svg

* Added icons/sport-shoe.json

* Update sport-shoe.svg

---------

Co-authored-by: Karsa <contact@karsa.org>
2026-03-24 17:41:27 +01:00
Karsa 25d96b7ec0 feat(pipeline): fix linting issues 2026-03-24 17:41:09 +01:00
Karsa 10962ce51b feat(pipeline): add separate Angular publish step using dist as the working directory & stripping publishConfig.directory from its package.json 2026-03-24 17:36:52 +01:00
Karsa 7dfbd21bce feat(pipeline): revert debug changes made to release.yml 2026-03-24 17:23:20 +01:00
Karsa b18f27ea8d feat(pipeline): add angular debug to release workflow 2026-03-24 17:14:31 +01:00
Karsa 5e6186ff70 feat(pipeline): add angular debug to release workflow 2026-03-24 17:12:55 +01:00
Karsa acb947a770 Merge remote-tracking branch 'origin/main' 2026-03-24 17:11:02 +01:00
Karsa e9bec8e6f8 feat(pipeline): add angular debug to release workflow 2026-03-24 17:10:48 +01:00
Karsa 4ea3561788 feat(pipeline): add angular debug to release workflow 2026-03-24 17:09:32 +01:00
Karsa 76c1b0caa9 feat(pipeline): add release-angular workflow for testing 2026-03-24 16:45:40 +01:00
Karsa f2eab2c30a feat(icons): debug angular release 2026-03-24 16:21:47 +01:00
Karsa 3fc90467a3 feat(pipeline): re-revert from the working-directory solution & set default tag as latest 2026-03-24 16:18:39 +01:00
Rajnish MehtaandRajnish Mehta 519aae64d6 feat(icons): added shield-cog icon (#3902)
* Added icons/shield-cog.svg

* Added icons/shield-cog.json

* Added icons/shield-cog-2.svg

* Added icons/shield-cog-2.json

* name issue fixed

* Add shield-cog-corner.svg icon file

* Add shield-cog-corner.json icon file

* Rename shield-cog-mid.svg to shield-cog.svg

* Add shield-cog.json file

---------

Co-authored-by: Rajnish Mehta <43362941-KnarliX@users.noreply.replit.com>
2026-03-24 16:12:32 +01:00
Karsa 7a395122a1 feat(pipeline): revert to working-directory solution for testing 2026-03-24 16:10:24 +01:00
105 changed files with 2593 additions and 1157 deletions
+2 -2
View File
@@ -41,13 +41,13 @@ Common scopes: icons, docs, studio, site, dev
### Naming <!-- ONLY for new icons -->
<!-- All of these requirements must be fulfilled. -->
- [ ] I've read and followed the [naming conventions](https://lucide.dev/guide/design/icon-design-guide#naming-conventions)
- [ ] I've read and followed the [naming conventions](https://lucide.dev/contribute/icon-design-guide#naming-conventions)
- [ ] I've named icons by what they are rather than their use case.
- [ ] I've provided meta JSON files in `icons/[iconName].json`.
### Design <!-- ONLY for new icons -->
<!-- All of these requirements must be fulfilled. -->
- [ ] I've read and followed the [icon design guidelines](https://lucide.dev/guide/design/icon-design-guide)
- [ ] I've read and followed the [icon design guidelines](https://lucide.dev/contribute/icon-design-guide)
- [ ] I've made sure that the icons look sharp on low DPI displays.
- [ ] I've made sure that the icons look consistent with the icon set in size, optical volume and density.
- [ ] I've made sure that the icons are visually centered.
+3
View File
@@ -23,6 +23,9 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Execute unit tests for build-font
run: pnpm test:font
- name: Create font in ./lucide-font
run: pnpm build:font
+4 -1
View File
@@ -13,7 +13,7 @@ on:
type: string
tag:
description: NPM tag
default: 'next' # set to latest after v1 release
default: 'latest'
type: string
permissions:
@@ -136,6 +136,9 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Execute unit tests for build-font
run: pnpm test:font
- name: Create font in ./lucide-font
run: pnpm build:font --saveCodePoints
env:
+4
View File
@@ -26,9 +26,13 @@ packages/**/src/utils/*.ts
packages/**/src/dynamicIconImports.ts
packages/**/DynamicIcon.d.ts
packages/**/dynamicIconImports.js
packages/**/dynamicIconImports.mjs
packages/**/dynamicIconImports.d.ts
packages/**/dynamicIconImports.d.mts
packages/**/dynamicIconImports.js.map
packages/**/dynamicIconImports.mjs.map
packages/**/dynamic.d.ts
packages/**/dynamic.d.mts
packages/**/dynamic.mjs.map
packages/**/dynamic.mjs
packages/**/LICENSE
+1 -1
View File
@@ -39,7 +39,7 @@ This means adding them could:
Lucide is all about **visual consistency**.
Adding brand logos would:
- Break [our own design rules](https://lucide.dev/guide/design/icon-design-guide#icon-design-principles) for shapes, proportions, and stroke.
- Break [our own design rules](https://lucide.dev/contribute/icon-design-guide#icon-design-principles) for shapes, proportions, and stroke.
- Mix two fundamentally different categories of graphics (pictograms vs. corporate logos).
- Create a library where a subset of icons will always look "out of place".
+6 -1
View File
@@ -80,6 +80,8 @@ Thank you to all the people who contributed to Lucide!
<img src="https://opencollective.com/lucide-icons/contributors.svg?width=800" />
</a>
[//]: <> (Sponsors)
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
@@ -88,7 +90,10 @@ Thank you to all the people who contributed to Lucide!
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="docs/public/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
[//]: <> (Open Collective backers)
### Hero backers 🦸
<a href="https://https://zephyr-cloud.io/"><img src="docs/public/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud – From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="docs/public/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
+1
View File
@@ -105,6 +105,7 @@
"order": 6,
"icon": "angular",
"docsAlias": "angular",
"packageDirname": "angular",
"shields": [
{
"alt": "npm",
+14 -1
View File
@@ -459,7 +459,20 @@ const SvgPreview = (
/>
<ColoredPath
paths={paths}
colors={['#dfdfd6']}
colors={[
'#1982c4',
'#4267AC',
'#6a4c93',
'#B55379',
'#FF595E',
'#FF7655',
'#ff924c',
'#FFAE43',
'#ffca3a',
'#C5CA30',
'#8ac926',
'#52A675',
]}
/>
<Radii
paths={paths}
@@ -1,15 +1,22 @@
<script setup lang="ts">
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue';
import Icon from '@lucide/vue/src/Icon';
import RocketDuotoneIcon from '../home/RocketDuotoneIcon.ts';
</script>
<template>
<div class="top-notification">
<p>
<Icon
:iconNode="RocketDuotoneIcon"
size="18"
class="rocket-icon"
/>
<VPLink
class="notification-link"
href="/guide/version-1"
>Lucide v1 is out!</VPLink
>🚀<br />
>
You're looking at the site for v1, for v0 go to
<VPLink
class="notification-link"
@@ -21,11 +28,7 @@ import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue';
</template>
<style>
:root {
--vp-layout-top-height: 64px;
}
@media (min-width: 640px) {
@media (min-width: 960px) {
:root {
--vp-layout-top-height: 32px;
}
@@ -34,19 +37,19 @@ import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue';
<style scoped>
.top-notification {
position: fixed;
position: sticky;
top: 0;
left: 0;
width: 100%;
z-index: 9999;
display: flex;
height: var(--vp-layout-top-height);
justify-content: center;
align-items: center;
background: var(--vp-c-brand-dark);
color: var(--vp-c-bg-alt-down);
font-size: 14px;
padding: 0 16px;
padding: 4px 16px;
text-wrap: balance;
}
.notification-link {
@@ -58,9 +61,9 @@ import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue';
display: none !important;
}
@media (min-width: 640px) {
.top-notification br {
display: none;
}
.rocket-icon {
display: inline-block;
vertical-align: middle;
margin-right: 4px;
}
</style>
@@ -118,5 +118,15 @@ label {
font-weight: bold;
margin-bottom: 4px;
display: block;
position: relative;
}
.framework-select:before {
content: ' ';
position: absolute;
inset: 0 -16px -24px;
background: color-mix(in srgb, var(--vp-c-bg-alt), transparent 50%);
backdrop-filter: blur(4px);
mask-image: linear-gradient(to bottom, black calc(100% - 16px), transparent 100%);
}
</style>
@@ -3,25 +3,7 @@ import Badge from '../base/Badge.vue';
import { moveRight } from '../../../data/iconNodes';
import Icon from '@lucide/vue/src/Icon';
import { data } from './HomeHeroInfoBefore.data';
const rocketDuoTone = [
[
'path',
{
d: 'M8 18a2 2 0 0 0-2-2c-2.53-.078-3.247 3.605-3.5 5.5 1.895-.253 5.578-.97 5.5-3.5',
fill: 'currentColor',
opacity: 0.5,
},
],
[
'path',
{
d: 'M22 2c0 2.72-.78 7.5-6 11 0 0 1.08 3.38 0 5-.969 1.448-4 2-4 2v-5l-3-3H4s.553-3.033 2-4c1.616-1.077 4.982.043 5 .05A12.88 12.88 0 0 1 22 2m-6 3a3 3 0 1 0 0 6 3 3 0 0 0 0-6',
fill: 'currentColor',
'fill-rule': 'evenodd',
'clip-rule': 'evenodd',
},
],
] as const;
import RocketDuotoneIcon from './RocketDuotoneIcon.ts';
</script>
<template>
@@ -33,7 +15,7 @@ const rocketDuoTone = [
class="badge-special confetti-button animate"
href="/guide/version-1"
>
<Icon :iconNode="rocketDuoTone" />
<Icon :iconNode="RocketDuotoneIcon" />
Version 1
<Icon
:iconNode="moveRight"
@@ -0,0 +1,21 @@
const RocketDuotoneIcon = [
[
'path',
{
d: 'M8 18a2 2 0 0 0-2-2c-2.53-.078-3.247 3.605-3.5 5.5 1.895-.253 5.578-.97 5.5-3.5',
fill: 'currentColor',
opacity: 0.5,
},
],
[
'path',
{
d: 'M22 2c0 2.72-.78 7.5-6 11 0 0 1.08 3.38 0 5-.969 1.448-4 2-4 2v-5l-3-3H4s.553-3.033 2-4c1.616-1.077 4.982.043 5 .05A12.88 12.88 0 0 1 22 2m-6 3a3 3 0 1 0 0 6 3 3 0 0 0 0-6',
fill: 'currentColor',
'fill-rule': 'evenodd',
'clip-rule': 'evenodd',
},
],
] as const;
export default RocketDuotoneIcon;
@@ -5,11 +5,18 @@ import IconButton from '../base/IconButton.vue';
import VPDocAsideCarbonAds from 'vitepress/dist/client/theme-default/components/VPDocAsideCarbonAds.vue';
import { x } from '../../../data/iconNodes';
import Icon from '@lucide/vue/src/Icon';
import { onMounted, ref } from 'vue';
import { computed, onMounted, ref } from 'vue';
const { theme } = useData();
const showAd = useSessionStorage('show-carbon-ads', true);
const showAd = useSessionStorage('show-carbon-ads', true, {
initOnMounted: true,
});
const carbonLoaded = ref(true);
const shouldHideAd = computed(() => !showAd.value || !carbonLoaded.value);
function hideAd() {
showAd.value = false;
}
defineProps<{
drawerOpen: boolean;
@@ -28,13 +35,13 @@ onMounted(() => {
<div
:class="{
'drawer-open': drawerOpen,
'hide-ad': !(showAd && carbonLoaded),
'hide-ad': shouldHideAd,
}"
class="floating-ad"
v-if="theme.carbonAds"
>
<IconButton
@click="showAd = false"
@click="hideAd"
class="hide-button"
>
<Icon
@@ -66,6 +73,7 @@ onMounted(() => {
.floating-ad.hide-ad {
transform: translateX(224px);
opacity: 0;
pointer-events: none;
}
.floating-ad.drawer-open.hide-ad {
@@ -96,6 +104,7 @@ onMounted(() => {
position: absolute;
top: 8px;
right: 8px;
z-index: 3;
background-color: transparent;
}
</style>
@@ -9,7 +9,7 @@ import Sandpack from '~/.vitepress/theme/components/editors/SandpackPreact.vue'
# Combining icons
You can combine multiple icons into a single icon by nesting SVG elements.
This is useful if you want to create custom icons icons by combining existing ones.
This is useful if you want to create custom icons by combining existing ones.
::: sandpack {showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="lucide-preact"}
@@ -6,7 +6,7 @@ description: Learn how to combine multiple icons into a single icon nested SVG e
# Combining icons
You can combine multiple icons into a single icon by nesting SVG elements.
This is useful if you want to create custom icons icons by combining existing ones.
This is useful if you want to create custom icons by combining existing ones.
```SnackPlayer name=State&ext=js&dependencies=react-native-svg,lucide-react-native
import React, {useState, useEffect} from 'react';
+1 -1
View File
@@ -9,7 +9,7 @@ import Sandpack from '~/.vitepress/theme/components/editors/Sandpack.vue'
# Combining icons
You can combine multiple icons into a single icon by nesting SVG elements.
This is useful if you want to create custom icons icons by combining existing ones.
This is useful if you want to create custom icons by combining existing ones.
::: sandpack {template=react showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="lucide-react"}
+1 -1
View File
@@ -5,7 +5,7 @@ description: Learn about the different types exported by the `lucide-react` pack
# TypeScript Support
List of exported types from the `lucide-react` package.
These can be used to type your components when using Lucide icons in a TypeScript React project
These can be used to type your components when using Lucide icons in a TypeScript React project.
## `LucideProps`
+1 -1
View File
@@ -10,7 +10,7 @@ import Sandpack from '~/.vitepress/theme/components/editors/Sandpack.vue'
# Combining icons
You can combine multiple icons into a single icon by nesting SVG elements.
This is useful if you want to create custom icons icons by combining existing ones.
This is useful if you want to create custom icons by combining existing ones.
::: sandpack {template=vite-solid showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="lucide-solid"}
@@ -9,7 +9,7 @@ import Sandpack from '~/.vitepress/theme/components/editors/SandpackSvelte.vue';
# Combining icons
You can combine multiple icons into a single icon by nesting SVG elements.
This is useful if you want to create custom icons icons by combining existing ones.
This is useful if you want to create custom icons by combining existing ones.
::: sandpack {template=vite-svelte showTabs=false editorHeight=400 editorWidthPercentage=60}
+1 -1
View File
@@ -9,7 +9,7 @@ import Sandpack from '~/.vitepress/theme/components/editors/SandpackVue.vue'
# Combining icons
You can combine multiple icons into a single icon by nesting SVG elements.
This is useful if you want to create custom icons icons by combining existing ones.
This is useful if you want to create custom icons by combining existing ones.
::: sandpack {template=vue showTabs=false editorHeight=400 editorWidthPercentage=60 dependencies="@lucide/vue"}
+1 -1
View File
@@ -57,7 +57,7 @@
"element-to-path": "^1.2.1",
"fuse.js": "^7.1.0",
"jszip": "^3.10.1",
"lodash-es": "^4.17.23",
"lodash-es": "^4.18.1",
"lucide": "workspace:*",
"lucide-react": "workspace:*",
"motion-v": "1.10.3",
+15
View File
@@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" width="138" height="42" fill="none" viewBox="0 0 138 42">
<path fill="#fff" fill-rule="evenodd" d="M138 4a4 4 0 0 0-4-4H4a4 4 0 0 0-4 4v34a4 4 0 0 0 4 4h130a4 4 0 0 0 4-4z" clip-rule="evenodd"/>
<path fill="url(#a)" fill-rule="evenodd" d="M16 27.6a6.6 6.6 0 0 0 5.5-10.4l4.3-4.3c.3-.3.1-1-.4-1a29 29 0 0 1-4.8-1.4h-.1l-.6-.3A11.4 11.4 0 0 0 5.1 24.5c.2.6 1 .9 1.5.6q.7-.6.5-1.4a9.3 9.3 0 0 1 14.6-10l-2 1.8q-1.6-1-3.7-1.1a6.6 6.6 0 0 0-5.4 10.4L6.3 29c-.4.3-.2 1 .3 1a29 29 0 0 1 4.8 1.4h.1l.6.3a11 11 0 0 0 12-2.7 11 11 0 0 0 2.8-11.6c-.2-.6-1-.9-1.5-.6q-.6.6-.5 1.4a9.3 9.3 0 0 1-14.6 10l2-1.8q1.6 1 3.7 1.1m4.4-6.6a4.5 4.5 0 0 1-6.7 4l6.2-6.1q.5.9.5 2.1m-9 0a4.5 4.5 0 0 1 6.8-3.9L12 23.3q-.6-1-.6-2.3" clip-rule="evenodd"/>
<path fill="url(#b)" d="M57.7 19.3q3.5.1 3.5 4 0 3.6-3.5 3.8-2.3 0-2.8-1.9h-.3l.2 1.4v3.7h-1.4V19.5h1.4l-.2 1.7h.3q.6-1.9 2.8-1.9m16.6 7.4h.3l2-7.2h1.5l-2.5 8.3q-.4 2-2.4 2.2l-2.2.2v-1.4h2.5q.5.1.5-.4l.5-1.5H73l-2.2-7.4h1.4zm-26-7.4q3.3 0 3.3 3.7H46c0 1.7.5 3 2.5 3q2 0 2-2.1H52q-.4 3.1-3.5 3.2-3.8-.1-3.9-3.9 0-3.7 3.7-3.9m46.1-3q4 0 4.4 4.2h-1.5q-.2-3-3-3-3 0-3 4.2t3.3 4.2q3 0 3.2-3h1.4q-.3 4.3-4.6 4.2-4.7 0-4.7-5.4t4.5-5.4m16 3q3.6.1 3.8 4-.2 3.6-3.9 3.8-3.7-.1-3.8-3.9.1-3.7 3.8-3.9m6.4 4.6q0 2 2 2 2.3-.2 2.3-2.4v-4h1.4v5.8q-.1.6.4.4l.9-.1v1.3h-.8q-1.6 0-1.7-1.6h-.3q-.5 1.8-2.6 1.8-2.9 0-2.8-3v-4.6h1.5zm15.6 1.4q-.1.6.4.4l.9-.1v1.3h-.8q-1.7-.1-1.8-1.7h-.3q-.4 1.9-2.8 2-3.4-.2-3.5-4 .1-3.7 3.5-3.9 2.4 0 2.8 2h.3l-.1-1.4v-4h1.4zm-89-5.7L36 25.4v.3h7.8v1.2h-8v-3l7.5-5.8v-.3H36v-1.2h7.4zm21 .3L64 21h.3q.5-1.8 2.7-1.8 2.8 0 2.7 3v4.6h-1.5l.3-4.4q0-2-2-2-2.3.1-2.3 2.5v4H63V15.7h1.4zM80.5 21h.3q.4-1.7 2.2-1.6h1.3V21l-1.6-.2q-2 0-2 2.3v4h-1.5v-7.5h1.4zm20.9-5.3q2 0 2 2v8h2.2v1.1h-6v-1.2h2.4v-8.4q.1-.6-.5-.4l-1.4.3v-1.4zm8.8 4.7q-2.4 0-2.4 2.7t2.4 2.7q2.4 0 2.5-2.7 0-2.6-2.5-2.7m-52.8 0q-2.6 0-2.6 2.7.1 2.6 2.6 2.7 2.3 0 2.3-2.7 0-2.6-2.3-2.7m70.8 0q-2.4 0-2.4 2.7 0 2.6 2.4 2.7 2.7 0 2.6-2.7-.1-2.6-2.6-2.7m-80 0q-1.8 0-2.1 1.5h4q0-1.4-1.9-1.5"/>
<defs>
<linearGradient id="a" x1="4.6" x2="27.4" y1="21" y2="21" gradientUnits="userSpaceOnUse">
<stop stop-color="#010101"/>
<stop offset="1" stop-color="#949494"/>
</linearGradient>
<linearGradient id="b" x1="35.7" x2="133.7" y1="23.1" y2="23.1" gradientUnits="userSpaceOnUse">
<stop stop-color="#010101"/>
<stop offset="1" stop-color="#646464"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

+23
View File
@@ -0,0 +1,23 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"kemie",
"ericfennis",
"karsa-mistmere",
"jguddas"
],
"tags": [
"food",
"dish",
"restaurant",
"course",
"meal",
"meat",
"bbq",
"steak",
"vegetarian"
],
"categories": [
"food-beverage"
]
}
+18
View File
@@ -0,0 +1,18 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M11.771 6.109a2.5 2.5 0 0 1 3.12 3.12" />
<path d="M17.852 12.185a6.5 6.5 0 0 0-9.035-9.04" />
<path d="M18.013 18.013C15.029 20.349 10.831 22 7 22a3 3 0 0 1-2.68-1.66L2.4 16.5" />
<path d="m18.5 6 2.19 4.5a6.48 6.48 0 0 1-.139 4.393" />
<path d="m2 2 20 20" />
<path d="M6.355 6.37a7 7 0 0 0-.075.23c-1.1 3.13-.78 3.9-3.18 6.08A3 3 0 0 0 5 18c3.356 0 6.993-1.267 9.85-3.151" />
</svg>

After

Width:  |  Height:  |  Size: 608 B

+20
View File
@@ -0,0 +1,20 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"colebemis",
"csandman",
"ericfennis",
"danielbayley",
"pettelau"
],
"tags": [
"alarm",
"notification",
"sound",
"reminder"
],
"categories": [
"account",
"notifications"
]
}
+16
View File
@@ -0,0 +1,16 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M10.268 21a2 2 0 0 0 3.464 0" />
<path d="m15 8 2 2 4-4" />
<path d="M16.8607 4.4824A6 6 0 0 0 6 8C6 12.499 4.589 13.956 3.262 15.326" />
<path d="M3.262 15.326A1 1 0 0 0 4 17H20A1 1 0 0 0 20.74 15.327C20.209 14.779 19.665 14.218 19.203 13.454" />
</svg>

After

Width:  |  Height:  |  Size: 473 B

+29
View File
@@ -0,0 +1,29 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"colebemis",
"csandman",
"siarie",
"ericfennis",
"jguddas",
"ZeenatLawal",
"swastik7805",
"karsa-mistmere"
],
"tags": [
"unsaved",
"unfavorite",
"unmarked",
"unlabel",
"disabled",
"removed",
"unpin",
"unread",
"unclip",
"marker",
"untag"
],
"categories": [
"account"
]
}
+15
View File
@@ -0,0 +1,15 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M19 19v1a1 1 0 0 1-1.496.868l-4.512-2.578a2 2 0 0 0-1.984 0l-4.512 2.578A1 1 0 0 1 5 20V5" />
<path d="m2 2 20 20" />
<path d="M8.656 3H17a2 2 0 0 1 2 2v8.344" />
</svg>

After

Width:  |  Height:  |  Size: 386 B

+2 -2
View File
@@ -12,8 +12,8 @@
"unlike",
"unfavorite",
"remove",
"damage",
"ui & ux"
"delete",
"damage"
],
"categories": [
"medical",
+28
View File
@@ -0,0 +1,28 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"colebemis",
"ericfennis",
"danielbayley",
"karsa-mistmere",
"jguddas",
"Ayberkyvs",
"UsamaKhan",
"swastik7805"
],
"tags": [
"unlike",
"unfavorite",
"remove",
"reject",
"dismiss",
"delete",
"clear"
],
"categories": [
"social",
"multimedia",
"design",
"shapes"
]
}
+15
View File
@@ -0,0 +1,15 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m15.5 12.5 5 5" />
<path d="m20.5 12.5-5 5" />
<path d="M21.955 8.774a5.5 5.5 0 0 0-9.546-2.95.6.6 0 0 1-.818 0A5.5 5.5 0 0 0 2 9.5c0 2.3 1.5 4 3 5.5l5.508 5.332a2 2 0 0 0 2.57.352" />
</svg>

After

Width:  |  Height:  |  Size: 408 B

+27
View File
@@ -0,0 +1,27 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"colebemis",
"danielbayley",
"jguddas",
"juanisidoro",
"karsa-mistmere",
"Spleefies"
],
"tags": [
"stack",
"pile",
"pages",
"sheets",
"paperwork",
"copies",
"copy",
"layers",
"remove",
"delete"
],
"categories": [
"design",
"layout"
]
}
+17
View File
@@ -0,0 +1,17 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 .83.18 2 2 0 0 0 .83-.18l8.58-3.9a1 1 0 0 0 0-1.832z" />
<path d="M16 17h6" />
<path d="M2.003 11.995a1 1 0 0 0 .597.915l8.58 3.91a2 2 0 0 0 .83.18" />
<path d="M2.003 16.995a1 1 0 0 0 .597.915l8.58 3.91a2 2 0 0 0 .83.18 2 2 0 0 0 .83-.18l2.11-.96" />
<path d="M22.018 12.004a1 1 0 0 1-.598.916l-.177.08" />
</svg>

After

Width:  |  Height:  |  Size: 610 B

+24
View File
@@ -0,0 +1,24 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"colebemis",
"karsa-mistmere",
"ericfennis",
"csandman",
"TonySullivan"
],
"tags": [
"location",
"navigation",
"travel",
"waypoint",
"marker",
"drop"
],
"categories": [
"text",
"navigation",
"travel",
"account"
]
}
+16
View File
@@ -0,0 +1,16 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M 12.248 21.969 a 1 1 0 0 1 -0.849 -0.17 C 9.539 20.193 4 14.993 4 10 a 8 8 0 0 1 16 0 C 20 10.42 19.961 10.841 19.888 11.262" />
<path d="m22 22-1.88-1.88" />
<circle cx="12" cy="10" r="3" />
<circle cx="18" cy="18" r="3" />
</svg>

After

Width:  |  Height:  |  Size: 451 B

+18
View File
@@ -0,0 +1,18 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"kongsgard"
],
"tags": [
"signal",
"broadcast",
"connectivity",
"live",
"frequency"
],
"categories": [
"devices",
"multimedia",
"social"
]
}
+18
View File
@@ -0,0 +1,18 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M13.414 13.414a2 2 0 1 1-2.828-2.828" />
<path d="M16.247 7.761a6 6 0 0 1 1.744 4.572" />
<path d="M19.075 4.933a10 10 0 0 1 2.234 10.72" />
<path d="m2 2 20 20" />
<path d="M4.925 19.067a10 10 0 0 1 0-14.134" />
<path d="M7.753 16.239a6 6 0 0 1 0-8.478" />
</svg>

After

Width:  |  Height:  |  Size: 487 B

+2 -2
View File
@@ -9,7 +9,7 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M16.466 7.5C15.643 4.237 13.952 2 12 2 9.239 2 7 6.477 7 12s2.239 10 5 10c.342 0 .677-.069 1-.2" />
<path d="m15.194 13.707 3.814 1.86-1.86 3.814" />
<path d="M19 15.57c-1.804.885-4.274 1.43-7 1.43-5.523 0-10-2.239-10-5s4.477-5 10-5c4.838 0 8.873 1.718 9.8 4" />
<path d="M16.47214 7.52786 A 5 10 0 1 0 13 21.79796" />
<path d="M21.79796 11 A 10 5 0 1 0 19 15.57071" />
</svg>

Before

Width:  |  Height:  |  Size: 486 B

After

Width:  |  Height:  |  Size: 371 B

+60
View File
@@ -0,0 +1,60 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"colebemis",
"karsa-mistmere",
"RajnishKMehta"
],
"tags": [
"cybersecurity",
"secure",
"safety",
"protection",
"guardian",
"armored",
"armoured",
"defense",
"defence",
"defender",
"block",
"threat",
"prevention",
"antivirus",
"vigilance",
"vigilant",
"detection",
"scan",
"find",
"strength",
"strong",
"tough",
"invincible",
"invincibility",
"invulnerable",
"undamaged",
"audit",
"admin",
"verification",
"crest",
"shieldcog",
"bravery",
"knight",
"foot soldier",
"infantry",
"trooper",
"pawn",
"battle",
"war",
"military",
"army",
"cadet",
"scout"
],
"categories": [
"account",
"security",
"development",
"gaming",
"shapes"
]
}
+22
View File
@@ -0,0 +1,22 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M11 22c-3.806-1.45-7-3.966-7-9V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1v4" />
<path d="M14.923 16.547 14 16.164" />
<path d="m14.923 18.843-.923.383" />
<path d="M16.547 14.923 16.164 14" />
<path d="m16.547 20.467-.383.924" />
<path d="m18.843 14.923.383-.923" />
<path d="m19.225 21.391-.382-.924" />
<path d="m20.467 16.547.923-.383" />
<path d="m20.467 18.843.923.383" />
<circle cx="17.695" cy="17.695" r="3" />
</svg>

After

Width:  |  Height:  |  Size: 709 B

+59
View File
@@ -0,0 +1,59 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"colebemis",
"karsa-mistmere",
"RajnishKMehta"
],
"tags": [
"cybersecurity",
"secure",
"safety",
"protection",
"guardian",
"armored",
"armoured",
"defense",
"defence",
"defender",
"block",
"threat",
"prevention",
"antivirus",
"vigilance",
"vigilant",
"detection",
"scan",
"find",
"strength",
"strong",
"tough",
"invincible",
"invincibility",
"invulnerable",
"undamaged",
"audit",
"admin",
"verification",
"crest",
"bravery",
"knight",
"foot soldier",
"infantry",
"trooper",
"pawn",
"battle",
"war",
"military",
"army",
"cadet",
"scout"
],
"categories": [
"account",
"security",
"development",
"gaming",
"shapes"
]
}
+22
View File
@@ -0,0 +1,22 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m10.929 14.467-.383.924" />
<path d="M10.929 8.923 10.546 8" />
<path d="M13.225 8.923 13.608 8" />
<path d="m13.607 15.391-.382-.924" />
<path d="m14.849 10.547.923-.383" />
<path d="m14.849 12.843.923.383" />
<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" />
<path d="m9.305 10.547-.923-.383" />
<path d="m9.305 12.843-.923.383" />
<circle cx="12.077" cy="11.695" r="3" />
</svg>

After

Width:  |  Height:  |  Size: 738 B

+20
View File
@@ -0,0 +1,20 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"Youya-ui"
],
"tags": [
"footwear",
"sports",
"running",
"athletic",
"shoe",
"sneaker",
"training",
"exercise",
"fitness"
],
"categories": [
"sports"
]
}
+15
View File
@@ -0,0 +1,15 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m15 10.42 4.8-5.07" />
<path d="M19 18h3" />
<path d="M9.5 22 21.414 9.415A2 2 0 0 0 21.2 6.4l-5.61-4.208A1 1 0 0 0 14 3v2a2 2 0 0 1-1.394 1.906L8.677 8.053A1 1 0 0 0 8 9c-.155 6.393-2.082 9-4 9a2 2 0 0 0 0 4h14" />
</svg>

After

Width:  |  Height:  |  Size: 439 B

@@ -19,6 +19,12 @@
"deprecationReason": "alias.name",
"deprecated": true,
"toBeRemovedInVersion": "v1.0"
},
{
"name": "text-select",
"deprecated": true,
"deprecationReason": "alias.name",
"toBeRemovedInVersion": "v1.0"
}
]
}

Before

Width:  |  Height:  |  Size: 601 B

After

Width:  |  Height:  |  Size: 601 B

+14
View File
@@ -0,0 +1,14 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"jguddas",
"karsa-mistmere"
],
"tags": [
"tags",
"history"
],
"categories": [
"time"
]
}
+20
View File
@@ -0,0 +1,20 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M4 12h.01" />
<path d="M4 16h.01" />
<path d="M4 20h.01" />
<path d="M4 4h.01" />
<path d="M4 8h.01" />
<path d="M9.414 13.414a2 2 0 0 0 1.414.586H19a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1h-8.172a2 2 0 0 0-1.414.586L8 12z" />
<path d="M9.414 21.414a2 2 0 0 0 1.414.586H19a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1h-8.172a2 2 0 0 0-1.414.586L8 20z" />
<path d="M9.414 5.414A2 2 0 0 0 10.828 6H19a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1h-8.172a2 2 0 0 0-1.414.586L8 4z" />
</svg>

After

Width:  |  Height:  |  Size: 675 B

+2 -1
View File
@@ -10,7 +10,8 @@
"camera",
"lightning",
"electricity",
"energy"
"energy",
"power"
],
"categories": [
"connectivity",
+3 -1
View File
@@ -9,7 +9,9 @@
"camera",
"lightning",
"electricity",
"energy"
"energy",
"power",
"quick"
],
"categories": [
"connectivity",
+3 -2
View File
@@ -4,6 +4,7 @@
"build": "pnpm -r --filter './packages/**' build",
"test": "pnpm -r --filter './packages/**' test",
"test:update": "pnpm -r --filter './packages/**' --filter !'./packages/lucide-angular' test -- -u",
"test:font": "pnpm --filter build-font test",
"lucide": "pnpm --filter lucide",
"lucide-angular": "pnpm --filter lucide-angular",
"lucide-react": "pnpm --filter lucide-react",
@@ -21,7 +22,7 @@
"addjsons": "node ./scripts/addMissingIconJsonFiles.mts",
"checkIcons": "node ./scripts/checkIconsAndCategories.mts",
"generate:changelog": "node ./scripts/generateChangelog.mts",
"generate:backers": "node scripts/updateOpenCollectiveBackers.mts",
"generate:sponsors": "node scripts/updateSponsors.mts",
"generate:contributors": "node ./scripts/updateContributors.mts icons/*.svg",
"generate:nextJSAliases": "node ./scripts/generateNextJSAliases.mts",
"suggest:tags": "node ./scripts/suggestTags.mts",
@@ -71,7 +72,7 @@
"prettier-plugin-astro": "^0.14.1",
"semver": "^7.7.3",
"simple-git": "^3.32.3",
"svgo": "^3.3.2",
"svgo": "^3.3.3",
"svgson": "^5.3.1",
"vitest": "4.0.12",
"yargs": "^17.7.2",
+16 -2
View File
@@ -63,6 +63,8 @@ Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintaine
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
[//]: <> (Sponsors)
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
@@ -71,7 +73,19 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
### Hero backers 🦸
<a href="https://https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud – From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://www.scipress.io?utm_source=lucide"><img src="https://lucide.dev/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
<a href="https://www.paxhistoria.co/"><img src="https://lucide.dev/sponsors/paxhistoria.svg?" width="180" alt="Pax Historia – An alternate history sandbox game" /></a>
### Backers ☕
<a href="https://www.fina.money/"><img src="https://lucide.dev/sponsors/fina-money.png" width="180" alt="Fina Money – Modular Finance Tracker" /></a>
### Other contributors 💸
You can find all our past and non-recurring financial contributors at [our Open Collective page](https://opencollective.com/lucide-icons).
+1 -1
View File
@@ -11,7 +11,7 @@
"projectType": "library",
"root": ".",
"sourceRoot": "./src",
"prefix": "lib",
"prefix": "lucide",
"architect": {
"build": {
"builder": "@angular/build:ng-packagr",
+1 -1
View File
@@ -23,7 +23,7 @@
"clean": "rm -rf dist && rm -rf ./src/icons/*.ts",
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --iconFileExtension=.ts --exportFileName=lucide-angular.ts --useDefaultExports=0",
"build:ng": "ng build --configuration production",
"test": "pnpm prebuild && ng test --no-watch",
"test": "pnpm build:icons && ng test --no-watch",
"test:watch": "ng test",
"lint": "npx eslint 'src/**/*.{js,jsx,ts,tsx,html,css,scss}' --quiet --fix",
"e2e": "ng e2e",
+1 -1
View File
@@ -13,7 +13,7 @@ export interface LucideConfig {
* Width and height.
* @default 24
*/
size: number;
size: string | number;
/**
* Stroke width
* @default 2
@@ -113,12 +113,6 @@ describe('LucideIconBase', () => {
expect(getSvgAttribute('width')).toBe('18');
expect(getSvgAttribute('height')).toBe('18');
});
it('should use default on invalid string', () => {
size.set('large');
fixture.detectChanges();
expect(getSvgAttribute('width')).toBe('24');
expect(getSvgAttribute('height')).toBe('24');
});
});
describe('strokeWidth', () => {
@@ -132,9 +126,9 @@ describe('LucideIconBase', () => {
expect(getSvgAttribute('stroke-width')).toBe('1.41');
});
it('should allow string stroke width', () => {
strokeWidth.set('1px');
strokeWidth.set('0.125rem');
fixture.detectChanges();
expect(getSvgAttribute('stroke-width')).toBe('1');
expect(getSvgAttribute('stroke-width')).toBe('0.125rem');
});
});
+5 -21
View File
@@ -13,20 +13,6 @@ import { LucideIconData, Nullable } from './types';
import defaultAttributes from './default-attributes';
import { lucideIconTemplate } from './lucide-icon-template';
function transformNumericStringInput(
value: Nullable<string | number>,
defaultValue: number,
): number {
if (typeof value === 'string') {
const parsedValue = parseInt(value, 10);
if (isNaN(parsedValue)) {
return defaultValue;
}
return parsedValue;
}
return value ?? defaultValue;
}
/**
* @internal
*/
@@ -37,10 +23,10 @@ function transformNumericStringInput(
host: {
...defaultAttributes,
class: 'lucide',
'[attr.width]': 'size().toString(10)',
'[attr.height]': 'size().toString(10)',
'[attr.width]': 'size()',
'[attr.height]': 'size()',
'[attr.stroke]': 'color()',
'[attr.stroke-width]': 'strokeWidth().toString(10)',
'[attr.stroke-width]': 'strokeWidth()',
'[attr.aria-hidden]': '!title()',
},
})
@@ -68,8 +54,7 @@ export abstract class LucideIconBase {
* @default 24
*/
readonly size = input(this.iconConfig.size, {
transform: (value: Nullable<string | number>) =>
transformNumericStringInput(value, this.iconConfig.size),
transform: (value: Nullable<string | number>) => value ?? this.iconConfig.size,
});
/**
* Stroke color.
@@ -83,8 +68,7 @@ export abstract class LucideIconBase {
* @default 2
*/
readonly strokeWidth = input(this.iconConfig.strokeWidth, {
transform: (value: Nullable<string | number>) =>
transformNumericStringInput(value, this.iconConfig.strokeWidth),
transform: (value: Nullable<string | number>) => value ?? this.iconConfig.strokeWidth,
});
/**
* If set to true, it adds [`vector-effect="non-scaling-stroke"`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Attribute/vector-effect) to child elements.
+2 -2
View File
@@ -19,9 +19,9 @@ export type LucideIconData = {
*/
interface LucideIconProps {
title: Signal<Nullable<string>>;
size: Signal<Nullable<number>>;
size: Signal<Nullable<number | string>>;
color: Signal<Nullable<string>>;
strokeWidth: Signal<Nullable<number>>;
strokeWidth: Signal<Nullable<number | string>>;
absoluteStrokeWidth: Signal<Nullable<boolean>>;
}
+6 -1
View File
@@ -60,6 +60,8 @@ Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintaine
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
[//]: <> (Sponsors)
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
@@ -68,7 +70,10 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
[//]: <> (Open Collective backers)
### Hero backers 🦸
<a href="https://https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud – From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
+2 -2
View File
@@ -57,9 +57,9 @@
"linkedom": "^0.18.5",
"prettier": "^3.4.2",
"typescript": "^5.8.3",
"vite": "^7.3.1",
"vite": "^7.3.2",
"vitest": "^4.1.1",
"astro": "^6.0.8"
"astro": "^6.1.6"
},
"peerDependencies": {
"astro": "^4 || ^5 || ^6"
+16 -2
View File
@@ -68,6 +68,8 @@ Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintaine
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
[//]: <> (Sponsors)
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
@@ -76,7 +78,19 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
### Hero backers 🦸
<a href="https://https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud – From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://www.scipress.io?utm_source=lucide"><img src="https://lucide.dev/sponsors/scipress.svg" width="180" alt="Scipress sponsor badge" /></a>
<a href="https://github.com/pdfme/pdfme"><img src="../../docs/public/sponsors/pdfme.svg" width="180" alt="pdfme sponsor badge" /></a>
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
<a href="https://www.paxhistoria.co/"><img src="https://lucide.dev/sponsors/paxhistoria.svg?" width="180" alt="Pax Historia – An alternate history sandbox game" /></a>
### Backers ☕
<a href="https://www.fina.money/"><img src="https://lucide.dev/sponsors/fina-money.png" width="180" alt="Fina Money – Modular Finance Tracker" /></a>
### Other contributors 💸
You can find all our past and non-recurring financial contributors at [our Open Collective page](https://opencollective.com/lucide-icons).
+6 -1
View File
@@ -59,6 +59,8 @@ Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintaine
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
[//]: <> (Sponsors)
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
@@ -67,7 +69,10 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
[//]: <> (Open Collective backers)
### Hero backers 🦸
<a href="https://https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud – From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
+6 -1
View File
@@ -59,6 +59,8 @@ Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintaine
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
[//]: <> (Sponsors)
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
@@ -67,7 +69,10 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
[//]: <> (Open Collective backers)
### Hero backers 🦸
<a href="https://https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud – From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
+2 -2
View File
@@ -24,11 +24,11 @@
"author": "Eric Fennis",
"amdName": "lucide-preact",
"main": "dist/cjs/lucide-preact.js",
"module": "dist/esm/lucide-preact.js",
"module": "dist/esm/lucide-preact.mjs",
"exports": {
".": {
"types": "./dist/lucide-preact.d.ts",
"import": "./dist/esm/lucide-preact.js",
"import": "./dist/esm/lucide-preact.mjs",
"require": "./dist/cjs/lucide-preact.js"
}
},
+4 -2
View File
@@ -17,11 +17,12 @@ const bundles = [
inputs,
outputDir,
preserveModules: true,
extension: 'mjs',
},
];
const configs = bundles
.map(({ inputs, outputDir, format, preserveModules }) =>
.map(({ inputs, outputDir, format, preserveModules, extension = 'js' }) =>
inputs.map((input) => ({
input,
plugins: plugins({ pkg }),
@@ -31,9 +32,10 @@ const configs = bundles
...(preserveModules
? {
dir: `${outputDir}/${format}`,
entryFileNames: `[name].${extension}`,
}
: {
file: `${outputDir}/${format}/${outputFileName}.js`,
file: `${outputDir}/${format}/${outputFileName}.${extension}`,
}),
preserveModules,
format,
+6 -1
View File
@@ -59,6 +59,8 @@ Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintaine
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
[//]: <> (Sponsors)
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
@@ -67,7 +69,10 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
[//]: <> (Open Collective backers)
### Hero backers 🦸
<a href="https://https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud – From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
+6 -6
View File
@@ -24,20 +24,20 @@
"author": "Eric Fennis",
"amdName": "lucide-react-native",
"main": "dist/cjs/lucide-react-native.js",
"module": "dist/esm/lucide-react-native.js",
"module": "dist/esm/lucide-react-native.mjs",
"typings": "dist/lucide-react-native.d.ts",
"react-native": "dist/esm/lucide-react-native.js",
"react-native": "dist/esm/lucide-react-native.mjs",
"exports": {
".": {
"types": "./dist/lucide-react-native.d.ts",
"import": "./dist/esm/lucide-react-native.js",
"browser": "./dist/esm/lucide-react-native.js",
"import": "./dist/esm/lucide-react-native.mjs",
"browser": "./dist/esm/lucide-react-native.mjs",
"require": "./dist/cjs/lucide-react-native.js"
},
"./icons": {
"types": "./dist/icons.d.ts",
"import": "./dist/esm/icons/index.js",
"browser": "./dist/esm/icons/index.js",
"import": "./dist/esm/icons/index.mjs",
"browser": "./dist/esm/icons/index.mjs",
"require": "./dist/cjs/icons/index.js"
}
},
@@ -18,11 +18,12 @@ const bundles = [
inputs,
outputDir,
preserveModules: true,
extension: 'mjs',
},
];
const configs = bundles
.map(({ inputs, outputDir, format, preserveModules }) =>
.map(({ inputs, outputDir, format, preserveModules, extension = 'js' }) =>
inputs.map((input) => ({
input,
plugins: plugins({ pkg }),
@@ -33,9 +34,10 @@ const configs = bundles
? {
dir: `${outputDir}/${format}`,
exports: 'auto',
entryFileNames: `[name].${extension}`,
}
: {
file: `${outputDir}/${format}/${outputFileName}.js`,
file: `${outputDir}/${format}/${outputFileName}.${extension}`,
}),
format,
preserveModules,
+6 -1
View File
@@ -59,6 +59,8 @@ Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintaine
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
[//]: <> (Sponsors)
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
@@ -67,7 +69,10 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
[//]: <> (Open Collective backers)
### Hero backers 🦸
<a href="https://https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud – From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
+4
View File
@@ -0,0 +1,4 @@
// This file is make sure that we can import from "lucide-react/dynamic" without having to specify the extension. This is a workaround for the fact that some bundlers (like Webpack) don't support importing from "lucide-react/dynamic.mjs" without specifying the extension.
// Unfortunately, we can't use exports field in package.json because it breaks some runtime environments. See https://github.com/lucide-icons/lucide/issues/2743#issuecomment-2626784300
// eslint-disable-next-line import/no-unresolved
export * from './dynamic.mjs';
+1 -1
View File
@@ -1 +1 @@
export { default } from './dist/esm/dynamicIconImports.js';
export { default } from './dist/esm/dynamicIconImports.mjs';
+7 -5
View File
@@ -24,22 +24,24 @@
"author": "Eric Fennis",
"amdName": "lucide-react",
"main": "dist/cjs/lucide-react.js",
"module": "dist/esm/lucide-react.js",
"module": "dist/esm/lucide-react.mjs",
"typings": "dist/lucide-react.d.ts",
"sideEffects": false,
"files": [
"dist",
"dynamic.js",
"dynamic.mjs",
"dynamic.js.map",
"dynamic.mjs.map",
"dynamic.d.mts",
"dynamic.d.ts",
"dynamicIconImports.mjs",
"dynamicIconImports.js.map",
"dynamicIconImports.d.ts"
"dynamicIconImports.d.ts",
"dynamicIconImports.d.mts"
],
"scripts": {
"build": "pnpm clean && pnpm copy:license && pnpm build:icons && pnpm typecheck && pnpm build:bundles",
"copy:license": "cp ../../LICENSE ./LICENSE",
"clean": "rm -rf dist && rm -rf stats && rm -rf ./src/icons/*.ts && rm -f dynamic.* && rm -f dynamicIconImports.d.ts",
"clean": "rm -rf dist && rm -rf stats && rm -rf ./src/icons/*.ts && rm -f dynamic.{d.ts,d.mts,mts,mjs,mjs.map} && rm -f dynamicIconImports.d.ts",
"build:icons": "build-icons --output=./src --templateSrc=./scripts/exportTemplate.mts --renderUniqueKey --withAliases --withDynamicImports --separateAliasesFile --separateAliasesFileIgnore=fingerprint --aliasesFileExtension=.ts --iconFileExtension=.ts --exportFileName=index.ts",
"build:bundles": "rollup -c ./rollup.config.mjs",
"typecheck": "tsc",
+26 -5
View File
@@ -14,31 +14,41 @@ const bundles = [
format: 'cjs',
inputs,
outputDir: 'dist/cjs',
extension: 'js',
},
{
format: 'esm',
inputs,
outputDir: 'dist/esm',
preserveModules: true,
extension: 'mjs',
},
{
format: 'esm',
inputs: ['src/dynamicIconImports.ts', 'src/DynamicIcon.ts', ...aliasesEntries],
outputDir: 'dist/esm',
external: [/src/],
preserveModules: true,
extension: 'mjs',
paths: (id) => {
if (id.match(/src/)) {
const [, modulePath] = id.match(/src\/(.*)\.ts/);
return `./${modulePath}.mjs`;
}
},
},
{
format: 'esm',
inputs: ['src/dynamic.ts'],
outputFile: 'dynamic.mjs',
external: [/src/],
extension: 'mjs',
paths: (id) => {
if (id.match(/src/)) {
const [, modulePath] = id.match(/src\/(.*)\.ts/);
return `dist/esm/${modulePath}.js`;
return `dist/esm/${modulePath}.mjs`;
}
},
},
@@ -54,6 +64,7 @@ const configs = bundles
minify,
preserveModules,
entryFileNames,
extension = 'js',
external = [],
paths,
}) =>
@@ -63,7 +74,7 @@ const configs = bundles
...plugins({ pkg, minify }),
// Make sure we emit "use client" directive to make it compatible with Next.js
preserveDirectives({
include: ['src/DynamicIcon.ts', 'src/context.ts', 'src/Icon.ts'],
include: ['src/lucide-react.ts', 'src/DynamicIcon.ts', 'src/context.ts', 'src/Icon.ts'],
suppressPreserveModulesWarning: true,
}),
],
@@ -73,12 +84,12 @@ const configs = bundles
...(preserveModules
? {
dir: outputDir,
entryFileNames: entryFileNames ?? `[name].${extension}`,
}
: {
file: outputFile ?? `${outputDir}/${outputFileName}.js`,
file: outputFile ?? `${outputDir}/${outputFileName}.${extension}`,
}),
paths,
entryFileNames,
format,
sourcemap: true,
preserveModules,
@@ -100,6 +111,11 @@ export default [
file: `dynamicIconImports.d.ts`,
format: 'es',
},
// Extra declaration file with .d.mts extension for better compatibility with ESM environments
{
file: `dynamicIconImports.d.mts`,
format: 'es',
},
],
plugins: [dts()],
},
@@ -110,6 +126,11 @@ export default [
file: `dynamic.d.ts`,
format: 'es',
},
// Extra declaration file with .d.mts extension for better compatibility with ESM environments
{
file: `dynamic.d.mts`,
format: 'es',
},
],
plugins: [dts()],
},
+6 -1
View File
@@ -59,6 +59,8 @@ Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintaine
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
[//]: <> (Sponsors)
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
@@ -67,7 +69,10 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
[//]: <> (Open Collective backers)
### Hero backers 🦸
<a href="https://https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud – From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
+7 -7
View File
@@ -25,7 +25,7 @@
"author": "Eric Fennis",
"source": "src/lucide-solid.ts",
"main": "dist/cjs/lucide-solid.js",
"module": "dist/esm/lucide-solid.js",
"module": "dist/esm/lucide-solid.mjs",
"types": "dist/types/lucide-solid.d.ts",
"files": [
"dist"
@@ -34,22 +34,22 @@
".": {
"types": "./dist/types/lucide-solid.d.ts",
"solid": "./dist/source/lucide-solid.jsx",
"import": "./dist/esm/lucide-solid.js",
"browser": "./dist/esm/lucide-solid.js",
"import": "./dist/esm/lucide-solid.mjs",
"browser": "./dist/esm/lucide-solid.mjs",
"require": "./dist/cjs/lucide-solid.js"
},
"./icons": {
"types": "./dist/types/lucide-solid.d.ts",
"solid": "./dist/source/lucide-solid.jsx",
"import": "./dist/esm/lucide-solid.js",
"browser": "./dist/esm/lucide-solid.js",
"import": "./dist/esm/lucide-solid.mjs",
"browser": "./dist/esm/lucide-solid.mjs",
"require": "./dist/cjs/lucide-solid.js"
},
"./icons/*": {
"types": "./dist/types/icons/*.d.ts",
"solid": "./dist/source/icons/*.jsx",
"import": "./dist/esm/icons/*.js",
"browser": "./dist/esm/icons/*.js",
"import": "./dist/esm/icons/*.mjs",
"browser": "./dist/esm/icons/*.mjs",
"require": "./dist/cjs/icons/*.js"
}
},
+4 -2
View File
@@ -23,11 +23,12 @@ const bundles = [
inputs,
outputDir,
preserveModules: true,
extension: 'mjs',
},
];
const configs = bundles
.map(({ inputs, outputDir, format, preserveModules }) =>
.map(({ inputs, outputDir, format, preserveModules, extension = 'js' }) =>
inputs.map((input) => ({
input,
plugins: [
@@ -117,9 +118,10 @@ const configs = bundles
? {
dir: `${outputDir}/${format}`,
exports: 'auto',
entryFileNames: `[name].${extension}`,
}
: {
file: `${outputDir}/${format}/${outputFileName}.js`,
file: `${outputDir}/${format}/${outputFileName}.${extension}`,
}),
format: format === 'source' ? 'esm' : format,
preserveModules,
+6 -1
View File
@@ -73,6 +73,8 @@ Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintaine
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
[//]: <> (Sponsors)
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
@@ -81,7 +83,10 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
[//]: <> (Open Collective backers)
### Hero backers 🦸
<a href="https://https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud – From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
+1 -1
View File
@@ -24,7 +24,7 @@
],
"source": "src/lucide-static.js",
"main": "dist/cjs/lucide-static.js",
"module": "dist/esm/lucide-static.js",
"module": "dist/esm/lucide-static.mjs",
"typings": "dist/lucide-static.d.ts",
"sideEffects": false,
"scripts": {
+4 -2
View File
@@ -16,11 +16,12 @@ const bundles = [
inputs,
outputDir,
preserveModules: true,
extension: 'mjs',
},
];
const configs = bundles
.map(({ inputs, outputDir, format, minify, preserveModules }) =>
.map(({ inputs, outputDir, format, minify, preserveModules, extension = 'js' }) =>
inputs.map((input) => ({
input,
plugins: plugins({ pkg, minify }),
@@ -29,9 +30,10 @@ const configs = bundles
...(preserveModules
? {
dir: `${outputDir}/${format}`,
entryFileNames: `[name].${extension}`,
}
: {
file: `${outputDir}/${format}/${outputFileName}${minify ? '.min' : ''}.js`,
file: `${outputDir}/${format}/${outputFileName}${minify ? '.min' : ''}.${extension}`,
}),
format,
sourcemap: true,
@@ -15,6 +15,7 @@ export default async function generateSprite(
value: '',
attributes: {
id: name,
viewBox: parsedSvg.attributes.viewBox,
},
children: parsedSvg.children,
}));
+6 -1
View File
@@ -60,6 +60,8 @@ Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintaine
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
[//]: <> (Sponsors)
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
@@ -68,7 +70,10 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
[//]: <> (Open Collective backers)
### Hero backers 🦸
<a href="https://https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud – From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
+6 -1
View File
@@ -59,6 +59,8 @@ Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintaine
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
[//]: <> (Sponsors)
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
@@ -67,7 +69,10 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
[//]: <> (Open Collective backers)
### Hero backers 🦸
<a href="https://https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud – From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
+6 -1
View File
@@ -69,6 +69,8 @@ Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintaine
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
[//]: <> (Sponsors)
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
@@ -77,7 +79,10 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
[//]: <> (Open Collective backers)
### Hero backers 🦸
<a href="https://https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud – From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
+1 -1
View File
@@ -25,7 +25,7 @@
"source": "src/lucide.js",
"main": "dist/cjs/lucide.js",
"main:umd": "dist/umd/lucide.js",
"module": "dist/esm/lucide.js",
"module": "dist/esm/lucide.mjs",
"unpkg": "dist/umd/lucide.min.js",
"typings": "dist/lucide.d.ts",
"sideEffects": false,
+1
View File
@@ -53,6 +53,7 @@ const configs = bundles
...(preserveModules
? {
dir: `${outputDir}/${format}`,
entryFileNames: '[name].mjs',
}
: {
file: `${outputDir}/${format}/${outputFileName}${minify ? '.min' : ''}.js`,
+6 -1
View File
@@ -60,6 +60,8 @@ Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintaine
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
[//]: <> (Sponsors)
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
@@ -68,7 +70,10 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
[//]: <> (Open Collective backers)
### Hero backers 🦸
<a href="https://https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud – From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
@@ -7,6 +7,8 @@ import { getJSBanner } from './license.mts';
const currentDir = getCurrentDirPath(import.meta.url);
const targetDirectory = path.join(currentDir, '../dist');
const jsBanner = getJSBanner();
const files = await readdir(targetDirectory, {
recursive: true,
encoding: 'utf-8',
@@ -22,43 +24,10 @@ for (const file of files) {
// eslint-disable-next-line no-await-in-loop
const contents = (await readFile(filepath, { encoding: 'utf-8' })) as unknown as string;
let newContents = contents;
const ext = path.extname(filepath);
let license;
if (/\.(js|mjs|cjs|ts)/.test(ext)) {
license = getJSBanner();
}
if (license) {
newContents = license + contents;
}
// Places icon block comment at the top of the Svelte component class
if (/icons\/(.*?)\.svelte\.d\.ts/.test(filepath)) {
const svelteFilepath = filepath.replace('.d.ts', '');
// eslint-disable-next-line no-await-in-loop
const svelteFileContents = (await readFile(svelteFilepath, { encoding: 'utf-8' })) as unknown as string;;
const blockCommentRegex = /\/\*\*\n\s\*\s(@component\s@name)[\s\S]*?\*\//;
const blockCommentMatch = blockCommentRegex.exec(svelteFileContents);
if (blockCommentMatch !== null) {
const blockComment = blockCommentMatch[0];
const exportClassRegex = /export default class (\w+) extends SvelteComponentTyped<(.*?)> {/;
if (exportClassRegex.test(newContents)) {
newContents = newContents.replace(
exportClassRegex,
`${blockComment}\nexport default class $1 extends SvelteComponentTyped<$2> {`,
);
}
}
}
if (newContents !== contents) {
// eslint-disable-next-line no-await-in-loop
await writeFile(filepath, newContents, { encoding: 'utf-8' });
await writeFile(filepath, jsBanner + contents, { encoding: 'utf-8' });
}
}
+20 -29
View File
@@ -1,43 +1,34 @@
import base64SVG from '@lucide/build-icons/utils/base64SVG';
import { getJSBanner } from './license.mts';
import { getHTMLBanner } from './license.mts';
import defineExportTemplate from '@lucide/build-icons/utils/defineExportTemplate';
export default defineExportTemplate(async ({
iconName,
children,
componentName,
getSvg,
deprecated,
deprecationReason,
}) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
export default defineExportTemplate(
async ({ iconName, children, getSvg, deprecated, deprecationReason }) => {
const svgContents = await getSvg();
const svgBase64 = base64SVG(svgContents);
return `\
return `\
${getHTMLBanner()}
<script lang="ts">
${getJSBanner()}
import Icon from '../Icon.svelte';
import type { IconNode, IconProps } from '../types.js';
let props: IconProps = $props();
const iconNode: IconNode = ${JSON.stringify(children)};
/**
* @component @name ${componentName}
* @description Lucide SVG icon component, renders SVG Element with children.
*
* @preview ![img](data:image/svg+xml;base64,${svgBase64}) - https://lucide.dev/icons/${iconName}
* @see https://lucide.dev/guide/packages/lucide-svelte - Documentation
*
* @param {Object} props - Lucide icons props and any valid SVG attribute
* @returns {FunctionalComponent} Svelte component
* ${deprecated ? `@deprecated ${deprecationReason}` : ''}
*/
</script>
<Icon name="${iconName}" {...props} iconNode={iconNode}>
{@render props.children?.()}
</Icon>
<!--
@component
Lucide SVG icon component, renders SVG Element with children.
@preview ![img](data:image/svg+xml;base64,${svgBase64}) - https://lucide.dev/icons/${iconName}
@see https://lucide.dev/guide/packages/lucide-svelte - Documentation
${deprecated ? `\n@deprecated ${deprecationReason}\n` : ''}\
-->
<Icon name="${iconName}" {...props} iconNode={iconNode} />
`;
});
},
);
+17 -2
View File
@@ -3,11 +3,26 @@ import pkg from '../package.json' with { type: 'json' };
const license = fs.readFileSync('LICENSE', 'utf-8');
export function getHTMLBanner() {
return `\
<!--
${pkg.name} v${pkg.version} - ${pkg.license}
This source code is licensed under the ${pkg.license} license.
See the LICENSE file in the root directory of this source tree.
-->
`;
}
export function getJSBanner() {
return `/**
return `\
/**
* @file
* @license ${pkg.name} v${pkg.version} - ${pkg.license}
*
* ${license.split('\n').join('\n * ')}
* This source code is licensed under the ${pkg.license} license.
* See the LICENSE file in the root directory of this source tree.
*/
`;
}
@@ -2,6 +2,7 @@
exports[`Using lucide icon components > should add a class to the element 1`] = `
<svg
aria-hidden="true"
class="lucide-icon lucide lucide-smile my-icon"
fill="none"
height="24"
@@ -51,14 +52,13 @@ exports[`Using lucide icon components > should add a class to the element 1`] =
<!---->
<!---->
<!---->
</svg>
`;
exports[`Using lucide icon components > should adjust the size, stroke color and stroke width 1`] = `
<div>
<svg
aria-hidden="true"
class="lucide-icon lucide lucide-smile"
fill="none"
height="48"
@@ -108,8 +108,6 @@ exports[`Using lucide icon components > should adjust the size, stroke color and
<!---->
<!---->
<!---->
</svg>
@@ -126,6 +124,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
class="lucide-icon lucide lucide-smile"
>
<circle cx="12"
@@ -153,6 +152,7 @@ exports[`Using lucide icon components > should not scale the strokeWidth when ab
exports[`Using lucide icon components > should render an component 1`] = `
<div>
<svg
aria-hidden="true"
class="lucide-icon lucide lucide-smile"
fill="none"
height="24"
@@ -202,8 +202,6 @@ exports[`Using lucide icon components > should render an component 1`] = `
<!---->
<!---->
<!---->
</svg>
@@ -260,12 +258,10 @@ exports[`Using lucide icon components > should render an icon slot 1`] = `
</line>
<!---->
<!---->
<text>
Test
</text>
<!---->
</svg>
@@ -94,3 +94,23 @@ describe('Using lucide icon components', () => {
expect(IconComponent).toHaveAttribute('stroke-width', '1');
});
});
describe('Icon Component Accessibility', () => {
it('should have aria-hidden prop when no aria prop or children are present', async () => {
const { container } = render(Smile, {
props: {
size: 48,
color: 'red',
absoluteStrokeWidth: true,
},
});
expect(container.firstChild).toHaveAttribute('aria-hidden', 'true');
});
it('should not have aria-hidden prop when there are children that could be a <title> element', async () => {
const { container } = render(TestSlots);
expect(container.firstChild).not.toHaveAttribute('aria-hidden');
});
});
+6 -1
View File
@@ -52,6 +52,8 @@ Join the [Discord server](https://discord.gg/EH6nSts) to chat with the maintaine
Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/license).
[//]: <> (Sponsors)
## Sponsors
<a href="https://vercel.com?utm_source=lucide&utm_campaign=oss">
@@ -60,7 +62,10 @@ Lucide is licensed under the ISC license. See [LICENSE](https://lucide.dev/licen
<a href="https://www.digitalocean.com/?refcode=b0877a2caebd&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge"><img src="https://lucide.dev/digitalocean.svg" width="200" alt="DigitalOcean Referral Badge" /></a>
[//]: <> (Open Collective backers)
### Hero backers 🦸
<a href="https://https://zephyr-cloud.io/"><img src="https://lucide.dev/sponsors/zephyr-cloud.svg" width="180" alt="Zephyr Cloud – From idea to prod: fast micro-frontend delivery!" /></a>
### Awesome backers 🍺
<a href="https://github.com/pdfme/pdfme"><img src="https://lucide.dev/sponsors/pdfme.svg" width="180" alt="pdfme – Open-source PDF generation library built with TypeScript and React." /></a>
+1 -1
View File
@@ -23,7 +23,7 @@
"amdName": "lucide-vue",
"source": "build/lucide-vue.js",
"main": "dist/cjs/lucide-vue.js",
"module": "dist/esm/lucide-vue.js",
"module": "dist/esm/lucide-vue.mjs",
"typings": "dist/lucide-vue.d.ts",
"sideEffects": false,
"files": [
+4 -2
View File
@@ -17,11 +17,12 @@ const bundles = [
inputs,
outputDir,
preserveModules: true,
extension: 'mjs',
},
];
const configs = bundles
.map(({ inputs, outputDir, format, minify, preserveModules }) =>
.map(({ inputs, outputDir, format, minify, preserveModules, extension = 'js' }) =>
inputs.map((input) => ({
input,
plugins: plugins({ pkg, minify }),
@@ -31,9 +32,10 @@ const configs = bundles
...(preserveModules
? {
dir: `${outputDir}/${format}`,
entryFileNames: `[name].${extension}`,
}
: {
file: `${outputDir}/${format}/${outputFileName}.js`,
file: `${outputDir}/${format}/${outputFileName}.${extension}`,
}),
format,
preserveModules,
+1245 -899
View File
File diff suppressed because it is too large Load Diff
@@ -2,13 +2,13 @@ import { readdirSync } from 'node:fs';
import { join } from 'node:path';
import { existsSync, readFileSync, writeFileSync } from 'fs';
function updateOpenCollectiveBackers() {
function updateSponsors() {
const packagesPath = join(import.meta.dirname, '../packages');
const mainReadme = readFileSync(join(import.meta.dirname, '../README.md'), 'utf8');
const awesomeRegex = /(\[\/\/]: <> \(Open Collective backers\).+)$/s;
const awesomeBlock = awesomeRegex.exec(mainReadme);
if (awesomeBlock?.[1]) {
const packageAwesomeBlock = awesomeBlock[1].replaceAll('src="docs/public/sponsors', 'src="https://lucide.dev/sponsors')
const sponsorsRegex = /(\[\/\/]: <> \(Sponsors\).+)$/s;
const sponsorsBlock = sponsorsRegex.exec(mainReadme);
if (sponsorsBlock?.[1]) {
const packageSponsorsBlock = sponsorsBlock[1].replaceAll('src="docs/public', 'src="https://lucide.dev')
readdirSync(packagesPath).forEach((packagePath) => {
if (['lucide-figma', 'shared'].includes(packagePath) || packagePath.startsWith('.')) {
return;
@@ -16,19 +16,19 @@ function updateOpenCollectiveBackers() {
const readmePath = join(packagesPath, packagePath, 'README.md');
if (existsSync(readmePath)) {
const readmeContent = readFileSync(readmePath, 'utf8');
if (awesomeRegex.test(readmeContent)) {
writeFileSync(readmePath, readmeContent.replace(awesomeRegex, packageAwesomeBlock));
console.log(`Upgraded Awesome backers block in ${readmePath}`);
if (sponsorsRegex.test(readmeContent)) {
writeFileSync(readmePath, readmeContent.replace(sponsorsRegex, packageSponsorsBlock));
console.log(`Upgraded Sponsors block in ${readmePath}`);
} else {
console.error(`Could not find Awesome backers block in ${readmePath}`)
console.error(`Could not find Sponsors block in ${readmePath}`)
}
} else {
console.warn(`No readme found at ${readmePath}`);
}
})
} else {
console.error('Could not identify Awesome backers block')
console.error('Could not identify Sponsors block')
}
}
updateOpenCollectiveBackers();
updateSponsors();
+2 -1
View File
@@ -6,7 +6,8 @@
"main": "main.ts",
"type": "module",
"scripts": {
"start": "node ./src/main.ts"
"start": "node ./src/main.ts",
"test": "vitest run"
},
"keywords": [],
"author": "",
@@ -0,0 +1,141 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { put } from '@vercel/blob';
import { allocateCodePoints } from './allocateCodepoints.ts';
vi.mock('@vercel/blob', () => ({
put: vi.fn(),
}));
function mockLatestCodePoints(codePoints: Record<string, number>) {
const response = {
json: vi.fn().mockResolvedValue(structuredClone(codePoints)),
};
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(response));
}
describe('allocateCodePoints', () => {
beforeEach(() => {
vi.spyOn(console, 'log').mockImplementation(() => {});
vi.spyOn(console, 'warn').mockImplementation(() => {});
});
afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllGlobals();
});
it('keeps existing code points and allocates new ones in sequence', async () => {
mockLatestCodePoints({ camera: 57400 });
const codePoints = await allocateCodePoints({
iconsWithAliases: [
['camera', ['camera-old']],
['new-icon', []],
],
});
expect(codePoints.camera).toBe(57400);
expect(codePoints['camera-old']).toBe(57400);
expect(codePoints['new-icon']).toBe(57401);
expect(fetch).toHaveBeenCalledWith(
'https://geoxmjocxfnaryc4.public.blob.vercel-storage.com/latest/font/codepoints.json',
);
});
it('preserves legacy alias code points when alias and canonical differ', async () => {
mockLatestCodePoints({ camera: 57400, shutter: 57405 });
const codePoints = await allocateCodePoints({
iconsWithAliases: [['camera', ['shutter']]],
});
expect(codePoints.camera).toBe(57400);
expect(codePoints.shutter).toBe(57405);
expect(console.log).toHaveBeenCalledWith(
expect.stringContaining('adding a legacy alias codepoint'),
);
});
it('throws when a legacy alias code point is already allocated and fixes are disabled', async () => {
mockLatestCodePoints({ 'icon-b': 57402, 'icon-a': 57400, 'alias-a': 57402 });
await expect(
allocateCodePoints({
iconsWithAliases: [
['icon-b', []],
['icon-a', ['alias-a']],
],
}),
).rejects.toThrow(
"The code point for alias 'alias-a' for 'icon-a' is already allocated to: 'icon-b'.",
);
});
it('throws when two canonical icons share the same code point without allowFixes', async () => {
mockLatestCodePoints({ 'icon-a': 57400, 'icon-b': 57400 });
await expect(
allocateCodePoints({
iconsWithAliases: [
['icon-a', []],
['icon-b', []],
],
}),
).rejects.toThrow(
"We couldn't assign a unique codepoint for 'icon-b', since '57400' was already taken up by 'icon-a'",
);
});
it('skips conflicting legacy alias code points when fixes are enabled', async () => {
mockLatestCodePoints({ 'icon-b': 57402, 'icon-a': 57400, 'alias-a': 57402 });
const codePoints = await allocateCodePoints({
allowFixes: true,
iconsWithAliases: [
['icon-b', []],
['icon-a', ['alias-a']],
],
});
expect(codePoints['icon-a']).toBe(57400);
expect(codePoints['alias-a']).toBe(57400);
expect(console.warn).toHaveBeenCalledWith(
expect.stringContaining("we're simply not adding this custom alias codepoint"),
);
});
it('reassigns duplicate canonical code points when fixes are enabled', async () => {
mockLatestCodePoints({ 'icon-a': 57400, 'icon-b': 57400 });
const codePoints = await allocateCodePoints({
allowFixes: true,
iconsWithAliases: [
['icon-a', []],
['icon-b', []],
],
});
expect(codePoints['icon-a']).toBe(57400);
expect(codePoints['icon-b']).toBe(57401);
expect(console.warn).toHaveBeenCalledWith(
expect.stringContaining("Assigning a new codepoint for 'icon-b'"),
);
});
it('uploads the merged code point map when saveCodePoints is enabled', async () => {
mockLatestCodePoints({ camera: 57400 });
await allocateCodePoints({
saveCodePoints: true,
iconsWithAliases: [['camera', ['camera-old']]],
});
expect(put).toHaveBeenCalledWith(
'latest/font/codepoints.json',
JSON.stringify({ camera: 57400, 'camera-old': 57400 }, null, 2),
{ access: 'public', allowOverwrite: true },
);
expect(console.log).toHaveBeenCalledWith('Code points uploaded to Vercel Blob Storage.');
});
});
+66 -20
View File
@@ -1,6 +1,4 @@
import { type IconAliases } from '@lucide/helpers';
import { promises as fs } from 'fs';
import { cwd } from 'process';
import { put } from '@vercel/blob';
const VERCEL_BLOB_CODEPOINTS_PATH = 'latest/font/codepoints.json';
@@ -17,43 +15,91 @@ async function getLatestCodePoints(): Promise<CodePoints> {
interface AllocateCodePointsOptions {
saveCodePoints?: boolean;
allowFixes?: boolean;
iconsWithAliases: IconAliases;
}
export async function allocateCodePoints({
saveCodePoints = false,
allowFixes = false,
iconsWithAliases,
}: AllocateCodePointsOptions): Promise<CodePoints> {
const baseCodePoints = await getLatestCodePoints();
const latestCodePoints = await getLatestCodePoints();
const endCodePoint = Math.max(...Object.values(baseCodePoints));
let maxCodePoint = Math.max(...Object.values(latestCodePoints));
let codePointMap = new Map<number, string>();
let newCodePoints: CodePoints = {};
await Promise.all(
iconsWithAliases.map(async ([iconName, aliases]) => {
if (!baseCodePoints[iconName]) {
console.log('Code point not found creating new one for', iconName);
baseCodePoints[iconName] = endCodePoint + 1;
}
for (const [iconName, aliases] of iconsWithAliases) {
let codePoint: number | null = null;
aliases.forEach((alias, index) => {
if (baseCodePoints[alias]) {
return;
// Check if the icon is already present.
if (iconName in latestCodePoints) {
codePoint = latestCodePoints[iconName];
}
// Check if one of the aliases of the icon is already present, we need to add this as a legacy codepoint for backwards compatibility
for (const alias of aliases) {
if (alias in latestCodePoints) {
if (codePoint && codePoint !== latestCodePoints[alias]) {
if (codePointMap.has(latestCodePoints[alias])) {
if (allowFixes) {
console.warn(
`The code point for alias '${alias}' for '${iconName}' is already allocated to: '${codePointMap.get(latestCodePoints[alias])}', we're simply not adding this custom alias codepoint.`,
);
} else {
throw new Error(
`The code point for alias '${alias}' for '${iconName}' is already allocated to: '${codePointMap.get(latestCodePoints[alias])}'.`,
);
}
} else {
console.log(
`'${alias}' uses a different codepoint than its canonical name '${iconName}', adding a legacy alias codepoint for: ${latestCodePoints[alias]}.`,
);
codePointMap.set(latestCodePoints[alias], iconName);
newCodePoints[alias] = latestCodePoints[alias];
}
}
codePoint ??= latestCodePoints[alias];
}
}
console.log('Code point not found creating new one for', alias);
// Assign a new codepoint, if no existing code point was found.
codePoint ??= ++maxCodePoint;
baseCodePoints[alias] = endCodePoint + index + 1;
});
}),
);
if (allowFixes && codePointMap.has(codePoint)) {
console.warn(
`Assigning a new codepoint for '${iconName}', since '${codePoint}' was already taken up by '${codePointMap.get(codePoint)}'.`,
);
codePoint = ++maxCodePoint;
}
if (codePointMap.has(codePoint)) {
throw new Error(
`We couldn't assign a unique codepoint for '${iconName}', since '${codePoint}' was already taken up by '${codePointMap.get(codePoint)}'`,
);
}
codePointMap.set(codePoint, iconName);
for (const name of [iconName, ...aliases]) {
if (!newCodePoints[name]) {
newCodePoints[name] = codePoint;
}
}
}
// Write the changes to the original map to keep its order.
for (const [name, codePoint] of Object.entries(newCodePoints)) {
latestCodePoints[name] = codePoint;
}
if (saveCodePoints) {
const content = JSON.stringify(baseCodePoints, null, 2);
const content = JSON.stringify(latestCodePoints, null, 2);
await put(VERCEL_BLOB_CODEPOINTS_PATH, content, { access: 'public', allowOverwrite: true });
console.log('Code points uploaded to Vercel Blob Storage.');
}
return baseCodePoints;
return latestCodePoints;
}
+63
View File
@@ -0,0 +1,63 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { buildFont } from './buildFont.ts';
import svgtofont from 'svgtofont';
vi.mock('svgtofont', () => ({
default: vi.fn(),
}));
describe('buildFont', () => {
beforeEach(() => {
vi.spyOn(console, 'time').mockImplementation(() => {});
vi.spyOn(console, 'timeEnd').mockImplementation(() => {});
vi.spyOn(console, 'log').mockImplementation(() => {});
});
afterEach(() => {
vi.restoreAllMocks();
});
it('calls svgtofont with expected settings and unicode resolver', async () => {
vi.mocked(svgtofont).mockResolvedValue(undefined);
await buildFont({
inputDir: '/icons',
targetDir: '/dist',
fontName: 'lucide',
classNamePrefix: 'icon',
codePoints: { camera: 57400 },
startUnicode: 57400,
});
expect(console.time).toHaveBeenCalledWith('Font generation');
expect(console.timeEnd).toHaveBeenCalledWith('Font generation');
expect(svgtofont).toHaveBeenCalledTimes(1);
const [options] = vi.mocked(svgtofont).mock.calls[0];
expect(options.src).toBe('/icons');
expect(options.dist).toBe('/dist');
expect(options.fontName).toBe('lucide');
expect(options.classNamePrefix).toBe('icon');
expect(options.addLigatures).toBe(true);
expect(options.getIconUnicode('camera')).toEqual([String.fromCharCode(57400), 57400]);
expect(() => options.getIconUnicode('missing')).toThrow('No codepoint found for icon: missing');
});
it('logs errors from svgtofont and still finishes timing', async () => {
const error = new Error('svgtofont failed');
vi.mocked(svgtofont).mockRejectedValue(error);
await buildFont({
inputDir: '/icons',
targetDir: '/dist',
fontName: 'lucide',
classNamePrefix: 'icon',
codePoints: { camera: 57400 },
startUnicode: 57400,
});
expect(console.log).toHaveBeenCalledWith(error);
expect(console.timeEnd).toHaveBeenCalledWith('Font generation');
});
});

Some files were not shown because too many files have changed in this diff Show More