Compare commits

..

44 Commits

Author SHA1 Message Date
Eric Fennis
5236cc3072 Merge branch 'main' of https://github.com/lucide-icons/lucide into fix-stable-code-points 2025-12-18 15:59:50 +01:00
Eric Fennis
3c3b25202a Make code point consistent 2025-12-18 12:48:57 +01:00
David Castilla Ortiz
69bf052ee5 Enable ligatures in font build configuration (#3876) 2025-12-18 12:17:28 +01:00
Eric Fennis
4efc8e07a0 adds addLigatures to config 2025-12-18 12:16:00 +01:00
Eric Fennis
5be50c241c Merge branch 'main' of https://github.com/lucide-icons/lucide into fix-stable-code-points 2025-12-18 12:15:22 +01:00
Eric Fennis
506f3652c3 Adds next solution after build 2025-12-18 12:14:28 +01:00
Karsa
6b4075b89b feat(icons): added toolbox icon (#3871)
* Added icons/toolbox.svg

* Added icons/toolbox.json
2025-12-18 11:44:47 +01:00
Jacek Tomaszewski
7a68e10b12 fix(lucide-react-native): remove icons namespace export to enable tree-shaking (#3868)
* fix(lucide-react-native): remove icons namespace export to enable tree-shaking

The `export * as icons from './icons'` statement defeats tree-shaking
because bundlers cannot determine which exports from the namespace are
actually used at build time. This causes all 1600+ icons to be included
in the final bundle even when only a few are imported.

This change removes the namespace re-export while keeping all individual
icon exports available via `export * from './icons'`.

BREAKING CHANGE: The `icons` namespace export is no longer available.
Users should import icons directly: `import { Activity } from 'lucide-react-native'`
instead of `import { icons } from 'lucide-react-native'; icons.Activity`.

* Add icons entry file to improve treeshaking

* Format code

---------

Co-authored-by: Eric Fennis <eric.fennis@gmail.com>
2025-12-18 11:44:26 +01:00
Jakob Guddas
a4531a9985 fix(react-native-web): only add className prop to parent Icon component (#3892) 2025-12-18 11:43:31 +01:00
Eric Fennis
23b85f7834 Fixing code points build 2025-12-18 10:58:14 +01:00
Eric Fennis
9392a8f84f update lockfile 2025-12-18 10:17:14 +01:00
Eric Fennis
115fb243af Merge branch 'main' of https://github.com/lucide-icons/lucide into fix-stable-code-points 2025-12-18 10:16:46 +01:00
taimar
3edcd9e0c3 fix and unify color-picker font-size (#3889) 2025-12-15 14:59:14 +01:00
Jakob Guddas
0b8f99326c fix(icons): changed paint-bucket icon (#3880)
* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.svg

* Updated icons/paint-bucket.json

* Updated icons/paint-bucket.json
2025-12-12 13:27:37 +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
Eric Fennis
5408bc1d69 Adjust workflow 2025-12-10 14:13:56 +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
efa795aa4c Merge branch 'main' of https://github.com/lucide-icons/lucide into fix-stable-code-points 2025-12-10 13:27:12 +01:00
Eric Fennis
ba46fcf4fc Add lucide-font to gitignore 2025-12-10 13:27:03 +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
Eric Fennis
484984ad68 Refactor font building 2025-12-10 10:45:52 +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
83 changed files with 3164 additions and 903 deletions

View File

@@ -11,6 +11,9 @@ permissions:
id-token: write # Required for OIDC
contents: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
create-release:
if: github.repository == 'lucide-icons/lucide' && startsWith(github.event.head_commit.message, 'feat(icons)')
@@ -36,25 +39,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: |
@@ -68,38 +65,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@v6
- 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

@@ -20,7 +20,10 @@ on:
permissions:
id-token: write # Required for OIDC
contents: read
contents: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
pre-release:
@@ -72,9 +75,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
@@ -109,9 +109,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
@@ -141,11 +138,8 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Outline svg Icons
run: pnpm build:outline-icons
- name: Create font in ./lucide-font
run: pnpm build:font
run: pnpm build:font --saveCodePoints
- name: 'Upload to Artifacts'
uses: actions/upload-artifact@v4
@@ -157,7 +151,9 @@ 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@v6
- uses: actions/download-artifact@v4

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

1
.gitignore vendored
View File

@@ -14,6 +14,7 @@ coverage
stats
*.log
outlined
lucide-font
packages/**/src/icons/*.js
packages/**/src/icons/*.ts
packages/**/src/icons/*.tsx

1
.npmrc
View File

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

View File

@@ -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>\
`,
},
{

View File

@@ -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>\
`,
},
{

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

@@ -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,27 +54,33 @@ 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;
font-size: 13px;
text-align: left;
border: 1px solid transparent;
cursor: text;
display: flex;
align-items: center;
gap: 2px;
transition:
color 0.25s,
border-color 0.25s,
background-color 0.25s;
}
.color-input-text {
@@ -75,19 +90,22 @@ const value = computed({
border: none;
background: transparent;
color: var(--vp-c-text-1);
font-size: 14px;
font-size: 13px;
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

@@ -22,16 +22,16 @@ export default {
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

@@ -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

@@ -29,7 +29,7 @@ export default function useSearchPlaceholder(
}
}
state.value = {
isNoResults: query in BRAND_STOPWORDS || (searchResults.length === 0 && query !== ''),
isNoResults: query in BRAND_STOPWORDS && searchResults.length === 0 && query !== '',
isBrand: query in BRAND_STOPWORDS,
query: BRAND_STOPWORDS[query] ?? query,
};

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

@@ -102,10 +102,16 @@ The example below imports all ES Modules, so exercise caution when using it. Imp
### Icon Component Example
```jsx
import { icons } from 'lucide-react-native';
```tsx
import * as icons from 'lucide-react-native/icons';
const Icon = ({ name, color, size }) => {
interface IconProps {
name: keyof typeof icons;
color?: string;
size?: number;
}
const Icon = ({ name, color, size }: IconProps) => {
const LucideIcon = icons[name];
return <LucideIcon color={color} size={size} />;
@@ -116,11 +122,11 @@ export default Icon;
#### Using the Icon Component
```jsx
```tsx
import Icon from './Icon';
const App = () => {
return <Icon name="house" />;
return <Icon name="House" />;
};
export default App;

View File

@@ -9,14 +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",
"prebuild:brandStopwords": "node --experimental-strip-types ./scripts/writeBrandStopwords.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",

22
icons/balloon.json Normal file
View File

@@ -0,0 +1,22 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"peteruithoven"
],
"tags": [
"party",
"festival",
"congratulations",
"celebration",
"decoration",
"colorful",
"floating",
"fun",
"birthday",
"event",
"entertainment"
],
"categories": [
"emoji"
]
}

15
icons/balloon.svg Normal file
View File

@@ -0,0 +1,15 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 16v1a2 2 0 0 0 2 2h1a2 2 0 0 1 2 2v1" />
<path d="M12 6a2 2 0 0 1 2 2" />
<path d="M18 8c0 4-3.5 8-6 8s-6-4-6-8a6 6 0 0 1 12 0" />
</svg>

After

Width:  |  Height:  |  Size: 358 B

View File

@@ -1,7 +1,8 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"karsa-mistmere"
"karsa-mistmere",
"jguddas"
],
"tags": [
"cleaning",

View File

@@ -10,7 +10,7 @@
stroke-linejoin="round"
>
<path d="m16 22-1-4" />
<path d="M19 13.99a1 1 0 0 0 1-1V12a2 2 0 0 0-2-2h-3a1 1 0 0 1-1-1V4a2 2 0 0 0-4 0v5a1 1 0 0 1-1 1H6a2 2 0 0 0-2 2v.99a1 1 0 0 0 1 1" />
<path d="M5 14h14l1.973 6.767A1 1 0 0 1 20 22H4a1 1 0 0 1-.973-1.233z" />
<path d="M19 14a1 1 0 0 0 1-1v-1a2 2 0 0 0-2-2h-3a1 1 0 0 1-1-1V4a2 2 0 0 0-4 0v5a1 1 0 0 1-1 1H6a2 2 0 0 0-2 2v1a1 1 0 0 0 1 1" />
<path d="M19 14H5l-1.973 6.767A1 1 0 0 0 4 22h16a1 1 0 0 0 .973-1.233z" />
<path d="m8 22 1-4" />
</svg>

Before

Width:  |  Height:  |  Size: 474 B

After

Width:  |  Height:  |  Size: 470 B

14
icons/cannabis-off.json Normal file
View File

@@ -0,0 +1,14 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"nickveles"
],
"tags": [
"cannabis",
"weed",
"leaf"
],
"categories": [
"nature"
]
}

18
icons/cannabis-off.svg Normal file
View File

@@ -0,0 +1,18 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 22v-4c1.5 1.5 3.5 3 6 3 0-1.5-.5-3.5-2-5" />
<path d="M13.988 8.327C13.902 6.054 13.365 3.82 12 2a9.3 9.3 0 0 0-1.445 2.9" />
<path d="M17.375 11.725C18.882 10.53 21 7.841 21 6c-2.324 0-5.08 1.296-6.662 2.684" />
<path d="m2 2 20 20" />
<path d="M21.024 15.378A15 15 0 0 0 22 15c-.426-1.279-2.67-2.557-4.25-2.907" />
<path d="M6.995 6.992C5.714 6.4 4.29 6 3 6c0 2 2.5 5 4 6-1.5 0-4.5 1.5-5 3 3.5 1.5 6 1 6 1-1.5 1.5-2 3.5-2 5 2.5 0 4.5-1.5 6-3" />
</svg>

After

Width:  |  Height:  |  Size: 681 B

29
icons/circle-pile.json Normal file
View File

@@ -0,0 +1,29 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"colebemis",
"nathan-de-pachtere"
],
"tags": [
"off",
"zero",
"record",
"shape",
"circle-pile",
"circle",
"pile",
"stack",
"layer",
"structure",
"form",
"group",
"collection",
"stock",
"inventory",
"materials",
"warehouse"
],
"categories": [
"shapes"
]
}

9
icons/circle-pile.svg Normal file
View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="19" r="2" />
<circle cx="12" cy="5" r="2" />
<circle cx="16" cy="12" r="2" />
<circle cx="20" cy="19" r="2" />
<circle cx="4" cy="19" r="2" />
<circle cx="8" cy="12" r="2" />
</svg>

After

Width:  |  Height:  |  Size: 397 B

33
icons/cloud-backup.json Normal file
View File

@@ -0,0 +1,33 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"ericfennis",
"jguddas",
"danielbayley",
"karsa-mistmere"
],
"tags": [
"storage",
"memory",
"bytes",
"servers",
"backup",
"timemachine",
"rotate",
"synchronize",
"synchronise",
"refresh",
"reconnect",
"transfer",
"data",
"security",
"upload",
"save",
"remote",
"safety"
],
"categories": [
"arrows",
"files"
]
}

15
icons/cloud-backup.svg Normal file
View File

@@ -0,0 +1,15 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M21 15.251A4.5 4.5 0 0 0 17.5 8h-1.79A7 7 0 1 0 3 13.607" />
<path d="M7 11v4h4" />
<path d="M8 19a5 5 0 0 0 9-3 4.5 4.5 0 0 0-4.5-4.5 4.82 4.82 0 0 0-3.41 1.41L7 15" />
</svg>

After

Width:  |  Height:  |  Size: 393 B

27
icons/cloud-sync.json Normal file
View File

@@ -0,0 +1,27 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"colebemis",
"csandman",
"ericfennis",
"karsa-mistmere"
],
"tags": [
"synchronize",
"synchronise",
"refresh",
"reconnect",
"transfer",
"backup",
"storage",
"upload",
"download",
"connection",
"network",
"data"
],
"categories": [
"arrows",
"files"
]
}

17
icons/cloud-sync.svg Normal file
View File

@@ -0,0 +1,17 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m17 18-1.535 1.605a5 5 0 0 1-8-1.5" />
<path d="M17 22v-4h-4" />
<path d="M20.996 15.251A4.5 4.5 0 0 0 17.495 8h-1.79a7 7 0 1 0-12.709 5.607" />
<path d="M7 10v4h4" />
<path d="m7 14 1.535-1.605a5 5 0 0 1 8 1.5" />
</svg>

After

Width:  |  Height:  |  Size: 442 B

28
icons/fishing-hook.json Normal file
View File

@@ -0,0 +1,28 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"7ender",
"jguddas",
"karsa-mistmere",
"jamiemlaw"
],
"tags": [
"sea",
"boating",
"angler",
"bait",
"reel",
"tackle",
"marine",
"outdoors",
"fish",
"fishing",
"hook",
"sports",
"travel"
],
"categories": [
"sports",
"travel"
]
}

15
icons/fishing-hook.svg Normal file
View File

@@ -0,0 +1,15 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m17.586 11.414-5.93 5.93a1 1 0 0 1-8-8l3.137-3.137a.707.707 0 0 1 1.207.5V10" />
<path d="M20.414 8.586 22 7" />
<circle cx="19" cy="10" r="2" />
</svg>

After

Width:  |  Height:  |  Size: 369 B

21
icons/hd.json Normal file
View File

@@ -0,0 +1,21 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"ahtohbi4",
"jamiemlaw",
"karsa-mistmere",
"jguddas"
],
"tags": [
"tv",
"resolution",
"video",
"high definition",
"720p",
"1080p"
],
"categories": [
"devices",
"multimedia"
]
}

17
icons/hd.svg Normal file
View File

@@ -0,0 +1,17 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M10 12H6" />
<path d="M10 15V9" />
<path d="M14 14.5a.5.5 0 0 0 .5.5h1a2.5 2.5 0 0 0 2.5-2.5v-1A2.5 2.5 0 0 0 15.5 9h-1a.5.5 0 0 0-.5.5z" />
<path d="M6 15V9" />
<rect x="2" y="5" width="20" height="14" rx="2" />
</svg>

After

Width:  |  Height:  |  Size: 440 B

22
icons/layers-plus.json Normal file
View File

@@ -0,0 +1,22 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"juanisidoro",
"karsa-mistmere"
],
"tags": [
"stack",
"layers",
"add",
"new",
"increase",
"create",
"positive",
"copy",
"upgrade"
],
"categories": [
"design",
"layout"
]
}

17
icons/layers-plus.svg Normal file
View File

@@ -0,0 +1,17 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 .83.18 2 2 0 0 0 .83-.18l8.58-3.9a1 1 0 0 0 0-1.831z" />
<path d="M16 17h6" />
<path d="M19 14v6" />
<path d="M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 .825.178" />
<path d="M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l2.116-.962" />
</svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@@ -9,13 +9,15 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M6 19v-3" />
<path d="M10 19v-3" />
<path d="M14 19v-3" />
<path d="M18 19v-3" />
<path d="M8 11V9" />
<path d="M16 11V9" />
<path d="M12 11V9" />
<path d="M2 15h20" />
<path d="M2 7a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v1.1a2 2 0 0 0 0 3.837V17a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-5.1a2 2 0 0 0 0-3.837Z" />
<path d="M12 12v-2" />
<path d="M12 18v-2" />
<path d="M16 12v-2" />
<path d="M16 18v-2" />
<path d="M2 11h1.5" />
<path d="M20 18v-2" />
<path d="M20.5 11H22" />
<path d="M4 18v-2" />
<path d="M8 12v-2" />
<path d="M8 18v-2" />
<rect x="2" y="6" width="20" height="10" rx="2" />
</svg>

Before

Width:  |  Height:  |  Size: 532 B

After

Width:  |  Height:  |  Size: 510 B

View File

@@ -9,15 +9,14 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M10 12h4" />
<path d="M10 17h4" />
<path d="M10 7h4" />
<path d="M18 12h2" />
<path d="M18 16h2" />
<path d="M18 20h2" />
<path d="M18 4h2" />
<path d="M18 8h2" />
<path d="M18 18h2" />
<path d="M18 6h2" />
<path d="M4 12h2" />
<path d="M4 16h2" />
<path d="M4 20h2" />
<path d="M4 4h2" />
<path d="M4 8h2" />
<path d="M8 2a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2h-1.5c-.276 0-.494.227-.562.495a2 2 0 0 1-3.876 0C9.994 2.227 9.776 2 9.5 2z" />
<path d="M4 18h2" />
<path d="M4 6h2" />
<rect x="6" y="2" width="12" height="20" rx="2" />
</svg>

Before

Width:  |  Height:  |  Size: 598 B

After

Width:  |  Height:  |  Size: 471 B

View File

@@ -9,8 +9,8 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m19 11-8-8-8.6 8.6a2 2 0 0 0 0 2.8l5.2 5.2c.8.8 2 .8 2.8 0L19 11Z" />
<path d="m5 2 5 5" />
<path d="M2 13h15" />
<path d="M22 20a2 2 0 1 1-4 0c0-1.6 1.7-2.4 2-4 .3 1.6 2 2.4 2 4Z" />
<path d="M11 7 6 2" />
<path d="M18.992 12H2.041" />
<path d="M21.145 18.38A3.34 3.34 0 0 1 20 16.5a3.3 3.3 0 0 1-1.145 1.88c-.575.46-.855 1.02-.855 1.595A2 2 0 0 0 20 22a2 2 0 0 0 2-2.025c0-.58-.285-1.13-.855-1.595" />
<path d="m8.5 4.5 2.148-2.148a1.205 1.205 0 0 1 1.704 0l7.296 7.296a1.205 1.205 0 0 1 0 1.704l-7.592 7.592a3.615 3.615 0 0 1-5.112 0l-3.888-3.888a3.615 3.615 0 0 1 0-5.112L5.67 7.33" />
</svg>

Before

Width:  |  Height:  |  Size: 409 B

After

Width:  |  Height:  |  Size: 622 B

25
icons/search-alert.json Normal file
View File

@@ -0,0 +1,25 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"colebemis",
"ericfennis",
"jguddas",
"Veatec22"
],
"tags": [
"find",
"scan",
"magnifier",
"magnifying glass",
"stop",
"warning",
"alert",
"error",
"anomaly",
"lens"
],
"categories": [
"text",
"social"
]
}

16
icons/search-alert.svg Normal file
View File

@@ -0,0 +1,16 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="11" cy="11" r="8" />
<path d="m21 21-4.3-4.3" />
<path d="M11 7v4" />
<path d="M11 15h.01" />
</svg>

After

Width:  |  Height:  |  Size: 321 B

24
icons/stone.json Normal file
View File

@@ -0,0 +1,24 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"Alportan",
"karsa-mistmere"
],
"tags": [
"mineral",
"geology",
"nature",
"solid",
"pebble",
"crystal",
"ore",
"hard",
"coal",
"stone",
"rock",
"boulder"
],
"categories": [
"nature"
]
}

15
icons/stone.svg Normal file
View File

@@ -0,0 +1,15 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M11.264 2.205A4 4 0 0 0 6.42 4.211l-4 8a4 4 0 0 0 1.359 5.117l6 4a4 4 0 0 0 4.438 0l6-4a4 4 0 0 0 1.576-4.592l-2-6a4 4 0 0 0-2.53-2.53z" />
<path d="M11.99 22 14 12l7.822 3.184" />
<path d="M14 12 8.47 2.302" />
</svg>

After

Width:  |  Height:  |  Size: 435 B

View File

@@ -9,6 +9,6 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M17 14V2" />
<path d="M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22a3.13 3.13 0 0 1-3-3.88Z" />
<path d="M17 14V2" />
</svg>

Before

Width:  |  Height:  |  Size: 399 B

After

Width:  |  Height:  |  Size: 399 B

View File

@@ -9,6 +9,6 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M7 10v12" />
<path d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z" />
<path d="M7 10v12" />
</svg>

Before

Width:  |  Height:  |  Size: 400 B

After

Width:  |  Height:  |  Size: 400 B

View File

@@ -1,7 +1,8 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"jguddas"
"jguddas",
"karsa-mistmere"
],
"tags": [
"plane",

View File

@@ -11,7 +11,7 @@
>
<path d="M10.5 17h1.227a2 2 0 0 0 1.345-.52L18 12" />
<path d="m12 13.5 3.75.5" />
<path d="m4.5 8 10.58-5.06a1 1 0 0 1 1.342.488L18.5 8" />
<path d="m3.173 8.18 11-5a2 2 0 0 1 2.647.993L18.56 8" />
<path d="M6 10V8" />
<path d="M6 14v1" />
<path d="M6 19v2" />

Before

Width:  |  Height:  |  Size: 477 B

After

Width:  |  Height:  |  Size: 477 B

View File

@@ -1,7 +1,8 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"jguddas"
"jguddas",
"karsa-mistmere"
],
"tags": [
"trip",

View File

@@ -9,7 +9,7 @@
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m4.5 8 10.58-5.06a1 1 0 0 1 1.342.488L18.5 8" />
<path d="m3.173 8.18 11-5a2 2 0 0 1 2.647.993L18.56 8" />
<path d="M6 10V8" />
<path d="M6 14v1" />
<path d="M6 19v2" />

Before

Width:  |  Height:  |  Size: 390 B

After

Width:  |  Height:  |  Size: 390 B

37
icons/toolbox.json Normal file
View File

@@ -0,0 +1,37 @@
{
"$schema": "../icon.schema.json",
"contributors": [
"karsa-mistmere"
],
"tags": [
"toolkit",
"tools",
"trunk",
"chest",
"box",
"storage",
"utility",
"utilities",
"container",
"kit",
"set",
"repair",
"fix",
"service",
"maintenance",
"mechanic",
"workshop",
"construction",
"hardware",
"equipment",
"gear",
"handyman",
"engineering",
"craft",
"diy"
],
"categories": [
"tools",
"home"
]
}

17
icons/toolbox.svg Normal file
View File

@@ -0,0 +1,17 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M16 12v4" />
<path d="M16 6a2 2 0 0 1 1.414.586l4 4A2 2 0 0 1 22 12v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 .586-1.414l4-4A2 2 0 0 1 8 6z" />
<path d="M16 6V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2" />
<path d="M2 14h20" />
<path d="M8 12v4" />
</svg>

After

Width:  |  Height:  |  Size: 471 B

View File

@@ -7,8 +7,8 @@ const filenamesToAjvOption = (filenames) => filenames.map((filename) => `-d ${fi
/** @satisfies {import('lint-staged').Config} */
const config = {
'icons/*.svg': [
'node ./scripts/optimizeStagedSvgs.mjs',
'node ./scripts/generateNextJSAliases.mjs',
'node ./scripts/optimizeStagedSvgs.mts',
'node ./scripts/generateNextJSAliases.mts',
],
'icons/*.json': (filenames) => [
`ajv --spec=draft2020 -s icon.schema.json ${filenamesToAjvOption(filenames)}`,

View File

View File

@@ -16,7 +16,7 @@
"lucide-svelte": "pnpm --filter lucide-svelte",
"lucide-static": "pnpm --filter lucide-static",
"build:outline-icons": "pnpm --filter outline-svg start",
"build:font": "pnpm --filter docs prebuild:releaseJson && pnpm --filter build-font start",
"build:font": "pnpm --filter build-font start",
"optimize": "node ./scripts/optimizeSvgs.mts",
"addjsons": "node ./scripts/addMissingIconJsonFiles.mts",
"checkIcons": "node ./scripts/checkIconsAndCategories.mts",
@@ -73,9 +73,9 @@
"zod": "^3.25.67"
},
"engines": {
"node": ">=23.0.0"
"node": ">=24.11.1"
},
"packageManager": "pnpm@10.23.0+sha512.21c4e5698002ade97e4efe8b8b4a89a8de3c85a37919f957e7a0f30f38fbc5bbdd05980ffe29179b2fb6e6e691242e098d945d1601772cad0fef5fb6411e2a4b",
"packageManager": "pnpm@10.24.0+sha512.01ff8ae71b4419903b65c60fb2dc9d34cf8bb6e06d03bde112ef38f7a34d6904c424ba66bea5cdcf12890230bf39f9580473140ed9c946fef328b6e5238a345a",
"pnpm": {
"packageExtensions": {
"vue-template-compiler": {

View File

@@ -24,11 +24,23 @@
"author": "Eric Fennis",
"amdName": "lucide-react-native",
"main": "dist/cjs/lucide-react-native.js",
"main:umd": "dist/umd/lucide-react-native.js",
"module": "dist/esm/lucide-react-native.js",
"unpkg": "dist/umd/lucide-react-native.min.js",
"typings": "dist/lucide-react-native.d.ts",
"react-native": "dist/esm/lucide-react-native.js",
"exports": {
".": {
"types": "./dist/lucide-react-native.d.ts",
"import": "./dist/esm/lucide-react-native.js",
"browser": "./dist/esm/lucide-react-native.js",
"require": "./dist/cjs/lucide-react-native.js"
},
"./icons": {
"types": "./dist/icons.d.ts",
"import": "./dist/esm/icons/index.js",
"browser": "./dist/esm/icons/index.js",
"require": "./dist/cjs/icons/index.js"
}
},
"sideEffects": false,
"files": [
"dist"

View File

@@ -5,7 +5,7 @@ import pkg from './package.json' with { type: 'json' };
const packageName = 'LucideReact';
const outputFileName = 'lucide-react-native';
const outputDir = 'dist';
const inputs = ['src/lucide-react-native.ts'];
const inputs = ['src/lucide-react-native.ts', 'src/icons/index.ts'];
const bundles = [
{
format: 'cjs',
@@ -60,6 +60,16 @@ export default [
],
plugins: [dts()],
},
{
input: inputs[1],
output: [
{
file: `dist/icons.d.ts`,
format: 'es',
},
],
plugins: [dts()],
},
{
input: `src/${outputFileName}.suffixed.ts`,
output: [

View File

@@ -31,6 +31,7 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
absoluteStrokeWidth,
children,
iconNode,
className,
...rest
},
ref,
@@ -46,6 +47,7 @@ const Icon = forwardRef<SVGSVGElement, IconComponentProps>(
{
ref,
...defaultAttributes,
className,
width: size,
height: size,
...customAttrs,

View File

@@ -1,5 +1,4 @@
export * from './icons';
export * as icons from './icons';
export * from './aliases/prefixed';
export * from './types';

View File

@@ -1,5 +1,4 @@
export * from './icons';
export * as icons from './icons';
export * from './aliases/suffixed';
export * from './types';

View File

@@ -1,5 +1,4 @@
export * from './icons';
export * as icons from './icons';
export * from './aliases';
export * from './types';

143
pnpm-lock.yaml generated
View File

@@ -199,7 +199,7 @@ importers:
version: 7.7.1
nitropack:
specifier: 2.8.1
version: 2.8.1(db0@0.3.4)(encoding@0.1.13)(ioredis@5.8.2)(xml2js@0.6.2)
version: 2.8.1(@vercel/blob@2.0.0)(db0@0.3.4)(encoding@0.1.13)(ioredis@5.8.2)(xml2js@0.6.2)
rollup-plugin-copy:
specifier: ^3.5.0
version: 3.5.0
@@ -226,7 +226,7 @@ importers:
version: 6.9.1
astro:
specifier: ^5.16.0
version: 5.16.0(@types/node@24.10.1)(db0@0.3.4)(ioredis@5.8.2)(jiti@2.6.1)(less@4.2.0)(rollup@4.53.3)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(typescript@5.9.3)(yaml@2.8.0)
version: 5.16.0(@types/node@24.10.1)(@vercel/blob@2.0.0)(db0@0.3.4)(ioredis@5.8.2)(jiti@2.6.1)(less@4.2.0)(rollup@4.53.3)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(typescript@5.9.3)(yaml@2.8.0)
jest-serializer-html:
specifier: ^7.1.0
version: 7.1.0
@@ -813,10 +813,16 @@ importers:
minimist:
specifier: ^1.2.8
version: 1.2.8
oslllo-svg-fixer:
specifier: ^5.0.0
version: 5.0.0(encoding@0.1.13)
svgtofont:
specifier: ^6.5.0
version: 6.5.0(@types/svg2ttf@5.0.1)(chokidar@3.6.0)
devDependencies:
'@lucide/helpers':
specifier: workspace:*
version: link:../build-helpers
'@types/fs-extra':
specifier: ^11.0.4
version: 11.0.4
@@ -858,15 +864,6 @@ importers:
specifier: ^22
version: 22.19.1
tools/outline-svg:
dependencies:
minimist:
specifier: ^1.2.8
version: 1.2.8
oslllo-svg-fixer:
specifier: ^5.0.0
version: 5.0.0(encoding@0.1.13)
tools/rollup-plugins:
dependencies:
'@rollup/plugin-node-resolve':
@@ -5612,6 +5609,10 @@ packages:
cpu: [x64]
os: [win32]
'@vercel/blob@2.0.0':
resolution: {integrity: sha512-oAj7Pdy83YKSwIaMFoM7zFeLYWRc+qUpW3PiDSblxQMnGFb43qs4bmfq7dr/+JIfwhs6PTwe1o2YBwKhyjWxXw==}
engines: {node: '>=20.0.0'}
'@vercel/nft@0.24.4':
resolution: {integrity: sha512-KjYAZty7boH5fi5udp6p+lNu6nawgs++pHW+3koErMgbRkkHuToGX/FwjN5clV1FcaM3udfd4zW/sUapkMgpZw==}
engines: {node: '>=16'}
@@ -6167,6 +6168,9 @@ packages:
async-limiter@1.0.1:
resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==}
async-retry@1.3.3:
resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==}
async-sema@3.1.1:
resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==}
@@ -8112,10 +8116,6 @@ packages:
resolution: {integrity: sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==}
engines: {node: '>=8'}
fast-glob@3.3.2:
resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
engines: {node: '>=8.6.0'}
fast-glob@3.3.3:
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
engines: {node: '>=8.6.0'}
@@ -8880,6 +8880,10 @@ packages:
is-buffer@1.1.6:
resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
is-buffer@2.0.5:
resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==}
engines: {node: '>=4'}
is-builtin-module@3.2.1:
resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==}
engines: {node: '>=6'}
@@ -8978,6 +8982,9 @@ packages:
resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
engines: {node: '>= 0.4'}
is-node-process@1.2.0:
resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==}
is-number-object@1.0.7:
resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
engines: {node: '>= 0.4'}
@@ -12382,6 +12389,10 @@ packages:
throat@5.0.0:
resolution: {integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==}
throttleit@2.1.0:
resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==}
engines: {node: '>=18'}
through2@2.0.5:
resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
@@ -16704,14 +16715,14 @@ snapshots:
dependencies:
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
'@types/node': 24.10.1
'@types/node': 22.19.1
jest-mock: 29.7.0
'@jest/fake-timers@29.7.0':
dependencies:
'@jest/types': 29.6.3
'@sinonjs/fake-timers': 10.3.0
'@types/node': 24.10.1
'@types/node': 22.19.1
jest-message-util: 29.7.0
jest-mock: 29.7.0
jest-util: 29.7.0
@@ -16745,7 +16756,7 @@ snapshots:
'@jest/schemas': 29.6.3
'@types/istanbul-lib-coverage': 2.0.4
'@types/istanbul-reports': 3.0.1
'@types/node': 24.10.1
'@types/node': 22.19.1
'@types/yargs': 17.0.35
chalk: 4.1.2
@@ -18452,11 +18463,11 @@ snapshots:
'@types/body-parser@1.19.5':
dependencies:
'@types/connect': 3.4.38
'@types/node': 12.20.55
'@types/node': 22.19.1
'@types/bonjour@3.5.13':
dependencies:
'@types/node': 12.20.55
'@types/node': 22.19.1
'@types/chai@5.2.2':
dependencies:
@@ -18465,17 +18476,17 @@ snapshots:
'@types/connect-history-api-fallback@1.5.4':
dependencies:
'@types/express-serve-static-core': 4.19.5
'@types/node': 12.20.55
'@types/node': 22.19.1
'@types/connect@3.4.38':
dependencies:
'@types/node': 12.20.55
'@types/node': 22.19.1
'@types/cookie@0.4.1': {}
'@types/cors@2.8.17':
dependencies:
'@types/node': 12.20.55
'@types/node': 22.19.1
'@types/debug@4.1.12':
dependencies:
@@ -18503,7 +18514,7 @@ snapshots:
'@types/express-serve-static-core@4.19.5':
dependencies:
'@types/node': 12.20.55
'@types/node': 22.19.1
'@types/qs': 6.9.15
'@types/range-parser': 1.2.7
'@types/send': 0.17.4
@@ -18517,7 +18528,7 @@ snapshots:
'@types/fontkit@2.0.8':
dependencies:
'@types/node': 24.10.1
'@types/node': 22.19.1
'@types/fs-extra@11.0.4':
dependencies:
@@ -18531,11 +18542,11 @@ snapshots:
'@types/glob@7.2.0':
dependencies:
'@types/minimatch': 6.0.0
'@types/node': 24.10.1
'@types/node': 22.19.1
'@types/graceful-fs@4.1.9':
dependencies:
'@types/node': 24.10.1
'@types/node': 22.19.1
'@types/hast@3.0.4':
dependencies:
@@ -18596,7 +18607,7 @@ snapshots:
'@types/node-forge@1.3.11':
dependencies:
'@types/node': 12.20.55
'@types/node': 22.19.1
'@types/node@12.20.55': {}
@@ -18635,7 +18646,7 @@ snapshots:
'@types/resolve@1.17.1':
dependencies:
'@types/node': 12.20.55
'@types/node': 22.19.1
'@types/resolve@1.20.2': {}
@@ -18650,7 +18661,7 @@ snapshots:
'@types/send@0.17.4':
dependencies:
'@types/mime': 1.3.5
'@types/node': 12.20.55
'@types/node': 22.19.1
'@types/serve-index@1.9.4':
dependencies:
@@ -18659,12 +18670,12 @@ snapshots:
'@types/serve-static@1.15.7':
dependencies:
'@types/http-errors': 2.0.4
'@types/node': 12.20.55
'@types/node': 22.19.1
'@types/send': 0.17.4
'@types/sockjs@0.3.36':
dependencies:
'@types/node': 12.20.55
'@types/node': 22.19.1
'@types/stack-utils@2.0.1': {}
@@ -18677,7 +18688,7 @@ snapshots:
'@types/ws@8.5.12':
dependencies:
'@types/node': 12.20.55
'@types/node': 22.19.1
'@types/yargs-parser@21.0.3': {}
@@ -18947,6 +18958,15 @@ snapshots:
'@unrs/resolver-binding-win32-x64-msvc@1.6.2':
optional: true
'@vercel/blob@2.0.0':
dependencies:
async-retry: 1.3.3
is-buffer: 2.0.5
is-node-process: 1.2.0
throttleit: 2.1.0
undici: 5.28.5
optional: true
'@vercel/nft@0.24.4(encoding@0.1.13)':
dependencies:
'@mapbox/node-pre-gyp': 1.0.11(encoding@0.1.13)
@@ -19591,7 +19611,7 @@ snapshots:
dependencies:
tslib: 2.8.1
astro@5.16.0(@types/node@24.10.1)(db0@0.3.4)(ioredis@5.8.2)(jiti@2.6.1)(less@4.2.0)(rollup@4.53.3)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(typescript@5.9.3)(yaml@2.8.0):
astro@5.16.0(@types/node@24.10.1)(@vercel/blob@2.0.0)(db0@0.3.4)(ioredis@5.8.2)(jiti@2.6.1)(less@4.2.0)(rollup@4.53.3)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(typescript@5.9.3)(yaml@2.8.0):
dependencies:
'@astrojs/compiler': 2.13.0
'@astrojs/internal-helpers': 0.7.5
@@ -19646,7 +19666,7 @@ snapshots:
ultrahtml: 1.6.0
unifont: 0.6.0
unist-util-visit: 5.0.0
unstorage: 1.17.3(db0@0.3.4)(ioredis@5.8.2)
unstorage: 1.17.3(@vercel/blob@2.0.0)(db0@0.3.4)(ioredis@5.8.2)
vfile: 6.0.3
vite: 6.4.1(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0)
vitefu: 1.1.1(vite@6.4.1(@types/node@24.10.1)(jiti@2.6.1)(less@4.2.0)(sass@1.77.8)(stylus@0.56.0)(terser@5.44.1)(yaml@2.8.0))
@@ -19697,6 +19717,11 @@ snapshots:
async-limiter@1.0.1: {}
async-retry@1.3.3:
dependencies:
retry: 0.13.1
optional: true
async-sema@3.1.1: {}
async@2.6.4:
@@ -20260,7 +20285,7 @@ snapshots:
chrome-launcher@0.15.2:
dependencies:
'@types/node': 24.10.1
'@types/node': 22.19.1
escape-string-regexp: 4.0.0
is-wsl: 2.2.0
lighthouse-logger: 1.4.2
@@ -20271,7 +20296,7 @@ snapshots:
chromium-edge-launcher@0.2.0:
dependencies:
'@types/node': 24.10.1
'@types/node': 22.19.1
escape-string-regexp: 4.0.0
is-wsl: 2.2.0
lighthouse-logger: 1.4.2
@@ -20503,7 +20528,7 @@ snapshots:
copy-webpack-plugin@10.2.1(webpack@5.76.1(@swc/core@1.7.23(@swc/helpers@0.5.17))(esbuild@0.14.22)):
dependencies:
fast-glob: 3.3.2
fast-glob: 3.3.3
glob-parent: 6.0.2
globby: 12.2.0
normalize-path: 3.0.0
@@ -21072,7 +21097,7 @@ snapshots:
dependencies:
'@types/cookie': 0.4.1
'@types/cors': 2.8.17
'@types/node': 12.20.55
'@types/node': 22.19.1
accepts: 1.3.8
base64id: 2.0.0
cookie: 0.4.2
@@ -21937,14 +21962,6 @@ snapshots:
merge2: 1.4.1
micromatch: 4.0.8
fast-glob@3.3.2:
dependencies:
'@nodelib/fs.stat': 2.0.5
'@nodelib/fs.walk': 1.2.8
glob-parent: 5.1.2
merge2: 1.4.1
micromatch: 4.0.8
fast-glob@3.3.3:
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -22380,7 +22397,7 @@ snapshots:
dependencies:
array-union: 3.0.1
dir-glob: 3.0.1
fast-glob: 3.3.2
fast-glob: 3.3.3
ignore: 5.3.2
merge2: 1.4.1
slash: 4.0.0
@@ -22884,6 +22901,9 @@ snapshots:
is-buffer@1.1.6: {}
is-buffer@2.0.5:
optional: true
is-builtin-module@3.2.1:
dependencies:
builtin-modules: 3.3.0
@@ -22960,6 +22980,9 @@ snapshots:
is-negative-zero@2.0.3: {}
is-node-process@1.2.0:
optional: true
is-number-object@1.0.7:
dependencies:
has-tostringtag: 1.0.2
@@ -23170,7 +23193,7 @@ snapshots:
dependencies:
'@jest/types': 29.6.3
'@types/graceful-fs': 4.1.9
'@types/node': 24.10.1
'@types/node': 22.19.1
anymatch: 3.1.3
fb-watchman: 2.0.2
graceful-fs: 4.2.11
@@ -23197,7 +23220,7 @@ snapshots:
jest-mock@29.7.0:
dependencies:
'@jest/types': 29.6.3
'@types/node': 24.10.1
'@types/node': 22.19.1
jest-util: 29.7.0
jest-regex-util@29.6.3: {}
@@ -23209,7 +23232,7 @@ snapshots:
jest-util@29.7.0:
dependencies:
'@jest/types': 29.6.3
'@types/node': 24.10.1
'@types/node': 22.19.1
chalk: 4.1.2
ci-info: 3.8.0
graceful-fs: 4.2.11
@@ -23226,13 +23249,13 @@ snapshots:
jest-worker@27.5.1:
dependencies:
'@types/node': 12.20.55
'@types/node': 22.19.1
merge-stream: 2.0.0
supports-color: 8.1.1
jest-worker@29.7.0:
dependencies:
'@types/node': 24.10.1
'@types/node': 22.19.1
jest-util: 29.7.0
merge-stream: 2.0.0
supports-color: 8.1.1
@@ -24637,7 +24660,7 @@ snapshots:
node-gyp-build: 4.8.4
optional: true
nitropack@2.8.1(db0@0.3.4)(encoding@0.1.13)(ioredis@5.8.2)(xml2js@0.6.2):
nitropack@2.8.1(@vercel/blob@2.0.0)(db0@0.3.4)(encoding@0.1.13)(ioredis@5.8.2)(xml2js@0.6.2):
dependencies:
'@cloudflare/kv-asset-handler': 0.3.4
'@netlify/functions': 2.8.2
@@ -24702,7 +24725,7 @@ snapshots:
unctx: 2.4.1
unenv: 1.10.0
unimport: 3.14.6(rollup@4.53.3)
unstorage: 1.17.3(db0@0.3.4)(ioredis@5.8.2)
unstorage: 1.17.3(@vercel/blob@2.0.0)(db0@0.3.4)(ioredis@5.8.2)
optionalDependencies:
xml2js: 0.6.2
transitivePeerDependencies:
@@ -25147,7 +25170,7 @@ snapshots:
dependencies:
ansi-colors: 4.1.3
cli-progress: 3.12.0
fast-glob: 3.3.2
fast-glob: 3.3.3
oslllo-potrace: 3.0.0(encoding@0.1.13)
oslllo-svg2: 3.0.0(encoding@0.1.13)
oslllo-validator: 3.1.0
@@ -27289,7 +27312,7 @@ snapshots:
stylus-loader@6.2.0(stylus@0.56.0)(webpack@5.76.1(@swc/core@1.7.23(@swc/helpers@0.5.17))(esbuild@0.14.22)):
dependencies:
fast-glob: 3.3.2
fast-glob: 3.3.3
klona: 2.0.6
normalize-path: 3.0.0
stylus: 0.56.0
@@ -27621,6 +27644,9 @@ snapshots:
throat@5.0.0: {}
throttleit@2.1.0:
optional: true
through2@2.0.5:
dependencies:
readable-stream: 2.3.8
@@ -28082,7 +28108,7 @@ snapshots:
'@unrs/resolver-binding-win32-ia32-msvc': 1.6.2
'@unrs/resolver-binding-win32-x64-msvc': 1.6.2
unstorage@1.17.3(db0@0.3.4)(ioredis@5.8.2):
unstorage@1.17.3(@vercel/blob@2.0.0)(db0@0.3.4)(ioredis@5.8.2):
dependencies:
anymatch: 3.1.3
chokidar: 4.0.3
@@ -28093,6 +28119,7 @@ snapshots:
ofetch: 1.5.1
ufo: 1.6.1
optionalDependencies:
'@vercel/blob': 2.0.0
db0: 0.3.4
ioredis: 5.8.2

File diff suppressed because it is too large Load Diff

View File

@@ -1,149 +0,0 @@
import { readJson } from 'fs-extra/esm';
import svgtofont from 'svgtofont';
import getArgumentOptions from 'minimist';
import path from 'path';
const fontName = 'lucide';
const classNamePrefix = 'icon';
const startUnicode = 57400;
const inputDir = path.join(process.cwd(), '../../', 'outlined');
const cliArguments = getArgumentOptions(process.argv.slice(2));
const { outputDir = 'lucide-font' } = cliArguments;
const targetDir = path.join(process.cwd(), '../../', outputDir);
const releaseMetaDataDir = path.join(process.cwd(), '../../', 'docs/.vitepress/data');
const releaseMetaDataPath = path.resolve(releaseMetaDataDir, 'releaseMetaData.json');
const releaseMetaData = convertReleaseMetaData(await getReleaseMetaData());
async function getReleaseMetaData() {
let releaseMetaData = {};
try {
releaseMetaData = await readJson(releaseMetaDataPath);
} catch (err) {
throw new Error('Execution stopped because no release information was found.');
}
return releaseMetaData;
}
type Releases = Record<string, ReleaseMetaData>;
type ReleaseMetaData = {
createdRelease: {
version: string;
date: string;
};
changedRelease: {
version: string;
date: string;
};
};
type ReleaseMetaDataWithName = ReleaseMetaData & {
name: string;
};
function convertReleaseMetaData(releases: Releases) {
return Object.entries(releases)
.map(([key, data]) => ({
...data,
name: key,
}))
.sort((a, b) => sortMultiple(a, b, [sortByCreatedReleaseDate, sortByName]))
.map((value, index) => ({ ...value, index }))
.map((value, index) => ({
...value,
unicode: index + startUnicode,
}));
}
type CollatorFunction = (a: ReleaseMetaDataWithName, b: ReleaseMetaDataWithName) => number;
function sortMultiple(
a: ReleaseMetaDataWithName,
b: ReleaseMetaDataWithName,
collators: CollatorFunction[] = [],
) {
const comparison = collators?.shift?.()?.(a, b) ?? 0;
if (comparison === 0 && collators.length > 0) return sortMultiple(a, b, collators);
return comparison;
}
function sortByCreatedReleaseDate(a: ReleaseMetaDataWithName, b: ReleaseMetaDataWithName) {
const [dateA, dateB] = [a, b].map((value) => new Date(value.createdRelease.date).valueOf());
return Number(dateA > dateB) - Number(dateA < dateB);
}
function sortByName(a: ReleaseMetaDataWithName, b: ReleaseMetaDataWithName) {
return new Intl.Collator('en-US').compare(a.name, b.name);
}
function getIconUnicode(name: string): [string, number] {
const { unicode } = releaseMetaData.find(({ name: iconName }) => iconName === name) ?? {
unicode: startUnicode,
};
return [String.fromCharCode(unicode), startUnicode];
}
async function init() {
console.time('Font generation');
try {
await svgtofont({
src: path.resolve(process.cwd(), inputDir),
dist: path.resolve(process.cwd(), targetDir),
// styleTemplates: path.resolve(process.cwd(), 'styles'), // Add different templates if needed
fontName,
classNamePrefix,
css: {
fontSize: 'inherit',
},
emptyDist: true,
useCSSVars: false,
outSVGReact: false,
outSVGPath: false,
svgicons2svgfont: {
fontHeight: 1000, // At least 1000 is recommended
normalize: false,
},
generateInfoData: true,
website: {
title: 'Lucide',
logo: undefined,
meta: {
description: 'Lucide icons as TTF/EOT/WOFF/WOFF2/SVG.',
keywords: 'Lucide,TTF,EOT,WOFF,WOFF2,SVG',
},
corners: {
url: 'https://github.com/lucide-icons/lucide',
width: 62, // default: 60
height: 62, // default: 60
bgColor: '#dc3545', // default: '#151513'
},
links: [
{
title: 'GitHub',
url: 'https://github.com/lucide-icons/lucide',
},
{
title: 'Feedback',
url: 'https://github.com/lucide-icons/lucide/issues',
},
{
title: 'Font Class',
url: 'index.html',
},
{
title: 'Unicode',
url: 'unicode.html',
},
],
},
getIconUnicode,
});
} catch (err) {
console.log(err);
}
console.timeEnd('Font generation');
}
init();

View File

@@ -6,7 +6,7 @@
"main": "main.ts",
"type": "module",
"scripts": {
"start": "node ./main.ts"
"start": "node ./src/main.ts"
},
"keywords": [],
"author": "",
@@ -14,9 +14,11 @@
"dependencies": {
"fs-extra": "^11.2.0",
"minimist": "^1.2.8",
"oslllo-svg-fixer": "^5.0.0",
"svgtofont": "^6.5.0"
},
"devDependencies": {
"@lucide/helpers": "workspace:*",
"@types/fs-extra": "^11.0.4",
"@types/minimist": "^1.2.5",
"@types/node": "^22"

View File

@@ -0,0 +1,60 @@
import { type IconAliases } from "@lucide/helpers";
import path from "path";
import { promises as fs } from 'fs';
import { cwd } from "process";
export type CodePoints = Record<string, number>;
async function getLatestCodePoints(): Promise<CodePoints> {
// This is for the first release where no codepoints.json exists yet
const codepointsContents = await fs.readFile(path.join(cwd(), 'codepoints.json'), 'utf-8')
return JSON.parse(codepointsContents) as CodePoints
// Next releases will use the codepoints.json from latest release in lucide-static.
// const codepointsContents = await fetch('https://unpkg.com/lucide-static@latest/font/codepoints.json')
// return codepointsContents.json() as Promise<CodePoints>
}
interface AllocateCodePointsOptions {
saveCodePoints?: boolean;
iconsWithAliases: IconAliases
}
export async function allocateCodePoints({
saveCodePoints = false,
iconsWithAliases
}: AllocateCodePointsOptions): Promise<CodePoints> {
const baseCodePoints = await getLatestCodePoints()
const endCodePoint = Math.max(...Object.values(baseCodePoints))
await Promise.all(
iconsWithAliases.map(async ([iconName, aliases]) => {
if(!baseCodePoints[iconName]) {
console.log('Code point not found creating new one for', iconName);
baseCodePoints[iconName] = endCodePoint + 1;
}
aliases.forEach((alias, index) => {
if (baseCodePoints[alias]) {
return;
}
console.log('Code point not found creating new one for');
baseCodePoints[alias] = endCodePoint + index + 1;
});
})
)
if (saveCodePoints) {
await fs.writeFile(
path.join(cwd(), 'codepoints.json'),
JSON.stringify(baseCodePoints, null, 2),
'utf-8'
);
}
return baseCodePoints;
}

View File

@@ -0,0 +1,86 @@
import svgtofont from 'svgtofont';
import { type CodePoints } from './allocateCodepoints.ts';
interface BuildFontOptions {
inputDir: string;
targetDir: string;
fontName: string;
classNamePrefix: string;
codePoints: CodePoints
startUnicode: number;
}
export async function buildFont({
inputDir,
targetDir,
fontName,
classNamePrefix,
codePoints,
startUnicode
}: BuildFontOptions) {
console.time('Font generation');
try {
await svgtofont({
src: inputDir,
dist: targetDir,
fontName,
classNamePrefix,
css: {
fontSize: 'inherit',
},
emptyDist: true,
useCSSVars: false,
outSVGReact: false,
outSVGPath: false,
addLigatures: true,
svgicons2svgfont: {
fontHeight: 1000, // At least 1000 is recommended
normalize: false,
},
generateInfoData: true,
website: {
title: 'Lucide',
logo: undefined,
meta: {
description: 'Lucide icons as TTF/EOT/WOFF/WOFF2/SVG.',
keywords: 'Lucide,TTF,EOT,WOFF,WOFF2,SVG',
},
corners: {
url: 'https://github.com/lucide-icons/lucide',
width: 62, // default: 60
height: 62, // default: 60
bgColor: '#dc3545', // default: '#151513'
},
links: [
{
title: 'GitHub',
url: 'https://github.com/lucide-icons/lucide',
},
{
title: 'Feedback',
url: 'https://github.com/lucide-icons/lucide/issues',
},
{
title: 'Font Class',
url: 'index.html',
},
{
title: 'Unicode',
url: 'unicode.html',
},
],
},
getIconUnicode: (name: string) => {
if (!codePoints[name]) {
throw new Error(`No codepoint found for icon: ${name}`);
}
const unicode = codePoints[name];
return [String.fromCharCode(unicode), startUnicode];
},
});
} catch (err) {
console.log(err);
}
console.timeEnd('Font generation');
}

View File

@@ -0,0 +1,15 @@
import { type IconAliases } from "@lucide/helpers";
import { type CodePoints } from "./allocateCodepoints.ts";
export function hasMissingCodePoints(iconsWithAliases: IconAliases, codePoints: CodePoints): boolean {
return iconsWithAliases.map(([iconName, aliases]) => ([iconName, ...aliases]))
.flat()
.some(name => {
if (!codePoints?.[name]) {
console.log(`Missing code point for icon/alias: ${name}`);
return true;
}
return false;
});
}

View File

@@ -0,0 +1,52 @@
import getArgumentOptions from 'minimist';
import path from 'path';
import { promises as fs } from 'fs';
import { getAllIconAliases } from '@lucide/helpers';
import { outlineSVG } from './outlineSVGs.ts';
import { allocateCodePoints } from './allocateCodepoints.ts';
import { buildFont } from './buildFont.ts';
import { hasMissingCodePoints } from './helpers.ts';
const fontName = 'lucide';
const classNamePrefix = 'icon';
const startUnicode = 57400;
const outputDir = 'lucide-font';
const {
saveCodePoints = false,
} = getArgumentOptions(process.argv.slice(2)) ?? {}
const repoRoot = path.join(process.cwd(), '../../')
const iconsDir = path.join(repoRoot, 'icons');
const outlinedDir = path.join(repoRoot, 'outlined');
const targetDir = path.join(repoRoot, outputDir);
const iconsWithAliases = await getAllIconAliases(iconsDir)
await outlineSVG({
iconsDir,
outlinedDir,
iconsWithAliases
});
const codePoints = await allocateCodePoints({
saveCodePoints,
iconsWithAliases
});
if (hasMissingCodePoints(iconsWithAliases, codePoints)) {
throw new Error('Some icons or aliases are missing code points. See log for details.');
}
await buildFont({
inputDir: outlinedDir,
targetDir,
fontName,
classNamePrefix,
codePoints,
startUnicode,
});
await fs.copyFile(path.join(process.cwd(), 'codepoints.json'), path.join(targetDir, 'codepoints.json'));

View File

@@ -0,0 +1,49 @@
import { promises as fs } from 'fs';
import SVGFixer from 'oslllo-svg-fixer';
import { getAllIconAliases, type IconAliases } from '@lucide/helpers';
import path from 'path';
interface OutlineSVGOptions {
iconsDir: string;
outlinedDir: string;
iconsWithAliases: IconAliases
}
export async function outlineSVG({
iconsDir,
outlinedDir,
iconsWithAliases
}: OutlineSVGOptions) {
console.time('icon outliner');
try {
try {
await fs.mkdir(outlinedDir);
} catch (error) { } // eslint-disable-line no-empty
await SVGFixer(iconsDir, outlinedDir, {
showProgressBar: true,
traceResolution: 800,
}).fix();
console.log('Duplicate icons with aliases..');
await Promise.all(iconsWithAliases.map(async ([iconName, aliases]) => {
const sourcePath = path.join(outlinedDir, `${iconName}.svg`);
await Promise.all(aliases.map(async (aliasName) => {
const destinationPath = path.join(outlinedDir, `${aliasName}.svg`);
try {
await fs.copyFile(sourcePath, destinationPath);
console.log(`Copied ${iconName}.svg to ${aliasName}.svg`);
} catch (err) {
console.log(`Failed to copy ${sourcePath} to ${destinationPath}:`, err);
}
}));
}));
console.timeEnd('icon outliner');
} catch (err) {
console.log(err);
}
}

View File

@@ -7,6 +7,7 @@ export * from './src/appendFile.ts';
export * from './src/writeFile.ts';
export * from './src/writeFileIfNotExists.ts';
export * from './src/readAllMetadata.ts';
export * from './src/getAllIconAliases.ts';
export * from './src/readMetadata.ts';
export * from './src/readSvgDirectory.ts';
export * from './src/readSvg.ts';

View File

@@ -0,0 +1,20 @@
import { readAllMetadata } from "./readAllMetadata.ts";
export type IconAliases = [iconName: string, aliases: string[]][];
export const getAllIconAliases = async (iconsDir: string): Promise<IconAliases> => {
const metaDataFiles = await readAllMetadata(iconsDir)
return Object.entries(metaDataFiles).map(([iconName, metadata]) => {
const { aliases } = metadata;
if (!aliases?.length) return [iconName, []];
const aliasesNames = aliases.map(alias =>
typeof alias === 'string' ? alias : alias.name,
);
return [iconName, aliasesNames]
})
}

View File

@@ -1,6 +1,7 @@
import fs from 'fs/promises';
import path from 'path';
import { readMetadata } from './readMetadata.ts';
import { type IconMetadata } from '../../build-icons/types.ts';
/**
* Reads metadata from the icons/categories directories
@@ -8,7 +9,7 @@ import { readMetadata } from './readMetadata.ts';
* @param {string} directory
* @returns {object} A map of icon or category metadata
*/
export const readAllMetadata = async (directory: string): Promise<Record<string, unknown>> => {
export const readAllMetadata = async (directory: string): Promise<Record<string, IconMetadata>> => {
const directoryContent = await fs.readdir(directory);
const metaDataPromises = directoryContent
@@ -16,6 +17,7 @@ export const readAllMetadata = async (directory: string): Promise<Record<string,
.map(async (file) => [path.basename(file, '.json'), await readMetadata(file, directory)]);
const metadata = await Promise.all(metaDataPromises);
if (metadata.length === 0) {
throw new Error(`No metadata files found in directory: ${directory}`);
}

View File

@@ -1,3 +0,0 @@
# @lucide/outline-svg
A internal used package to outline SVGs.

View File

@@ -1,29 +0,0 @@
import { promises as fs } from 'fs';
import SVGFixer from 'oslllo-svg-fixer';
import getArgumentOptions from 'minimist';
import path from 'path';
const inputDir = path.join(process.cwd(), '../../icons');
const cliArguments = getArgumentOptions(process.argv.slice(2));
const { outputDir = 'outlined' } = cliArguments;
const targetDir = path.join(process.cwd(), '../../', outputDir);
async function init() {
console.time('icon outliner');
try {
try {
await fs.mkdir(targetDir);
} catch (error) {} // eslint-disable-line no-empty
await SVGFixer(inputDir, targetDir, {
showProgressBar: true,
traceResolution: 800,
}).fix();
console.timeEnd('icon outliner');
} catch (err) {
console.log(err);
}
}
init();

View File

@@ -1,18 +0,0 @@
{
"name": "@lucide/outline-svg",
"description": "A internal used package to outline SVGs.",
"private": true,
"version": "2.0.0",
"main": "main.ts",
"type": "module",
"scripts": {
"start": "node ./main.ts"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"minimist": "^1.2.8",
"oslllo-svg-fixer": "^5.0.0"
}
}

View File

@@ -1,18 +0,0 @@
{
"compilerOptions": {
"strict": true,
"declaration": true,
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "node",
"module": "ESNext",
"target": "ESNext",
"esModuleInterop": true,
"lib": ["esnext"],
"resolveJsonModule": true,
"allowImportingTsExtensions": true,
"noEmit": true,
"sourceMap": true,
"outDir": "./dist",
},
}