Compare commits

...
Author SHA1 Message Date
Jiacheng Jianggithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Karsa
f53e5bfff0 feat(icons): add house-cog icon (#4904)
* feat(icons): add house-cog icon

* Apply suggestion from @github-actions[bot]

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Apply suggestion from @github-actions[bot]

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Apply suggestion from @github-actions[bot]

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(icons): address house-cog review feedback

* Update icons/house-cog.json

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

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Karsa <contact@karsa.org>
2026-09-24 07:42:30 +02:00
Eric Fennisandcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> f06ac67e33 chore(typchecking): More typecheck jobs for all packages (#4885)
* Add typecheck jobs

* update lockfile

* Fix types

* Fix lucide solid types

* Fix type lucide-react-native

* fix: align react-native svg mock children typing

* fix: format build font spec

* fix: restore shared workflow and RN class context

* fix: restore lucide-react build typecheck

* fix: keep react build generating icons

* fix: preserve builder color prop types

* test: cover RN provider class merge

* Revert 711255b

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-09-22 13:49:09 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> b0b1a4f8ab chore(deps): bump the vue-deps group with 3 updates (#4893)
Bumps the vue-deps group with 3 updates: [@vitejs/plugin-vue](https://github.com/vitejs/vite-plugin-vue/tree/HEAD/packages/plugin-vue), [@vue/test-utils](https://github.com/vuejs/test-utils) and [vue](https://github.com/vuejs/core).


Updates `@vitejs/plugin-vue` from 6.0.8 to 6.0.9
- [Release notes](https://github.com/vitejs/vite-plugin-vue/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-vue/blob/main/packages/plugin-vue/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-vue/commits/plugin-vue@6.0.9/packages/plugin-vue)

Updates `@vue/test-utils` from 2.5.0 to 2.5.1
- [Release notes](https://github.com/vuejs/test-utils/releases)
- [Commits](https://github.com/vuejs/test-utils/compare/v2.5.0...v2.5.1)

Updates `vue` from 3.5.42 to 3.5.43
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/compare/v3.5.42...v3.5.43)

---
updated-dependencies:
- dependency-name: "@vitejs/plugin-vue"
  dependency-version: 6.0.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: vue-deps
- dependency-name: "@vue/test-utils"
  dependency-version: 2.5.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: vue-deps
- dependency-name: vue
  dependency-version: 3.5.43
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: vue-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-22 07:54:14 +02:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
3a37bfa5cd chore(deps-dev): bump the angular-deps group across 1 directory with 14 updates (#4895)
* chore(deps-dev): bump the angular-deps group across 1 directory with 14 updates

Bumps the angular-deps group with 14 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@angular-eslint/builder](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/builder) | `22.2.0` | `22.5.0` |
| [@angular-eslint/schematics](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/schematics) | `22.2.0` | `22.5.0` |
| [@angular/build](https://github.com/angular/angular-cli) | `22.1.7` | `22.1.8` |
| [@angular/cli](https://github.com/angular/angular-cli) | `22.1.7` | `22.1.8` |
| [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `22.1.5` | `22.1.7` |
| [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `22.1.5` | `22.1.7` |
| [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `22.1.5` | `22.1.7` |
| [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `22.1.5` | `22.1.7` |
| [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `22.1.5` | `22.1.7` |
| [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `22.1.5` | `22.1.7` |
| [@angular/platform-server](https://github.com/angular/angular/tree/HEAD/packages/platform-server) | `22.1.5` | `22.1.7` |
| [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `22.1.5` | `22.1.7` |
| [@vitest/browser-playwright](https://github.com/vitest-dev/vitest/tree/HEAD/packages/browser-playwright) | `4.1.10` | `5.0.1` |
| [angular-eslint](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/angular-eslint) | `22.2.0` | `22.5.0` |



Updates `@angular-eslint/builder` from 22.2.0 to 22.5.0
- [Release notes](https://github.com/angular-eslint/angular-eslint/releases)
- [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/builder/CHANGELOG.md)
- [Commits](https://github.com/angular-eslint/angular-eslint/commits/v22.5.0/packages/builder)

Updates `@angular-eslint/schematics` from 22.2.0 to 22.5.0
- [Release notes](https://github.com/angular-eslint/angular-eslint/releases)
- [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/schematics/CHANGELOG.md)
- [Commits](https://github.com/angular-eslint/angular-eslint/commits/v22.5.0/packages/schematics)

Updates `@angular/build` from 22.1.7 to 22.1.8
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/v22.1.7...v22.1.8)

Updates `@angular/cli` from 22.1.7 to 22.1.8
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/v22.1.7...v22.1.8)

Updates `@angular/common` from 22.1.5 to 22.1.7
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.1.7/packages/common)

Updates `@angular/compiler` from 22.1.5 to 22.1.7
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.1.7/packages/compiler)

Updates `@angular/compiler-cli` from 22.1.5 to 22.1.7
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.1.7/packages/compiler-cli)

Updates `@angular/core` from 22.1.5 to 22.1.7
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.1.7/packages/core)

Updates `@angular/forms` from 22.1.5 to 22.1.7
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.1.7/packages/forms)

Updates `@angular/platform-browser` from 22.1.5 to 22.1.7
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.1.7/packages/platform-browser)

Updates `@angular/platform-server` from 22.1.5 to 22.1.7
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.1.7/packages/platform-server)

Updates `@angular/router` from 22.1.5 to 22.1.7
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.1.7/packages/router)

Updates `@vitest/browser-playwright` from 4.1.10 to 5.0.1
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.1/packages/browser-playwright)

Updates `angular-eslint` from 22.2.0 to 22.5.0
- [Release notes](https://github.com/angular-eslint/angular-eslint/releases)
- [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/angular-eslint/CHANGELOG.md)
- [Commits](https://github.com/angular-eslint/angular-eslint/commits/v22.5.0/packages/angular-eslint)

---
updated-dependencies:
- dependency-name: "@angular-eslint/builder"
  dependency-version: 22.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: angular-deps
- dependency-name: "@angular-eslint/schematics"
  dependency-version: 22.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: angular-deps
- dependency-name: "@angular/build"
  dependency-version: 22.1.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: angular-deps
- dependency-name: "@angular/cli"
  dependency-version: 22.1.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: angular-deps
- dependency-name: "@angular/common"
  dependency-version: 22.1.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: angular-deps
- dependency-name: "@angular/compiler"
  dependency-version: 22.1.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: angular-deps
- dependency-name: "@angular/compiler-cli"
  dependency-version: 22.1.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: angular-deps
- dependency-name: "@angular/core"
  dependency-version: 22.1.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: angular-deps
- dependency-name: "@angular/forms"
  dependency-version: 22.1.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: angular-deps
- dependency-name: "@angular/platform-browser"
  dependency-version: 22.1.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: angular-deps
- dependency-name: "@angular/platform-server"
  dependency-version: 22.1.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: angular-deps
- dependency-name: "@angular/router"
  dependency-version: 22.1.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: angular-deps
- dependency-name: "@vitest/browser-playwright"
  dependency-version: 5.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: angular-deps
- dependency-name: angular-eslint
  dependency-version: 22.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: angular-deps
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: keep vitest browser provider on v4

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-09-22 07:53:16 +02:00
Karsa 16fb55f52c fix(icons): changed mail-pen (#4899)
adjusted the off grid pen element
2026-09-22 07:52:34 +02:00
Jakob GuddasandEric Fennis 951813ce76 fix(icons): changed map-pinned icon (#4880)
* Updated icons/map-pinned.svg

* Updated icons/map-pinned.json

* Updated icons/map-pinned.svg

* Updated icons/map-pinned.json

* Updated icons/map-pinned.svg

* Updated icons/map-pinned.json

* Update map-pinned.json

* Update map-pinned.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-09-19 12:26:19 +02:00
ba6751ac45 feat(site): Better icon detail page and add unreleased flag (#4877)
* setup icon popularity call

* Adjust sort button

* Adjust sorting

* Adds aliases

* Format code

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Add check for env variable

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Improve styling on mobile

* Better icon detail page

* Update detail page

* feat: make edit button more visible

* Fix types

* Add unreleased version tag

* Adjust icon detail

* Added edit button

* format code

* Replace pencil sparkles

* Revert changes

* Fix types

* Add docs checks

* Fix types

* Fix awaiting release

* revert icons changes

* Format code

* Fix type issues

* Update CarbonAdOverlay to not display

Disable CarbonAdOverlay by setting drawerOpen to false.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Fix types

* Fix condition for showing awaiting release icons

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Format code

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jakob Guddas <github@jguddas.de>
2026-09-18 14:22:47 +02:00
99c1037550 test(packages/shared): cover buildLucideIconForReact (#4872)
buildLucideIconForReact is the entry point both lucide-react and
lucide-react-native use to build an icon, and its whole job is renaming
the hyphenated SVG attributes to the spellings React expects. Nothing
tested that rename directly: the other build helpers have specs in
packages/icons/tests, and this one had none anywhere in the repo.

The spec pins the five renames it applies, checks that attributes React
already accepts are left alone, that aria-hidden stays hyphenated, that
vector-effect is renamed on child nodes for a non-scaling stroke, and
that a caller's own attributeNames are merged without displacing the
React spellings.

Verified that the spec is not vacuous: removing the class -> className
entry from the rename map fails two of the five tests.

Co-authored-by: Vugar Bakhishov <vugarbbakhishov-hub@users.noreply.github.com>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-09-18 14:08:40 +02:00
Eric Fennis e32a4a2c10 Fix type lucide-vue (#4883) 2026-09-18 12:29:14 +02:00
Oleg Bezrukavnikov 5d9cc1580a fix(packages/react-native): pass testID to the rendered Svg element (#4881)
The Icon component destructured `testID` out of its props and re-emitted it as
`data-testid`, which is a web attribute. react-native-svg only forwards `testID`
to the native view (see extractProps) and drops `data-testid`, so
`<Icon testID="my-icon" />` rendered an element that no React Native test query
or UI automation tool can find. `LucideProps` extends `SvgProps`, so the prop
type checks and the failure is silent.

v0 spread `testID` straight onto `Svg`, so this is a regression for React Native
consumers. An explicitly passed `data-testid` still reaches the element through
the remaining props, so nothing is lost for react-native-web users.

The react-native-svg test mock now maps `testID` onto `data-testid`, so the
DOM based test suite identifies elements the way React Native does. Every
existing `testID` test fails against the previous behaviour.
2026-09-18 11:57:55 +02:00
Nathan De PachtereandEric Fennis d9f72b2525 feat(icons): added line-dot-bottom-vertical icon (#3857)
* Added icons/line-dot-bottom-vertical.svg

* Added icons/line-dot-bottom-vertical.json

* Update line-dot-bottom-vertical.json

* Format code

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-09-17 16:30:55 +02:00
Nathan De Pachteregithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Copilot Autofix powered by AIEric Fennis
0d0408c43b feat(icons): added line-dot-top-vertical icon (#3856)
* Added icons/line-dot-top-vertical.svg

* Added icons/line-dot-top-vertical.json

* Update icons/line-dot-top-vertical.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Remove unused tags from line-dot-top-vertical.json

Removed unused tags from the line-dot-top-vertical icon.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Update line-dot-top-vertical.json

* format code

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-09-17 16:30:01 +02:00
Daniel BayleyandEric Fennis 55d30bda04 feat(icons): tweak card-sim chip (#3649)
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-09-17 15:21:22 +02:00
Liang XuandLiang Xu f90f10a24a feat(packages): export __iconNode data across framework packages (#4761)
Fixes #4644

Signed-off-by: Liang Xu <lx3133584@users.noreply.github.com>
Co-authored-by: Liang Xu <lx3133584@users.noreply.github.com>
2026-09-17 14:58:48 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> f3e36fb0d7 chore(deps-dev): bump react-native (#4673)
Bumps the react-native-deps group with 1 update in the / directory: [react-native](https://github.com/react/react-native/tree/HEAD/packages/react-native).


Updates `react-native` from 0.76.9 to 0.87.1
- [Release notes](https://github.com/react/react-native/releases)
- [Changelog](https://github.com/react/react-native/blob/main/CHANGELOG-0.7x.md)
- [Commits](https://github.com/react/react-native/commits/v0.87.1/packages/react-native)

---
updated-dependencies:
- dependency-name: react-native
  dependency-version: 0.86.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: react-native-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-17 14:56:09 +02:00
KarsaandEric Fennis 4faa47c0b0 fix(packages/svelte,solid): fix shared type imports in Solid and Svelte (#4846)
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-09-17 14:30:42 +02:00
Nathan De Pachteregithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Copilot Autofix powered by AIEric Fennis
e593a44ad2 feat(icons): added line-dot-left-horizontal icon (#3855)
* Added icons/line-dot-left-horizontal.svg

* Added icons/line-dot-left-horizontal.json

* Update icons/line-dot-left-horizontal.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Add use cases to line-dot-left-horizontal.json

Added use cases for line-dot-left-horizontal icon.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-09-17 13:41:06 +02:00
dba8fb7932 feat(icons): added square-sparkles icon (#3610)
* Added icons/square-sparkles.svg

* Added icons/square-sparkles.json

* apply design from @karsa-mistmere

* Add use-cases field to square-sparkles.json

* Add use cases for square sparkles icon

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-09-17 13:40:38 +02:00
Tylergithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Claude Sonnet 5Eric Fennis
a87cecebe3 feat(icons): added briefcase-plus icon (#4757)
* feat(icons): added `briefcase-plus` icon

* Update icons/briefcase-plus.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update icons/briefcase-plus.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update icons/briefcase-plus.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* chore(lab): remove `briefcase-plus` from lab

Icon has been promoted to icons/, no longer needed in lab.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Updated briefcase-plus icon

* Update contributors in briefcase-plus.json

Added 'jguddas' to contributors list in briefcase-plus.json

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2026-09-17 09:25:14 +02:00
psj.devgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Copilot Autofix powered by AIPeter Jamrozinski
3b9ea6d087 feat(icons): added square-dashed-plus (#4849)
* feat(icons): added square-dashed-plus

* Update icons/square-dashed-plus.json

Suggested via PR AI

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update icons/square-dashed-plus.json

Suggested by PR AI

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Update icons/square-dashed-plus.json

Suggestion made by PR AI

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Revise use cases for square dashed plus icon

AI PR Suggestion

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Peter Jamrozinski <peter.jamrozinski@johnsonfit.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-09-17 09:21:20 +02:00
Jakob Guddas 076b52527f fix(icons): changed broccoli icon (#4871)
* Updated icons/broccoli.svg

* Updated icons/broccoli.json
2026-09-16 10:18:44 +02:00
Karsa 58ef6830c6 fix(icons): changed calendar-chevrons-right (#4865) 2026-09-14 21:04:50 +02:00
HPRILLER f032bed22e fix(icons): remove clipped path from save-off (#4848) 2026-09-14 19:58:53 +02:00
conanClaude Opus 5Copilot Autofix powered by AIgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Eric FennisKarsa
266fd1c717 feat(icons): add rotate-cw-clock icon (#3979)
* Added icons/update.svg

* Added icons/update.json

* Updated icons/update.json

* chore(icons): rename update to update-clockwise

* refactor(icons): rename `update-clockwise` to `rotate-cw-clock`

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Add use cases for rotate-cw-clock icon

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Update icons/rotate-cw-clock.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Refine use-case descriptions in rotate-cw-clock.json

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Update rotate-cw-clock.json

* Update rotate-cw-clock.svg

* Update rotate-cw-clock.json

* Update rotate-cw-clock.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Karsa <contact@karsa.org>
2026-09-14 11:55:42 +02:00
Ethan Hazelgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Eric FennisKarsa
357a50175e feat(icons): added square-dashed-x icon (#4535)
* feat(icons): added square-dashed-x icon

* Update icons/square-dashed-x.json

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(icons): split square-dashed-x icon into two designs

* Update square-dashed-x.svg

* Update square-dashed-x-corner.svg

* Update square-dashed-x.json

* Update square-dashed-x-corner.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Karsa <contact@karsa.org>
2026-09-14 11:49:49 +02:00
a50c54cfff feat(icons): Added cupcake icon (#3000)
* Added svg and json for cupcake icon

* Update cupcake.svg

* Update cupcake.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Karsa <contact@karsa.org>
2026-09-14 11:45:48 +02:00
Alec Rust bd95db1af8 feat(icons): added tube-lotion icon (#4029)
* Added icons/sunscreen.svg

* Added icons/sunscreen.json

* fix(icons): apply reviewed tube-lotion design and metadata

* fix(icons): correct tube-lotion contributor attribution
2026-09-14 11:39:55 +02:00
Saraj Dhakalgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>Copilot Autofix powered by AIEric Fennis
6b474e16d4 feat(icons): added 'nepali-rupee' icon (#4608)
* Add Nepali Rupee icon

* Apply suggestions from code review

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* refactor: center nepali rupee icon and use perfect circular arc

* Updated with horizontally centered

* Update use cases for Nepali rupee

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-09-14 11:36:57 +02:00
Karsa 566032a95d feat(icons): added door-closed-package icon (#4814)
* Added icons/door-closed-package.svg

* Added icons/door-closed-package.json
2026-09-14 11:31:33 +02:00
Karsa 042bf77119 feat(icons): delegated faucet icon from lab (#4764) 2026-09-14 11:30:28 +02:00
Ubaid UllahandKarsa 5b8975c9a3 feat(icons): add lambda icon (#4017)
* feat(icons): added lambda icon

* fix(icons/lambda): fix linting issues

* feat(icons/lambda): clean up metadata

---------

Co-authored-by: Karsa <contact@karsa.org>
2026-09-14 11:17:11 +02:00
Karsa 7eb8afe2f8 feat(icons): added monitor-pc icon (#4791)
* Added icons/monitor-pc.svg

* Added icons/monitor-pc.json
2026-09-14 11:15:28 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 08b2fd2455 chore(deps-dev): bump astro from 7.3.0 to 7.3.2 in the astro-deps group (#4860)
Bumps the astro-deps group with 1 update: [astro](https://github.com/withastro/astro/tree/HEAD/packages/astro).


Updates `astro` from 7.3.0 to 7.3.2
- [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@7.3.2/packages/astro)

---
updated-dependencies:
- dependency-name: astro
  dependency-version: 7.3.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: astro-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-14 08:35:43 +02:00
172 changed files with 3468 additions and 2323 deletions
+55
View File
@@ -0,0 +1,55 @@
name: Docs Checks
on:
pull_request:
paths:
- docs/**
- .github/workflows/docs.yml
- package.json
- pnpm-workspace.yaml
- pnpm-lock.yaml
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Release metadata is generated by diffing every release tag
fetch-depth: 0
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build docs
run: pnpm --filter @lucide/docs build
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Release metadata is generated by diffing every release tag
fetch-depth: 0
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Generate docs data
run: pnpm --filter @lucide/docs run "/^prebuild:.*/"
- name: Typecheck
run: pnpm --filter @lucide/docs typecheck
+16
View File
@@ -46,3 +46,19 @@ jobs:
- name: Test
run: pnpm --filter @lucide/astro test
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: 'package.json'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck
run: pnpm --filter @lucide/astro typecheck
+16
View File
@@ -37,3 +37,19 @@ jobs:
with:
name: lucide-font
path: lucide-font
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck
run: pnpm --filter build-font typecheck
+16
View File
@@ -29,3 +29,19 @@ jobs:
- name: Build
run: pnpm --filter @lucide/lab build
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck
run: pnpm --filter @lucide/lab typecheck
+16
View File
@@ -31,3 +31,19 @@ jobs:
- name: Test
run: pnpm --filter lucide-preact test
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck
run: pnpm --filter lucide-preact typecheck
+16
View File
@@ -47,3 +47,19 @@ jobs:
- name: Test
run: pnpm --filter lucide-react-native test
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck
run: pnpm --filter lucide-react-native typecheck
+16
View File
@@ -70,3 +70,19 @@ jobs:
- name: Run integration checks
run: pnpm test:integrations:react
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck
run: pnpm --filter lucide-react typecheck
+17 -1
View File
@@ -27,4 +27,20 @@ jobs:
run: pnpm install --frozen-lockfile
- name: Test
run: pnpm --filter lucide-react test
run: pnpm --filter @lucide/shared test
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck
run: pnpm --filter @lucide/shared typecheck
+16
View File
@@ -47,3 +47,19 @@ jobs:
- name: Test
run: pnpm --filter lucide-solid test
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck
run: pnpm --filter lucide-solid typecheck
+16
View File
@@ -26,3 +26,19 @@ jobs:
- name: Build
run: pnpm --filter lucide-static build
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck
run: pnpm --filter lucide-static typecheck
+1 -1
View File
@@ -61,5 +61,5 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Test
- name: Typecheck
run: pnpm --filter @lucide/svelte typecheck
+16
View File
@@ -47,3 +47,19 @@ jobs:
- name: Test
run: pnpm --filter @lucide/vue test
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Test
run: pnpm --filter @lucide/vue typecheck
+16
View File
@@ -46,3 +46,19 @@ jobs:
- name: Test
run: pnpm --filter lucide test
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck
run: pnpm --filter lucide typecheck
+1
View File
@@ -1,6 +1,7 @@
.DS_Store
.idea
.next
.astro
.obsidian
.now
.cache
+1 -2
View File
@@ -1,13 +1,12 @@
import { eventHandler, setResponseHeader } from 'h3';
import iconNodes from '../../data/iconNodes/index.ts';
import { IconNodeWithKeys } from '../../theme/types';
import iconMetaData from '../../data/iconMetaData';
import releaseMeta from '../../data/releaseMetaData.json';
import categories from '../../data/categoriesData.json';
const dataResponse = {
icons: Object.entries(iconNodes).reduce((acc, [name, iconNode]) => {
const newIconNode = (iconNode as IconNodeWithKeys).map(([name, { key, ...attrs }]) => {
const newIconNode = iconNode.map(([name, { key, ...attrs }]) => {
return [name, attrs];
});
+1 -2
View File
@@ -1,6 +1,5 @@
import { eventHandler, getQuery, setResponseHeader } from 'h3';
import iconNodes from '../../data/iconNodes';
import { IconNodeWithKeys } from '../../theme/types';
export default eventHandler((event) => {
const query = getQuery(event);
@@ -19,7 +18,7 @@ export default eventHandler((event) => {
return [name, iconNode];
}
const newIconNode = (iconNode as IconNodeWithKeys).map(([name, { key, ...attrs }]) => {
const newIconNode = iconNode.map(([name, { key, ...attrs }]) => {
return [name, attrs];
});
+1 -1
View File
@@ -97,7 +97,7 @@ export default defineConfig({
{
text: 'Resources',
items: [
...resourcesSidebar[0].items,
...resourcesSidebar[0].items.map(({ text, link }) => ({ text, link })),
{ text: 'How to use icons', link: '/how-to/' },
{ text: 'Contributing icons', link: '/contribute/icons/' },
],
+2 -3
View File
@@ -1,4 +1,3 @@
import { SVGProps } from 'react';
import { getCommands } from './utils';
export type Point = { x: number; y: number };
@@ -15,8 +14,8 @@ export type Path = {
};
export type PathProps<
RequiredProps extends keyof SVGProps<SVGPathElement | SVGRectElement | SVGCircleElement>,
NeverProps extends keyof SVGProps<SVGPathElement | SVGRectElement | SVGCircleElement>,
RequiredProps extends keyof React.SVGProps<SVGPathElement | SVGRectElement | SVGCircleElement>,
NeverProps extends keyof React.SVGProps<SVGPathElement | SVGRectElement | SVGCircleElement>,
> = Required<Pick<React.SVGProps<SVGElement & SVGRectElement & SVGCircleElement>, RequiredProps>> &
Omit<
React.SVGProps<SVGPathElement & SVGRectElement & SVGCircleElement>,
+1 -4
View File
@@ -53,10 +53,7 @@ export const getCommands = (src: string) =>
getNodes(src)
.map(convertToPathNode)
.flatMap(({ d, name }, idx) =>
new SVGPathData(d)
.toAbs()
// @ts-expect-error `commands` is typed as a union of command types svgson does not narrow
.commands.map((c, cIdx) => ({ ...c, id: idx, idx: cIdx, name })),
new SVGPathData(d).toAbs().commands.map((c, cIdx) => ({ ...c, id: idx, idx: cIdx, name })),
);
const getPaths = (src: string) => {
+3 -3
View File
@@ -4,7 +4,7 @@ import { Category, IconEntity } from '../theme/types';
const directory = path.join(process.cwd(), '../categories');
export function getAllCategoryFiles(): Category[] {
export function getAllCategoryFiles(): Omit<Category, 'iconCount'>[] {
const fileNames = fs.readdirSync(directory).filter((file) => path.extname(file) === '.json');
return fileNames.map((fileName) => {
@@ -21,9 +21,9 @@ export function getAllCategoryFiles(): Category[] {
}
export function mapCategoryIconCount(
categories: Category[],
categories: Omit<Category, 'iconCount'>[],
icons: { categories: IconEntity['categories'] }[],
) {
): Category[] {
return categories.map((category) => ({
...category,
iconCount: icons.reduce(
+2 -1
View File
@@ -1,4 +1,5 @@
import { createLucideIcon } from 'lucide-react/src/lucide-react';
import React from 'react';
import createLucideIcon from 'lucide-react/src/createLucideIcon';
import { type LucideProps, type IconNode } from 'lucide-react/src/types';
import { IconEntity } from '../theme/types';
import { renderToStaticMarkup } from 'react-dom/server';
+2
View File
@@ -20,6 +20,8 @@ export async function getData(name: string) {
const iconNode = iconNodes[name];
const releaseData = releaseMeta?.[name] ?? {
fromFork: false,
awaitingRelease: false,
createdRelease: {
version: '0.0.0',
date: DATE_OF_FORK,
+1 -2
View File
@@ -1,7 +1,6 @@
import { readFileSync } from 'node:fs';
import { dirname, isAbsolute, resolve } from 'node:path';
import type MarkdownIt from 'markdown-it';
import type { RenderRule } from 'markdown-it/lib/renderer.mjs';
import type { MarkdownIt, RenderRule } from './types';
import container from 'markdown-it-container';
type ContainerArgs = [typeof container, string, { render: RenderRule }];
+1 -3
View File
@@ -1,6 +1,4 @@
import type MarkdownIt from 'markdown-it';
import type { RenderRule } from 'markdown-it/lib/renderer.mjs';
import type Token from 'markdown-it/lib/token.mjs';
import type { MarkdownIt, RenderRule, Token } from './types';
import container from 'markdown-it-container';
import sandpackTheme from '../theme/sandpackTheme.json';
+1 -1
View File
@@ -16,7 +16,7 @@
* />
*/
import type MarkdownIt from 'markdown-it';
import type { MarkdownIt } from './types';
type SnackParams = Record<string, string>;
+7
View File
@@ -0,0 +1,7 @@
import type { MarkdownRenderer } from 'vitepress';
// VitePress 1.x renders with markdown-it 14, so derive the plugin types from its renderer
// instead of the markdown-it 15 package installed in docs.
export type MarkdownIt = MarkdownRenderer;
export type RenderRule = NonNullable<MarkdownIt['renderer']['rules'][string]>;
export type Token = Parameters<RenderRule>[0][number];
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { useLocalStorage } from '@vueuse/core';
import IconButton from './base/IconButton.vue';
import Button from './base/Button.vue';
import { x } from '../../data/iconNodes';
import Icon from '@lucide/vue/src/Icon';
import { computed, onMounted, ref } from 'vue';
@@ -65,16 +65,17 @@ function hidePopup() {
}"
class="ask-overlay"
>
<IconButton
<Button
@click="hidePopup"
class="hide-button"
>
<Icon
:iconNode="x"
name="x"
:size="20"
absoluteStrokeWidth
/>
</IconButton>
</Button>
<h2 class="title">
We value your feedback!
@@ -1,7 +1,38 @@
<script setup lang="ts">
import { computed } from 'vue';
const props = defineProps<{
href?: string;
}>()
const isExternal = computed(() => {
return props.href?.startsWith('http') ?? false
})
const component = computed(() => {
return props.href ? 'a' : 'button'
})
const target = computed(() => {
return isExternal.value ? '_blank' : undefined
})
const rel = computed(() => {
return isExternal.value ? 'noreferrer noopener' : undefined
})
</script>
<template>
<button v-bind="$attrs" class="icon-button">
<component
:is="component"
v-bind="$attrs"
:href="href"
:target="target"
:rel="rel"
class="icon-button"
>
<slot />
</button>
</component>
</template>
<style scoped>
@@ -16,12 +47,21 @@
transition: color 0.25s, border-color 0.25s, background-color 0.25s;
border-radius: 6px;
background-color: var(--vp-c-bg-alt);
color: inherit;
text-decoration: none;
/* width: 56px;
height: 56px; */
font-size: 24px;
gap: 8px;
font-size: 16px;
font-weight: 500;
transition: color 0.1s, border-color 0.1s, background-color 0.1s;
}
.icon-button span {
padding: 0 4px;
display: block;
}
.icon-button:hover {
border-color: var(--vp-button-alt-hover-border);
color: var(--vp-button-alt-hover-text);
@@ -14,7 +14,7 @@ export interface InputProps {
import { ref, onMounted, nextTick, watch } from 'vue';
import Icon from '@lucide/vue/src/Icon';
import { x } from '../../../data/iconNodes';
import IconButton from './IconButton.vue';
import Button from './Button.vue';
const props = withDefaults(defineProps<InputProps>(), {
type: 'text',
@@ -68,9 +68,9 @@ defineExpose({
ref="input"
:value="modelValue"
v-bind="$attrs"
@input="$emit('update:modelValue', $event.target.value)"
@input="$emit('update:modelValue', ($event.target as HTMLInputElement).value)"
/>
<IconButton
<Button
@click="onClear"
v-if="type === 'search' && modelValue"
class="clear-button"
@@ -79,9 +79,10 @@ defineExpose({
>
<Icon
:iconNode="x"
name="x"
:size="20"
/>
</IconButton>
</Button>
<kbd
v-if="shortcut"
class="shortcut"
@@ -46,6 +46,7 @@ const value = computed({
<template #icon>
<Icon
:iconNode="search"
name="search"
class="search-icon"
/>
</template>
@@ -9,7 +9,8 @@ import RocketDuotoneIcon from '../home/RocketDuotoneIcon.ts';
<p>
<Icon
:iconNode="RocketDuotoneIcon"
size="18"
name="rocket-duotone"
:size="18"
class="rocket-icon"
/>
<VPLink
@@ -0,0 +1,74 @@
<script setup lang="ts">
import { TabGroup, TabList, Tab, TabPanels, TabPanel } from '@headlessui/vue'
const props = defineProps<{
tabs: string[];
}>();
</script>
<template>
<TabGroup>
<TabList class="tab-list">
<template v-for="(tab, index) in props.tabs" :key="index">
<Tab :id="`tab-${index}`" class="tab">{{ tab }}</Tab>
</template>
</TabList>
<TabPanels>
<template v-for="(_, name) in $slots">
<TabPanel>
<slot :name="name" />
</TabPanel>
</template>
</TabPanels>
</TabGroup>
</template>
<style lang="css" scoped>
.tab-list {
display: flex;
gap: 8px;
margin-bottom: 32px;
border-bottom: 1px solid var(--vp-c-divider);
justify-content: center;
}
.tab {
display: block;
line-height: 32px;
font-size: 16px;
padding: 10px 12px;
font-weight: 500;
color: var(--vp-c-text-2);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: color 0.5s;
border-bottom: 2px solid transparent;
}
.tab[aria-selected="true"] {
color: var(--vp-c-text-1);
transition: color 0.25s;
border-bottom: 2px solid var(--vp-c-brand);
}
@media (min-width: 860px) {
.tab-list {
justify-content: flex-start;
}
.tab {
font-size: 18px;
padding: 10px 16px;
}
}
@media (min-width: 960px) {
.tab {
}
}
@media (min-width: 1152px) {
.tab {
}
}
</style>
@@ -34,7 +34,7 @@ const percentage = computed<string>(() => `${((Number(props.modelValue) - props.
type="range"
v-bind="$attrs"
v-bind:value="modelValue"
v-on:input="$emit('update:modelValue', Number($event.target.value))"
v-on:input="$emit('update:modelValue', Number(($event.target as HTMLInputElement).value))"
:min="min"
:max="max"
:step="step"
@@ -1,16 +1,17 @@
<script setup lang="ts">
import { rotateCw } from '../../../data/iconNodes';
import Icon from '@lucide/vue/src/Icon';
import IconButton from './IconButton.vue';
import Button from './Button.vue';
</script>
<template>
<IconButton class="reset-button">
<Button class="reset-button">
<Icon
:size="20"
:iconNode="rotateCw"
name="rotate-cw"
/>
</IconButton>
</Button>
</template>
<style scoped>
@@ -27,6 +27,7 @@ const selected = defineModel<{ name: string; value: string; icon?: string; iconD
<slot name="end-icon">
<Icon
:iconNode="chevronsUpDown"
name="chevrons-up-down"
class="chevron-icon"
aria-hidden="true"
/>
@@ -57,6 +58,7 @@ const selected = defineModel<{ name: string; value: string; icon?: string; iconD
>
<Icon
:iconNode="check"
name="check"
class="check"
aria-hidden="true"
/>
@@ -1,6 +1,6 @@
<script lang="ts" setup>
import { type FunctionalComponent } from 'vue';
import { type LucideProps } from '@lucide/vue';
import { type LucideProps } from '@lucide/vue/src/types';
import { Sandpack } from 'sandpack-vue3';
defineProps<FunctionalComponent<LucideProps>>();
@@ -10,7 +10,14 @@ import { useLocalStorage } from '@vueuse/core';
const { page } = useData();
const router = useRouter();
const frameworks = [
interface Framework {
name: string;
icon: string;
iconDark?: string;
value: string;
}
const frameworks: Framework[] = [
{ name: 'Vanilla', icon: '/framework-logos/js.svg', value: '/guide/lucide/' },
{ name: 'React', icon: '/framework-logos/react.svg', value: '/guide/react/' },
{ name: 'Vue', icon: '/framework-logos/vue.svg', value: '/guide/vue/' },
@@ -48,7 +55,7 @@ const selected = computed(() => {
return current || (fallbackFramework.value?.value ? fallbackFramework.value : frameworks[0]);
});
function onSelectFramework(item: { name: string; icon: string; iconDark?: string; value: string }) {
function onSelectFramework(item: Framework) {
fallbackFramework.value = item;
if (item.value !== router.route.path) {
const likeRoute = router.route.path.replace(selected.value.value, item.value);
@@ -2,9 +2,12 @@
import { ref, shallowRef } from 'vue';
import { data } from './HomeHeroIconsCard.data';
import { useRouter } from 'vitepress';
import { motion, Variants, useScroll, useTransform } from 'motion-v';
import { motion, useScroll, useTransform, type Options as MotionOptions } from 'motion-v';
import LucideIcon from '../base/LucideIcon.vue';
// motion-v re-exports framer-motion's `Variants`, which doesn't match the `variants` prop of its components
type Variants = MotionOptions['variants'];
const emit = defineEmits(['animation-complete']);
const MotionLucideIcon = motion.create(LucideIcon);
@@ -129,7 +132,7 @@ const onAnimationComplete = (item) => {
const randomIndex = ref(Math.floor(Math.random() * 64));
const iconAnimationVariants = {
const iconAnimationVariants: Variants = {
initial: {
animationName: 'end',
opacity: 0,
@@ -149,7 +152,7 @@ const iconAnimationVariants = {
const shrinkIconAnimation = ref('initial');
const shrinkIconVariants = {
const shrinkIconVariants: Variants = {
initial: { strokeWidth: 2 },
shrinkIcons: (index) => ({
animationName: 'shrinkIcons',
@@ -69,7 +69,7 @@ const pickIcons = [
'stethoscope',
];
export default {
const loader = {
async load() {
const icons = Object.entries(iconNodes).map(([name, iconNode]) => ({ name, iconNode }));
@@ -96,3 +96,8 @@ export default {
};
},
};
declare const data: Awaited<ReturnType<typeof loader.load>>;
export { data };
export default loader;
@@ -1,4 +1,4 @@
export default {
const loader = {
async load() {
const version = await fetch('https://api.github.com/repos/lucide-icons/lucide/releases/latest')
.then((res) => {
@@ -16,3 +16,8 @@ export default {
};
},
};
declare const data: Awaited<ReturnType<typeof loader.load>>;
export { data };
export default loader;
@@ -15,10 +15,11 @@ import RocketDuotoneIcon from './RocketDuotoneIcon.ts';
class="badge-special confetti-button animate"
href="/guide/version-1"
>
<Icon :iconNode="RocketDuotoneIcon" />
<Icon :iconNode="RocketDuotoneIcon" name="rocket-duotone" />
Version 1
<Icon
:iconNode="moveRight"
name="move-right"
class=""
/>
</Badge>
@@ -41,8 +41,8 @@ const sizeCssVar = useCssVar(
)
syncRef(color, colorCssVar)
syncRef(strokeWidth, strokeWidthCssVar)
syncRef(size, sizeCssVar)
syncRef(strokeWidth, strokeWidthCssVar, { transform: { ltr: String, rtl: Number } })
syncRef(size, sizeCssVar, { transform: { ltr: String, rtl: Number } })
function resetStyle () {
color.value = 'currentColor'
@@ -1,4 +1,4 @@
export default {
const loader = {
async load() {
return {
packages: [
@@ -61,3 +61,8 @@ export default {
};
},
};
declare const data: Awaited<ReturnType<typeof loader.load>>;
export { data };
export default loader;
@@ -2,7 +2,7 @@
import { computed } from 'vue';
const props = defineProps<{
headingLevel: 1 | 2 | 3 | 4 | 5 | 6,
headingLevel?: 1 | 2 | 3 | 4 | 5 | 6,
}>()
const headingElement = computed(() => `h${props.headingLevel ?? 2}`)
@@ -1,4 +1,6 @@
const RocketDuotoneIcon = [
import type { LucideIconNode } from '@lucide/vue/src/types';
const RocketDuotoneIcon: LucideIconNode[] = [
[
'path',
{
@@ -16,6 +18,6 @@ const RocketDuotoneIcon = [
'clip-rule': 'evenodd',
},
],
] as const;
];
export default RocketDuotoneIcon;
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { useData } from 'vitepress';
import { useSessionStorage } from '@vueuse/core';
import IconButton from '../base/IconButton.vue';
import Button from '../base/Button.vue';
import VPDocAsideCarbonAds from 'vitepress/dist/client/theme-default/components/VPDocAsideCarbonAds.vue';
import { x } from '../../../data/iconNodes';
import Icon from '@lucide/vue/src/Icon';
@@ -40,16 +40,17 @@ onMounted(() => {
class="floating-ad"
v-if="theme.carbonAds"
>
<IconButton
<Button
@click="hideAd"
class="hide-button"
>
<Icon
:iconNode="x"
name="x"
:size="20"
absoluteStrokeWidth
/>
</IconButton>
</Button>
<VPDocAsideCarbonAds :carbon-ads="theme.carbonAds" overlay />
</div>
</template>
@@ -1,14 +1,17 @@
import { getAllCategoryFiles, mapCategoryIconCount } from '../../../lib/categories';
import iconsMetaData from '../../../data/iconMetaData';
export default {
const loader = {
async load() {
let categories = getAllCategoryFiles();
categories = mapCategoryIconCount(categories, Object.values(iconsMetaData));
const categories = mapCategoryIconCount(getAllCategoryFiles(), Object.values(iconsMetaData));
return {
categories,
};
},
};
declare const data: Awaited<ReturnType<typeof loader.load>>;
export { data };
export default loader;
@@ -2,7 +2,7 @@
import { ref, computed, watch } from 'vue'
import { useData } from 'vitepress'
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue'
import { isActive } from 'vitepress/dist/client/shared'
import { isActive } from 'vitepress/dist/client/shared.js'
import { useActiveAnchor } from '../../composables/useActiveAnchor'
import { data } from './CategoryList.data'
import CategoryListItem from './CategoryListItem.vue'
@@ -0,0 +1,29 @@
<script setup lang="ts">
import { pencilSparkles } from '../../../data/iconNodes';
import { IconEntity } from '../../types';
import getStudioLink from '../../utils/getStudioLink';
import Button from '../base/Button.vue';
import Icon from '@lucide/vue/src/Icon.ts';
defineProps<{
icon: IconEntity
}>()
</script>
<template>
<Button
:href="getStudioLink(icon.name, icon.iconNode, 'icon-info')"
target="_blank"
rel="noopener noreferrer"
>
<Icon :iconNode="pencilSparkles" name="pencil-sparkles" :size="24" />
<span class="text">Edit in studio</span>
</Button>
</template>
<style scoped>
.text {
margin: 0 4px;
}
</style>
@@ -30,10 +30,15 @@ const props = defineProps<{
.contributors {
display: flex;
flex-direction: row;
align-items: center;
align-items: start;
/* justify-content: flex-end; */
gap: 16px;
gap: 2px;
}
.contributors .label {
margin-top: 12px;
}
.avatar-group {
display: flex;
}
@@ -16,19 +16,17 @@ const tags = computed(() => {
</script>
<template>
<!-- <PageContainer class="overview"> -->
<IconPreview
:name="$params.name"
:iconNode="$params.iconNode"
class="preview"
customizable
/>
<div class="details">
<h1 class="title">
{{ $params.name }}
</h1>
</div>
<!-- </PageContainer> -->
<IconPreview
:name="$params.name"
:iconNode="$params.iconNode"
class="preview"
customizable
/>
<div class="details">
<h1 class="title">
{{ $params.name }}
</h1>
</div>
</template>
<style scoped>
@@ -6,7 +6,11 @@ import useConfetti from '../../composables/useConfetti';
const { animate, confetti } = useConfetti();
const slots = useSlots();
const copiedText = computed(() => slots.default?.()[0].children);
const copiedText = computed(() => {
const children = slots.default?.()[0].children;
return typeof children === 'string' ? children : '';
});
function copyText() {
navigator.clipboard.writeText(copiedText.value);
@@ -25,6 +29,7 @@ function copyText() {
<slot />
<Icon
:iconNode="copy"
name="copy"
:size="20"
class="copy-icon"
/>
@@ -2,7 +2,7 @@
import type { IconEntity } from '../../types';
import { computed } from 'vue';
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
import IconButton from '../base/IconButton.vue';
import Button from '../base/Button.vue';
import IconContributors from './IconContributors.vue';
import IconPreview from './IconPreview.vue';
import { x, expand } from '../../../data/iconNodes';
@@ -12,6 +12,7 @@ import Badge from '../base/Badge.vue';
import { computedAsync } from '@vueuse/core';
import { satisfies } from 'semver';
import { useExternalLibs } from '../../composables/useExternalLibs';
import EditInStudioButton from './EditInStudioButton.vue';
const props = defineProps<{
iconName: string | null;
@@ -70,12 +71,19 @@ const Expand = createLucideIcon('Expand', expand);
<div class="overlay-panel">
<nav class="overlay-menu">
<Badge
v-if="icon.createdRelease"
v-if="icon.awaitingRelease"
class="version awaiting-release"
>Unreleased</Badge
>
<Badge
v-else-if="icon.createdRelease"
class="version"
:href="releaseTagLink(icon.createdRelease.version)"
>v{{ icon.createdRelease.version }}</Badge
>
<IconButton
{{ icon.createdRelease.version }}
</Badge>
<EditInStudioButton :icon="icon"/>
<Button
@click="
go(
icon.externalLibrary
@@ -85,10 +93,10 @@ const Expand = createLucideIcon('Expand', expand);
"
>
<component :is="Expand" />
</IconButton>
<IconButton @click="onClose">
</Button>
<Button @click="onClose">
<component :is="CloseIcon" />
</IconButton>
</Button>
</nav>
<IconPreview
id="previewer"
@@ -200,7 +208,14 @@ const Expand = createLucideIcon('Expand', expand);
margin-right: 24px;
}
.version.awaiting-release {
color: var(--vp-c-danger-3);
}
.contributors {
justify-content: flex-end;
position: absolute;
bottom: 24px;
right: 24px;
}
</style>
@@ -10,30 +10,36 @@ import { computed } from 'vue';
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
import { diamond } from '../../../data/iconNodes';
import deprecationReasonTemplate from '../../../../../tools/build-icons/utils/deprecationReasonTemplate.ts';
import type { IconDeprecationReason } from '../../../../../tools/build-icons/types.ts';
import IconTags from "./IconTags.vue"
import getStudioLink from '../../utils/getStudioLink';
const props = defineProps<{
icon: IconEntity;
popoverPosition?: 'top' | 'bottom';
showEditButton?: boolean;
}>();
const { go } = useRouter();
const { page } = useData();
const tags = computed(() => {
if (!props.icon || !props?.icon?.tags) return [];
return props.icon.tags.join(' • ');
});
const DiamondIcon = createLucideIcon('Diamond', diamond);
const deprecatedTitle = computed(() => {
if (!props.icon.deprecationReason) return '';
return deprecationReasonTemplate(props.icon.deprecationReason, {
return deprecationReasonTemplate(props.icon.deprecationReason as IconDeprecationReason, {
componentName: props.icon.name,
iconName: props.icon.name,
toBeRemovedInVersion: props.icon.toBeRemovedInVersion,
});
});
const iconPath = computed(() => {
if (props.icon.externalLibrary) {
return `icons/${props.icon.externalLibrary}/${props.icon.name}`;
}
return `icons/${props.icon.name}`;
});
</script>
<template>
@@ -60,14 +66,7 @@ const deprecatedTitle = computed(() => {
Deprecated
</Badge>
</div>
<div
class="tags-scroller"
v-if="tags.length"
>
<p class="icon-tags horizontal-scroller">
{{ tags }}
</p>
</div>
<IconTags :tags="props.icon.tags" />
<div class="group">
<Badge
v-for="category in icon.categories"
@@ -80,26 +79,10 @@ const deprecatedTitle = computed(() => {
<div class="group buttons">
<VPButton
v-if="
!page?.relativePath?.startsWith?.(
icon.externalLibrary
? `icons/${icon.externalLibrary}/${icon.name}`
: `icons/${icon.name}`,
)
"
:href="
icon.externalLibrary
? `/icons/${icon.externalLibrary}/${icon.name}`
: `/icons/${icon.name}`
"
v-if="!page?.relativePath?.startsWith?.(iconPath)"
:href="iconPath"
text="See in action"
@click="
go(
icon.externalLibrary
? `/icons/${icon.externalLibrary}/${icon.name}`
: `/icons/${icon.name}`,
)
"
@click="go(iconPath)"
/>
<CopySVGButton
:name="icon.name"
@@ -115,6 +98,38 @@ const deprecatedTitle = computed(() => {
</template>
<style scoped>
.studio-edit-button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 0 14px;
min-height: 40px;
border: 1px solid var(--vp-button-alt-border);
border-radius: 999px;
background-color: var(--vp-button-alt-bg);
color: var(--vp-button-alt-text);
font-size: 15px;
font-weight: 600;
line-height: 1;
text-decoration: none;
transition:
border-color 0.2s,
color 0.2s,
background-color 0.2s,
transform 0.2s;
margin-left: 0;
margin-inline-start: 0;
}
.studio-edit-button:hover,
.studio-edit-button:focus-visible {
border-color: var(--vp-button-alt-hover-border);
color: var(--vp-button-alt-hover-text);
background-color: var(--vp-button-alt-hover-bg);
transform: translateY(-1px);
}
.group {
display: flex;
flex-wrap: wrap;
@@ -156,66 +171,6 @@ const deprecatedTitle = computed(() => {
background-color: var(--vp-c-brand-2);
}
.icon-tags {
font-size: 16px;
color: var(--vp-c-text-2);
font-weight: 500;
line-height: 28px;
white-space: nowrap;
position: absolute;
top: 0;
left: 0;
right: 0;
margin-top: 0;
margin-bottom: 0;
}
.tags-scroller {
position: relative;
max-width: 100%;
width: 100%;
height: 28px;
padding: 8px 0 16px;
margin-bottom: 16px;
margin-top: 8px;
align-items: center;
--gradient-background: var(--tags-gradient-background, var(--vp-c-bg-elv));
}
.horizontal-scroller {
overflow-x: scroll;
/* Hide Scrollbar */
-ms-overflow-style: none;
scrollbar-width: none;
scrollbar-width: thin; /* can also be normal, or none, to not render scrollbar */
scrollbar-color: var(--vp-c-text-4) transparent; /* foreground background */
}
.horizontal-scroller::-webkit-scrollbar {
width: 0;
display: none;
}
.horizontal-scroller::-webkit-scrollbar-track {
background: transparent;
}
.horizontal-scroller::-webkit-scrollbar-thumb {
background: transparent;
border: none;
}
.tags-scroller::after {
content: '';
position: absolute;
bottom: 0;
width: 32px;
height: 100%;
/* Background Gradient left to right */
background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, var(--gradient-background) 100%);
right: 0;
pointer-events: none;
}
.buttons {
margin-top: 24px;
}
@@ -3,7 +3,6 @@ import type { IconEntity } from '../../types';
import { computed, ref } from 'vue';
import createLucideIcon from '@lucide/vue/src/createLucideIcon';
import { useIconStyleContext } from '../../composables/useIconStyle';
import { squarePen } from '../../../data/iconNodes';
const props = defineProps<{
name: IconEntity['name'];
@@ -11,6 +10,8 @@ const props = defineProps<{
customizable?: boolean;
}>();
const activeSize = ref('1000%');
const { size, color, strokeWidth, absoluteStrokeWidth } = useIconStyleContext();
const previewIcon = ref();
@@ -20,31 +21,6 @@ const iconComponent = computed(() => {
if (!props.name || !props.iconNode) return null;
return createLucideIcon(props.name, props.iconNode);
});
const SquarePenIcon = createLucideIcon('SquarePen', squarePen);
const innerSvg = computed(() => {
if (!props.iconNode) return '';
const string = props.iconNode
.map(([tag, attrs]) => {
const attributes = Object.entries(attrs)
.map(([key, value]) => `${key}="${value}"`)
.join(' ');
return `<${tag} ${attributes}/>`;
})
.join('');
return string
.replace(/>[\r\n ]+</g, '><')
.replace(/(<.*?>)|\s+/g, (m, $1) => $1 || ' ')
.trim();
});
const base64InnerSvg = computed(() => {
if (typeof window === 'undefined') return '';
return btoa(innerSvg.value);
});
</script>
<template>
@@ -55,6 +31,8 @@ const base64InnerSvg = computed(() => {
:size="size"
:color="color"
:strokeWidth="absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth"
class="preview-icon"
:data-size="activeSize"
/>
<svg
class="icon-grid"
@@ -62,6 +40,7 @@ const base64InnerSvg = computed(() => {
fill="none"
stroke-width="0.1"
xmlns="http://www.w3.org/2000/svg"
:class="{ show: activeSize === '1000%' }"
>
<g
:key="`grid-${i}`"
@@ -83,16 +62,17 @@ const base64InnerSvg = computed(() => {
/>
</g>
</svg>
<a
:href="`https://studio.lucide.dev/edit?value=${encodeURIComponent(base64InnerSvg)}&name=${name}&utm_source=lucide.dev&utm_medium=icon-detail-preview`"
target="_blank"
rel="noopener noreferrer"
class="studio-button"
title="Open in Lucide Studio"
aria-label="Open in Lucide Studio"
>
<SquarePenIcon :size="14" />
</a>
<div class="size-buttons" :class="{ show: activeSize !== '1000%' }">
<button class="size-button" @click="activeSize = '1000%'" :class="{ active: activeSize === '1000%' }">
1000%
</button>
<button class="size-button" @click="activeSize = '200%'" :class="{ active: activeSize === '200%' }">
200%
</button>
<button class="size-button" @click="activeSize = '100%'" :class="{ active: activeSize === '100%' }">
100%
</button>
</div>
</div>
</template>
@@ -104,56 +84,87 @@ const base64InnerSvg = computed(() => {
top: 0;
left: 0;
stroke: var(--vp-c-divider);
opacity: 0;
transition: opacity 0.3s ease-in-out;
}
.icon-grid.show {
opacity: 1;
}
.icon-container {
height: 100%;
aspect-ratio: 1/1;
position: relative;
background: var(--vp-c-bg-alt);
border-radius: 8px;
display: flex;
overflow: hidden;
flex-shrink: 0;
}
.icon-container > :deep(svg:not(.icon-grid)) {
.icon-container:hover .size-buttons {
opacity: 1;
}
.icon-container > :deep(svg.preview-icon) {
width: 100%;
height: 100%;
position: relative;
z-index: 1;
color: var(--vp-c-neutral);
opacity: 0.8;
transition: all 0.3s;
margin: auto;
}
.studio-button {
position: absolute;
bottom: 8px;
right: 8px;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
padding: 5px;
display: flex;
align-items: center;
justify-content: center;
color: var(--vp-c-text-2);
opacity: 0;
transition: opacity 0.2s, color 0.2s, background-color 0.2s;
z-index: 2;
.icon-container > :deep(svg.preview-icon[data-size='1000%']) {
width: 100%;
height: 100%;
}
.icon-container:hover .studio-button,
.studio-button:focus-visible {
opacity: 1;
.icon-container > :deep(svg.preview-icon[data-size='200%']) {
width: 48px;
height: 48px;
}
.studio-button:hover,
.studio-button:focus-visible {
background: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
.icon-container > :deep(svg.preview-icon[data-size='100%']) {
width: 24px;
height: 24px;
}
.icon-component.customizable {
will-change: width, height, stroke-width, stroke;
/* color: var(--customize-color, currentColor);
stroke-width: var(--customize-strokeWidth, 2); */
}
.size-buttons {
position: absolute;
bottom: 8px;
left: 50%;
padding: 4px;
border-radius: 12px;
transform: translateX(-50%);
display: flex;
z-index: 99;
background: var(--vp-code-tab-bg);
opacity: 0;
transition: opacity 0.3s ease-in-out;
}
.size-buttons.show {
opacity: 1;
}
.size-button {
color: var(--vp-code-tab-text-color);
padding: 2px 8px;
}
.size-button.active {
background: var(--vp-c-neutral-inverse);
box-shadow: var(--vp-shadow-1);
border-radius: 4px;
color: var(--vp-code-tab-active-text-color);
text-decoration: none;
}
</style>
@@ -70,7 +70,6 @@ const prettyName = props.name
class="showcase"
v-if="iconComponent"
>
<h2 class="title">See this icon in action</h2>
<div class="showcase-grid">
<div class="showcase-item column">
<div class="placeholder"></div>
@@ -236,18 +235,17 @@ const prettyName = props.name
.showcase-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 48px;
margin-inline-start: 24px;
margin-block-start: 48px;
gap: 32px;
background: var(--vp-code-block-bg);
padding: 32px;
border-radius: 8px;
}
.showcase-item {
padding: 24px;
border-radius: 8px;
background-color: var(--vp-c-bg);
box-shadow:
var(--vp-shadow-4),
-24px -24px 0 var(--vp-c-bg-soft);
box-shadow: var(--vp-shadow-2);
display: flex;
align-items: center;
justify-content: center;
@@ -0,0 +1,110 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
const MAX_VISIBLE_TAGS = 5;
const props = defineProps<{
tags?: string[];
}>();
const tags = computed(() => props.tags ?? []);
const expanded = ref(false);
const hiddenCount = computed(() => Math.max(tags.value.length - MAX_VISIBLE_TAGS, 0));
const showAll = computed(() => expanded.value || hiddenCount.value === 0);
// The overlay reuses this component when switching icons, so collapse again
watch(tags, () => {
expanded.value = false;
});
</script>
<template>
<ul
v-if="tags.length"
class="icon-tags"
>
<!-- v-show keeps hidden tags in the rendered HTML so crawlers can index them -->
<li
v-for="(tag, index) in tags"
v-show="showAll || index < MAX_VISIBLE_TAGS"
:key="tag"
class="tag"
>
{{ tag }}
</li>
<li
v-if="!showAll"
class="more"
>
<button
type="button"
class="more-button"
:aria-label="`Show ${hiddenCount} more tags`"
@click="expanded = true"
>
+{{ hiddenCount }}
</button>
</li>
</ul>
</template>
<style scoped>
.icon-tags {
font-size: 16px;
color: var(--vp-c-text-2);
font-weight: 500;
line-height: 28px;
display: flex;
flex-wrap: wrap;
align-items: center;
list-style-type: disc;
gap: 4px 8px;
/* Clip bullets hanging before tags that start a wrapped line, padding leaves room for the focus ring */
overflow: hidden;
padding: 4px;
margin: 4px -4px 12px;
}
.tag {
margin-top: 0;
padding-left: 0px;
padding-right: 12px;
list-style-position: outside;
white-space: nowrap;
}
.tag:first-child {
list-style: none;
margin-left: 0;
padding-left: 0;
}
.more {
margin-top: 0;
list-style: none;
}
.more-button {
font: inherit;
color: var(--vp-c-text-2);
font-size: 12px;
padding: 0 8px;
margin-left: -4px;
border-radius: 16px;
background-color: var(--vp-c-bg-alt);
transition:
color 0.25s,
background-color 0.25s;
}
.more-button:hover,
.more-button:focus-visible {
color: var(--vp-c-text-1);
background-color: var(--vp-c-default-soft);
}
.more-button:focus-visible {
outline: 2px solid var(--vp-c-brand-1);
outline-offset: 2px;
}
</style>
@@ -163,7 +163,7 @@ const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay
function handleCloseDrawer() {
setActiveIconName('');
const url = new URL(window.location);
const url = new URL(window.location.href);
url.pathname = '/icons/categories';
if (searchQueryDebounced.value) {
@@ -35,7 +35,6 @@ const SORTING = [
},
]
const initialGridItems = computed(() => {
if (containerWidth.value === 0) return 120;
@@ -72,6 +71,8 @@ const sortedIcons = computed(() => {
return [...props.icons].sort((a, b) => (b.popularity || 0) - (a.popularity || 0));
case 'release-date':
return [...props.icons].sort((a, b) => {
if (a.awaitingRelease !== b.awaitingRelease) return a.awaitingRelease ? -1 : 1;
const aDate = a.createdRelease?.date ? new Date(a.createdRelease.date).getTime() : 0;
const bDate = b.createdRelease?.date ? new Date(b.createdRelease.date).getTime() : 0;
return bDate - aDate;
@@ -172,7 +173,7 @@ watch(searchQueryDebounced, () => {
function handleCloseDrawer() {
setActiveIconName('');
const url = new URL(window.location);
const url = new URL(window.location.href);
url.pathname = '/icons/';
if (searchQueryDebounced.value) {
@@ -207,6 +208,7 @@ function handleCloseDrawer() {
<template #start-icon>
<Icon
:iconNode="listSortDescending"
name="list-sort-descending"
class="chevron-icon"
aria-hidden="true"
/>
@@ -39,7 +39,7 @@ interface Placeholder {
finePrint?: string;
}
const brandPlaceholders: Placeholder[] = shallowReadonly([
const brandPlaceholders: readonly Placeholder[] = shallowReadonly([
{
title: 'Boooo! What a scary brand logo!',
message:
@@ -103,7 +103,7 @@ const brandPlaceholders: Placeholder[] = shallowReadonly([
},
]);
const notFoundPlaceholders: Omit<Placeholder, 'title'>[] = shallowReadonly([
const notFoundPlaceholders: readonly Omit<Placeholder, 'title'>[] = shallowReadonly([
{
message: 'We’ve looked for this icon for a bird’s eye view, but could not find it.',
icon: markRaw(bird),
@@ -130,7 +130,7 @@ const notFoundPlaceholders: Omit<Placeholder, 'title'>[] = shallowReadonly([
},
]);
function randomItem<T>(arr: T[]): T {
function randomItem<T>(arr: readonly T[]): T {
return arr[Math.floor(Math.random() * arr.length)];
}
@@ -9,7 +9,6 @@ defineProps<{
<template>
<section class="related-icons">
<h2 class="title">More icons like this</h2>
<IconGrid :icons="icons" />
</section>
</template>
@@ -34,8 +34,8 @@ const sizeCssVar = useCssVar('--customize-size', props.rootEl?.value ?? document
});
syncRef(color, colorCssVar, { direction: 'ltr' });
syncRef(strokeWidth, strokeWidthCssVar, { direction: 'ltr' });
syncRef(size, sizeCssVar, { direction: 'ltr' });
syncRef(strokeWidth, strokeWidthCssVar, { direction: 'ltr', transform: { ltr: String } });
syncRef(size, sizeCssVar, { direction: 'ltr', transform: { ltr: String } });
function resetStyle() {
color.value = STYLE_DEFAULTS.color;
@@ -61,6 +61,50 @@
}
}
.search-bar :deep(.select-button) {
padding: 11px 14px;
background-color: var(--vp-c-bg-alt);
color: var(--vp-c-text-2);
flex-shrink: 0;
flex: 1;
font-weight: 500;
cursor: pointer;
}
.search-bar :deep(.select-button):hover,
.search-bar :deep(.select-button):focus {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-alt);
}
.search-bar :deep(.select-options) {
min-width: 160px;
}
.search-bar :deep(.select-option) {
padding-right: 36px;
padding-left: 8px;
}
.search-bar :deep(.select-icon) {
display: none;
}
.search-bar :deep(.select-button) .select-text {
display: none;
}
@media (min-width: 640px) {
.search-bar :deep(.select-button) {
padding: 11px 16px 11px 16px;
}
.search-bar :deep(.select-button) .select-text {
display: block;
}
}
@media (min-width: 960px) {
.search-bar {
padding-top: 32px;
@@ -68,4 +112,7 @@
top: calc(var(--vp-layout-top-height, 0px) + 64px);
}
}
</style>
@@ -1,6 +1,6 @@
<script setup lang="ts">
import VPHero from 'vitepress/dist/client/theme-default/components/VPHero.vue'
import { useData } from 'vitepress/dist/client/theme-default/composables/data'
import { useData } from 'vitepress/dist/client/theme-default/composables/data.js'
import FakeInput from '../base/FakeInput.vue';
import { useRouter } from 'vitepress';
import { data } from '../home/HomeHeroIconsCard.data'
@@ -41,7 +41,7 @@ import {
sparkles,
} from '../../../data/iconNodes';
import createElement from 'lucide/src/createElement';
import { IconNode } from 'lucide';
import { IconNode } from 'lucide/src/types';
const icons: Component[] = [
heart, star, zap, code, feather, cloud, sun, moon, camera, music,
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { useData } from 'vitepress';
import { useSidebar } from 'vitepress/dist/client/theme-default/composables/sidebar';
import { useSidebar } from 'vitepress/dist/client/theme-default/composables/sidebar.js';
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue';
import { computed } from 'vue';
import SurveyAskOverlay from '../SurveyAskOverlay.vue';
@@ -2,7 +2,7 @@ import packageDataList from '../../../data/packageData.json';
import thirdPartyPackages from '../../../data/packageData.thirdParty.json';
import fetchPackages from '../../../lib/fetchPackages';
export default {
const loader = {
async load() {
const packageJsonList = await fetchPackages();
@@ -29,3 +29,8 @@ export default {
};
},
};
declare const data: Awaited<ReturnType<typeof loader.load>>;
export { data };
export default loader;
@@ -1,5 +1,5 @@
import { onMounted, onUpdated, onUnmounted } from 'vue';
import { throttleAndDebounce } from 'vitepress/dist/client/theme-default/support/utils';
import { throttleAndDebounce } from 'vitepress/dist/client/theme-default/support/utils.js';
/*
* This file is compied and adjusted from vitepress/dist/client/theme-default/composables/useActiveAnchor.ts
@@ -1,10 +1,10 @@
import Fuse from 'fuse.js';
import Fuse, { type FuseOptionKeyObject } from 'fuse.js';
import { shallowRef, computed, Ref } from 'vue';
const useSearch = <T>(
query: Ref<string>,
collection: Ref<T[]>,
keys: Fuse.FuseOptionKeyObject<T>[] = [],
keys: FuseOptionKeyObject<T>[] = [],
) => {
const index = shallowRef(
new Fuse(collection.value, {
+4
View File
@@ -152,6 +152,10 @@ html {
.VPHomeHero .container {
flex-direction: row;
}
.VPDoc:has(.vp-doc[class*=' _icons_']) {
padding: 32px 32px 0;
}
}
.VPNavBarHamburger .container > span {
+11 -2
View File
@@ -1,11 +1,17 @@
export type IconNode = [elementName: string, attrs: Record<string, string>][];
export type IconNodeWithKeys = [elementName: string, attrs: Record<string, string>, key: string][];
export interface Alias {
name: string;
deprecationReason: string;
deprecated: boolean;
}
export interface IconMetaData {
tags: string[];
categories: string[];
contributors: string[];
aliases?: string[];
aliases?: Alias[];
deprecated?: boolean;
deprecationReason?: string;
toBeRemovedInVersion?: string;
@@ -18,6 +24,8 @@ export interface IconEntity extends IconMetaData {
externalLibrary?: ExternalLibs;
createdRelease?: Release;
changedRelease?: Release;
fromFork?: boolean;
awaitingRelease?: boolean;
popularity?: number;
}
@@ -43,7 +51,8 @@ export interface PackageItem {
packageDirname?: string;
description: string;
icon: string;
iconDark: string;
iconDark?: string;
iconClass?: string;
shields: Shield[];
source: string;
documentation: string;
+2 -2
View File
@@ -1,5 +1,5 @@
const chunkArray = <ArrayType>(stream: ArrayType, size: number) => {
return stream.reduce<ArrayType[][]>(
const chunkArray = <ItemType>(stream: ItemType[], size: number) => {
return stream.reduce<ItemType[][]>(
(chunks, item, idx, arr) =>
idx % size == 0 ? [...chunks, arr.slice(idx, idx + size)] : chunks,
[],
@@ -0,0 +1,25 @@
import type { IconNode } from '../types';
function getInnerSvg(iconNode: IconNode) {
return iconNode
.map(([tag, attrs]) => {
const attributes = Object.entries(attrs)
.map(([key, value]) => `${key}="${value}"`)
.join(' ');
return `<${tag} ${attributes}/>`;
})
.join('')
.replace(/>[\r\n ]+</g, '><')
.replace(/(<.*?>)|\s+/g, (m, $1) => $1 || ' ')
.trim();
}
export default function getStudioLink(
name: string,
iconNode: IconNode,
utmMedium = 'icon-detail-preview',
) {
const base64InnerSvg = btoa(getInnerSvg(iconNode ?? []));
return `https://studio.lucide.dev/edit?value=${encodeURIComponent(base64InnerSvg)}&name=${name}&utm_source=lucide.dev&utm_medium=${utmMedium}`;
}
+5
View File
@@ -1,3 +1,4 @@
/// <reference types="vitepress/client" />
import { type IconNode } from '@lucide/vue/src/types';
import Vue from 'vue';
@@ -31,7 +32,11 @@ declare module '*.node.json' {
}
declare global {
// Defined by VitePress at build time, used by its VPDocAsideCarbonAds component
const __CARBON__: boolean;
interface Window {
_carbonads?: unknown;
ExpoSnack?: {
/**
* Initialize all snack players on the page
+101 -93
View File
@@ -12,6 +12,7 @@ import { computed } from 'vue'
import { useData } from 'vitepress'
import IconPreview from '~/.vitepress/theme/components/icons/IconPreview.vue'
import IconPreviewSmall from '~/.vitepress/theme/components/icons/IconPreviewSmall.vue'
import EditInStudioButton from '~/.vitepress/theme/components/icons/EditInStudioButton.vue'
import IconInfo from '~/.vitepress/theme/components/icons/IconInfo.vue'
import IconContributors from '~/.vitepress/theme/components/icons/IconContributors.vue'
import IconShowcase from '~/.vitepress/theme/components/icons/IconShowcase.vue'
@@ -19,9 +20,11 @@ import RelatedIcons from '~/.vitepress/theme/components/icons/RelatedIcons.vue'
import CodeGroup from '~/.vitepress/theme/components/base/CodeGroup.vue'
import Badge from '~/.vitepress/theme/components/base/Badge.vue'
import Label from '~/.vitepress/theme/components/base/Label.vue'
import PageTabs from '~/.vitepress/theme/components/base/PageTabs.vue'
import { data } from './codeExamples.data'
import { toCamelCase, toPascalCase } from '@lucide/shared'
import { satisfies } from 'semver'
import CarbonAdOverlay from '~/.vitepress/theme/components/icons/CarbonAdOverlay.vue';
const { params } = useData()
@@ -49,75 +52,92 @@ function releaseTagLink(version) {
}
</script>
<div :class="$style.layout">
<div :class="$style.iconPreviews">
<div class="layout">
<div class="actions">
<EditInStudioButton :icon="params" />
</div>
<div class="iconPreviews">
<IconPreview
id="previewer"
:name="params.name"
:iconNode="params.iconNode"
:class="$style.preview"
/>
<IconPreviewSmall
:name="params.name"
:iconNode="params.iconNode"
:class="$style.smallPreview"
class="preview"
/>
</div>
<div >
<div :class="$style.info">
<div>
<div class="info">
<IconInfo :icon="params" />
<div :class="$style.meta">
<div class="meta">
<div
v-if="params.createdRelease?.version"
:class="$style.version"
v-if="params.awaitingRelease"
class="version"
>
<Label>Created:</Label>
<Badge class="awaiting-release">Unreleased</Badge>
</div>
<div
v-else-if="params.createdRelease?.version"
class="version"
>
<Label>Created:</Label>
<Badge
:href="releaseTagLink(params.createdRelease.version)"
>
v{{params.createdRelease.version}}
{{params.createdRelease.version}}
</Badge>
</div>
<div
v-if="params.changedRelease?.version"
:class="$style.version"
v-if="!params.awaitingRelease && params.changedRelease?.version"
class="version"
>
<Label>Last changed:</Label>
<Badge
:href="releaseTagLink(params.changedRelease.version)"
>
v{{params.changedRelease.version}}
{{params.changedRelease.version}}
</Badge>
</div>
<IconContributors :icon="params" :class="$style.contributors"/>
<IconContributors :icon="params" class="contributors"/>
</div>
</div>
</div>
</div>
<PageTabs
:tabs="['More like this', 'Code examples', 'See in action']"
>
<template #tab-0>
<RelatedIcons
v-if="params.relatedIcons"
:icons="params.relatedIcons"
/>
</template>
<template #tab-1>
<CodeGroup
:groups="tabs"
groupName="icon-code-example"
:class="$style.code"
class="code"
>
<div
class="blocks"
v-html="codeExample"
/>
</CodeGroup>
</div>
</div>
</template>
<div class="icon-page-sections">
<IconShowcase
:name="params.name"
:iconNode="params.iconNode"
/>
<template #tab-2>
<IconShowcase
:name="params.name"
:iconNode="params.iconNode"
/>
</template>
</PageTabs>
<RelatedIcons
v-if="params.relatedIcons"
:icons="params.relatedIcons"
/>
</div>
<CarbonAdOverlay :drawerOpen="false" />
<style module>
<style scoped>
.preview {
grid-area: preview;
margin-bottom: 24px;
@@ -130,31 +150,52 @@ function releaseTagLink(version) {
align-items: flex-start;
}
.actions {
text-align: right;
display: flex;
justify-content: flex-end;
margin: -12px -12px 24px 0;
}
.meta {
margin-left: auto;
margin-top: 24px;
display: flex;
gap: 24px;
align-items: flex-start;
}
.info {
--tags-gradient-background: var(--vp-c-bg);
}
.icon-info {
text-align: center;
}
.version .label {
margin-top: 12px;
}
.badge {
margin: 12px 0;
}
.version, .contributors {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: flex-start;
margin-bottom: 0px;
justify-content: flex-start;
gap: 4px;
}
.version:first-child {
margin-bottom: 8px;
.version .awaiting-release {
color: var(--vp-c-danger-3);
}
.iconPreviews {
display: flex;
justify-content: flex-start;
justify-content: center;
gap: 24px;
}
@@ -178,6 +219,13 @@ function releaseTagLink(version) {
.iconPreviews {
flex-direction: column;
justify-content: flex-start;
}
.actions {
position: absolute;
right: 0;
margin: 0;
}
.smallPreview {
@@ -185,61 +233,6 @@ function releaseTagLink(version) {
align-items: center;
}
}
@media (min-width: 860px) {
.info {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.meta {
border-left: 1px solid var(--vp-c-divider);
padding-left: 16px;
margin-top: 0;
}
.version, .contributors {
flex-direction: column;
}
}
@media (min-width: 960px) {
.info {
display: block;
justify-content: space-between;
align-items: flex-start;
}
.meta {
border-left: none;
padding-left: 0;
margin-top: 24px;
}
.version, .contributors {
flex-direction: row;
}
}
@media (min-width: 1152px) {
.info {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.meta {
border-left: 1px solid var(--vp-c-divider);
padding-left: 16px;
margin-top: 0;
}
.version, .contributors {
flex-direction: row;
margin-bottom: 8px;
}
}
</style>
<style>
@@ -249,4 +242,19 @@ function releaseTagLink(version) {
margin-block-end: 64px;
padding-top: 32px;
}
.tab-list {
margin: 24px -24px 24px;
}
@media (min-width: 640px) {
.tab-list {
margin: 24px 0 24px;
}
.iconPreviews {
justify-content: flex-start;
}
}
</style>
+22 -11
View File
@@ -3,17 +3,28 @@ import * as iconDetails from '../.vitepress/data/iconDetails';
export default {
async load() {
return {
icons: Object.entries(iconDetails).map(
([, { name, iconNode, popularity, createdRelease, aliases = [] }]) => ({
name,
iconNode,
popularity: popularity?.count ?? 0,
aliases: aliases
.map((alias) => (typeof alias === 'string' ? alias : alias?.name))
.filter((alias): alias is string => Boolean(alias)),
createdRelease,
}),
),
icons: Object.entries(iconDetails)
.map(
([
,
{ name, iconNode, popularity, createdRelease, awaitingRelease, aliases = [], fromFork },
]) => ({
name,
iconNode,
popularity: popularity?.count ?? 0,
aliases: aliases
.map((alias) => (typeof alias === 'string' ? alias : alias?.name))
.filter((alias): alias is string => Boolean(alias)),
createdRelease,
awaitingRelease,
fromFork,
}),
// We hide icons that are not released yet, we make exceptions for preview environments
)
.filter(
({ awaitingRelease }) =>
!awaitingRelease || process.env.VITE_SHOW_AWAITING_RELEASE === 'true',
),
};
},
};
+1 -2
View File
@@ -1,11 +1,10 @@
import iconNodes from '../../.vitepress/data/lab/iconNodes';
import iconPopularity from '../../.vitepress/data/lab/iconPopularity';
import iconMetaData from '../../.vitepress/data/lab/iconMetaData';
import { type IconEntity } from '../../.vitepress/theme/types';
export default {
paths: async () => {
return (Object.entries(iconNodes) as unknown as IconEntity[]).map(([name, iconNode]) => {
return Object.entries(iconNodes).map(([name, iconNode]) => {
const iconDetails = iconMetaData[name];
delete iconDetails['$schema'];
+7 -4
View File
@@ -15,7 +15,7 @@
"prebuild:categoriesJson": "node ./scripts/writeCategoriesMetadata.mts",
"prebuild:relatedIcons": "node ./scripts/writeIconRelatedIcons.mts",
"prebuild:iconDetails": "node ./scripts/writeIconDetails.mts",
"prebuild:iconPopularity": "node ./scripts/writeiconPopularity.mts && node ./scripts/writeiconPopularityIndex.mts",
"prebuild:iconPopularity": "node --env-file-if-exists=.env ./scripts/writeiconPopularity.mts && node ./scripts/writeiconPopularityIndex.mts",
"prebuild:brandStopwords": "node ./scripts/writeBrandStopwords.mts",
"prebuild:writeLabIconNodes": "node ./scripts/writeLabIconNodes.mts",
"prebuild:writeLabIconMetaIndex": "node ./scripts/writeLabIconMetaIndex.mts",
@@ -24,7 +24,8 @@
"prebuild:api": "nitropack prepare",
"build:api": "NODE_OPTIONS=--max-old-space-size=8192 nitropack build",
"build": "pnpm run /^prebuild:.*/ && pnpm run build:api && pnpm run build:docs && pnpm postbuild:vercelJson",
"preview": "node .output/server/index.mjs"
"preview": "node .output/server/index.mjs",
"typecheck": "vue-tsc --noEmit"
},
"author": "Eric Fennis",
"license": "ISC",
@@ -47,9 +48,11 @@
"svg-path-commander": "^2.3.3",
"svg-pathdata": "^9.0.0",
"svgson": "^5.3.1",
"typescript": "^6.0.3",
"vitepress": "^1.6.4",
"vitepress-plugin-group-icons": "^1.7.6",
"vitepress-plugin-llms": "^1.13.5"
"vitepress-plugin-llms": "^1.13.5",
"vue-tsc": "^3.3.11"
},
"dependencies": {
"@floating-ui/vue": "^2.0.1",
@@ -68,6 +71,6 @@
"react": "18.3.1",
"react-dom": "18.3.1",
"sandpack-vue3": "3.1.12",
"vue": "^3.5.42"
"vue": "^3.5.43"
}
}
+3 -2
View File
@@ -27,12 +27,13 @@ import iconNode from '../iconNodes/${iconName}.node.json' with { type: 'json' };
import metaData from '../../../../icons/${iconName}.json' with { type: 'json' };
import releaseData from '../releaseMetadata/${iconName}.json' with { type: 'json' };
import popularity from '../iconPopularity/${iconName}.json' with { type: 'json' };
import { IconMetaData, IconNode } from '~/.vitepress/theme/types';
const { tags, categories, contributors, aliases, deprecated, deprecationReason, toBeRemovedInVersion } = metaData;
const { tags, categories, contributors, aliases, deprecated, deprecationReason, toBeRemovedInVersion }: IconMetaData = metaData;
const iconDetails = {
name: '${iconName}',
iconNode,
iconNode: iconNode as IconNode,
contributors,
tags,
categories,
+10 -3
View File
@@ -20,6 +20,7 @@ if (!fs.existsSync(iconNodesDirectory)) {
const iconIndexFile = path.resolve(iconNodesDirectory, `index.ts`);
const iconIndexFileImports = [];
const iconIndexFileNodes = [];
const iconIndexFileExports = [];
const iconIndexFileDefaultExports = [];
@@ -32,7 +33,10 @@ const writeIconFiles = Object.entries(icons).map(async ([iconName, { children }]
await fs.promises.writeFile(location, output, 'utf-8');
iconIndexFileImports.push(
`import ${toCamelCase(iconName)}Node from './${iconName}.node.json' with { type: "json" };`,
`import ${toCamelCase(iconName)}Json from './${iconName}.node.json' with { type: "json" };`,
);
iconIndexFileNodes.push(
`const ${toCamelCase(iconName)}Node = ${toCamelCase(iconName)}Json as IconNode;`,
);
iconIndexFileExports.push(` ${toCamelCase(iconName)}Node as ${toCamelCase(iconName)},`);
iconIndexFileDefaultExports.push(` '${iconName}': ${toCamelCase(iconName)}Node,`);
@@ -43,16 +47,19 @@ try {
await fs.promises.writeFile(
iconIndexFile,
`\
import type { IconNode } from '../../theme/types';
${iconIndexFileImports.join('\n')}
${iconIndexFileNodes.join('\n')}
export {
${iconIndexFileExports.join('\n')}
}
export default {
${iconIndexFileDefaultExports.join('\n')}
}
`,
} satisfies Record<string, IconNode>;
`,
'utf-8',
);
+10 -3
View File
@@ -20,6 +20,7 @@ if (!fs.existsSync(iconNodesDirectory)) {
const iconIndexFile = path.resolve(iconNodesDirectory, `index.ts`);
const iconIndexFileImports: string[] = [];
const iconIndexFileNodes: string[] = [];
const iconIndexFileExports: string[] = [];
const iconIndexFileDefaultExports: string[] = [];
@@ -32,7 +33,10 @@ const writeIconFiles = Object.entries(icons).map(async ([iconName, { children }]
await fs.promises.writeFile(location, output, 'utf-8');
iconIndexFileImports.push(
`import ${toCamelCase(iconName)}Node from './${iconName}.node.json' with { type: "json" };`,
`import ${toCamelCase(iconName)}Json from './${iconName}.node.json' with { type: "json" };`,
);
iconIndexFileNodes.push(
`const ${toCamelCase(iconName)}Node = ${toCamelCase(iconName)}Json as IconNode;`,
);
iconIndexFileExports.push(` ${toCamelCase(iconName)}Node as ${toCamelCase(iconName)},`);
iconIndexFileDefaultExports.push(` '${iconName}': ${toCamelCase(iconName)}Node,`);
@@ -43,16 +47,19 @@ try {
await fs.promises.writeFile(
iconIndexFile,
`\
import type { IconNode } from '../../../theme/types';
${iconIndexFileImports.join('\n')}
${iconIndexFileNodes.join('\n')}
export {
${iconIndexFileExports.join('\n')}
}
export default {
${iconIndexFileDefaultExports.join('\n')}
}
`,
} satisfies Record<string, IconNode>;
`,
'utf-8',
);
+26 -5
View File
@@ -40,7 +40,21 @@ const fetchAllReleases = async () => {
);
};
const getIconNamesAtTag = async (tag: string) => {
// --full-tree resolves `icons/` from the repo root instead of the cwd (docs/)
const files = await git.raw(['ls-tree', '--full-tree', '--name-only', tag, 'icons/']);
return new Set(
files
.split('\n')
.filter((file) => file.endsWith('.svg'))
.map((file) => path.basename(file, '.svg')),
);
};
const tags = await fetchAllReleases();
const forkIconNames = await getIconNamesAtTag(tags[0]);
const releasedIconNames = await getIconNamesAtTag(tags[tags.length - 1]);
const comparisonsPromises = tags.map(async (tag, index) => {
const previousTag = tags[index - 1];
@@ -150,19 +164,26 @@ try {
const iconName = path.basename(iconJsonFile, '.json');
const metaDir = path.resolve(releaseMetaDataDirectory, `${iconName}.json`);
if (!(iconName in newReleaseMetaData)) {
const metaData = await fs.promises.readFile(path.join(ICONS_DIR, iconJsonFile), 'utf-8');
const iconMetaData = JSON.parse(metaData);
const aliases = iconMetaData.aliases ?? [];
const fromFork = [iconName, ...aliases.map((alias) => alias.name)].some((name) =>
forkIconNames.has(name),
);
const awaitingRelease = !releasedIconNames.has(iconName);
if (!(iconName in newReleaseMetaData) && !fromFork && !awaitingRelease) {
console.error(`Could not find release metadata for icon '${iconName}'.`);
}
const contents = {
fromFork,
awaitingRelease,
...defaultReleaseMetaData,
...(newReleaseMetaData[iconName] ?? {}),
};
const metaData = await fs.promises.readFile(path.join(ICONS_DIR, iconJsonFile), 'utf-8');
const iconMetaData = JSON.parse(metaData);
const aliases = iconMetaData.aliases ?? [];
if (aliases.length) {
aliases.forEach((alias) => {
if (!(alias.name in newReleaseMetaData)) {
+1
View File
@@ -7,6 +7,7 @@
"allowImportingTsExtensions": true,
"allowSyntheticDefaultImports": true,
"noEmit": true,
"skipLibCheck": true,
"paths": {
"~/.vitepress/*": ["./.vitepress/*"]
}
+39
View File
@@ -0,0 +1,39 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"colebemis",
"csandman",
"ericfennis",
"tylerkade",
"jguddas"
],
"use-cases": [
"Indicating you're adding luggage",
"adding a new business or company profile",
"creating or saving a professional portfolio",
"adding a job or role to a user's profile",
"indicating add to briefcase or save for later in a product/service listing"
],
"tags": [
"work",
"bag",
"baggage",
"folder",
"new",
"add",
"create",
"increase",
"briefcase",
"portfolio",
"business",
"career",
"employment",
"professional",
"plus"
],
"categories": [
"transportation",
"files",
"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="M13.354 20H4a2 2 0 01-2-2V8a2 2 0 012-2h16a2 2 0 012 2v3.354" />
<path d="M16 11.354V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v16" />
<path d="M16 17h6" />
<path d="M19 14v6" />
</svg>

After

Width:  |  Height:  |  Size: 392 B

+2 -1
View File
@@ -3,7 +3,8 @@
"contributors": [
"rrod497",
"jamiemlaw",
"karsa-mistmere"
"karsa-mistmere",
"jguddas"
],
"use-cases": [],
"tags": [
+4 -4
View File
@@ -9,8 +9,8 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M10 13a3 3 0 0 1-2.121-5.121" />
<path d="M15.606 14.204c-3.5 1.5-5.899 4.503-8.899 7.503A1 1 0 0 1 6 22c-2 0-4-2-4-4a1 1 0 0 1 .293-.707c1.911-1.911 3.823-3.578 5.347-5.441" />
<path d="M16.573 14.737A4 4 0 0 1 14 11" />
<path d="M7.14 10.907a4 4 0 1 1 2.756-7.43A4 4 0 0 1 16.7 4.48a2 2 0 0 1 2.82 2.82 4 4 0 0 1 1.002 6.805A4 4 0 1 1 13 16" />
<path d="M10 13a3 3 0 01-2.121-5.121" />
<path d="M15.606 14.204c-3.5 1.5-5.899 4.503-8.899 7.503A1 1 0 016 22c-2 0-4-2-4-4a1 1 0 01.293-.707c1.911-1.911 3.823-3.578 5.347-5.441" />
<path d="M16.573 14.737A4 4 0 0114 11" />
<path d="M7.14 10.907a4 4 0 112.756-7.43A4 4 0 0116.7 4.48a2 2 0 012.82 2.82 4 4 0 011.002 6.805 4 4 0 11-7.51 1.59" />
</svg>

Before

Width:  |  Height:  |  Size: 572 B

After

Width:  |  Height:  |  Size: 560 B

+1 -1
View File
@@ -12,7 +12,7 @@
<path d="m13 21 3-3-3-3" />
<path d="M16 2v3" />
<path d="m19 21 3-3-3-3" />
<path d="M21 11.5V5.05a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2L9 21" />
<path d="M21 11.5V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h4" />
<path d="M3 9h18" />
<path d="M8 2v3" />
</svg>

Before

Width:  |  Height:  |  Size: 413 B

After

Width:  |  Height:  |  Size: 407 B

+1 -1
View File
@@ -12,5 +12,5 @@
<path d="M12 14v4" />
<path d="M14.172 2a2 2 0 0 1 1.414.586l3.828 3.828A2 2 0 0 1 20 7.828V20a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2z" />
<path d="M8 14h8" />
<rect x="8" y="10" width="8" height="8" rx="1" />
<rect x="8" y="10" width="8" height="8" rx="2" />
</svg>

Before

Width:  |  Height:  |  Size: 433 B

After

Width:  |  Height:  |  Size: 433 B

+27
View File
@@ -0,0 +1,27 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"AnnaSasDev",
"briz123",
"ajokt123",
"rrod497",
"karsa-mistmere"
],
"use-cases": [
"Representing cupcakes in bakery product catalogs",
"Filtering dessert options in food ordering apps",
"Marking treats for parties or celebrations"
],
"tags": [
"cake",
"dessert",
"food",
"celebration",
"sweet",
"baking",
"cooking"
],
"categories": [
"food-beverage"
]
}
+19
View File
@@ -0,0 +1,19 @@
<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 22v-9" />
<path d="M14 4h1a3 3 0 013 3l-.004.125A4 4 0 0121 11v2" />
<path d="m15.5 22 1.5-9" />
<path d="M21 13a1 1 0 01.919 1.394l-2.74 6.394A2 2 0 0117.34 22H6.659a2 2 0 01-1.838-1.212l-2.74-6.394A1 1 0 013 13z" />
<path d="M3 13v-2a4 4 0 013.003-3.875L6 7a3 3 0 013-3h1" />
<path d="M8.5 22 7 13" />
<circle cx="12" cy="4" r="2" />
</svg>

After

Width:  |  Height:  |  Size: 571 B

+25
View File
@@ -0,0 +1,25 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"karsa-mistmere"
],
"use-cases": [
"Indicating package delivery to a home or residence",
"Representing packages left at a recipient's door",
"Tracking doorstep or unattended deliveries",
"Selecting a delivery location or drop-off point"
],
"tags": [
"delivery",
"parcel",
"doorstep",
"shipping",
"drop-off",
"courier"
],
"categories": [
"home",
"food-beverage",
"transportation"
]
}
+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="M18 13v3" />
<path d="M19 9V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16" />
<path d="M2 21h8" />
<path d="M9 12h.01" />
<rect x="14" y="13" width="8" height="8" rx="1" />
</svg>

After

Width:  |  Height:  |  Size: 387 B

+43
View File
@@ -0,0 +1,43 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"danielbayley",
"karsa-mistmere"
],
"use-cases": [
"Indicating water sources on maps",
"Controlling faucets in smart home apps",
"Representing kitchen or bathroom plumbing fixtures",
"Showing drinking water amenities in travel apps"
],
"tags": [
"tap",
"spigot",
"water",
"drinking",
"fresh",
"source",
"pipes",
"plumbing",
"plumber",
"sink",
"basin",
"kitchen",
"bathroom",
"washroom",
"fixture",
"valve",
"flow",
"drinkable",
"potable",
"amenities",
"utilities",
"hydration"
],
"categories": [
"food-beverage",
"home",
"navigation",
"travel"
]
}
+19
View File
@@ -0,0 +1,19 @@
<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.083 5.428 5.57 4.083a2 2 0 10.001 3.834l4.512-1.345" />
<path d="M12 8v3" />
<path d="m13.917 5.428 4.511-1.345a2 2 0 110 3.834l-4.51-1.345" />
<path d="M18 17v-4.006" />
<path d="M22 11v8" />
<path d="M22 12h-3a1 1 0 00-1 .994h-2.539a4 4 0 00-6.915-.012L7 13a5 5 0 00-5 5v1a1 1 0 001 1h2a1 1 0 001-1v-1a1 1 0 01.995-1l1.552.018a4 4 0 006.907 0L18 17a1 1 0 001 1h3" />
<circle cx="12" cy="6" r="2" />
</svg>

After

Width:  |  Height:  |  Size: 636 B

+30
View File
@@ -0,0 +1,30 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"jguddas",
"karsa-mistmere",
"danielbayley",
"UsamaKhan",
"ajaxjiang96"
],
"use-cases": [
"Opening household settings in smart home applications",
"Managing property configuration in real estate dashboards",
"Configuring home automation rules in IoT device management interfaces",
"Accessing home security settings in residential alarm systems"
],
"tags": [
"home",
"building",
"residence",
"settings",
"gear",
"configuration",
"property",
"automation"
],
"categories": [
"buildings",
"home"
]
}
+23
View File
@@ -0,0 +1,23 @@
<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.584 21H5a2 2 0 01-2-2v-9a2 2 0 01.709-1.527l7-6a2 2 0 012.582 0l7 6A2 2 0 0121 10.001v.583" />
<path d="M14 12H10a1 1 0 00-1 1v8" />
<path d="m14.305 19.53.923-.382" />
<path d="m15.229 16.852-.924-.383" />
<path d="m16.852 15.228-.383-.923" />
<path d="m16.852 20.773-.383.924" />
<path d="m19.148 15.228.383-.923" />
<path d="m19.53 21.697-.382-.924" />
<path d="m20.773 16.852.922-.383" />
<path d="m20.773 19.148.922.383" />
<circle cx="18" cy="18" r="3" />
</svg>

After

Width:  |  Height:  |  Size: 705 B

+33
View File
@@ -0,0 +1,33 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"karsa-mistmere",
"kaleidosium",
"UbaidUllah9962"
],
"use-cases": [
"Representing anonymous functions in code editors",
"Representing serverless functions in cloud architecture diagrams",
"Representing lambda notation in math and science"
],
"tags": [
"greek",
"symbol",
"math",
"function",
"programming",
"serverless",
"anonymous",
"functional",
"cloud",
"wavelength",
"calculus",
"linear algebra"
],
"categories": [
"development",
"math",
"science",
"text"
]
}
+2 -4
View File
@@ -9,8 +9,6 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M8 7V5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
<rect width="20" height="14" x="2" y="7" rx="2" />
<path d="M15 14H9" />
<path d="M12 11v6" />
<path d="M11.38 10 5 20" />
<path d="M19 18a2 2 0 01-2 2c-4.87-.003-5.052-16-10-16a2 2 0 00-2 2" />
</svg>

Before

Width:  |  Height:  |  Size: 363 B

After

Width:  |  Height:  |  Size: 312 B

+23
View File
@@ -0,0 +1,23 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"nathan-de-pachtere"
],
"use-cases": [
"Marking the first commit in version-control graphs",
"Indicating an initial measurement point on a process timeline"
],
"tags": [
"code",
"version",
"control",
"station",
"waypoint",
"start",
"first"
],
"categories": [
"development",
"navigation"
]
}

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