Compare commits

...

64 Commits

Author SHA1 Message Date
Eric Fennis
881e733159 Merge branch 'main' of https://github.com/lucide-icons/lucide into next 2025-12-12 09:34:11 +01:00
Alexandru Portan
7abb61630e feat(icons): added stone icon (#3850)
* Added icons/stone.svg

* Added icons/stone.json

* Update icons/stone.json

Added suggested tags

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

* Update stone.svg

Updated based on suggested changes

* Update icons/stone.json

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jakob Guddas <github@jguddas.de>
2025-12-12 09:26:54 +01:00
Eric Fennis
3b0d158ea1 fix(site): Small adjustments color picker and add clear button search bar (#3851)
* Small adjustments

* Format code

* format code

* Remove default value

* format code

* update yml file

* Format code

* Update docs/.vitepress/theme/components/base/Input.vue

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

* Add extra check if null or undefined

---------

Co-authored-by: Karsa <contact@karsa.org>
2025-12-12 09:25:39 +01:00
Veles
124572c83b feat(icons): added cannabis-off icon (#3748)
* Added icons/cannabis-off.svg

* Added icons/cannabis-off.json

* fix: applied optimization by jguddas

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
Co-authored-by: Karsa <contact@karsa.org>
2025-12-11 09:15:49 +01:00
7ender
4fcfb6a4d1 feat(icons): added fishing-hook icon (#3837)
* Added icons/fishing-hook.svg

* Added icons/fishing-hook.json

* Update icons/fishing-hook.json

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

* Update fishing-hook.svg

upd, Big thanks to  @jguddas, @karsa-mistmere and @jamiemlaw

* Update fishing-hook.json

+ contributors

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-10 14:06:51 +01:00
Jamie Law
0f732b411d feat(icons): added hd icon (#2958)
* Add `hd` icon

* Update hd.svg

* Update hd.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-10 13:56:10 +01:00
Eric Fennis
ce09c31f08 Merge branch 'main' of https://github.com/lucide-icons/lucide 2025-12-10 13:12:58 +01:00
Eric Fennis
c2b059fb60 ci(release.yml): Remove deprecated token assignment 2025-12-10 13:12:55 +01:00
Peter Uithoven
b3c80d027a feat(icons): added balloon icon (#2519)
* Added icons/balloon.svg

* Added icons/balloon.json

* Updated icons/balloon.svg

* Optimize paths

* Update tags

---------

Co-authored-by: peteruithoven <peter@metabolic.nl>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-10 13:07:01 +01:00
Eric Fennis
20f30bb5ea Merge branch 'main' of https://github.com/lucide-icons/lucide 2025-12-10 13:05:38 +01:00
Eric Fennis
c47ae67a3b ci(ci.yml): Apply env variables 2025-12-10 13:05:34 +01:00
Nathan De Pachtere
7866a5a5c6 feat(icons): added circle-pile icon (#3681)
* Added icons/circle-pile.svg

* Added icons/circle-pile.json

* Add tags

* Reduce high
2025-12-10 12:58:51 +01:00
Eric Fennis
92bc88b001 ci(ci.yml): Fix latest tag from steps and remove superfluous steps 2025-12-10 12:58:06 +01:00
Eric Fennis
e75fbcdec4 feat(icons): Add cloud-sync and cloud-backup (#3466)
* Add icons

* Update cloud-backup.json

* Update cloud-sync.json

* Format code

---------

Co-authored-by: Karsa <contact@karsa.org>
2025-12-10 12:49:56 +01:00
Eric Fennis
4cef8283a7 ci(ci.yml): Fix LATEST_TAG assignment 2025-12-10 12:49:24 +01:00
Wiktor Żagiel
330f4b37db feat(icons): add search-error icon (#3292)
* feat(icons): add `search-error` icon

* Update icons/search-error.svg

Co-authored-by: Jakob Guddas <github@jguddas.de>

* Update icons/search-error.json

Co-authored-by: Jakob Guddas <github@jguddas.de>

* chore(icons): rename `search-error` to `search-alert`

* Fix indentation contributors in search-alert.json

* Format code

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-10 11:33:12 +01:00
Sage Fennel
fd31cb44a8 docs(dev): Fix code sample for vanilla JS (#3836)
* Fix code sample for vanilla JS

The .append method of elements takes plain text, not HTML. This updates the code to use .insertAdjacentHTML instead.

* Update createLabCodeExamples.ts

* fix(site): update createCodeExamples.ts vanilla JS code example

* Update createLabCodeExamples.ts

* Update createCodeExamples.ts

* Update createCodeExamples.ts

---------

Co-authored-by: Karsa <contact@karsa.org>
2025-12-10 11:18:57 +01:00
Juan Isidoro García Cifuentes
790d30dbfa feat(icons): added layers-plus icon (#3367)
* Added icons/layers-plus.svg

* Added icons/layers-plus.json

* Update layers-plus.json

fix: updated contributors to reflect actual author

* fix(icon): update layers-plus icon to follow size and spacing guidelines

* fix(icon): adjust layers-plus plus sign alignment and slope

* fix(icon): apply optimized version from reviewer to align on grid and spacing

* chore: add karsa-mistmere as contributor

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-10 11:13:25 +01:00
Karsa
e7c075785f fix(icons): changed tickets icon (#3859)
* Updated icons/tickets.svg

* Updated icons/tickets.json

* Updated icons/tickets-plane.svg

* Updated icons/tickets-plane.json
2025-12-10 11:10:26 +01:00
Ian Jones
6d4c91707d fix(icons): Swap thumbs-up thumbs-down paths to fix fill issue (#3873)
* Swap thumbs-up.svg paths

* Swap thumbs-down.svg paths
2025-12-10 11:09:27 +01:00
Jakob Guddas
c0ea92ebe7 fix(icons): changed brush-cleaning icon (#3863)
* Updated icons/brush-cleaning.svg

* Updated icons/brush-cleaning.json
2025-12-10 11:08:55 +01:00
Jakob Guddas
42dc5508dd fix(icons): changed paint-bucket icon (#3865)
* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.svg
2025-12-10 11:07:50 +01:00
Eric Fennis
4dda432471 chore(repo): Update Node version and overal cleanup (#3861)
* update ci script

* Update ci workflow

* Update node version
2025-12-10 11:06:48 +01:00
Karsa
0775d8647e fix(site): only show search placeholder if there aren't any results 2025-12-10 08:50:17 +01:00
Karsa
83ef8fc98d fix(icons): changed microchip icon (#3018)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/microchip.svg

---------

Co-authored-by: Karsa <karsa@karsa.org>
2025-12-09 15:47:57 +01:00
Karsa
5b56ef705d fix(icons): changed memory-stick icon (#3017)
* [github] Added issue template forms

* [github] yaml => yml

* Syntax fixes

* Further syntax fixes

* Sort issue templates

* Update 02_bug_report.yml

* Updated icons/memory-stick.svg

* Update memory-stick.svg

---------

Co-authored-by: Karsa <karsa@karsa.org>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-09 15:46:35 +01:00
Karsa
dafe529892 fix: fixed linting issues introduced in c4e5730bc4 (#3858) 2025-12-05 15:21:00 +01:00
Ahmed Dghaies
151c5b145c feat(icons): added van icon (#3821)
* Add van icon

* update tags

* edit icon

* edit contributors

* Update icons/van.json

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

---------

Co-authored-by: Karsa <contact@karsa.org>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-05 14:35:58 +01:00
Eric Fennis
d6f9043096 style(ci): Format ci.yml 2025-12-05 14:32:32 +01:00
Karsa
b4405f05ab feat(site): add brand stop words to icon search (#3824)
* feat(site): added extended no results placeholder with brand icon stop words

* feat(site): fix grammatical error

* feat: extract brand stopwords & update github action to use these stopwords

* Apply suggestions from code review

Co-authored-by: Jakob Guddas <github@jguddas.de>

* feat: only use icon name section for closing brand request issues

* feat: added mcp brand stopword

---------

Co-authored-by: Jakob Guddas <github@jguddas.de>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-05 14:18:46 +01:00
Jakob Guddas
9076da5f1b fix(icons): changed bubbles icon (#3774)
* Updated icons/bubbles.svg

* Updated icons/bubbles.svg

* Updated icons/bubbles.json
2025-12-05 13:46:42 +01:00
Jamie Law
2e7d806282 fix(icons): changed flashlight icons (#3843)
* Update flashlight.svg

Arcified ends and adjusted the tapering

* Update flashlight-off.svg

Update to match `flashlight.svg`

* Update flashlight.json

Added attribution and tags

* Update flashlight-off.json

* Update flashlight.svg

Correctly round bottom corners

* Update flashlight-off.svg
2025-12-05 09:53:31 +01:00
Eric Fennis
c4e5730bc4 fix(workflow): Fix permissions release worklfow 2025-12-05 09:27:34 +01:00
Nathan De Pachtere
02b35e2518 feat(icons): added estimated-weight icon (#3822)
* Added icons/estimated-weight.svg

* Added icons/estimated-weight.json

* Update icons/estimated-weight.json

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

* Change name + reshape tilde

* Change name to weight-tilde

* Update weight-tilde.json

removed weight and tilde, since they're part of the name

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Karsa <contact@karsa.org>
2025-12-05 09:18:42 +01:00
Jakob Guddas
f183c3ba20 feat(preview-comment): add symmetry preview (#3823) 2025-12-05 09:11:17 +01:00
Eden Yemini
67e9efb801 fix(icons): Shrink square-scissors icons to match optical volume (#3603)
* Update square-scissors to match optical volume

* Update square-bottom-dashed-scissors to match optical volume

---------

Co-authored-by: Karsa <contact@karsa.org>
2025-12-03 13:26:43 +01:00
Jakob Guddas
2e4c9a65be fix(icons): changed thermometer-sun icon (#3773)
* Updated icons/thermometer-sun.svg

* Updated icons/thermometer-sun.json

* Modify SVG paths in thermometer-sun icon
2025-12-03 13:01:26 +01:00
Jamie Law
de4e8d0acd fix(icons): changed plug icon (#3841)
* Updated icons/plug.svg

* Updated icons/plug.json
2025-12-03 12:45:33 +01:00
Ahmed Dghaies
1f113d4274 feat(icons): added scooter icon (#3818)
* Add scooter icon

* edit tags

* edit icon

* edit icon

* deit icon

* edit icon

* edit contributors

* Update scooter.json

---------

Co-authored-by: Karsa <contact@karsa.org>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-11-28 14:42:51 +01:00
Eric Fennis
dffffc7aff fix: Reverts vercel output path 2025-11-27 11:06:47 +01:00
Eric Fennis
d58a2e43c6 Merge branch 'next' of https://github.com/lucide-icons/lucide into next 2025-11-27 10:58:43 +01:00
Eric Fennis
5ecf78bb8a Merge branch 'main' of https://github.com/lucide-icons/lucide into next 2025-11-27 10:58:34 +01:00
Eric Fennis
f78061b488 ci(workflows): Enable trusted publishing in release (#3808)
* Add permissions for trusted publishing to workflows

* update pnpm

* Update workflow deps

* format code
2025-11-27 10:58:14 +01:00
Eric Fennis
aa8f74eb9e fix(icons): Remove brand icons (#3639)
* Remove brand icons

* Apply feedback

* adjust schema
2025-11-27 10:56:32 +01:00
Eric Fennis
7327637532 Merge branch 'main' of https://github.com/lucide-icons/lucide into next 2025-11-27 10:56:13 +01:00
Eric Fennis
076e0bbcd9 chore(dependencies): Update dependencies (#3809)
* update deps

* Remove ignoredBuiltDependencies

* Fix build errors

* try this

* update config

* add coma

* Try this

* Revert import change

* try this

* add wasm import

* Load wasm

* try this

* Revert nitro version change

* Revert nitro config change
2025-11-27 10:50:19 +01:00
Muhammad Aqib Bashir
33bb95edcd feat(icon): add book-search icon (#3573) (#3580)
* feat(icon): add book-search icon

* fix(icon): fix vertical alignment of `book-search` icon  .

- Fix the vertical alignment of the icon in the 24x24 grid of pixels 
- It had 3 columns on the left side and 1 on the right side, now made 2 on both sides

* fix: remove extra tag "book search"
2025-11-26 09:29:40 +01:00
Jakob Guddas
4c8650a7bf feat(icons): added form icon (#3558)
* Added icons/form.svg

* Added icons/form.json

* Update icons/form.json

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

* Update icons/form.json

* Updated icons/form.svg

* Updated icons/form.json

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-11-26 09:27:36 +01:00
Jamie Law
ebf400befd fix(icons): changed scale icon (#3800)
* Updated icons/scale.svg

* Updated icons/scale.json
2025-11-26 09:26:04 +01:00
Eric Fennis
0f6dc52a8a Add waves arrow up down (#3463) 2025-11-23 17:45:08 +01:00
Eric Fennis
08bd4b33a0 Merge branch 'main' of https://github.com/lucide-icons/lucide into next 2025-11-21 14:58:50 +01:00
Eric Fennis
83d5ccc3e8 chore(metadata): Adjust navigation category (#3461)
* update categories

* Remove navigation from arrows and chevron icons
2025-11-21 14:10:05 +01:00
Jeevan Mahesha
a3886eb829 feat(angular): update peer dependencies for Angular to support version 21.x (#3807) 2025-11-21 10:37:24 +01:00
Epifânio Francisco
86957f0205 fix(docs): correct package name and description for Flutter and Lustre package (#3701) (#3703)
* fix(docs): correct package name and description for Flutter package (#3701)

* Revert "fix(docs): correct package naming in Packages section (#3701)"

This reverts commit c9b738b4af1ffc114bf7e8e2b81754a54b118b97.

* fix: correct package name from lucide-lustre to lucide_lustre and fix typo (lustre → flutter)
2025-11-21 09:30:20 +01:00
Jakob Guddas
465b98df74 fix(icons): changed calendars icon (#3795)
* Added icons/calendars.svg

* Added icons/calendars.json

* Updated icons/calendars.svg

* Updated icons/calendars.svg

* Updated icons/calendars.json

* Updated icons/calendars.svg
2025-11-17 17:19:26 +01:00
Jakob Guddas
d3b2255c0a feat(icons): added calendars icon (#3788)
* Added icons/calendars.svg

* Added icons/calendars.json

* Updated icons/calendars.svg

* Updated icons/calendars.svg

* Updated icons/calendars.json
2025-11-17 10:06:12 +01:00
Jamie Law
a4beac39a3 fix(icons): update anchor icon (#2523)
* Update `anchor` icon

* Update anchor.svg

* Update anchor.json

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-11-17 10:04:50 +01:00
Jakob Guddas
53311befff fix(icons): changed folder-git-2 icon (#3790)
* Updated icons/folder-git-2.svg

* Updated icons/folder-git-2.json
2025-11-17 09:34:51 +01:00
Jakob Guddas
156b3379e4 fix(icons): changed ampersand icon (#3771)
* Updated icons/ampersand.svg

* Updated icons/ampersand.json
2025-11-17 09:34:10 +01:00
theiereman
8dd82dd592 feat(docs): added lucide-rails third-party package (#3769) 2025-11-17 09:32:52 +01:00
Eric Fennis
80d6f737e0 fix(icons): Rename fingerprint icon to fingerprint-pattern (#3767)
* Rename fingerprint icon

* Fix as well for lucide-react

* Fix lint error
2025-11-17 09:32:09 +01:00
Eric Fennis
b1675c4c33 chore(packages): Remove umd exports (#3641)
* Remove UMD export

* Revert package file change
2025-10-16 13:49:19 +02:00
Eric Fennis
758fa4b75f Merge branch 'main' into next 2025-09-19 13:59:13 +02:00
Eric Fennis
7bbb1e1fea feat(@lucide/vue): Rename Vue package name to @lucide/vue (#3337)
* Remove old vue 2 package

* Add @lucide/vue package

* Remove old vue 2 doc

* Update docs

* Adjust export template

* Adjust vue package!

* Fix tests

* Format code

* Update packages/vue/src/Icon.ts

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

* Adjust vue package in docs

* Update deadlinks

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-11 14:38:34 +02:00
302 changed files with 6543 additions and 7117 deletions

View File

@@ -9,9 +9,3 @@ strikethrough
touchpad
ungroup
toc
# Brands
codepen
codesandbox
dribbble
x.com

View File

@@ -7,6 +7,10 @@ on:
paths:
- icons/**/*.svg
permissions:
id-token: write # Required for OIDC
contents: write
jobs:
create-release:
if: github.repository == 'lucide-icons/lucide' && startsWith(github.event.head_commit.message, 'feat(icons)')
@@ -15,9 +19,9 @@ jobs:
VERSION: ${{ steps.new-version.outputs.NEW_VERSION }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -32,25 +36,19 @@ jobs:
id: latest-tag
run: echo "LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)" >> $GITHUB_OUTPUT
- name: Log latest tag
run: echo '${{ steps.latest-tag.outputs.LATEST_TAG }}'
- name: Check if we can patch
run: .github/workflows/version-up.sh --minor
run: pnpm semver $LATEST_TAG -i minor
env:
LATEST_TAG: ${{ steps.latest-tag.outputs.LATEST_TAG }}
- name: Create new version
id: new-version
run: echo "NEW_VERSION=$(.github/workflows/version-up.sh --minor)" >> $GITHUB_OUTPUT
- name: Create change log
id: change-log
run: |
CHANGE_LOG=$(pnpm run generate:changelog --old-tag=${{ steps.latest-tag.outputs.LATEST_TAG }})
CHANGE_LOG=$(tail -n +5 <<< $CHANGE_LOG)
echo $CHANGE_LOG
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "CHANGE_LOG<<$EOF" >> $GITHUB_OUTPUT
echo "$CHANGE_LOG" >> $GITHUB_OUTPUT
echo "$EOF" >> $GITHUB_OUTPUT
run: echo "NEW_VERSION=$(pnpm semver $LATEST_TAG -i minor)" >> $GITHUB_OUTPUT
env:
GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }}
LATEST_TAG: ${{ steps.latest-tag.outputs.LATEST_TAG }}
- name: Check output
run: |
@@ -64,38 +62,6 @@ jobs:
name: Version ${{ steps.new-version.outputs.NEW_VERSION }}
generate_release_notes: true
test-semantic-release:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Semantic Release
id: semantic
uses: cycjimmy/semantic-release-action@v4
with:
tag_format: ${version}
branches: |
['new-release-workflow']
extends: |
semantic-release-monorepo
extra_plugins: |
@semantic-release/github
@semantic-release/git
@semantic-release/release-notes-generator
conventional-changelog-conventionalcommits
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Log output
if: steps.semantic.outputs.new_release_published == 'true'
run: |
echo ${{ steps.semantic.outputs.new_release_version }}
echo ${{ steps.semantic.outputs.new_release_major_version }}
echo ${{ steps.semantic.outputs.new_release_minor_version }}
echo ${{ steps.semantic.outputs.new_release_patch_version }}
start-release:
if: github.repository == 'lucide-icons/lucide'
needs: create-release

View File

@@ -1,4 +1,4 @@
name: Close Issue with Banned Phrases
name: Close Icon Requests with Brand Terms
on:
issues:
@@ -11,25 +11,38 @@ jobs:
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Check for blocked phrases in issue title
- name: Load stopwords from JSON & check issue title & body
if: contains(github.event.issue.labels.*.name, '🙌 icon request')
run: |
ISSUE_TITLE=$(jq -r '.issue.title' "$GITHUB_EVENT_PATH")
BLOCKED_PHRASES=("twitter" "whatsapp" "logo" "google" "tiktok" "facebook" "slack" "discord" "bluesky" "spotify" "behance" "pix" "x.com" "telegram")
ISSUE_BODY=$(jq -r '.issue.body // ""' "$GITHUB_EVENT_PATH")
ICON_NAME_SECTION=$(printf '%s\n' "$ISSUE_BODY" | awk '/### Icon name/{flag=1; next} /^### /{flag=0} flag')
# Check title and body for blocked phrases
for PHRASE in "${BLOCKED_PHRASES[@]}"
do
if echo "$ISSUE_TITLE" | grep -i "$PHRASE"; then
gh issue close ${{ github.event.issue.number }} --reason "not planned" --comment "This looks like a duplicate, use the [search](https://github.com/lucide-icons/lucide/issues?q=is%3Aissue+$PHRASE) to find similar issues.
jq -r 'to_entries[] | "\(.key) \(.value)"' brand-stopwords.json | while read -r KEY VALUE; do
SAFE_KEY=$(printf '%s\n' "$KEY" | sed 's/[][\.^$*]/\\&/g')
SAFE_VALUE=$(printf '%s\n' "$VALUE" | sed 's/[][\.^$*]/\\&/g')
Read [our official statement about brand logos in Lucide](https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md).
if echo "$ISSUE_TITLE" | grep -iqE "$SAFE_KEY|$SAFE_VALUE" || \
{ [ -n "$ICON_NAME_SECTION" ] && echo "$ICON_NAME_SECTION" | grep -iqE "$SAFE_KEY|$SAFE_VALUE"; }; then
gh issue close ${{ github.event.issue.number }} \
--reason "not_planned" \
--comment "It looks like this request is about **${VALUE}**, which is a brand logo.
Lucide **does not accept** brand logos, and we do not plan to add them in the future. This is due to a combination of **legal restrictions**, **design consistency concerns**, and **practical maintenance reasons**.
[Click here to read our official statement about brand logos in Lucide.](./BRAND_LOGOS_STATEMENT.md)
You can [search for similar issues.](https://github.com/lucide-icons/lucide/issues?q=is%3Aissue+${VALUE})
Were always happy to help on [Discord](https://discord.gg/EH6nSts)."
Always happy to help on [Discord](https://discord.gg/EH6nSts)."
gh issue lock ${{ github.event.issue.number }} --reason spam
exit 1
exit 0
fi
done
env:
GH_TOKEN: ${{ github.token }}
env:
GH_TOKEN: ${{ github.token }}

View File

@@ -9,5 +9,5 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: actions/labeler@v5

View File

@@ -9,9 +9,9 @@ jobs:
lint-code:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -13,8 +13,8 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: 'package.json'
- name: Get changed files
@@ -34,6 +34,6 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Check Uniqueness of Aliases
run: "! cat <(printf \"%s\\n\" icons/*.json | while read -r name; do basename \"$name\" .json; done) <(jq -cr 'select(.aliases) | .aliases[] | if type==\"string\" then . else .name end' icons/*.json) | sort | uniq -c | grep -ve '^\\s*1 '"

View File

@@ -11,9 +11,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -27,9 +27,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -12,9 +12,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version-file: 'package.json'
cache: 'pnpm'

View File

@@ -10,9 +10,9 @@ jobs:
lucide-font:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -13,9 +13,9 @@ jobs:
lucide-preact:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -13,9 +13,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -29,9 +29,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -14,9 +14,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -30,9 +30,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -10,9 +10,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -13,9 +13,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -29,9 +29,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -11,9 +11,9 @@ jobs:
lucide-static:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -13,9 +13,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -29,9 +29,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -13,9 +13,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -29,9 +29,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -13,9 +13,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -29,9 +29,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

43
.github/workflows/lucide-vue.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: Lucide Vue checks
on:
pull_request:
paths:
- packages/lucide-vue/**
- packages/shared/**
- tools/build-icons/**
- tools/rollup-plugins/**
- pnpm-lock.yaml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm --filter @lucide/vue build
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Test
run: pnpm --filter @lucide/vue test

View File

@@ -12,9 +12,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -28,9 +28,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -14,8 +14,8 @@ jobs:
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: 'package.json'
@@ -25,7 +25,7 @@ jobs:
with:
files: icons/*.svg
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
- name: Install svgson for code preview (safer and faster than installing all deps)
run: npm install svgson@5.3.1 --force

View File

@@ -16,11 +16,11 @@ jobs:
steps:
# We checkout the main branch of main repository for security reasons.
# This is to prevent the workflow from running on a forked repository.
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
repository: lucide-icons/lucide
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'

View File

@@ -18,9 +18,13 @@ on:
description: Version
required: true
permissions:
id-token: write # Required for OIDC
contents: write
jobs:
pre-release:
if: github.repository == 'lucide-icons/lucide' && contains('["ericfennis", "karsa-mistmere"]', github.actor)
if: github.repository == 'lucide-icons/lucide' && contains('["ericfennis", "karsa-mistmere", "jguddas"]', github.actor)
runs-on: ubuntu-latest
outputs:
VERSION: ${{ steps.get_version.outputs.VERSION }}
@@ -39,7 +43,8 @@ jobs:
runs-on: ubuntu-latest
needs: pre-release
permissions:
id-token: write
id-token: write # Required for OIDC
contents: read
strategy:
fail-fast: false
matrix:
@@ -55,11 +60,12 @@ jobs:
'lucide-svelte',
'@lucide/astro',
'@lucide/svelte',
'@lucide/vue',
]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -67,9 +73,6 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Set Auth Token
run: npm config set //registry.npmjs.org/:_authToken ${{ inputs.NPM_TOKEN || secrets.NPM_TOKEN }}
- name: Set new version
run: pnpm --filter ${{ matrix.package }} version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version
@@ -89,12 +92,14 @@ jobs:
runs-on: ubuntu-latest
needs: [pre-release, lucide-font]
permissions:
id-token: write
id-token: write # Required for OIDC
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: actions/download-artifact@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -102,9 +107,6 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Set Auth Token
run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
- name: Set new version
run: pnpm --filter lucide-static version --new-version ${{ needs.pre-release.outputs.VERSION }} --no-git-tag-version
@@ -124,9 +126,9 @@ jobs:
runs-on: ubuntu-latest
needs: pre-release
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: 'pnpm'
node-version-file: 'package.json'
@@ -150,9 +152,11 @@ jobs:
if: github.repository == 'lucide-icons/lucide'
runs-on: ubuntu-latest
needs: [pre-release, lucide-font]
permissions:
id-token: write # Required for OIDC
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: actions/download-artifact@v4
- name: Zip font and icons
run: |

View File

@@ -2,7 +2,8 @@ name: 'Request Review'
on:
pull_request_target:
types: [opened]
paths: icons/*.svg
paths:
- icons/*.svg
jobs:
request-review:
@@ -12,7 +13,7 @@ jobs:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
ref: refs/pull/${{ github.event.pull_request.number }}/merge

View File

@@ -1,284 +0,0 @@
#!/usr/bin/env bash
## Copyright (C) 2017, Oleksandr Kucherenko
## Last revisit: 2017-09-29
## get highest version tag for all branches
function highest_tag(){
local TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
echo "$TAG"
}
## extract current branch name
function current_branch(){
## expected: heads/{branch_name}
## expected: {branch_name}
local BRANCH=$(git rev-parse --abbrev-ref HEAD | cut -d"/" -f2)
echo "$BRANCH"
}
## get latest/head commit hash number
function head_hash(){
local COMMIT_HASH=$(git rev-parse --verify HEAD)
echo "$COMMIT_HASH"
}
## extract tag commit hash code, tag name provided by argument
function tag_hash(){
local TAG_HASH=$(git log -1 --format=format:"%H" $1 2>/dev/null | tail -n1)
echo "$TAG_HASH"
}
## get latest revision number
function latest_revision(){
local REV=$(git rev-list --count HEAD 2>/dev/null)
echo "$REV"
}
## parse last found tag, extract it PARTS
function parse_last(){
local position=$(($1-1))
# two parts found only
local SUBS=( ${PARTS[$position]//-/ } )
#echo ${SUBS[@]}, size: ${#SUBS}
# found NUMBER
PARTS[$position]=${SUBS[0]}
#echo ${PARTS[@]}
# found SUFFIX
if [[ ${#SUBS} -ge 1 ]]; then
PARTS[4]=${SUBS[1],,} #lowercase
#echo ${PARTS[@]}, ${SUBS[@]}
fi
}
## increment REVISION part, don't touch STAGE
function increment_revision(){
PARTS[3]=$(( PARTS[3] + 1 ))
IS_DIRTY=1
}
## increment PATCH part, reset all other lower PARTS, don't touch STAGE
function increment_patch(){
PARTS[2]=$(( PARTS[2] + 1 ))
PARTS[3]=0
IS_DIRTY=1
}
## increment MINOR part, reset all other lower PARTS, don't touch STAGE
function increment_minor(){
PARTS[1]=$(( PARTS[1] + 1 ))
PARTS[2]=0
PARTS[3]=0
IS_DIRTY=1
}
## increment MAJOR part, reset all other lower PARTS, don't touch STAGE
function incremet_major(){
PARTS[0]="v$(( PARTS[0] + 1 ))"
PARTS[1]=0
PARTS[2]=0
PARTS[3]=0
IS_DIRTY=1
}
## increment the number only of last found PART: REVISION --> PATCH --> MINOR. don't touch STAGE
function increment_last_found(){
if [[ "${#PARTS[3]}" == 0 || "${PARTS[3]}" == "0" ]]; then
if [[ "${#PARTS[2]}" == 0 || "${PARTS[2]}" == "0" ]]; then
increment_minor
else
increment_patch
fi
else
increment_revision
fi
# stage part is not EMPTY
if [[ "${#PARTS[4]}" != 0 ]]; then
IS_SHIFT=1
fi
}
## compose version from PARTS
function compose(){
MAJOR="${PARTS[0]}"
MINOR=".${PARTS[1]}"
PATCH=".${PARTS[2]}"
REVISION=".${PARTS[3]}"
SUFFIX="-${PARTS[4]}"
if [[ "${#PATCH}" == 1 ]]; then # if empty {PATCH}
PATCH=""
fi
if [[ "${#REVISION}" == 1 ]]; then # if empty {REVISION}
REVISION=""
fi
if [[ "${PARTS[3]}" == "0" ]]; then # if revision is ZERO
REVISION=""
fi
# shrink patch and revision
if [[ -z "${REVISION// }" ]]; then
if [[ "${PARTS[2]}" == "0" ]]; then
PATCH=".0"
fi
else # revision is not EMPTY
if [[ "${#PATCH}" == 0 ]]; then
PATCH=".0"
fi
fi
# remove suffix if we don't have a alpha/beta/rc
if [[ "${#SUFFIX}" == 1 ]]; then
SUFFIX=""
fi
echo "${MAJOR}${MINOR}${PATCH}${REVISION}${SUFFIX}" #full format
}
# initial version used for repository without tags
INIT_VERSION=0.0.0.0-alpha
# do GIT data extracting
TAG=$(highest_tag)
REVISION=$(latest_revision)
BRANCH=$(current_branch)
TAG_HASH=$(tag_hash $TAG)
HEAD_HASH=$(head_hash)
# if tag and branch commit hashes are different, than print info about that
#echo $HEAD_HASH vs $TAG_HASH
if [[ "$@" == "" ]]; then
if [[ "$TAG_HASH" == "$HEAD_HASH" ]]; then
echo "Tag $TAG and HEAD are aligned. We will stay on the TAG version."
echo ""
NO_ARGS_VALUE='--stay'
else
PATTERN="^[0-9]+.[0-9]+(.[0-9]+)*(-(alpha|beta|rc))*$"
if [[ "$BRANCH" =~ $PATTERN ]]; then
echo "Detected version branch '$BRANCH'. We will auto-increment the last version PART."
echo ""
NO_ARGS_VALUE='--default'
else
echo "Detected branch name '$BRANCH' than does not match version pattern. We will increase MINOR."
echo ""
NO_ARGS_VALUE='--minor'
fi
fi
fi
#
# {MAJOR}.{MINOR}[.{PATCH}[.{REVISION}][-(.*)]
#
# Suffix: alpha, beta, rc
# No Suffix --> {NEW_VERSION}-alpha
# alpha --> beta
# beta --> rc
# rc --> {VERSION}
#
PARTS=( ${TAG//./ } )
parse_last ${#PARTS[@]} # array size as argument
#echo ${PARTS[@]}
# if no parameters than emulate --default parameter
if [[ "$@" == "" ]]; then
set -- $NO_ARGS_VALUE
fi
# parse input parameters
for i in "$@"
do
key="$i"
case $key in
-a|--alpha) # switched to ALPHA
PARTS[4]="alpha"
IS_SHIFT=1
;;
-b|--beta) # switched to BETA
PARTS[4]="beta"
IS_SHIFT=1
;;
-c|--release-candidate) # switched to RC
PARTS[4]="rc"
IS_SHIFT=1
;;
-r|--release) # switched to RELEASE
PARTS[4]=""
IS_SHIFT=1
;;
-p|--patch) # increment of PATCH
increment_patch
;;
-e|--revision) # increment of REVISION
increment_revision
;;
-g|--git-revision) # use git revision number as a revision part§
PARTS[3]=$(( REVISION ))
IS_DIRTY=1
;;
-i|--minor) # increment of MINOR by default
increment_minor
;;
--default) # stay on the same stage, but increment only last found PART of version code
increment_last_found
;;
-m|--major) # increment of MAJOR
incremet_major
;;
-s|--stay) # extract version info
IS_DIRTY=1
NO_APPLY_MSG=1
;;
-t|--tag-only) # extract version info
TAG_ONLY=1
;;
--apply)
DO_APPLY=1
;;
-h|--help)
help
;;
esac
shift
done
# detected shift, but no increment
if [[ "$IS_SHIFT" == "1" ]]; then
# temporary disable stage shift
stage=${PARTS[4]}
PARTS[4]=''
# detect first run on repository, INIT_VERSION was used
if [[ "$(compose)" == "0.0" ]]; then
increment_minor
fi
PARTS[4]=$stage
fi
# no increment applied yet and no shift of state, do minor increase
if [[ "$IS_DIRTY$IS_SHIFT" == "" ]]; then
increment_minor
fi
compose
# is proposed tag in conflict with any other TAG
PROPOSED_HASH=$(tag_hash $(compose))
if [[ "${#PROPOSED_HASH}" -gt 0 && "$NO_APPLY_MSG" == "" ]]; then
echo -e "\033[31mERROR:\033[0m "
echo -e "\033[31mERROR:\033[0m Found conflict with existing tag \033[32m$(compose)\033[0m / $PROPOSED_HASH"
echo -e "\033[31mERROR:\033[0m Only manual resolving is possible now."
echo -e "\033[31mERROR:\033[0m "
echo -e "\033[31mERROR:\033[0m To Resolve try to add --revision or --patch modifier."
echo -e "\033[31mERROR:\033[0m "
echo ""
exit 1
fi

2
.gitignore vendored
View File

@@ -44,7 +44,7 @@ docs/.vitepress/data/releaseMetaData
docs/.vitepress/data/categoriesData.json
docs/.vitepress/data/iconDetails
docs/.vitepress/data/relatedIcons.json
docs/.vitepress/data/brandStopwords.json
docs/.vercel
docs/.nitro
.gitignore

1
.npmrc
View File

@@ -1 +0,0 @@
auto-install-peers=true

149
brand-stopwords.json Normal file
View File

@@ -0,0 +1,149 @@
{
"adobe": "Adobe",
"airplay": "AirPlay",
"amazon": "Amazon",
"angular": "Angular",
"aws": "AWS",
"azure": "Azure",
"bandcamp": "Bandcamp",
"behance": "Behance",
"bitbucket": "Bitbucket",
"blender": "Blender",
"bluesky": "BlueSky",
"bootstrap": "Bootstrap",
"brave": "Brave",
"chakra": "Chakra UI",
"chrome": "Chrome",
"codepen": "Codepen",
"codesandbox": "CodeSandbox",
"csharp": "C#",
"cypress": "Cypress",
"dart": "Dart",
"deezer": "Deezer",
"deno": "Deno",
"discord": "Discord",
"docker": "Docker",
"dribbble": "Dribbble",
"dropbox": "Dropbox",
"edge": "Edge",
"ember": "Ember",
"epic": "Epic Games",
"erlang": "Erlang",
"esbuild": "esbuild",
"eslint": "ESLint",
"facebook": "Facebook",
"figjam": "FigJam",
"figma": "Figma",
"firebase": "Firebase",
"firefox": "Firefox",
"framer": "Framer",
"gatsby": "Gatsby",
"gcp": "Google Cloud",
"github": "GitHub",
"gitlab": "GitLab",
"golang": "GoLang",
"google": "Google",
"gmail": "Gmail",
"gravatar": "Gravatar",
"haskell": "Haskell",
"instagram": "Instagram",
"java": "Java",
"javascript": "JavaScript",
"jest": "Jest",
"jira": "Jira",
"kotlin": "Kotlin",
"kubernetes": "Kubernetes",
"less": "Less",
"leetcode": "LeetCode",
"leet-code": "LeetCode",
"line": "LINE",
"linkedin": "LinkedIn",
"lua": "Lua",
"mariadb": "MariaDB",
"mcp": "MCP",
"messenger": "Messenger",
"microsoft": "Microsoft",
"mongodb": "MongoDB",
"mui": "Material UI",
"mysql": "MySQL",
"nestjs": "NestJS",
"netflix": "Netflix",
"netlify": "Netlify",
"next": "Next.js",
"nodejs": "Node.js",
"notion": "Notion",
"nostr": "Nostr",
"npm": "npm",
"nuxt": "Nuxt",
"opera": "Opera",
"oracle": "Oracle",
"patreon": "Patreon",
"paypal": "PayPal",
"perl": "Perl",
"php": "PHP",
"pinterest": "Pinterest",
"pix": "PiX",
"playstation": "PlayStation",
"playwright": "Playwright",
"pnpm": "pnpm",
"postcss": "PostCSS",
"postgresql": "PostgreSQL",
"prettier": "Prettier",
"prisma": "Prisma",
"python": "Python",
"qwik": "Qwik",
"react": "React",
"reddit": "Reddit",
"redis": "Redis",
"rollup": "Rollup",
"rust": "Rust",
"safari": "Safari",
"sass": "Sass",
"scala": "Scala",
"scss": "Sass",
"semantic": "Semantic UI",
"shopify": "Shopify",
"skype": "Skype",
"slack": "Slack",
"solid": "SolidJS",
"soundcloud": "SoundCloud",
"spotify": "Spotify",
"sqlite": "SQLite",
"squarespace": "Squarespace",
"steam": "Steam",
"stripe": "Stripe",
"substack": "Substack",
"supabase": "Supabase",
"surge": "Surge",
"svelte": "Svelte",
"swift": "Swift",
"tailwind": "Tailwind CSS",
"telegram": "Telegram",
"terraform": "Terraform",
"tesla": "Tesla",
"tidal": "Tidal",
"tiktok": "TikTok",
"trello": "Trello",
"twitch": "Twitch",
"twitter": "Twitter",
"typescript": "TypeScript",
"unity": "Unity",
"unreal": "Unreal Engine",
"vercel": "Vercel",
"vimeo": "Vimeo",
"vite": "Vite",
"vitest": "Vitest",
"vue": "Vue",
"webpack": "Webpack",
"wechat": "WeChat",
"whatsapp": "WhatsApp",
"windows": "Windows",
"wix": "Wix",
"x.com": "X.com",
"x-social": "X.com",
"xbox": "Xbox",
"yarn": "Yarn",
"youtube": "YouTube",
"zig": "Zig",
"zoom": "Zoom"
}

View File

@@ -1,5 +0,0 @@
{
"$schema": "../category.schema.json",
"title": "Brands",
"icon": "facebook"
}

View File

@@ -1,5 +1,5 @@
{
"$schema": "../category.schema.json",
"title": "Navigation",
"title": "Navigation, Maps, and POIs",
"icon": "compass"
}

View File

@@ -9,6 +9,7 @@ if (process.env.NODE_ENV === 'development') {
wasm = fs.readFileSync(require.resolve('@resvg/resvg-wasm/index_bg.wasm'));
} else {
// @ts-ignore
wasm = resvg_wasm;
}

View File

@@ -0,0 +1,55 @@
import { eventHandler, setResponseHeader, defaultContentType } from 'h3';
import { renderToString } from 'react-dom/server';
import { createElement } from 'react';
import Diff from '../../../lib/SvgPreview/Diff.tsx';
export default eventHandler((event) => {
const { params } = event.context;
const pathData = params.data.split('/');
const data = pathData.at(-1).slice(0, -4);
const [operation] = pathData;
const newSrc = Buffer.from(data, 'base64')
.toString('utf8')
.replaceAll('\n', '')
.replace(/<svg[^>]*>|<\/svg>/g, '');
const width = parseInt(
(newSrc.includes('<svg ') ? newSrc.match(/width="(\d+)"/)?.[1] : null) ?? '24',
);
const height = parseInt(
(newSrc.includes('<svg ') ? newSrc.match(/height="(\d+)"/)?.[1] : null) ?? '24',
);
const children = [];
let oldSrc = '';
if (operation.startsWith('rotate-')) {
const degrees = parseInt(operation.replace('rotate-', ''));
if (isNaN(degrees)) return '';
oldSrc = `<g transform="rotate(${degrees} ${width / 2} ${height / 2})">${newSrc}</g>`;
} else if (operation === 'flip-horizontal') {
oldSrc = `<g transform="scale(1, -1) translate(0, -${height})">${newSrc}</g>`;
} else if (operation === 'flip-vertical') {
oldSrc = `<g transform="scale(-1, 1) translate(-${width}, 0)">${newSrc}</g>`;
} else if (operation === 'flip-backslash') {
oldSrc = `<g transform="rotate(90, ${width / 2}, ${height / 2}) scale(1, -1) translate(0, -${height})">${newSrc}</g>`;
} else if (operation === 'flip-slash') {
oldSrc = `<g transform="rotate(90, ${width / 2}, ${height / 2}) scale(-1, 1) translate(-${width}, 0)">${newSrc}</g>`;
} else {
return '';
}
const svg = Buffer.from(
// We can't use jsx here, is not supported here by nitro.
renderToString(
createElement(Diff, { oldSrc, newSrc, showGrid: true, height, width }, children),
),
).toString('utf8');
defaultContentType(event, 'image/svg+xml');
setResponseHeader(event, 'Cache-Control', 'public,max-age=31536000');
return svg;
});

View File

@@ -15,10 +15,6 @@
"name": "arrows",
"title": "Arrows"
},
{
"name": "brands",
"title": "Brands"
},
{
"name": "buildings",
"title": "Buildings"
@@ -101,7 +97,7 @@
},
{
"name": "navigation",
"title": "Navigation"
"title": "Navigation, Maps, and POIs"
},
{
"name": "notifications",

View File

@@ -31,20 +31,12 @@
}
]
},
"lucide-vue-next": {
"@lucide/vue": {
"order": 2,
"icon": "vue-next",
"icon": "vue",
"docsAlias": "lucide-vue",
"packageDirname": "vue",
"shields": [
{
"alt": "npm",
"src": "https://img.shields.io/npm/v/lucide-vue-next",
"href": "https://www.npmjs.com/package/lucide-vue-next"
},
{
"alt": "npm",
"src": "https://img.shields.io/npm/dw/lucide-vue-next",
"href": "https://www.npmjs.com/package/lucide-vue-next"
}
]
},
"lucide-svelte": {

View File

@@ -78,7 +78,7 @@
"documentation": "https://github.com/swisnl/nuxt-lucide-icons/blob/main/README.md"
},
{
"name": "lucide-lustre",
"name": "lucide_lustre",
"description": "A library providing https://lucide.dev icons to lustre.",
"icon": "/framework-logos/lustre.webp",
"shields": [
@@ -98,7 +98,7 @@
},
{
"name": "lucide_icons_flutter",
"description": "A library providing https://lucide.dev icons to lustre.",
"description": "A library providing https://lucide.dev icons to Flutter.",
"icon": "/framework-logos/flutter.svg",
"shields": [
{
@@ -152,5 +152,24 @@
],
"source": "https://github.com/kaugesaar/lucide-go",
"documentation": "https://github.com/kaugesaar/lucide-go/blob/master/README.md"
},
{
"name": "lucide-rails",
"description": "Ruby on Rails views helper method for rendering Lucide icons.",
"icon": "/framework-logos/rails.svg",
"shields": [
{
"alt": "Latest Stable Version",
"src": "https://img.shields.io/gem/v/lucide-rails",
"href": "https://rubygems.org/gems/lucide-rails"
},
{
"alt": "Total Downloads",
"src": "https://img.shields.io/gem/dt/lucide-rails",
"href": "https://rubygems.org/gems/lucide-rails"
}
],
"source": "https://github.com/heyvito/lucide-rails",
"documentation": "https://github.com/heyvito/lucide-rails/blob/master/README.md"
}
]

View File

@@ -1,4 +1,4 @@
import { bundledLanguages, getHighlighter, type ThemeRegistration } from 'shikiji';
import { bundledLanguages, createHighlighter, type ThemeRegistration } from 'shiki';
type CodeExampleType = {
title: string;
@@ -9,14 +9,20 @@ type CodeExampleType = {
const getIconCodes = (): CodeExampleType => {
return [
{
language: 'js',
language: 'html',
title: 'Vanilla',
code: `\
import { createIcons, icons } from 'lucide';
<script>
import { createIcons, $CamelCase } from 'lucide';
createIcons({ icons });
createIcons({
icons: {
$CamelCase
}
});
</script>
document.body.append('<i data-lucide="$Name"></i>');\
<i data-lucide="$Name"></i>\
`,
},
{
@@ -37,7 +43,7 @@ export default App;
language: 'vue',
title: 'Vue',
code: `<script setup>
import { $PascalCase } from 'lucide-vue-next';
import { $PascalCase } from '@lucide/vue';
</script>
<template>
@@ -112,7 +118,7 @@ export type ThemeOptions =
| { light: ThemeRegistration; dark: ThemeRegistration };
const highLightCode = async (code: string, lang: string, active?: boolean) => {
const highlighter = await getHighlighter({
const highlighter = await createHighlighter({
themes: ['github-light', 'github-dark'],
langs: Object.keys(bundledLanguages),
});

View File

@@ -1,5 +1,5 @@
import { bundledLanguages, type ThemeRegistration } from 'shikiji';
import { getHighlighter } from 'shikiji';
import { bundledLanguages, type ThemeRegistration } from 'shiki';
import { createHighlighter } from 'shiki';
type CodeExampleType = {
title: string;
@@ -10,10 +10,11 @@ type CodeExampleType = {
const getIconCodes = (): CodeExampleType => {
return [
{
language: 'js',
language: 'html',
title: 'Vanilla',
code: `\
import { createIcons, icons } from 'lucide';
<script>
import { createIcons } from 'lucide';
import { $CamelCase } from '@lucide/lab';
createIcons({
@@ -21,8 +22,9 @@ createIcons({
$CamelCase
}
});
</script>
document.body.append('<i data-lucide="$Name"></i>');\
<i data-lucide="$Name"></i>\
`,
},
{
@@ -119,7 +121,7 @@ export type ThemeOptions =
| { light: ThemeRegistration; dark: ThemeRegistration };
const highLightCode = async (code: string, lang: string, active?: boolean) => {
const highlighter = await getHighlighter({
const highlighter = await createHighlighter({
themes: ['github-light', 'github-dark'],
langs: Object.keys(bundledLanguages),
});

View File

@@ -1,12 +1,12 @@
import { bundledLanguages, type ThemeRegistration } from 'shikiji';
import { getHighlighter } from 'shikiji';
import { bundledLanguages, type ThemeRegistration } from 'shiki';
import { createHighlighter } from 'shiki';
export type ThemeOptions =
| ThemeRegistration
| { light: ThemeRegistration; dark: ThemeRegistration };
const highLightCode = async (code: string, lang: string, active?: boolean) => {
const highlighter = await getHighlighter({
const highlighter = await createHighlighter({
themes: ['github-light', 'github-dark'],
langs: Object.keys(bundledLanguages),
});

View File

@@ -1,5 +1,5 @@
import { createLucideIcon } from 'lucide-react/src/lucide-react';
import { type LucideProps, type IconNode } 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';
import { IconContent } from './generateZip';

View File

@@ -74,7 +74,7 @@ const sidebar: UserConfig<DefaultTheme.Config>['themeConfig']['sidebar'] = {
},
{
text: 'Lucide Vue',
link: '/guide/packages/lucide-vue-next',
link: '/guide/packages/lucide-vue',
},
{
text: 'Lucide Svelte',

View File

@@ -1,23 +1,31 @@
<script setup lang="ts">
import { useData } from 'vitepress';
import { computed } from 'vue';
const props = defineProps<{
modelValue: string
id: string
}>()
modelValue: string;
id: string;
}>();
const emit = defineEmits(['update:modelValue'])
const { isDark } = useData();
const emit = defineEmits(['update:modelValue']);
const value = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
get: () => {
if (props.modelValue == null || props.modelValue === 'currentColor') {
return isDark.value ? '#ffffff' : '#000000';
}
return props.modelValue;
},
set: (val) => emit('update:modelValue', val),
});
</script>
<template>
<div class="color-picker">
<div class="color-input-wrapper">
<!-- TODO: Add currentColor div if value is currentColor -->
<input
type="color"
:id="id"
@@ -33,6 +41,7 @@ const value = computed({
class="color-input-text"
aria-label="Color picker input"
v-model="value"
placeholder="[default]"
/>
</div>
</template>
@@ -45,19 +54,21 @@ const value = computed({
top: -5px;
left: -5px;
}
.color-input-wrapper {
height: 24px;
width: 24px;
overflow: hidden;
position: relative;
border-radius: 12px;
border-radius: 4px;
flex-shrink: 0;
}
.color-picker {
background: var(--color-picker-bg, var(--vp-c-bg-alt));
border-radius: 8px;
color: var(--vp-c-text-2);
padding: 4px 8px;
padding: 3px 8px 3px 3px;
height: auto;
font-size: 14px;
text-align: left;
@@ -66,6 +77,10 @@ const value = computed({
display: flex;
align-items: center;
gap: 2px;
transition:
color 0.25s,
border-color 0.25s,
background-color 0.25s;
}
.color-input-text {
@@ -79,15 +94,18 @@ const value = computed({
text-align: left;
border-radius: 8px;
cursor: text;
transition: border-color 0.25s, background 0.4s ease;
transition:
border-color 0.25s,
background 0.4s ease;
letter-spacing: 1px;
}
.color-picker:hover, .color-picker:focus {
.color-picker:hover,
.color-picker:focus {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-alt);
}
.color-input[value="currentColor"] {
.color-input[value='currentColor'] {
}
</style>

View File

@@ -1,22 +1,27 @@
<script setup>
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import { search } from '../../../data/iconNodes'
const SearchIcon = createLucideIcon('search', search)
import Icon from 'lucide-vue-next/src/Icon';
import { search } from '../../../data/iconNodes';
defineProps({
shortcut: {
type: String,
required: false
}
})
required: false,
},
});
</script>
<template>
<button class="fake-input">
<component :is="SearchIcon" class="search-icon"/>
<slot/>
<kbd v-if="shortcut" class="shortcut">{{ shortcut }}</kbd>
<Icon
:iconNode="search"
class="search-icon"
/>
<slot />
<kbd
v-if="shortcut"
class="shortcut"
>{{ shortcut }}</kbd
>
</button>
</template>
@@ -34,10 +39,14 @@ defineProps({
cursor: text;
display: flex;
gap: 12px;
transition: color 0.25s, border-color 0.25s, background-color 0.25s;
transition:
color 0.25s,
border-color 0.25s,
background-color 0.25s;
}
.fake-input:hover, .fake-input:focus {
.fake-input:hover,
.fake-input:focus {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-alt);
}

View File

@@ -5,7 +5,6 @@
</template>
<style scoped>
.icon-button {
display: inline-flex;
border: 1px solid transparent;
@@ -30,9 +29,9 @@
}
.icon-button:active {
border-color: var(--vp-button-alt-active-border);
color: var(--vp-button-alt-active-text);
background-color: var(--vp-button-alt-active-bg);
border-color: var(--vp-button-alt-active-border);
color: var(--vp-button-alt-active-text);
background-color: var(--vp-button-alt-active-bg);
}
.icon-button.active {

View File

@@ -1,60 +1,90 @@
<script lang="ts">
export default {
inheritAttrs: false,
}
};
export interface InputProps {
type: string
modelValue: string
shortcut?: string
type: string;
modelValue: string;
shortcut?: string;
}
</script>
<script setup lang="ts">
import { ref, onMounted, nextTick, watch } from 'vue'
import { ref, onMounted, nextTick, watch } from 'vue';
import Icon from 'lucide-vue-next/src/Icon';
import { x } from '../../../data/iconNodes';
import IconButton from './IconButton.vue';
const props = withDefaults(defineProps<InputProps>(), {
type: 'text'
})
type: 'text',
});
const input = ref()
const wrapperEl = ref()
const shortcutEl = ref()
const input = ref();
const wrapperEl = ref();
const shortcutEl = ref();
defineEmits(['change', 'input', 'update:modelValue'])
const emit = defineEmits(['change', 'input', 'update:modelValue']);
const updateShortcutSpacing = () => {
nextTick(() => {
if (shortcutEl.value && wrapperEl.value) {
const shortcutWidth = shortcutEl.value.offsetWidth
wrapperEl.value.style.setProperty('--shortcut-width', `${shortcutWidth}px`)
const shortcutWidth = shortcutEl.value.offsetWidth;
wrapperEl.value.style.setProperty('--shortcut-width', `${shortcutWidth}px`);
}
})
}
});
};
onMounted(updateShortcutSpacing)
watch(() => props.shortcut, updateShortcutSpacing)
onMounted(updateShortcutSpacing);
watch(() => props.shortcut, updateShortcutSpacing);
function onClear() {
emit('update:modelValue', '');
input.value.focus();
}
defineExpose({
focus: () => {
input.value.focus()
}
})
input.value.focus();
},
});
</script>
<template>
<div class="input-wrapper" ref="wrapperEl">
<slot name="icon" class="icon" />
<div
class="input-wrapper"
ref="wrapperEl"
>
<slot
name="icon"
class="icon"
/>
<input
:type="type"
class="input"
:class="{'has-icon': $slots.icon, 'has-shortcut': shortcut}"
:class="{ 'has-icon': $slots.icon, 'has-shortcut': shortcut }"
ref="input"
:value="modelValue"
v-bind="$attrs"
@input="$emit('update:modelValue', $event.target.value)"
/>
<kbd v-if="shortcut" class="shortcut" ref="shortcutEl">{{ shortcut }}</kbd>
<IconButton
@click="onClear"
v-if="type === 'search' && modelValue"
class="clear-button"
aria-label="Clear input"
>
<Icon
:iconNode="x"
:size="20"
/>
</IconButton>
<kbd
v-if="shortcut"
class="shortcut"
ref="shortcutEl"
>{{ shortcut }}</kbd
>
</div>
</template>
@@ -62,6 +92,7 @@ defineExpose({
.input-wrapper {
position: relative;
}
.input {
justify-content: flex-start;
border: 1px solid transparent;
@@ -71,13 +102,18 @@ defineExpose({
height: 40px;
background-color: var(--vp-c-bg-alt);
font-size: 14px;
transition:
color 0.25s,
border-color 0.25s,
background-color 0.25s;
}
.input.has-shortcut {
padding-right: calc(var(--shortcut-width, 40px) + 22px);
}
.input:hover, .input:focus {
.input:hover,
.input:focus {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-alt);
}
@@ -86,6 +122,14 @@ defineExpose({
padding-left: 52px;
}
.clear-button {
position: absolute;
right: 56px;
top: 9px;
padding: 4px;
transition: background-color .25s;
}
.shortcut {
position: absolute;
right: 12px;
@@ -111,7 +155,7 @@ defineExpose({
</style>
<style>
.input-wrapper svg {
.input-wrapper > svg {
position: absolute;
left: 16px;
top: 12px;

View File

@@ -1,38 +1,36 @@
<script lang="ts">
export default {
inheritAttrs: false,
}
};
</script>
<script setup lang="ts">
import { computed, ref } from 'vue'
import Input from './Input.vue'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import { search } from '../../../data/iconNodes'
const SearchIcon = createLucideIcon('search', search)
import { computed, ref } from 'vue';
import Input from './Input.vue';
import Icon from 'lucide-vue-next/src/Icon';
import { search } from '../../../data/iconNodes';
interface Props {
modelValue: string
shortcut?: string
modelValue: string;
shortcut?: string;
}
const props = defineProps<Props>()
const props = defineProps<Props>();
const input = ref()
const input = ref();
const emit = defineEmits(['update:modelValue'])
const emit = defineEmits(['update:modelValue']);
defineExpose({
focus: () => {
input.value.focus()
}
})
input.value.focus();
},
});
const value = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
set: (val) => emit('update:modelValue', val),
});
</script>
<template>
@@ -46,7 +44,10 @@ const value = computed({
class="input-wrapper"
>
<template #icon>
<component :is="SearchIcon" class="search-icon" />
<Icon
:iconNode="search"
class="search-icon"
/>
</template>
</Input>
</template>
@@ -62,7 +63,8 @@ const value = computed({
background-color: var(--vp-c-bg-alt);
}
.input:hover, .input:focus {
.input:hover,
.input:focus {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-alt);
}

View File

@@ -1,14 +1,15 @@
<script setup lang="ts">
import { rotateCw } from '../../../data/iconNodes'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import IconButton from "./IconButton.vue";
const RotateIcon = createLucideIcon('RotateIcon', rotateCw)
import { rotateCw } from '../../../data/iconNodes';
import Icon from 'lucide-vue-next/src/Icon';
import IconButton from './IconButton.vue';
</script>
<template>
<IconButton class="reset-button">
<RotateIcon :size="20"/>
<Icon
:size="20"
:iconNode="rotateCw"
/>
</IconButton>
</template>
@@ -32,6 +33,7 @@ const RotateIcon = createLucideIcon('RotateIcon', rotateCw)
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(359deg);
}

View File

@@ -13,25 +13,25 @@ export default {
label: 'Lucide documentation for React',
},
{
name: 'lucide-vue-next',
name: 'lucide-vue',
logo: '/framework-logos/vue.svg',
label: 'Lucide documentation for Vue 3',
label: 'Lucide documentation for Vue',
},
{
name: 'lucide-svelte',
logo: '/framework-logos/svelte.svg',
label: 'Lucide documentation for Svelte',
},
{
name: 'lucide-preact',
logo: '/framework-logos/preact.svg',
label: 'Lucide documentation for Preact',
},
{
name: 'lucide-solid',
logo: '/framework-logos/solid.svg',
label: 'Lucide documentation for Solid',
},
{
name: 'lucide-preact',
logo: '/framework-logos/preact.svg',
label: 'Lucide documentation for Preact',
},
{
name: 'lucide-angular',
logo: '/framework-logos/angular.svg',
@@ -48,11 +48,6 @@ export default {
logo: '/framework-logos/react-native.svg',
label: 'Lucide documentation for React Native',
},
{
name: 'lucide-flutter',
logo: '/framework-logos/flutter.svg',
label: 'Lucide documentation for Flutter',
},
],
};
},

View File

@@ -2,45 +2,48 @@
import { useData } from 'vitepress';
import { useSessionStorage } from '@vueuse/core';
import IconButton from '../base/IconButton.vue';
import VPDocAsideCarbonAds from 'vitepress/dist/client/theme-default/components/VPDocAsideCarbonAds.vue'
import { x } from '../../../data/iconNodes'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
import VPDocAsideCarbonAds from 'vitepress/dist/client/theme-default/components/VPDocAsideCarbonAds.vue';
import { x } from '../../../data/iconNodes';
import Icon from 'lucide-vue-next/src/Icon';
import { onMounted, ref } from 'vue';
const { theme } = useData()
const showAd = useSessionStorage('show-carbon-ads', true)
const carbonLoaded = ref(true)
const { theme } = useData();
const showAd = useSessionStorage('show-carbon-ads', true);
const carbonLoaded = ref(true);
defineProps<{
drawerOpen: boolean
}>()
const CloseIcon = createLucideIcon('Close', x)
drawerOpen: boolean;
}>();
onMounted(() => {
setTimeout(() => {
if (window?._carbonads == null) {
carbonLoaded.value = false
carbonLoaded.value = false;
}
}, 5000)
})
}, 5000);
});
</script>
<template>
<div
:class="{
'drawer-open': drawerOpen,
'hide-ad': !(showAd && carbonLoaded)
'hide-ad': !(showAd && carbonLoaded),
}"
class="floating-ad"
v-if="theme.carbonAds"
>
<IconButton @click="showAd = false" class="hide-button">
<component :is="CloseIcon" :size="20" absoluteStrokeWidth />
<IconButton
@click="showAd = false"
class="hide-button"
>
<Icon
:iconNode="x"
:size="20"
absoluteStrokeWidth
/>
</IconButton>
<VPDocAsideCarbonAds
:carbon-ads="theme.carbonAds"
/>
<VPDocAsideCarbonAds :carbon-ads="theme.carbonAds" />
</div>
</template>
@@ -51,7 +54,9 @@ onMounted(() => {
bottom: 32px;
width: 224px;
right: 32px;
transition: opacity 0.5s, transform 0.25s ease;
transition:
opacity 0.5s,
transform 0.25s ease;
}
.floating-ad.drawer-open {
@@ -67,8 +72,11 @@ onMounted(() => {
transform: translateY(-288px) translateX(224px);
}
.floating-ad.drawer-open, .floating-ad.hide-ad {
transition: opacity 0.25s, transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
.floating-ad.drawer-open,
.floating-ad.hide-ad {
transition:
opacity 0.25s,
transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
}
@media (min-width: 1280px) {

View File

@@ -1,70 +1,68 @@
<script setup lang="ts">
import { ref } from 'vue';
import ButtonMenu from '../base/ButtonMenu.vue'
import ButtonMenu from '../base/ButtonMenu.vue';
import { useIconStyleContext } from '../../composables/useIconStyle';
import useConfetti from '../../composables/useConfetti';
import getSVGIcon from '../../utils/getSVGIcon';
import downloadData from '../../utils/downloadData';
const downloadText = 'Download!'
const copiedText = 'Copied!'
const confettiText = ref(copiedText)
const downloadText = 'Download!';
const copiedText = 'Copied!';
const confettiText = ref(copiedText);
const props = defineProps<{
name: string
popoverPosition?: 'top' | 'bottom'
}>()
name: string;
popoverPosition?: 'top' | 'bottom';
}>();
const { size } = useIconStyleContext()
const { size } = useIconStyleContext();
const { animate, confetti } = useConfetti()
const { animate, confetti } = useConfetti();
function copySVG() {
confettiText.value = copiedText
const svgString = getSVGIcon()
confettiText.value = copiedText;
const svgString = getSVGIcon();
navigator.clipboard.writeText(svgString)
navigator.clipboard.writeText(svgString);
confetti()
confetti();
}
function copyDataUrl() {
confettiText.value = copiedText
const svgString = getSVGIcon()
confettiText.value = copiedText;
const svgString = getSVGIcon();
// Create SVG data url
const dataUrl = `data:image/svg+xml;base64,${btoa(svgString)}`
navigator.clipboard.writeText(dataUrl)
const dataUrl = `data:image/svg+xml;base64,${btoa(svgString)}`;
navigator.clipboard.writeText(dataUrl);
confetti()
confetti();
}
function downloadSVG() {
confettiText.value = downloadText
const svgString = getSVGIcon()
confettiText.value = downloadText;
const svgString = getSVGIcon();
downloadData(`${props.name}.svg`, `data:image/svg+xml;base64,${btoa(svgString)}`)
confetti()
downloadData(`${props.name}.svg`, `data:image/svg+xml;base64,${btoa(svgString)}`);
confetti();
}
function downloadPNG() {
confettiText.value = downloadText
const svgString = getSVGIcon()
confettiText.value = downloadText;
const svgString = getSVGIcon();
const canvas = document.createElement('canvas');
canvas.width = size.value;
canvas.height = size.value;
const ctx = canvas.getContext("2d");
const ctx = canvas.getContext('2d');
const image = new Image();
image.src = `data:image/svg+xml;base64,${btoa(svgString)}`;
image.onload = function() {
image.onload = function () {
ctx.drawImage(image, 0, 0);
downloadData(`${props.name}.png`, canvas.toDataURL('image/png'))
confetti()
}
downloadData(`${props.name}.png`, canvas.toDataURL('image/png'));
confetti();
};
}
</script>
<template>
@@ -75,10 +73,10 @@ function downloadPNG() {
:data-confetti-text="confettiText"
:popoverPosition="popoverPosition"
:options="[
{ text: 'Copy SVG' , onClick: copySVG },
{ text: 'Copy Data URL' , onClick: copyDataUrl },
{ text: 'Download SVG' , onClick: downloadSVG },
{ text: 'Download PNG' , onClick: downloadPNG },
{ text: 'Copy SVG', onClick: copySVG },
{ text: 'Copy Data URL', onClick: copyDataUrl },
{ text: 'Download SVG', onClick: downloadSVG },
{ text: 'Download PNG', onClick: downloadPNG },
]"
/>
</template>

View File

@@ -1,41 +1,44 @@
<script setup lang="ts">
import { computed, useSlots } from 'vue';
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import { copy } from '../../../data/iconNodes'
import { copy } from '../../../data/iconNodes';
import useConfetti from '../../composables/useConfetti';
const { animate, confetti } = useConfetti()
const slots = useSlots()
import Icon from 'lucide-vue-next/src/Icon';
const { animate, confetti } = useConfetti();
const slots = useSlots();
const copiedText = computed(() => slots.default?.()[0].children)
const copiedText = computed(() => slots.default?.()[0].children);
function copyText() {
navigator.clipboard.writeText(copiedText.value)
navigator.clipboard.writeText(copiedText.value);
confetti()
confetti();
}
const Copy = createLucideIcon('ChevronUp', copy)
</script>
<template>
<h1
class="icon-name confetti-button"
:class="{animate}"
:class="{ animate }"
data-confetti-text="Copied!"
@click="copyText"
>
<slot />
<Copy :size="20" class="copy-icon"/>
<Icon
:iconNode="copy"
:size="20"
class="copy-icon"
/>
</h1>
</template>
<style scoped>
@import './confetti.css';
.icon-name {
font-size: 24px;
font-weight: 500;
line-height: 32px;
transition: background ease-in .15s;;
transition: background ease-in 0.15s;
padding: 2px 8px;
border-radius: 8px;
width: auto;
@@ -48,7 +51,7 @@ const Copy = createLucideIcon('ChevronUp', copy)
}
.icon-name:hover .copy-icon {
opacity: .9;
opacity: 0.9;
}
.icon-name:before,
@@ -65,10 +68,10 @@ const Copy = createLucideIcon('ChevronUp', copy)
opacity: 0;
margin-left: 12px;
margin-top: 6px;
transition:ease .3s opacity;
transition: ease 0.3s opacity;
}
.icon-name:hover .copy-icon:hover {
opacity: .6;
opacity: 0.6;
}
</style>

View File

@@ -29,7 +29,12 @@ const props = defineProps<{
const iconComponent = computed(() => {
if (!props.name || !props.iconNode) return null;
return createLucideIcon(props.name, props.iconNode);
try {
return createLucideIcon(props.name, props.iconNode);
} catch (error) {
console.warn(`Icon ${props.name} not found, using fallback`);
return null;
}
});
const CalendarIcon = createLucideIcon('calendar', Calendar.iconNode);
@@ -61,7 +66,7 @@ const prettyName = props.name
</script>
<template>
<section class="showcase">
<section class="showcase" v-if="iconComponent">
<h2 class="title">See this icon in action</h2>
<div class="showcase-grid">
<div class="showcase-item column">

View File

@@ -1,19 +1,19 @@
<script setup lang="ts">
import { ref, computed, defineAsyncComponent, onMounted, watch, watchEffect } from 'vue';
import { ref, computed, defineAsyncComponent, onMounted } from 'vue';
import type { IconEntity, Category } from '../../types';
import useSearch from '../../composables/useSearch';
import InputSearch from '../base/InputSearch.vue';
import useSearchInput from '../../composables/useSearchInput';
import useSearchShortcut from '../../utils/useSearchShortcut';
import StickyBar from './StickyBar.vue';
import IconsCategory from './IconsCategory.vue';
import IconsCategory, { CategoryRow } from './IconsCategory.vue';
import useFetchTags from '../../composables/useFetchTags';
import useFetchCategories from '../../composables/useFetchCategories';
import { useElementSize, useEventListener, useVirtualList } from '@vueuse/core';
import chunkArray from '../../utils/chunkArray';
import { CategoryRow } from './IconsCategory.vue';
import useScrollToCategory from '../../composables/useScrollToCategory';
import CarbonAdOverlay from './CarbonAdOverlay.vue';
import useSearchPlaceholder from '../../utils/useSearchPlaceholder.ts';
const ICON_SIZE = 56;
const ICON_GRID_GAP = 8;
@@ -40,10 +40,10 @@ const { execute: fetchTags, data: tags } = useFetchTags();
const { execute: fetchCategories, data: categoriesMap } = useFetchCategories();
const overviewEl = ref<HTMLElement | null>(null);
const { width: containerWidth } = useElementSize(overviewEl)
const { width: containerWidth } = useElementSize(overviewEl);
const columnSize = computed(() => {
return Math.floor((containerWidth.value) / ((ICON_SIZE + ICON_GRID_GAP)));
return Math.floor(containerWidth.value / (ICON_SIZE + ICON_GRID_GAP));
});
const mappedIcons = computed(() => {
@@ -71,26 +71,27 @@ const searchResults = useSearch(searchQueryDebounced, mappedIcons, [
const categories = computed(() => {
if (!props.categories?.length || !props.icons?.length) return [];
return props.categories
.map(({ name, title }) => {
const categoryIcons = props.icons.filter((icon) => {
const iconCategories = icon?.externalLibrary ? icon.categories : props.iconCategories[icon.name]
return props.categories.map(({ name, title }) => {
const categoryIcons = props.icons.filter((icon) => {
const iconCategories = icon?.externalLibrary
? icon.categories
: props.iconCategories[icon.name];
return iconCategories?.includes(name);
});
return iconCategories?.includes(name);
});
const searchedCategoryIcons = isSearching
? categoryIcons.filter((icon) =>
searchResults.value.some((item) => item?.name === icon?.name)
)
: categoryIcons;
const searchedCategoryIcons = isSearching
? categoryIcons.filter((icon) =>
searchResults.value.some((item) => item?.name === icon?.name),
)
: categoryIcons;
return {
title,
name,
icons: searchedCategoryIcons,
};
})
return {
title,
name,
icons: searchedCategoryIcons,
};
});
});
const categoriesList = computed(() => {
@@ -107,26 +108,24 @@ const categoriesList = computed(() => {
return acc;
}, []);
});
const searchPlaceholder = useSearchPlaceholder(searchQuery, searchResults);
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
categoriesList,
{
itemHeight: ICON_SIZE + ICON_GRID_GAP,
overscan: 10
},
)
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(categoriesList, {
itemHeight: ICON_SIZE + ICON_GRID_GAP,
overscan: 10,
});
useScrollToCategory({
categories,
categoriesList,
scrollTo,
searchQueryDebounced,
})
});
onMounted(() => {
containerProps.ref.value = document.documentElement;
useEventListener(window, 'scroll', containerProps.onScroll)
})
useEventListener(window, 'scroll', containerProps.onScroll);
});
function onFocusSearchInput() {
if (tags.value == null) {
@@ -145,16 +144,13 @@ function handleCloseDrawer() {
window.history.pushState({}, '', '/icons/categories');
}
watchEffect(() => {
console.log(props.icons.find((icon) => icon.name === 'burger'));
});
</script>
<template>
<div ref="overviewEl" class="overview-container">
<div
ref="overviewEl"
class="overview-container"
>
<StickyBar class="category-search">
<InputSearch
:placeholder="`Search ${icons.length} icons ...`"
@@ -166,8 +162,9 @@ watchEffect(() => {
/>
</StickyBar>
<NoResults
v-if="categories.length === 0"
:searchQuery="searchQuery"
v-if="searchPlaceholder.isNoResults"
:searchQuery="searchPlaceholder.query"
:isBrandSearch="searchPlaceholder.isBrand"
@clear="searchQuery = ''"
/>
<div v-bind="wrapperProps">
@@ -208,8 +205,4 @@ watchEffect(() => {
.icons {
margin-bottom: 8px;
}
.overview-container {
padding-bottom: 288px;
}
</style>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, computed, defineAsyncComponent, onMounted, onBeforeUnmount, watch } from 'vue';
import { ref, computed, defineAsyncComponent, onMounted, watch } from 'vue';
import type { IconEntity } from '../../types';
import { useElementSize, useEventListener, useVirtualList } from '@vueuse/core';
import { useRoute } from 'vitepress';
@@ -13,17 +13,18 @@ import useFetchTags from '../../composables/useFetchTags';
import useFetchCategories from '../../composables/useFetchCategories';
import chunkArray from '../../utils/chunkArray';
import CarbonAdOverlay from './CarbonAdOverlay.vue';
import useSearchPlaceholder from '../../utils/useSearchPlaceholder.ts';
const ICON_SIZE = 56;
const ICON_GRID_GAP = 8;
const initialGridItems = computed(() => {
if (containerWidth.value === 0) return 120;
const itemsPerRow = columnSize.value || 10;
const visibleRows = Math.ceil(window.innerHeight / (ICON_SIZE + ICON_GRID_GAP));
return Math.min(itemsPerRow * (visibleRows + 2), 200);
return Math.min(itemsPerRow * (visibleRows + 2), 200);
});
const props = defineProps<{
@@ -36,10 +37,10 @@ const { execute: fetchTags, data: tags } = useFetchTags();
const { execute: fetchCategories, data: categories } = useFetchCategories();
const overviewEl = ref<HTMLElement | null>(null);
const { width: containerWidth } = useElementSize(overviewEl)
const { width: containerWidth } = useElementSize(overviewEl);
const columnSize = computed(() => {
return Math.floor((containerWidth.value) / ((ICON_SIZE + ICON_GRID_GAP)));
return Math.floor(containerWidth.value / (ICON_SIZE + ICON_GRID_GAP));
});
const mappedIcons = computed(() => {
@@ -71,29 +72,27 @@ const searchResults = useSearch(searchQueryDebounced, mappedIcons, [
{ name: 'tags', weight: 2 },
{ name: 'categories', weight: 1 },
]);
const searchPlaceholder = useSearchPlaceholder(searchQuery, searchResults);
const chunkedIcons = computed(() => {
return chunkArray(searchResults.value, columnSize.value);
});
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
chunkedIcons,
{
itemHeight: ICON_SIZE + ICON_GRID_GAP,
overscan: 10
},
)
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(chunkedIcons, {
itemHeight: ICON_SIZE + ICON_GRID_GAP,
overscan: 10,
});
onMounted(() => {
containerProps.ref.value = document.documentElement;
useEventListener(window, 'scroll', containerProps.onScroll)
useEventListener(window, 'scroll', containerProps.onScroll);
// Check if we should focus the search input from URL parameter
const route = useRoute()
const route = useRoute();
if (route.data?.relativePath && window.location.search.includes('focus')) {
searchInput.value?.focus()
searchInput.value?.focus();
}
})
});
function setActiveIconName(name: string) {
activeIconName.value = name;
@@ -113,8 +112,8 @@ const NoResults = defineAsyncComponent(() => import('./NoResults.vue'));
const IconDetailOverlay = defineAsyncComponent(() => import('./IconDetailOverlay.vue'));
watch(searchQueryDebounced, () => {
scrollTo(0)
})
scrollTo(0);
});
function handleCloseDrawer() {
setActiveIconName('');
@@ -124,7 +123,10 @@ function handleCloseDrawer() {
</script>
<template>
<div ref="overviewEl" class="overview-container">
<div
ref="overviewEl"
class="overview-container"
>
<StickyBar>
<InputSearch
:placeholder="`Search ${icons.length} icons ...`"
@@ -136,8 +138,9 @@ function handleCloseDrawer() {
/>
</StickyBar>
<NoResults
v-if="searchResults.length === 0 && searchQuery !== ''"
:searchQuery="searchQuery"
v-if="searchPlaceholder.isNoResults"
:searchQuery="searchPlaceholder.query"
:isBrandSearch="searchPlaceholder.isBrand"
@clear="searchQuery = ''"
/>
<IconGrid
@@ -183,8 +186,4 @@ function handleCloseDrawer() {
.input-wrapper {
width: 100%;
}
.overview-container {
padding-bottom: 288px;
}
</style>

View File

@@ -1,56 +1,218 @@
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { bird, squirrel, rabbit } from '../../../data/iconNodes'
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon'
import {useEventListener} from '@vueuse/core'
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue'
import { IconNode } from '../../types'
import { ref, onMounted, computed, markRaw, shallowReadonly, watch } from 'vue';
import {
bird,
squirrel,
rabbit,
ghost,
castle,
drama,
dog,
cat,
wandSparkles,
save,
snowflake,
cake,
fish,
turtle,
rat,
worm,
testTubeDiagonal,
sword,
} from '../../../data/iconNodes';
import createLucideIcon from 'lucide-vue-next/src/createLucideIcon';
import { useEventListener } from '@vueuse/core';
import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue';
import { IconNode } from '../../types';
defineProps<{
searchQuery: string
}>()
const { searchQuery, isBrandSearch } = defineProps<{
searchQuery: string;
isBrandSearch: boolean;
}>();
defineEmits(['clear'])
defineEmits(['clear']);
const animalIcon = ref<HTMLElement>()
const randomAnimal = computed<IconNode>(() => {
return Math.random() > 0.5 ? squirrel : Math.random() > 0.5 ? rabbit : bird
})
const animalComponent = computed(() => createLucideIcon('animal', randomAnimal.value))
const flip = ref(false)
interface Placeholder {
title: string;
message: string;
icon: IconNode;
finePrint?: string;
}
const brandPlaceholders: Placeholder[] = shallowReadonly([
{
title: 'Boooo! What a scary brand logo!',
message:
'[name] and its friends often haunt this search box, but you wont ever find them here.',
icon: markRaw(ghost),
},
{
title: 'Thank You Mario!',
message: 'But [name] is in another castle!',
icon: markRaw(castle),
},
{
title: '[name] did audition for our icon set',
message: '...but didnt make the callback.',
icon: markRaw(drama),
},
{
title: 'Such Search. Very [name].',
message: 'Much not here. So Wow.',
icon: markRaw(dog),
},
{
title: 'I Can Has [name]?',
message: 'No [name] for you in here.',
icon: markRaw(cat),
},
{
title: 'Loading [name]...',
message: 'Fatal error: our cartridge contains only open-source pixels.',
icon: markRaw(save),
},
{
title: '[name] Shall Not Pass',
message: 'Do not look to its coming at first light of any day.',
icon: markRaw(wandSparkles),
},
{
title: 'Winter is coming',
message: 'But [name] sure isnt.',
icon: markRaw(snowflake),
},
{
title: 'The cake is a lie',
message: 'And so is the promise of an icon for [name] at Lucide.',
icon: markRaw(cake),
},
{
title: 'Its not a bug',
message: 'Having no [name] icon around is a feature.',
icon: markRaw(worm),
},
{
title: 'The lab exploded',
message: 'We tried mixing [name] with open-source icons.',
icon: markRaw(testTubeDiagonal),
},
{
title: 'Its Dangerous to Go Alone',
message: 'Take this icon instead — its not [name], but it might help.',
icon: markRaw(sword),
},
]);
const notFoundPlaceholders: Omit<Placeholder, 'title'>[] = shallowReadonly([
{
message: 'Weve looked for this icon for a birds eye view, but could not find it.',
icon: markRaw(bird),
},
{
message: 'We checked every tree. Only acorns, no results.',
icon: markRaw(squirrel),
},
{
message: 'Youve gone too deep into the rabbit hole — this icon doesnt exist.',
icon: markRaw(rabbit),
},
{
message: 'This icon seems to have slipped through the net.',
icon: markRaw(fish),
},
{
message: 'This icon might exist in the future… but it hasnt arrived yet.',
icon: markRaw(turtle),
},
{
message: 'Rats! This icon seems to have slipped through the cracks.',
icon: markRaw(rat),
},
]);
function randomItem<T>(arr: T[]): T {
return arr[Math.floor(Math.random() * arr.length)];
}
const placeholderIcon = ref<HTMLElement>();
const placeholder = ref<Placeholder>();
watch(
() => isBrandSearch,
() => {
placeholder.value = isBrandSearch
? {
...randomItem(brandPlaceholders),
finePrint:
'Lucide does not accept brand logos, and we do not plan to add them in the future. This is due to a combination of legal restrictions, design consistency concerns, and practical maintenance reasons.',
}
: {
title: `No results for “[name]”`,
finePrint:
'This icon doesnt seem to exist… yet. Try searching similar terms, browsing existing requests, or opening a new one.',
...randomItem(notFoundPlaceholders),
};
},
{ immediate: true },
);
const iconComponent = computed(() => createLucideIcon('placeholder', placeholder.value.icon));
const flip = ref(false);
onMounted(() => {
useEventListener(document, 'mousemove', (mouseEvent) => {
const {width, height, x, y} = animalIcon.value.getBoundingClientRect()
const { width, x } = placeholderIcon.value.getBoundingClientRect();
const centerX = (width / 2) + x
flip.value = mouseEvent.x < centerX
})
})
const centerX = width / 2 + x;
flip.value = !isBrandSearch && mouseEvent.x < centerX;
});
});
</script>
<template>
<div class="no-results">
<component
:is="animalComponent"
class="animal-icon"
ref="animalIcon"
:is="iconComponent"
class="placeholder-icon"
ref="placeholderIcon"
:class="{ flip }"
:strokeWidth="1"
/>
<h2 class="no-results-text">
No icons found for '{{ searchQuery }}'
</h2>
<h2 class="no-results-text">{{ placeholder.title.replace('[name]', searchQuery) }}</h2>
<p class="no-results-message">
{{ placeholder.message.replace('[name]', searchQuery) }}
</p>
<div class="divider"></div>
<p
v-if="placeholder.finePrint"
class="no-results-fine-print"
>
{{ placeholder.finePrint }}
</p>
<VPButton
text="Clear your search and try again"
theme="alt"
v-if="isBrandSearch"
text="Head over to Simple Icons"
theme="brand"
:href="`https://simpleicons.org/?q=${searchQuery}`"
target="_blank"
/>
<VPButton
v-else
text="Clear search & try again"
theme="brand"
@click="$emit('clear')"
/>
<span class="text-divider">or</span>
<VPButton
text="Search on Github issues"
v-if="isBrandSearch"
text="Read our statement on brand logos"
theme="alt"
href="https://github.com/lucide-icons/lucide/blob/main/BRAND_LOGOS_STATEMENT.md"
target="_blank"
/>
<VPButton
v-else
text="Search GitHub issues"
theme="alt"
:href="`https://github.com/lucide-icons/lucide/issues?q=is%3Aopen+${searchQuery}`"
target="_blank"
@@ -63,33 +225,38 @@ onMounted(() => {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding-block: 48px;
}
.animal-icon {
width: 160px;
height: 160px;
color: var(--vp-c-neutral);
opacity: 0.8;
margin-top: 72px;
.placeholder-icon {
width: 96px;
height: 96px;
color: var(--vp-c-text-1);
}
.animal-icon.flip {
.placeholder-icon.flip {
transform: rotateY(180deg);
}
@media (min-width: 960px) {
.animal-icon {
width: 240px;
height: 240px;
}
}
.no-results-text {
line-height: 40px;
line-height: 1.35;
font-size: 24px;
margin-top: 24px;
margin-bottom: 8px;
text-wrap: balance;
}
.no-results-message {
text-wrap: balance;
}
.no-results-fine-print {
max-inline-size: 60ch;
font-size: 14px;
margin-bottom: 32px;
text-align: center;
color: var(--vp-c-text-2);
text-wrap: balance;
}
.text-divider {
@@ -97,4 +264,10 @@ onMounted(() => {
font-size: 16px;
color: var(--vp-c-neutral);
}
.divider {
margin: 24px auto 18px;
width: 64px;
height: 1px;
background-color: var(--vp-c-divider);
}
</style>

View File

@@ -1,75 +1,72 @@
<script setup lang="ts">
import { shallowRef, type Ref, watch, computed } from 'vue'
import { useCssVar, syncRef } from '@vueuse/core'
import { STYLE_DEFAULTS, useIconStyleContext } from '../../composables/useIconStyle'
import RangeSlider from '../base/RangeSlider.vue'
import InputField from '../base/InputField.vue'
import ColorPicker from '../base/ColorPicker.vue'
import ResetButton from '../base/ResetButton.vue'
import Switch from '../base/Switch.vue'
import { shallowRef, type Ref, watch, computed } from 'vue';
import { useCssVar, syncRef } from '@vueuse/core';
import { STYLE_DEFAULTS, useIconStyleContext } from '../../composables/useIconStyle';
import RangeSlider from '../base/RangeSlider.vue';
import InputField from '../base/InputField.vue';
import ColorPicker from '../base/ColorPicker.vue';
import ResetButton from '../base/ResetButton.vue';
import Switch from '../base/Switch.vue';
const props = defineProps<{
rootEl?: Ref<HTMLElement>
}>()
rootEl?: Ref<HTMLElement>;
}>();
const { color, strokeWidth, size, absoluteStrokeWidth } = useIconStyleContext()
const documentRef = shallowRef<HTMLElement | undefined>(typeof document !== 'undefined' ? document?.documentElement : undefined)
const { color, strokeWidth, size, absoluteStrokeWidth } = useIconStyleContext();
const documentRef = shallowRef<HTMLElement | undefined>(
typeof document !== 'undefined' ? document?.documentElement : undefined,
);
const colorCssVar = useCssVar(
'--customize-color',
props.rootEl?.value ?? documentRef.value,
{
initialValue: `${STYLE_DEFAULTS.color}`
}
)
const colorCssVar = useCssVar('--customize-color', props.rootEl?.value ?? documentRef.value, {
initialValue: `${STYLE_DEFAULTS.color}`,
});
const strokeWidthCssVar = useCssVar(
'--customize-strokeWidth',
props.rootEl?.value ?? documentRef.value,
{
initialValue: `${STYLE_DEFAULTS.strokeWidth}`
}
)
initialValue: `${STYLE_DEFAULTS.strokeWidth}`,
},
);
const sizeCssVar = useCssVar(
'--customize-size',
props.rootEl?.value ?? documentRef.value,
{
initialValue: `${STYLE_DEFAULTS.size}`
}
)
const sizeCssVar = useCssVar('--customize-size', props.rootEl?.value ?? documentRef.value, {
initialValue: `${STYLE_DEFAULTS.size}`,
});
syncRef(color, colorCssVar, { direction: 'ltr' })
syncRef(strokeWidth, strokeWidthCssVar, { direction: 'ltr' })
syncRef(size, sizeCssVar, { direction: 'ltr' })
syncRef(color, colorCssVar, { direction: 'ltr' });
syncRef(strokeWidth, strokeWidthCssVar, { direction: 'ltr' });
syncRef(size, sizeCssVar, { direction: 'ltr' });
function resetStyle () {
color.value = STYLE_DEFAULTS.color
strokeWidth.value = STYLE_DEFAULTS.strokeWidth
size.value = STYLE_DEFAULTS.size
absoluteStrokeWidth.value = STYLE_DEFAULTS.absoluteStrokeWidth
function resetStyle() {
color.value = STYLE_DEFAULTS.color;
strokeWidth.value = STYLE_DEFAULTS.strokeWidth;
size.value = STYLE_DEFAULTS.size;
absoluteStrokeWidth.value = STYLE_DEFAULTS.absoluteStrokeWidth;
}
watch(absoluteStrokeWidth, (enabled) => {
const htmlEl = document.documentElement
const htmlEl = document.documentElement;
htmlEl.classList.toggle('absolute-stroke-width', enabled)
})
htmlEl.classList.toggle('absolute-stroke-width', enabled);
});
const customizingActive = computed(() => {
return color.value !== STYLE_DEFAULTS.color
|| strokeWidth.value !== STYLE_DEFAULTS.strokeWidth
|| size.value !== STYLE_DEFAULTS.size
|| absoluteStrokeWidth.value !== STYLE_DEFAULTS.absoluteStrokeWidth
})
return (
color.value !== STYLE_DEFAULTS.color ||
strokeWidth.value !== STYLE_DEFAULTS.strokeWidth ||
size.value !== STYLE_DEFAULTS.size ||
absoluteStrokeWidth.value !== STYLE_DEFAULTS.absoluteStrokeWidth
);
});
</script>
<template>
<div class="customizer-card" :class="{ customized: customizingActive }">
<div
class="customizer-card"
:class="{ customized: customizingActive }"
>
<div class="card-header">
<h2 class="card-title">
Customizer
</h2>
<h2 class="card-title">Customizer</h2>
<ResetButton @click="resetStyle"></ResetButton>
</div>
<InputField
@@ -77,7 +74,11 @@ const customizingActive = computed(() => {
label="Color"
>
<template #display>
<ColorPicker v-model="color" id="icon-color" class="color-picker"/>
<ColorPicker
v-model="color"
id="icon-color"
class="color-picker"
/>
</template>
</InputField>
@@ -117,7 +118,7 @@ const customizingActive = computed(() => {
<InputField
id="absolute-stroke-width"
label="Absolute Stroke width"
label="Absolute stroke width"
>
<Switch
id="absolute-stroke-width"
@@ -143,6 +144,7 @@ const customizingActive = computed(() => {
font-size: 16px;
/* margin-bottom: 12px; */
}
.customizer-card {
background: var(--vp-c-bg);
padding: 12px 24px 24px;
@@ -151,7 +153,7 @@ const customizingActive = computed(() => {
position: relative;
z-index: 0;
border: 1px solid transparent;
transition: border-color .4s ease-in-out;
transition: border-color 0.4s ease-in-out;
}
.customizer-card.customized {

View File

@@ -0,0 +1,41 @@
import { ref, Ref, watch } from 'vue';
import BRAND_STOPWORDS from '../../data/brandStopwords.json' with { type: 'json' };
export default function useSearchPlaceholder(
searchQuery: Ref<string, string>,
results: Ref<{ name: string }[]>,
) {
const state = ref({
isNoResults: false,
isBrand: false,
query: '',
});
watch(
results,
() => {
const query = searchQuery.value;
const searchResults = results.value;
if (query.length > 0 && searchResults.length === 0) {
for (const stopword of Object.keys(BRAND_STOPWORDS)) {
if (stopword.startsWith(query)) {
state.value = {
isNoResults: true,
isBrand: true,
query: BRAND_STOPWORDS[stopword],
};
return;
}
}
}
state.value = {
isNoResults: query in BRAND_STOPWORDS && searchResults.length === 0 && query !== '',
isBrand: query in BRAND_STOPWORDS,
query: BRAND_STOPWORDS[query] ?? query,
};
},
{ immediate: true },
);
return state;
}

View File

@@ -1,4 +1,4 @@
import { IconNode } from 'lucide-vue-next/src/createLucideIcon';
import { type IconNode } from 'lucide-vue-next/src/types';
import Vue from 'vue';
declare module '*.vue' {
@@ -20,5 +20,6 @@ declare module 'node:module' {
}
declare module '*.node.json' {
export default IconNode;
const value: IconNode;
export default value;
}

View File

@@ -64,25 +64,24 @@ Implementation of the lucide icon library for Vue applications.
::: code-group
```sh [pnpm]
pnpm add lucide-vue-next
pnpm add @lucide/vue
```
```sh [yarn]
yarn add lucide-vue-next
yarn add @lucide/vue
```
```sh [npm]
npm install lucide-vue-next
npm install @lucide/vue
```
```sh [bun]
bun add lucide-vue-next
bun add @lucide/vue
```
:::
For more details, see the [documentation](packages/lucide-vue-next.md).
For Vue 2 use the `lucide-vue` package.
For more details, see the [documentation](packages/lucide-vue.md).
## Svelte
@@ -91,22 +90,22 @@ Implementation of the lucide icon library for Svelte applications.
::: code-group
```sh [pnpm]
pnpm add lucide-svelte
pnpm add @lucide/svelte
```
```sh [yarn]
yarn add lucide-svelte
yarn add @lucide/svelte
```
```sh [npm]
npm install lucide-svelte
npm install @lucide/svelte
```
```sh [bun]
bun add lucide-svelte
bun add @lucide/svelte
```
:::
> `@lucide/svelte` is only for Svelte 5, for Svelte 4 use the `lucide-svelte` package.
For more details, see the [documentation](packages/lucide-svelte.md).

View File

@@ -30,14 +30,7 @@ This package includes the following implementations of Lucide icons:
SVG sprites and icon fonts include **all icons**, which can significantly increase your app's bundle size and load time.
For production environments, we recommend using a bundler with tree-shaking support to include only the icons you actually use. Consider using:
- [lucide](lucide)
- [lucide-react](lucide-react)
- [lucide-vue](lucide-vue)
- [lucide-vue-next](lucide-vue-next)
- [lucide-angular](lucide-angular)
- [lucide-preact](lucide-preact)
For production environments, we recommend using a bundler with tree-shaking support to include only the icons you actually use. Consider using one of the framework-specific [packages](../../packages).
:::
## Installation

View File

@@ -1,148 +0,0 @@
# Lucide Vue Next
Vue 3 components for Lucide icons that leverage the Composition API and modern Vue features. Each icon is a reactive Vue component that renders as an inline SVG, providing excellent performance and developer experience in Vue 3 applications.
**What you can accomplish:**
- Use icons as Vue 3 components with full reactivity and TypeScript support
- Bind icon properties to reactive data and computed values
- Customize icons with props, slots, and Vue's powerful templating system
- Integrate seamlessly with Vue 3's Composition API and script setup syntax
- Build dynamic interfaces where icons respond to application state changes
## Installation
::: code-group
```sh [pnpm]
pnpm add lucide-vue-next
```
```sh [yarn]
yarn add lucide-vue-next
```
```sh [npm]
npm install lucide-vue-next
```
```sh [bun]
bun add lucide-vue-next
```
:::
## How to use
Lucide is built with ES Modules, so it's completely tree-shakable.
Each icon can be imported as a Vue component, which renders an inline SVG Element. This way only the icons that are imported into your project are included in the final bundle. The rest of the icons are tree-shaken away.
### Example
You can pass additional props to adjust the icon.
```vue
<script setup>
import { Camera } from 'lucide-vue-next';
</script>
<template>
<Camera
color="red"
:size="32"
/>
</template>
```
## Props
| name | type | default |
| ----------------------- | --------- | ------------ |
| `size` | *number* | 24 |
| `color` | *string* | currentColor |
| `stroke-width` | *number* | 2 |
| `absoluteStrokeWidth` | *boolean* | false |
| `default-class` | *string* | lucide-icon |
### Applying props
To customize the appearance of an icon, you can pass custom properties as props directly to the component. The component accepts all SVG attributes as props, which allows flexible styling of the SVG elements. See the list of SVG Presentation Attributes on [MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Presentation).
```vue
<template>
<Camera fill="red" />
</template>
```
## With Lucide lab or custom icons
[Lucide lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
They can be used by using the `Icon` component.
All props like regular lucide icons can be passed to adjust the icon appearance.
### Using the `Icon` component
This creates a single icon based on the iconNode passed and renders a Lucide icon component.
```vue
<script setup>
import { Icon } from 'lucide-vue-next';
import { baseball } from '@lucide/lab';
</script>
<template>
<Icon :iconNode="baseball" />
</template>
```
## One generic icon component
It is possible to create one generic icon component to load icons, but it is not recommended.
::: danger
The example below imports all ES Modules, so exercise caution when using it. Importing all icons will significantly increase the build size of the application, negatively affecting its performance. This is especially important when using bundlers like `Webpack`, `Rollup`, or `Vite`.
:::
### Icon Component Example
```vue
<script setup>
import { computed } from 'vue';
import * as icons from "lucide-vue-next";
const props = defineProps({
name: {
type: String,
required: true
},
size: Number,
color: String,
strokeWidth: Number,
defaultClass: String
})
const icon = computed(() => icons[props.name]);
</script>
<template>
<component
:is="icon"
:size="size"
:color="color"
:stroke-width="strokeWidth" :default-class="defaultClass"
/>
</template>
```
### Using the Icon Component
All other props listed above also work on the `Icon` Component.
```vue
<template>
<div id="app">
<Icon name="Airplay" />
</div>
</template>
```

View File

@@ -9,28 +9,24 @@ Vue 2 components for Lucide icons that integrate with Vue's Options API and temp
- Build applications using Vue 2's familiar syntax and patterns
- Bridge the gap while planning migration to Vue 3
::: danger
This package is deprecated. Vue 2 is EOF See [Announcement](https://v2.vuejs.org/eol/). Migrate to Vue 3.
:::
## Installation
::: code-group
```sh [pnpm]
pnpm add lucide-vue
pnpm add @lucide/vue
```
```sh [yarn]
yarn add lucide-vue
yarn add @lucide/vue
```
```sh [npm]
npm install lucide-vue
npm install @lucide/vue
```
```sh [bun]
bun add lucide-vue
bun add @lucide/vue
```
:::
@@ -43,21 +39,19 @@ Each icon can be imported as a Vue component, which renders an inline SVG Elemen
### Example
Additional props can be passed to adjust the icon:
You can pass additional props to adjust the icon.
```vue
<template>
<Camera color="red" :size="32" />
</template>
<script>
import { Camera } from 'lucide-vue';
export default {
name: 'My Component',
components: { Camera }
};
<script setup>
import { Camera } from '@lucide/vue';
</script>
<template>
<Camera
color="red"
:size="32"
/>
</template>
```
## Props
@@ -80,6 +74,28 @@ To customize the appearance of an icon, you can pass custom properties as props
</template>
```
## With Lucide lab or custom icons
[Lucide lab](https://github.com/lucide-icons/lucide-lab) is a collection of icons that are not part of the Lucide main library.
They can be used by using the `Icon` component.
All props like regular lucide icons can be passed to adjust the icon appearance.
### Using the `Icon` component
This creates a single icon based on the iconNode passed and renders a Lucide icon component.
```vue
<script setup>
import { Icon } from '@lucide/vue';
import { baseball } from '@lucide/lab';
</script>
<template>
<Icon :iconNode="baseball" />
</template>
```
## One generic icon component
It is possible to create one generic icon component to load icons, but it is not recommended.
@@ -91,30 +107,37 @@ The example below imports all ES Modules, so exercise caution when using it. Imp
### Icon Component Example
```vue
<template>
<component :is="icon" />
</template>
<script setup>
import { computed } from 'vue';
import * as icons from "@lucide/vue";
<script>
import * as icons from 'lucide-vue';
const props = defineProps({
name: {
type: String,
required: true
},
size: Number,
color: String,
strokeWidth: Number,
defaultClass: String
})
export default {
props: {
name: {
type: String,
required: true
}
},
computed: {
icon() {
return icons[this.name];
}
}
};
const icon = computed(() => icons[props.name]);
</script>
<template>
<component
:is="icon"
:size="size"
:color="color"
:stroke-width="strokeWidth" :default-class="defaultClass"
/>
</template>
```
#### Using the Icon Component
### Using the Icon Component
All other props listed above also work on the `Icon` Component.
```vue
<template>

View File

@@ -2,18 +2,23 @@ import { IconEntity } from '../../.vitepress/theme/types';
export default {
paths: async () => {
const iconDetailsResponse = await fetch('https://lab.lucide.dev/api/icon-details');
const iconDetails = (await iconDetailsResponse.json()) as Record<string, IconEntity>;
try {
const iconDetailsResponse = await fetch('https://lab.lucide.dev/api/icon-details');
const iconDetails = (await iconDetailsResponse.json()) as Record<string, IconEntity>;
return Object.values(iconDetails).map((iconEntity) => {
const params = {
externalLibrary: 'lab',
...iconEntity,
};
return Object.values(iconDetails).map((iconEntity) => {
const params = {
externalLibrary: 'lab',
...iconEntity,
};
return {
params,
};
});
return {
params,
};
});
} catch (error) {
console.error('Error fetching icon details:', error);
return [];
}
},
};

View File

@@ -9,13 +9,14 @@
"docs:build": "pnpm run /^prebuild:.*/ && vitepress build",
"docs:preview": "vitepress preview",
"build:docs": "vitepress build",
"prebuild:iconNodes": "node --experimental-strip-types ./scripts/writeIconNodes.mjs",
"prebuild:metaJson": "node --experimental-strip-types ./scripts/writeIconMetaIndex.mjs",
"prebuild:releaseJson": "node --experimental-strip-types ./scripts/writeReleaseMetadata.mjs",
"prebuild:categoriesJson": "node --experimental-strip-types ./scripts/writeCategoriesMetadata.mjs",
"prebuild:relatedIcons": "node --experimental-strip-types ./scripts/writeIconRelatedIcons.mjs",
"prebuild:iconDetails": "node --experimental-strip-types ./scripts/writeIconDetails.mjs",
"postbuild:vercelJson": "node --experimental-strip-types ./scripts/writeVercelOutput.mjs",
"prebuild:iconNodes": "node ./scripts/writeIconNodes.mjs",
"prebuild:metaJson": "node ./scripts/writeIconMetaIndex.mjs",
"prebuild:releaseJson": "node ./scripts/writeReleaseMetadata.mjs",
"prebuild:categoriesJson": "node ./scripts/writeCategoriesMetadata.mjs",
"prebuild:relatedIcons": "node ./scripts/writeIconRelatedIcons.mjs",
"prebuild:iconDetails": "node ./scripts/writeIconDetails.mjs",
"prebuild:brandStopwords": "node ./scripts/writeBrandStopwords.mjs",
"postbuild:vercelJson": "node ./scripts/writeVercelOutput.mjs",
"dev": "npx nitropack dev",
"prebuild:api": "npx nitropack prepare",
"build:api": "npx nitropack build",
@@ -28,35 +29,35 @@
"@lucide/build-icons": "workspace:*",
"@lucide/helpers": "workspace:*",
"@lucide/shared": "workspace:*",
"@rollup/plugin-replace": "^6.0.2",
"@types/semver": "^7.5.3",
"@rollup/plugin-replace": "^6.0.3",
"@types/semver": "^7.7.1",
"nitropack": "2.8.1",
"rollup-plugin-copy": "^3.5.0",
"vitepress": "^1.6.3",
"svg-path-commander": "^2.1.11"
"svg-path-commander": "^2.1.11",
"vitepress": "^1.6.4"
},
"dependencies": {
"@floating-ui/vue": "^1.0.3",
"@headlessui/vue": "^1.7.17",
"@floating-ui/vue": "^1.1.9",
"@headlessui/vue": "^1.7.23",
"@resvg/resvg-wasm": "^2.6.2",
"@vueuse/components": "^12.0.0",
"@vueuse/core": "^12.0.0",
"@vueuse/components": "^14.0.0",
"@vueuse/core": "^14.0.0",
"element-to-path": "^1.2.1",
"fuse.js": "^6.5.3",
"jszip": "^3.7.0",
"lodash": "^4.17.20",
"fuse.js": "^7.1.0",
"jszip": "^3.10.1",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"lucide-react": "workspace:*",
"lucide-vue-next": "workspace:*",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"sandpack-vue3": "3.1.11",
"semver": "^7.5.2",
"shikiji": "^0.7.4",
"simple-git": "^3.18.0",
"sitemap": "^7.1.1",
"semver": "^7.7.3",
"shiki": "^3.15.0",
"simple-git": "^3.30.0",
"sitemap": "^7.1.2",
"svg-pathdata": "^6.0.3",
"svgson": "^5.2.1",
"vue": "^3.5.18"
"svgson": "^5.3.1",
"vue": "^3.5.24"
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 -166 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<g fill="#CC0000">
<path d="M219.115365,150.633753 C219.115365,150.633753 241.684635,148.699244 241.684635,119.552645 C241.684635,90.4060453 214.343577,87.697733 214.343577,87.697733 L165.078086,87.697733 L165.078086,179.651385 L189.839798,179.651385 L189.839798,157.469018 L211.248363,179.651385 L247.875063,179.651385 L219.115365,150.633753 Z M209.571788,131.804534 L189.839798,131.804534 L189.839798,110.782872 L209.700756,110.782872 C209.700756,110.782872 215.246348,112.846348 215.246348,121.229219 C215.246348,129.612091 209.571788,131.804534 209.571788,131.804534 Z M302.815113,88.3425693 L277.666499,88.3425693 C259.74005,88.3425693 253.678589,104.592443 253.678589,112.330479 L253.678589,179.651385 L278.827204,179.651385 L278.827204,163.530479 L302.428212,163.530479 L302.428212,179.651385 L326.803023,179.651385 L326.803023,112.330479 C326.803023,92.7274559 309.005542,88.3425693 302.815113,88.3425693 Z M302.428212,137.479093 L278.698237,137.479093 L278.698237,115.167758 C278.698237,115.167758 278.698237,110.138035 286.565239,110.138035 L295.206045,110.138035 C302.170277,110.138035 302.299244,115.167758 302.299244,115.167758 L302.299244,137.479093 L302.428212,137.479093 Z M338.281108,88.3425693 L364.461461,88.3425693 L364.461461,179.651385 L338.281108,179.651385 L338.281108,88.3425693 Z M401.217128,156.050378 L401.217128,88.3425693 L375.165743,88.3425693 L375.165743,156.050378 L375.165743,179.651385 L401.217128,179.651385 L436.425189,179.651385 L436.425189,156.050378 L401.217128,156.050378 Z M445.065995,156.308312 L445.065995,179.651385 L487.238287,179.651385 C495.879093,179.651385 510.710327,173.33199 511.226196,155.663476 L511.226196,146.635768 C511.226196,131.546599 498.84534,122.647859 487.238287,122.647859 L466.216625,122.647859 L466.216625,111.81461 L507.873048,111.81461 L507.873048,88.3425693 L467.893199,88.3425693 C457.575819,88.3425693 443.776322,96.8544081 443.776322,112.71738 L443.776322,120.842317 C443.776322,136.70529 457.446851,144.830227 467.893199,144.830227 C496.910831,144.959194 460.928967,144.830227 487.754156,144.830227 L487.754156,156.179345 L445.065995,156.308312 Z">
</path>
<path d="M9.02770781,179.522418 L110.911839,179.522418 C110.911839,179.522418 91.4377834,90.6639798 155.921411,54.6821159 C169.978841,47.8468514 214.730479,22.311335 287.983879,76.4775819 C290.30529,74.543073 292.497733,72.995466 292.497733,72.995466 C292.497733,72.995466 225.434761,6.06146096 150.76272,13.5415617 C113.233249,16.8947103 67.0629723,51.0710327 39.9798489,96.2095718 C12.8967254,141.348111 9.02770781,179.522418 9.02770781,179.522418 Z M221.307809,20.763728 L221.823678,12.1229219 C220.662972,11.4780856 217.438791,9.93047859 209.313854,7.60906801 L208.797985,16.1209068 C213.053904,17.5395466 217.180856,19.0871537 221.307809,20.763728 Z M209.055919,48.1047859 L208.54005,56.2297229 C212.79597,56.3586902 217.051889,56.8745592 221.307809,57.77733 L221.823678,49.7813602 C217.438791,48.8785894 213.182872,48.3627204 209.055919,48.1047859 Z M161.338035,7.86700252 L162.627708,7.86700252 L160.048363,-7.10542736e-15 C156.050378,-7.10542736e-15 151.923426,0.257934509 147.667506,0.773803526 L150.117884,8.38287154 C153.857935,7.99596977 157.597985,7.86700252 161.338035,7.86700252 Z M167.528463,55.3269521 L170.49471,64.2256927 C174.234761,62.4201511 177.974811,60.8725441 181.714861,59.7118388 L178.877582,51.2 C174.492695,52.4896725 170.752645,53.9083123 167.528463,55.3269521 Z M108.97733,21.4085642 L103.173804,12.5098237 C99.9496222,14.186398 96.5964736,15.9919395 93.1143577,18.0554156 L99.0468514,27.0831234 C102.4,25.0196474 105.624181,23.0851385 108.97733,21.4085642 Z M135.415617,79.4438287 L141.606045,88.729471 C143.798489,85.5052897 146.377834,82.5390428 149.215113,79.572796 L143.411587,70.8030227 C140.44534,73.511335 137.737028,76.4775819 135.415617,79.4438287 Z M116.715365,120.971285 L127.161713,129.225189 C127.677582,124.195466 128.580353,119.165743 129.870025,114.13602 L120.584383,106.784887 C118.907809,111.556675 117.747103,116.328463 116.715365,120.971285 Z M60.2277078,59.7118388 L51.0710327,51.715869 C47.7178841,54.9400504 44.4937028,58.1642317 41.5274559,61.3884131 L51.4579345,69.9002519 C54.1662469,66.418136 57.1324937,62.9360202 60.2277078,59.7118388 Z M21.279597,117.360202 L6.44836272,111.943577 C3.99798489,117.489169 1.28967254,123.937531 0,127.419647 L14.8312343,132.836272 C16.5078086,128.451385 19.2161209,122.13199 21.279597,117.360202 Z M114.780856,153.728967 C115.038791,160.564232 115.683627,166.109824 116.328463,169.978841 L131.804534,175.524433 C130.643829,170.49471 129.483123,164.820151 128.70932,158.75869 L114.780856,153.728967 Z">
</path>

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -0,0 +1,15 @@
import fs from 'fs/promises';
import path from 'path';
const currentDir = process.cwd();
const dataDirectory = path.resolve(currentDir, '.vitepress/data');
const stopwordsSource = path.resolve(currentDir, `../brand-stopwords.json`);
const stopwordsFile = path.resolve(dataDirectory, `brandStopwords.json`);
fs.copyFile(stopwordsSource, stopwordsFile)
.then(() => {
console.log('Successfully copied brandStopwords.json file');
})
.catch((error) => {
throw new Error(`Something went wrong generating the brandStopwords.json file,\n ${error}`);
});

View File

@@ -34,6 +34,11 @@
],
"destination": "/icons",
"permanent": false
},
{
"source": "/guide/packages/lucide-vue-next",
"destination": "/guide/packages/lucide-vue",
"permanent": false
}
],
"headers": [

View File

@@ -56,7 +56,6 @@
"account",
"animals",
"arrows",
"brands",
"buildings",
"charts",
"communication",
@@ -134,7 +133,7 @@
"$defs": {
"iconDeprecationReasons": {
"type": "string",
"enum": ["icon.brand"]
"enum": ["icon.renamed"]
},
"aliasDeprecationReasons": {
"type": "string",

View File

@@ -16,8 +16,6 @@
],
"categories": [
"multimedia",
"connectivity",
"devices",
"brands"
"connectivity"
]
}

View File

@@ -2,7 +2,8 @@
"$schema": "../icon.schema.json",
"contributors": [
"danielbayley",
"karsa-mistmere"
"karsa-mistmere",
"jguddas"
],
"tags": [
"and",

View File

@@ -9,6 +9,6 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M17.5 12c0 4.4-3.6 8-8 8A4.5 4.5 0 0 1 5 15.5c0-6 8-4 8-8.5a3 3 0 1 0-6 0c0 3 2.5 8.5 12 13" />
<path d="M16 12h3" />
<path d="M17.5 12a8 8 0 0 1-8 8A4.5 4.5 0 0 1 5 15.5c0-6 8-4 8-8.5a3 3 0 1 0-6 0c0 3 2.5 8.5 12 13" />
</svg>

Before

Width:  |  Height:  |  Size: 339 B

After

Width:  |  Height:  |  Size: 337 B

View File

@@ -3,7 +3,8 @@
"contributors": [
"colebemis",
"csandman",
"ericfennis"
"ericfennis",
"karsa-mistmere"
],
"tags": [
"ship"

View File

@@ -9,7 +9,8 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 22V8" />
<path d="M5 12H2a10 10 0 0 0 20 0h-3" />
<circle cx="12" cy="5" r="3" />
<path d="M12 6v16" />
<path d="m19 13 2-1a9 9 0 0 1-18 0l2 1" />
<path d="M9 11h6" />
<circle cx="12" cy="4" r="2" />
</svg>

Before

Width:  |  Height:  |  Size: 309 B

After

Width:  |  Height:  |  Size: 334 B

View File

@@ -15,7 +15,6 @@
],
"categories": [
"arrows",
"navigation",
"gaming",
"files"
]

View File

@@ -15,7 +15,6 @@
],
"categories": [
"arrows",
"navigation",
"gaming"
]
}

View File

@@ -15,7 +15,6 @@
],
"categories": [
"arrows",
"navigation",
"gaming"
]
}

View File

@@ -17,7 +17,6 @@
],
"categories": [
"arrows",
"navigation",
"gaming"
]
}

View File

@@ -15,7 +15,6 @@
],
"categories": [
"arrows",
"navigation",
"gaming"
]
}

View File

@@ -17,7 +17,6 @@
],
"categories": [
"arrows",
"navigation",
"gaming"
]
}

View File

@@ -20,7 +20,6 @@
],
"categories": [
"arrows",
"navigation",
"text",
"development",
"gaming"

View File

@@ -20,7 +20,6 @@
],
"categories": [
"arrows",
"navigation",
"text",
"development",
"gaming"

View File

@@ -16,7 +16,6 @@
],
"categories": [
"arrows",
"navigation",
"files"
]
}

View File

@@ -10,7 +10,6 @@
"diagonal"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}

View File

@@ -10,7 +10,6 @@
"diagonal"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}

View File

@@ -12,7 +12,6 @@
"into"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}

View File

@@ -19,7 +19,6 @@
],
"categories": [
"arrows",
"navigation",
"files",
"development"
]

View File

@@ -19,7 +19,6 @@
"move"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}

View File

@@ -11,7 +11,6 @@
"south"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}

View File

@@ -15,7 +15,6 @@
"<-|"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}

View File

@@ -17,7 +17,6 @@
"->"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}

View File

@@ -15,7 +15,6 @@
"|<-"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}

View File

@@ -12,7 +12,6 @@
"<-"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}

View File

@@ -16,7 +16,6 @@
"|->"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}

View File

@@ -16,7 +16,6 @@
"->"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}

View File

@@ -20,7 +20,6 @@
],
"categories": [
"arrows",
"navigation",
"development"
]
}

View File

@@ -12,7 +12,6 @@
"->"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}

View File

@@ -19,7 +19,6 @@
"move"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}

View File

@@ -10,7 +10,6 @@
"out"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}

View File

@@ -18,7 +18,6 @@
],
"categories": [
"arrows",
"navigation",
"files",
"development"
]

View File

@@ -10,7 +10,6 @@
"diagonal"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}

View File

@@ -10,7 +10,6 @@
"diagonal"
],
"categories": [
"arrows",
"navigation"
"arrows"
]
}

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